From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/vgui2/chromehtml/chromewrapper.cpp | 882 +- mp/src/vgui2/chromehtml/html_chrome.cpp | 7016 ++++---- mp/src/vgui2/chromehtml/html_chrome.h | 1274 +- mp/src/vgui2/chromehtml/htmlmanager.h | 60 +- mp/src/vgui2/chromehtml/stdafx.h | 38 +- mp/src/vgui2/src/vgui_key_translation.cpp | 84 +- mp/src/vgui2/src/vgui_key_translation.h | 40 +- mp/src/vgui2/vgui_controls/AnalogBar.cpp | 918 +- mp/src/vgui2/vgui_controls/AnimatingImagePanel.cpp | 432 +- mp/src/vgui2/vgui_controls/AnimationController.cpp | 3300 ++-- mp/src/vgui2/vgui_controls/BitmapImagePanel.cpp | 728 +- mp/src/vgui2/vgui_controls/BuildFactoryHelper.cpp | 208 +- mp/src/vgui2/vgui_controls/BuildGroup.cpp | 2896 ++-- mp/src/vgui2/vgui_controls/BuildModeDialog.cpp | 2880 ++-- mp/src/vgui2/vgui_controls/Button.cpp | 2190 +-- mp/src/vgui2/vgui_controls/CheckButton.cpp | 410 +- mp/src/vgui2/vgui_controls/CheckButtonList.cpp | 424 +- mp/src/vgui2/vgui_controls/CircularProgressBar.cpp | 582 +- mp/src/vgui2/vgui_controls/ComboBox.cpp | 2082 +-- mp/src/vgui2/vgui_controls/ControllerMap.cpp | 320 +- .../vgui2/vgui_controls/DirectorySelectDialog.cpp | 1186 +- mp/src/vgui2/vgui_controls/Divider.cpp | 82 +- mp/src/vgui2/vgui_controls/EditablePanel.cpp | 2138 +-- mp/src/vgui2/vgui_controls/ExpandButton.cpp | 228 +- mp/src/vgui2/vgui_controls/FileOpenDialog.cpp | 3402 ++-- .../vgui2/vgui_controls/FileOpenStateMachine.cpp | 994 +- mp/src/vgui2/vgui_controls/FocusNavGroup.cpp | 864 +- mp/src/vgui2/vgui_controls/Frame.cpp | 4792 +++--- mp/src/vgui2/vgui_controls/GraphPanel.cpp | 616 +- mp/src/vgui2/vgui_controls/HTML.cpp | 4556 ++--- mp/src/vgui2/vgui_controls/Image.cpp | 564 +- mp/src/vgui2/vgui_controls/ImageList.cpp | 212 +- mp/src/vgui2/vgui_controls/ImagePanel.cpp | 926 +- mp/src/vgui2/vgui_controls/InputDialog.cpp | 472 +- .../vgui2/vgui_controls/KeyBindingHelpDialog.cpp | 710 +- .../vgui2/vgui_controls/KeyBoardEditorDialog.cpp | 1698 +- mp/src/vgui2/vgui_controls/KeyRepeat.cpp | 194 +- mp/src/vgui2/vgui_controls/Label.cpp | 2772 +-- mp/src/vgui2/vgui_controls/ListPanel.cpp | 6566 +++---- mp/src/vgui2/vgui_controls/ListViewPanel.cpp | 2164 +-- mp/src/vgui2/vgui_controls/Menu.cpp | 5406 +++--- mp/src/vgui2/vgui_controls/MenuBar.cpp | 504 +- mp/src/vgui2/vgui_controls/MenuButton.cpp | 700 +- mp/src/vgui2/vgui_controls/MenuItem.cpp | 1294 +- mp/src/vgui2/vgui_controls/MessageBox.cpp | 790 +- mp/src/vgui2/vgui_controls/MessageDialog.cpp | 718 +- mp/src/vgui2/vgui_controls/Panel.cpp | 16902 +++++++++---------- mp/src/vgui2/vgui_controls/PanelListPanel.cpp | 946 +- .../vgui2/vgui_controls/PerforceFileExplorer.cpp | 556 +- mp/src/vgui2/vgui_controls/PerforceFileList.cpp | 1140 +- mp/src/vgui2/vgui_controls/ProgressBar.cpp | 852 +- mp/src/vgui2/vgui_controls/ProgressBox.cpp | 720 +- mp/src/vgui2/vgui_controls/PropertyDialog.cpp | 606 +- mp/src/vgui2/vgui_controls/PropertyPage.cpp | 228 +- mp/src/vgui2/vgui_controls/PropertySheet.cpp | 3348 ++-- mp/src/vgui2/vgui_controls/QueryBox.cpp | 444 +- mp/src/vgui2/vgui_controls/RadioButton.cpp | 838 +- mp/src/vgui2/vgui_controls/RichText.cpp | 5488 +++--- mp/src/vgui2/vgui_controls/RotatingProgressBar.cpp | 398 +- mp/src/vgui2/vgui_controls/ScalableImagePanel.cpp | 530 +- mp/src/vgui2/vgui_controls/ScrollBar.cpp | 1604 +- mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp | 1210 +- .../vgui_controls/ScrollableEditablePanel.cpp | 172 +- mp/src/vgui2/vgui_controls/SectionedListPanel.cpp | 4338 ++--- mp/src/vgui2/vgui_controls/Slider.cpp | 1908 +-- mp/src/vgui2/vgui_controls/Splitter.cpp | 1530 +- mp/src/vgui2/vgui_controls/TextEntry.cpp | 8558 +++++----- mp/src/vgui2/vgui_controls/TextImage.cpp | 1970 +-- mp/src/vgui2/vgui_controls/ToggleButton.cpp | 204 +- mp/src/vgui2/vgui_controls/ToolWindow.cpp | 956 +- mp/src/vgui2/vgui_controls/Tooltip.cpp | 812 +- mp/src/vgui2/vgui_controls/TreeView.cpp | 5708 +++---- mp/src/vgui2/vgui_controls/TreeViewListControl.cpp | 630 +- mp/src/vgui2/vgui_controls/URLLabel.cpp | 316 +- mp/src/vgui2/vgui_controls/WizardPanel.cpp | 1440 +- mp/src/vgui2/vgui_controls/WizardSubPanel.cpp | 228 +- mp/src/vgui2/vgui_controls/consoledialog.cpp | 2510 +-- mp/src/vgui2/vgui_controls/controls.cpp | 144 +- .../vgui2/vgui_controls/cvartogglecheckbutton.cpp | 50 +- .../vgui2/vgui_controls/perforcefilelistframe.cpp | 1294 +- mp/src/vgui2/vgui_controls/savedocumentquery.cpp | 390 +- mp/src/vgui2/vgui_controls/subrectimage.cpp | 424 +- mp/src/vgui2/vgui_controls/vgui_controls.vpc | 432 +- 83 files changed, 70568 insertions(+), 70568 deletions(-) (limited to 'mp/src/vgui2') diff --git a/mp/src/vgui2/chromehtml/chromewrapper.cpp b/mp/src/vgui2/chromehtml/chromewrapper.cpp index 179974f0..88f6ee9d 100644 --- a/mp/src/vgui2/chromehtml/chromewrapper.cpp +++ b/mp/src/vgui2/chromehtml/chromewrapper.cpp @@ -1,441 +1,441 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: Creates a HTML control -// -// $NoKeywords: $ -//=============================================================================// -#include "winlite.h" -#include "html_chrome.h" -#include "tier1/interface.h" -#include "reliabletimer.h" -#include "htmlmanager.h" - -#include "html/htmlprotobuf.h" -#include - -//----------------------------------------------------------------------------- -// Purpose: owner object that gets responses from the CEF thread and dispatches them -//----------------------------------------------------------------------------- -class CHTMLController : public IHTMLChromeController -{ -public: - CHTMLController() { m_BrowserSerial = 0; m_nCefTargetFrameRate = 0; SetDefLessFunc( m_mapBrowserRequests ); SetDefLessFunc( m_mapBrowsers ); } - ~CHTMLController() {} - - bool Init( const char *pchHTMLCacheDir, const char *pchCookiePath ); - void Shutdown(); - - void SetWebCookie( const char *pchHostname, const char *pchKey, const char *pchValue, const char *pchPath, RTime32 nExpires ); - void GetWebCookiesForURL( CUtlString *pstrValue, const char *pchURL, const char *pchName ); - void SetClientBuildID( uint64 ulBuildID ); - - bool BHasPendingMessages(); - - void CreateBrowser( IHTMLResponses *pBrowser, bool bPopupWindow, const char *pchUserAgentIdentifier ); - void RemoveBrowser( IHTMLResponses *pBrowser ); - bool RunFrame(); - - void WakeThread() { AccessHTMLWrapper().WakeThread(); } - HTMLCommandBuffer_t *GetFreeCommandBuffer( EHTMLCommands eCmd, int iBrowser ) { return AccessHTMLWrapper().GetFreeCommandBuffer( eCmd, iBrowser ); } - void PushCommand( HTMLCommandBuffer_t *pCmd ) { AccessHTMLWrapper().PushCommand( pCmd ); } - - - bool GetMainThreadCommand( HTMLCommandBuffer_t **pCmd ) { return AccessHTMLWrapper().GetMainThreadCommand( pCmd ); } - void ReleaseCommandBuffer( HTMLCommandBuffer_t *pCmd ) { AccessHTMLWrapper().ReleaseCommandBuffer( pCmd ); } - -#ifdef DBGFLAG_VALIDATE - void Validate( CValidator &validator, const char *pchName ); - bool ChromePrepareForValidate(); - bool ChromeResumeFromValidate(); -#endif - - void SetCefThreadTargetFrameRate( uint32 nFPS ); - -private: - // keeps track of outstanding browser create requests - CUtlMap< uint32, IHTMLResponses *, int > m_mapBrowserRequests; - // the next unique identifier to use when doing a browser create - uint32 m_BrowserSerial; - // the map of browser handles to our html panel objects, used for cef thread command dispatching - CUtlMap< uint32, IHTMLResponses *, int > m_mapBrowsers; - - int m_nCefTargetFrameRate; -}; - - -static CHTMLController s_HTMLController; -EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CHTMLController, IHTMLChromeController, CHROMEHTML_CONTROLLER_INTERFACE_VERSION, s_HTMLController ); - - -//----------------------------------------------------------------------------- -// Purpose: request the cef thread to make a new browser -//----------------------------------------------------------------------------- -void CHTMLController::CreateBrowser( IHTMLResponses *pBrowser, bool bPopupWindow, const char *pchUserAgentIdentifier ) -{ - m_BrowserSerial++; - m_mapBrowserRequests.Insert( m_BrowserSerial, pBrowser ); - - CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserCreate ); - cmd.Body().set_request_id( m_BrowserSerial ); - cmd.Body().set_popup( bPopupWindow ); - cmd.Body().set_useragent( pchUserAgentIdentifier ); - HTMLCommandBuffer_t *pBuf = AccessHTMLWrapper().GetFreeCommandBuffer( eHTMLCommands_BrowserCreate, -1 ); - cmd.SerializeCrossProc( &pBuf->m_Buffer ); - AccessHTMLWrapper().PushCommand( pBuf ); -} - - -//----------------------------------------------------------------------------- -// Purpose: delete a browser we have -//----------------------------------------------------------------------------- -void CHTMLController::RemoveBrowser( IHTMLResponses *pBrowser ) -{ - - // pull ourselves from the browser handle list as we are doing away - FOR_EACH_MAP_FAST( m_mapBrowsers, i) - { - if ( m_mapBrowsers[i] == pBrowser ) - { - // tell the cef thread this browser is going away - CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserRemove ); - cmd.Body().set_browser_handle( pBrowser->BrowserGetIndex() ); - HTMLCommandBuffer_t *pBuf = AccessHTMLWrapper().GetFreeCommandBuffer( eHTMLCommands_BrowserRemove, pBrowser->BrowserGetIndex() ); - cmd.SerializeCrossProc( &pBuf->m_Buffer ); - AccessHTMLWrapper().PushCommand( pBuf ); - - // now kill it - m_mapBrowsers.RemoveAt( i ); - } - } - - // also remove us from pending list if in it - FOR_EACH_MAP_FAST( m_mapBrowserRequests, i) - { - if ( m_mapBrowserRequests[i] == pBrowser ) - m_mapBrowserRequests.RemoveAt( i ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: turn on the cef engine -//----------------------------------------------------------------------------- -bool CHTMLController::Init( const char *pchHTMLCacheDir, const char *pchCookiePath ) -{ -#if !defined(WIN64) && !defined(STATIC_TIER0) - ChromeInit( pchHTMLCacheDir, pchCookiePath ); -#endif - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: shutdown chrome -//----------------------------------------------------------------------------- -void CHTMLController::Shutdown() -{ -#if !defined(WIN64) && !defined(STATIC_TIER0) - ChromeShutdown(); -#endif -} - - - -// helper macro to dispatch messages from the cef thread -#define HTML_MSG_FUNC( eHTMLCommand, bodyType, commandFunc ) \ - case eHTMLCommand: \ -{ \ - CHTMLProtoBufMsg< bodyType > cmd( pCmd->m_eCmd ); \ - if ( !cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ) \ -{ \ - bError = true; \ -} \ - else \ -{ \ - int idx = m_mapBrowsers.Find( cmd.BodyConst().browser_handle() ); \ - if ( idx != m_mapBrowsers.InvalidIndex() ) \ -{ \ - if ( m_mapBrowsers[idx] ) \ - m_mapBrowsers[idx]->commandFunc( &cmd.BodyConst() ); \ -} \ -} \ -} \ - break; \ - - -//----------------------------------------------------------------------------- -// Purpose: process any ipc responses we have pending -//----------------------------------------------------------------------------- -bool CHTMLController::RunFrame() -{ - VPROF_BUDGET( "CHTMLController::RunFrame", VPROF_BUDGETGROUP_TENFOOT ); - HTMLCommandBuffer_t *pCmd; - bool bError = false; - bool bDidwork = false; - - // Paint messages are dispatched last to avoid doing excessive work on - // the main thread when two paint messages have stacked up in the queue. - // This could be greatly optimized by doing atomic buffer swaps instead - // of pushing the paint updates through a queue, but this helps for now. - // -henryg - CUtlVector< HTMLCommandBuffer_t * > vecDeferredPaint; - - while( GetMainThreadCommand( &pCmd ) ) - { - bool bRelease = true; - bDidwork = true; - //Msg( "Got response %d\n", pCmd->m_eCmd ); - switch( pCmd->m_eCmd ) - { - default: - break; - case eHTMLCommands_BrowserCreateResponse: - { - CHTMLProtoBufMsg< CMsgBrowserCreateResponse > cmd( pCmd->m_eCmd ); - if ( !cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ) - { - bError = true; - } - else - { - int idx = m_mapBrowserRequests.Find( cmd.BodyConst().request_id() ); - if ( idx != m_mapBrowserRequests.InvalidIndex() ) - { - m_mapBrowsers.Insert( cmd.BodyConst().browser_handle(), m_mapBrowserRequests[idx] ); - m_mapBrowserRequests[idx]->BrowserSetIndex( cmd.BodyConst().browser_handle() ); - m_mapBrowserRequests.RemoveAt( idx ); - } - } - } - break; - case eHTMLCommands_NeedsPaint: - { - bRelease = false; - vecDeferredPaint.AddToTail( pCmd ); - } - break; - - HTML_MSG_FUNC( eHTMLCommands_BrowserReady, CMsgBrowserReady, BrowserReady ); - HTML_MSG_FUNC( eHTMLCommands_StartRequest, CMsgStartRequest, BrowserStartRequest ); - HTML_MSG_FUNC( eHTMLCommands_URLChanged, CMsgURLChanged, BrowserURLChanged ); - HTML_MSG_FUNC( eHTMLCommands_FinishedRequest, CMsgFinishedRequest, BrowserFinishedRequest ); - HTML_MSG_FUNC( eHTMLCommands_ShowPopup, CMsgShowPopup, BrowserShowPopup ); - HTML_MSG_FUNC( eHTMLCommands_HidePopup, CMsgHidePopup, BrowserHidePopup ); - HTML_MSG_FUNC( eHTMLCommands_OpenNewTab, CMsgOpenNewTab, BrowserOpenNewTab ); - HTML_MSG_FUNC( eHTMLCommands_PopupHTMLWindow, CMsgPopupHTMLWindow, BrowserPopupHTMLWindow ); - HTML_MSG_FUNC( eHTMLCommands_SetHTMLTitle, CMsgSetHTMLTitle, BrowserSetHTMLTitle ); - HTML_MSG_FUNC( eHTMLCommands_LoadingResource, CMsgLoadingResource, BrowserLoadingResource ); - HTML_MSG_FUNC( eHTMLCommands_StatusText, CMsgStatusText, BrowserStatusText ); - HTML_MSG_FUNC( eHTMLCommands_SetCursor, CMsgSetCursor, BrowserSetCursor ); - HTML_MSG_FUNC( eHTMLCommands_FileLoadDialog, CMsgFileLoadDialog, BrowserFileLoadDialog ); - HTML_MSG_FUNC( eHTMLCommands_ShowToolTip, CMsgShowToolTip, BrowserShowToolTip ); - HTML_MSG_FUNC( eHTMLCommands_UpdateToolTip, CMsgUpdateToolTip, BrowserUpdateToolTip ); - HTML_MSG_FUNC( eHTMLCommands_HideToolTip, CMsgHideToolTip, BrowserHideToolTip ); - HTML_MSG_FUNC( eHTMLCommands_SearchResults, CMsgSearchResults, BrowserSearchResults ); - HTML_MSG_FUNC( eHTMLCommands_Close, CMsgClose, BrowserClose ); - HTML_MSG_FUNC( eHTMLCommands_GetZoomResponse, CMsgGetZoomResponse, BrowserGetZoomResponse ); - HTML_MSG_FUNC( eHTMLCommands_HorizontalScrollBarSizeResponse, CMsgHorizontalScrollBarSizeResponse, BrowserHorizontalScrollBarSizeResponse ); - HTML_MSG_FUNC( eHTMLCommands_VerticalScrollBarSizeResponse, CMsgVerticalScrollBarSizeResponse, BrowserVerticalScrollBarSizeResponse ); - HTML_MSG_FUNC( eHTMLCommands_LinkAtPositionResponse, CMsgLinkAtPositionResponse, BrowserLinkAtPositionResponse ); - HTML_MSG_FUNC( eHTMLCommands_ZoomToElementAtPositionResponse, CMsgZoomToElementAtPositionResponse, BrowserZoomToElementAtPositionResponse ); - HTML_MSG_FUNC( eHTMLCommands_JSAlert, CMsgJSAlert, BrowserJSAlert ); - HTML_MSG_FUNC( eHTMLCommands_JSConfirm, CMsgJSConfirm, BrowserJSConfirm ); - HTML_MSG_FUNC( eHTMLCommands_OpenSteamURL, CMsgOpenSteamURL, BrowserOpenSteamURL ); - HTML_MSG_FUNC( eHTMLCommands_CanGoBackandForward, CMsgCanGoBackAndForward, BrowserCanGoBackandForward ); - HTML_MSG_FUNC( eHTMLCommands_SizePopup, CMsgSizePopup, BrowserSizePopup ); - HTML_MSG_FUNC( eHTMLCommands_ScaleToValueResponse, CMsgScalePageToValueResponse, BrowserScalePageToValueResponse ); - HTML_MSG_FUNC( eHTMLCommands_RequestFullScreen, CMsgRequestFullScreen, BrowserRequestFullScreen ); - HTML_MSG_FUNC( eHTMLCommands_ExitFullScreen, CMsgExitFullScreen, BrowserExitFullScreen ); - HTML_MSG_FUNC( eHTMLCommands_GetCookiesForURLResponse, CMsgGetCookiesForURLResponse, BrowserGetCookiesForURLResponse ); - HTML_MSG_FUNC( eHTMLCommands_NodeGotFocus, CMsgNodeHasFocus, BrowserNodeGotFocus ); - HTML_MSG_FUNC( eHTMLCommands_SavePageToJPEGResponse, CMsgSavePageToJPEGResponse, BrowserSavePageToJPEGResponse ); - HTML_MSG_FUNC( eHTMLCommands_GetFocusedNodeValueResponse, CMsgFocusedNodeTextResponse, BrowserFocusedNodeValueResponse ); - } - if ( bError ) - { - Warning( "Failed to parse command %d", pCmd->m_eCmd ); - Assert( !"Bad Command" ); - } - if ( bRelease ) - { - ReleaseCommandBuffer( pCmd ); - } - } - - // Collapse deferred paints by browser ID and process them; the latest texture always - // has fully updated bits, we simply union its dirty rect with the skipped updates. - // Note: browser resizes always include a full dirty rect, we don't have to check here. - while ( vecDeferredPaint.Count() ) - { - // Pull the last paint off the queue - pCmd = vecDeferredPaint[ vecDeferredPaint.Count() - 1 ]; - int iBrowser = pCmd->m_iBrowser; - CHTMLProtoBufMsg cmd( eHTMLCommands_NeedsPaint ); - DbgVerify( cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ); - ReleaseCommandBuffer( pCmd ); - vecDeferredPaint.Remove( vecDeferredPaint.Count() - 1 ); - - - CMsgNeedsPaint &body = cmd.Body(); - CChromeUpdateRegion region; - if ( body.updatewide() && body.updatetall() ) - { - region.MarkDirtyRect( body.updatex(), body.updatey(), body.updatex() + body.updatewide(), body.updatey() + body.updatetall() ); - } - else - { - region.MarkAllDirty(); - } - - // Remove earlier paints for the same browser from the queue - for ( int i = vecDeferredPaint.Count() - 1; i >= 0; --i ) - { - if ( vecDeferredPaint[i]->m_iBrowser == iBrowser ) - { - // Decode - CHTMLProtoBufMsg cmdMerge( eHTMLCommands_NeedsPaint ); - DbgVerify( cmdMerge.BDeserializeCrossProc( &vecDeferredPaint[i]->m_Buffer ) ); - CMsgNeedsPaint &bodyMerge = cmdMerge.Body(); - - if ( body.browser_handle() == bodyMerge.browser_handle() ) - { - ReleaseCommandBuffer( vecDeferredPaint[i] ); - vecDeferredPaint.Remove( i ); - - // Merge update region - if ( bodyMerge.updatewide() && bodyMerge.updatetall() ) - { - region.MarkDirtyRect( bodyMerge.updatex(), bodyMerge.updatey(), bodyMerge.updatex() + bodyMerge.updatewide(), bodyMerge.updatey() + bodyMerge.updatetall() ); - } - else - { - region.MarkAllDirty(); - } - - // Send response to the skipped paint update to free up the texture slot - pCmd = GetFreeCommandBuffer( eHTMLCommands_NeedsPaintResponse, bodyMerge.browser_handle() ); - CHTMLProtoBufMsg cmdResponse( eHTMLCommands_NeedsPaintResponse ); - cmdResponse.Body().set_browser_handle( bodyMerge.browser_handle() ); - cmdResponse.Body().set_textureid( bodyMerge.textureid() ); - cmdResponse.SerializeCrossProc( &pCmd->m_Buffer ); - PushCommand( pCmd ); - } - } - } - - // Dispatch the merged update - int idxBrowser = m_mapBrowsers.Find( body.browser_handle() ); - if ( idxBrowser != m_mapBrowsers.InvalidIndex() ) - { - if ( m_mapBrowsers[idxBrowser] ) - { - int updateWide = region.GetUpdateWide( body.wide() ); - int updateTall = region.GetUpdateTall( body.tall() ); - if ( updateWide != body.wide() || updateTall != body.tall() ) - { - body.set_updatex( region.GetUpdateX( body.wide() ) ); - body.set_updatey( region.GetUpdateY( body.tall() ) ); - body.set_updatewide( updateWide ); - body.set_updatetall( updateTall ); - } - else - { - body.clear_updatex(); - body.clear_updatey(); - body.clear_updatewide(); - body.clear_updatetall(); - } - m_mapBrowsers[idxBrowser]->BrowserNeedsPaint( &body ); - } - } - } - - return bDidwork; -} - -//----------------------------------------------------------------------------- -// Purpose: set a cef cookie -//----------------------------------------------------------------------------- -void CHTMLController::SetWebCookie( const char *pchHostname, const char *pchKey, const char *pchValue, const char *pchPath, RTime32 nExpires ) -{ -#if !defined(WIN64) && !defined(STATIC_TIER0) - ChromeSetWebCookie( pchHostname, pchKey, pchValue, pchPath, nExpires ); -#endif -} - - -//----------------------------------------------------------------------------- -// Purpose: set the buildid to report -//----------------------------------------------------------------------------- -void CHTMLController::SetClientBuildID( uint64 ulBuildID ) -{ -#if !defined(WIN64) && !defined(STATIC_TIER0) - ChromeSetClientBuildID( ulBuildID ); -#endif -} - - -//----------------------------------------------------------------------------- -// Purpose: get the cef cookies for a url -//----------------------------------------------------------------------------- -void CHTMLController::GetWebCookiesForURL( CUtlString *pstrValue, const char *pchURL, const char *pchName ) -{ -#if !defined(WIN64) && !defined(STATIC_TIER0) - ChromeGetWebCookiesForURL( pstrValue, pchURL, pchName ); -#endif -} - - -//----------------------------------------------------------------------------- -// Purpose: true if any pending html message in the queue -//----------------------------------------------------------------------------- -bool CHTMLController::BHasPendingMessages() -{ - return AccessHTMLWrapper().BHasPendingMessages(); -} - - -//----------------------------------------------------------------------------- -// Purpose: tell the cef thread the frame rate to use if it changes -//----------------------------------------------------------------------------- -void CHTMLController::SetCefThreadTargetFrameRate( uint32 nFPS ) -{ - if ( nFPS != m_nCefTargetFrameRate ) - { - m_nCefTargetFrameRate = nFPS; - CHTMLProtoBufMsg cmd( eHTMLCommands_SetTargetFrameRate ); - cmd.Body().set_ntargetframerate( nFPS ); - HTMLCommandBuffer_t *pBuf = AccessHTMLWrapper().GetFreeCommandBuffer( eHTMLCommands_SetTargetFrameRate, -1 ); - cmd.SerializeCrossProc( &pBuf->m_Buffer ); - AccessHTMLWrapper().PushCommand( pBuf ); - } -} - -#ifdef DBGFLAG_VALIDATE -//----------------------------------------------------------------------------- -// Purpose: validate mem -//----------------------------------------------------------------------------- -void CHTMLController::Validate( CValidator &validator, const char *pchName ) -{ - ChromeValidate( validator, "ChromeValidate" ); - - validator.Push( "CHTMLController::ValidateStatics", NULL, pchName ); - ValidateObj( m_mapBrowserRequests ); - ValidateObj( m_mapBrowsers ); - validator.Pop(); -} - -bool CHTMLController::ChromeResumeFromValidate() -{ - return ::ChromeResumeFromValidate(); -} - -bool CHTMLController::ChromePrepareForValidate() -{ - return ::ChromePrepareForValidate(); -} -#endif // DBGFLAG_VALIDATE - - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Creates a HTML control +// +// $NoKeywords: $ +//=============================================================================// +#include "winlite.h" +#include "html_chrome.h" +#include "tier1/interface.h" +#include "reliabletimer.h" +#include "htmlmanager.h" + +#include "html/htmlprotobuf.h" +#include + +//----------------------------------------------------------------------------- +// Purpose: owner object that gets responses from the CEF thread and dispatches them +//----------------------------------------------------------------------------- +class CHTMLController : public IHTMLChromeController +{ +public: + CHTMLController() { m_BrowserSerial = 0; m_nCefTargetFrameRate = 0; SetDefLessFunc( m_mapBrowserRequests ); SetDefLessFunc( m_mapBrowsers ); } + ~CHTMLController() {} + + bool Init( const char *pchHTMLCacheDir, const char *pchCookiePath ); + void Shutdown(); + + void SetWebCookie( const char *pchHostname, const char *pchKey, const char *pchValue, const char *pchPath, RTime32 nExpires ); + void GetWebCookiesForURL( CUtlString *pstrValue, const char *pchURL, const char *pchName ); + void SetClientBuildID( uint64 ulBuildID ); + + bool BHasPendingMessages(); + + void CreateBrowser( IHTMLResponses *pBrowser, bool bPopupWindow, const char *pchUserAgentIdentifier ); + void RemoveBrowser( IHTMLResponses *pBrowser ); + bool RunFrame(); + + void WakeThread() { AccessHTMLWrapper().WakeThread(); } + HTMLCommandBuffer_t *GetFreeCommandBuffer( EHTMLCommands eCmd, int iBrowser ) { return AccessHTMLWrapper().GetFreeCommandBuffer( eCmd, iBrowser ); } + void PushCommand( HTMLCommandBuffer_t *pCmd ) { AccessHTMLWrapper().PushCommand( pCmd ); } + + + bool GetMainThreadCommand( HTMLCommandBuffer_t **pCmd ) { return AccessHTMLWrapper().GetMainThreadCommand( pCmd ); } + void ReleaseCommandBuffer( HTMLCommandBuffer_t *pCmd ) { AccessHTMLWrapper().ReleaseCommandBuffer( pCmd ); } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, const char *pchName ); + bool ChromePrepareForValidate(); + bool ChromeResumeFromValidate(); +#endif + + void SetCefThreadTargetFrameRate( uint32 nFPS ); + +private: + // keeps track of outstanding browser create requests + CUtlMap< uint32, IHTMLResponses *, int > m_mapBrowserRequests; + // the next unique identifier to use when doing a browser create + uint32 m_BrowserSerial; + // the map of browser handles to our html panel objects, used for cef thread command dispatching + CUtlMap< uint32, IHTMLResponses *, int > m_mapBrowsers; + + int m_nCefTargetFrameRate; +}; + + +static CHTMLController s_HTMLController; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CHTMLController, IHTMLChromeController, CHROMEHTML_CONTROLLER_INTERFACE_VERSION, s_HTMLController ); + + +//----------------------------------------------------------------------------- +// Purpose: request the cef thread to make a new browser +//----------------------------------------------------------------------------- +void CHTMLController::CreateBrowser( IHTMLResponses *pBrowser, bool bPopupWindow, const char *pchUserAgentIdentifier ) +{ + m_BrowserSerial++; + m_mapBrowserRequests.Insert( m_BrowserSerial, pBrowser ); + + CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserCreate ); + cmd.Body().set_request_id( m_BrowserSerial ); + cmd.Body().set_popup( bPopupWindow ); + cmd.Body().set_useragent( pchUserAgentIdentifier ); + HTMLCommandBuffer_t *pBuf = AccessHTMLWrapper().GetFreeCommandBuffer( eHTMLCommands_BrowserCreate, -1 ); + cmd.SerializeCrossProc( &pBuf->m_Buffer ); + AccessHTMLWrapper().PushCommand( pBuf ); +} + + +//----------------------------------------------------------------------------- +// Purpose: delete a browser we have +//----------------------------------------------------------------------------- +void CHTMLController::RemoveBrowser( IHTMLResponses *pBrowser ) +{ + + // pull ourselves from the browser handle list as we are doing away + FOR_EACH_MAP_FAST( m_mapBrowsers, i) + { + if ( m_mapBrowsers[i] == pBrowser ) + { + // tell the cef thread this browser is going away + CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserRemove ); + cmd.Body().set_browser_handle( pBrowser->BrowserGetIndex() ); + HTMLCommandBuffer_t *pBuf = AccessHTMLWrapper().GetFreeCommandBuffer( eHTMLCommands_BrowserRemove, pBrowser->BrowserGetIndex() ); + cmd.SerializeCrossProc( &pBuf->m_Buffer ); + AccessHTMLWrapper().PushCommand( pBuf ); + + // now kill it + m_mapBrowsers.RemoveAt( i ); + } + } + + // also remove us from pending list if in it + FOR_EACH_MAP_FAST( m_mapBrowserRequests, i) + { + if ( m_mapBrowserRequests[i] == pBrowser ) + m_mapBrowserRequests.RemoveAt( i ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: turn on the cef engine +//----------------------------------------------------------------------------- +bool CHTMLController::Init( const char *pchHTMLCacheDir, const char *pchCookiePath ) +{ +#if !defined(WIN64) && !defined(STATIC_TIER0) + ChromeInit( pchHTMLCacheDir, pchCookiePath ); +#endif + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: shutdown chrome +//----------------------------------------------------------------------------- +void CHTMLController::Shutdown() +{ +#if !defined(WIN64) && !defined(STATIC_TIER0) + ChromeShutdown(); +#endif +} + + + +// helper macro to dispatch messages from the cef thread +#define HTML_MSG_FUNC( eHTMLCommand, bodyType, commandFunc ) \ + case eHTMLCommand: \ +{ \ + CHTMLProtoBufMsg< bodyType > cmd( pCmd->m_eCmd ); \ + if ( !cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ) \ +{ \ + bError = true; \ +} \ + else \ +{ \ + int idx = m_mapBrowsers.Find( cmd.BodyConst().browser_handle() ); \ + if ( idx != m_mapBrowsers.InvalidIndex() ) \ +{ \ + if ( m_mapBrowsers[idx] ) \ + m_mapBrowsers[idx]->commandFunc( &cmd.BodyConst() ); \ +} \ +} \ +} \ + break; \ + + +//----------------------------------------------------------------------------- +// Purpose: process any ipc responses we have pending +//----------------------------------------------------------------------------- +bool CHTMLController::RunFrame() +{ + VPROF_BUDGET( "CHTMLController::RunFrame", VPROF_BUDGETGROUP_TENFOOT ); + HTMLCommandBuffer_t *pCmd; + bool bError = false; + bool bDidwork = false; + + // Paint messages are dispatched last to avoid doing excessive work on + // the main thread when two paint messages have stacked up in the queue. + // This could be greatly optimized by doing atomic buffer swaps instead + // of pushing the paint updates through a queue, but this helps for now. + // -henryg + CUtlVector< HTMLCommandBuffer_t * > vecDeferredPaint; + + while( GetMainThreadCommand( &pCmd ) ) + { + bool bRelease = true; + bDidwork = true; + //Msg( "Got response %d\n", pCmd->m_eCmd ); + switch( pCmd->m_eCmd ) + { + default: + break; + case eHTMLCommands_BrowserCreateResponse: + { + CHTMLProtoBufMsg< CMsgBrowserCreateResponse > cmd( pCmd->m_eCmd ); + if ( !cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ) + { + bError = true; + } + else + { + int idx = m_mapBrowserRequests.Find( cmd.BodyConst().request_id() ); + if ( idx != m_mapBrowserRequests.InvalidIndex() ) + { + m_mapBrowsers.Insert( cmd.BodyConst().browser_handle(), m_mapBrowserRequests[idx] ); + m_mapBrowserRequests[idx]->BrowserSetIndex( cmd.BodyConst().browser_handle() ); + m_mapBrowserRequests.RemoveAt( idx ); + } + } + } + break; + case eHTMLCommands_NeedsPaint: + { + bRelease = false; + vecDeferredPaint.AddToTail( pCmd ); + } + break; + + HTML_MSG_FUNC( eHTMLCommands_BrowserReady, CMsgBrowserReady, BrowserReady ); + HTML_MSG_FUNC( eHTMLCommands_StartRequest, CMsgStartRequest, BrowserStartRequest ); + HTML_MSG_FUNC( eHTMLCommands_URLChanged, CMsgURLChanged, BrowserURLChanged ); + HTML_MSG_FUNC( eHTMLCommands_FinishedRequest, CMsgFinishedRequest, BrowserFinishedRequest ); + HTML_MSG_FUNC( eHTMLCommands_ShowPopup, CMsgShowPopup, BrowserShowPopup ); + HTML_MSG_FUNC( eHTMLCommands_HidePopup, CMsgHidePopup, BrowserHidePopup ); + HTML_MSG_FUNC( eHTMLCommands_OpenNewTab, CMsgOpenNewTab, BrowserOpenNewTab ); + HTML_MSG_FUNC( eHTMLCommands_PopupHTMLWindow, CMsgPopupHTMLWindow, BrowserPopupHTMLWindow ); + HTML_MSG_FUNC( eHTMLCommands_SetHTMLTitle, CMsgSetHTMLTitle, BrowserSetHTMLTitle ); + HTML_MSG_FUNC( eHTMLCommands_LoadingResource, CMsgLoadingResource, BrowserLoadingResource ); + HTML_MSG_FUNC( eHTMLCommands_StatusText, CMsgStatusText, BrowserStatusText ); + HTML_MSG_FUNC( eHTMLCommands_SetCursor, CMsgSetCursor, BrowserSetCursor ); + HTML_MSG_FUNC( eHTMLCommands_FileLoadDialog, CMsgFileLoadDialog, BrowserFileLoadDialog ); + HTML_MSG_FUNC( eHTMLCommands_ShowToolTip, CMsgShowToolTip, BrowserShowToolTip ); + HTML_MSG_FUNC( eHTMLCommands_UpdateToolTip, CMsgUpdateToolTip, BrowserUpdateToolTip ); + HTML_MSG_FUNC( eHTMLCommands_HideToolTip, CMsgHideToolTip, BrowserHideToolTip ); + HTML_MSG_FUNC( eHTMLCommands_SearchResults, CMsgSearchResults, BrowserSearchResults ); + HTML_MSG_FUNC( eHTMLCommands_Close, CMsgClose, BrowserClose ); + HTML_MSG_FUNC( eHTMLCommands_GetZoomResponse, CMsgGetZoomResponse, BrowserGetZoomResponse ); + HTML_MSG_FUNC( eHTMLCommands_HorizontalScrollBarSizeResponse, CMsgHorizontalScrollBarSizeResponse, BrowserHorizontalScrollBarSizeResponse ); + HTML_MSG_FUNC( eHTMLCommands_VerticalScrollBarSizeResponse, CMsgVerticalScrollBarSizeResponse, BrowserVerticalScrollBarSizeResponse ); + HTML_MSG_FUNC( eHTMLCommands_LinkAtPositionResponse, CMsgLinkAtPositionResponse, BrowserLinkAtPositionResponse ); + HTML_MSG_FUNC( eHTMLCommands_ZoomToElementAtPositionResponse, CMsgZoomToElementAtPositionResponse, BrowserZoomToElementAtPositionResponse ); + HTML_MSG_FUNC( eHTMLCommands_JSAlert, CMsgJSAlert, BrowserJSAlert ); + HTML_MSG_FUNC( eHTMLCommands_JSConfirm, CMsgJSConfirm, BrowserJSConfirm ); + HTML_MSG_FUNC( eHTMLCommands_OpenSteamURL, CMsgOpenSteamURL, BrowserOpenSteamURL ); + HTML_MSG_FUNC( eHTMLCommands_CanGoBackandForward, CMsgCanGoBackAndForward, BrowserCanGoBackandForward ); + HTML_MSG_FUNC( eHTMLCommands_SizePopup, CMsgSizePopup, BrowserSizePopup ); + HTML_MSG_FUNC( eHTMLCommands_ScaleToValueResponse, CMsgScalePageToValueResponse, BrowserScalePageToValueResponse ); + HTML_MSG_FUNC( eHTMLCommands_RequestFullScreen, CMsgRequestFullScreen, BrowserRequestFullScreen ); + HTML_MSG_FUNC( eHTMLCommands_ExitFullScreen, CMsgExitFullScreen, BrowserExitFullScreen ); + HTML_MSG_FUNC( eHTMLCommands_GetCookiesForURLResponse, CMsgGetCookiesForURLResponse, BrowserGetCookiesForURLResponse ); + HTML_MSG_FUNC( eHTMLCommands_NodeGotFocus, CMsgNodeHasFocus, BrowserNodeGotFocus ); + HTML_MSG_FUNC( eHTMLCommands_SavePageToJPEGResponse, CMsgSavePageToJPEGResponse, BrowserSavePageToJPEGResponse ); + HTML_MSG_FUNC( eHTMLCommands_GetFocusedNodeValueResponse, CMsgFocusedNodeTextResponse, BrowserFocusedNodeValueResponse ); + } + if ( bError ) + { + Warning( "Failed to parse command %d", pCmd->m_eCmd ); + Assert( !"Bad Command" ); + } + if ( bRelease ) + { + ReleaseCommandBuffer( pCmd ); + } + } + + // Collapse deferred paints by browser ID and process them; the latest texture always + // has fully updated bits, we simply union its dirty rect with the skipped updates. + // Note: browser resizes always include a full dirty rect, we don't have to check here. + while ( vecDeferredPaint.Count() ) + { + // Pull the last paint off the queue + pCmd = vecDeferredPaint[ vecDeferredPaint.Count() - 1 ]; + int iBrowser = pCmd->m_iBrowser; + CHTMLProtoBufMsg cmd( eHTMLCommands_NeedsPaint ); + DbgVerify( cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ); + ReleaseCommandBuffer( pCmd ); + vecDeferredPaint.Remove( vecDeferredPaint.Count() - 1 ); + + + CMsgNeedsPaint &body = cmd.Body(); + CChromeUpdateRegion region; + if ( body.updatewide() && body.updatetall() ) + { + region.MarkDirtyRect( body.updatex(), body.updatey(), body.updatex() + body.updatewide(), body.updatey() + body.updatetall() ); + } + else + { + region.MarkAllDirty(); + } + + // Remove earlier paints for the same browser from the queue + for ( int i = vecDeferredPaint.Count() - 1; i >= 0; --i ) + { + if ( vecDeferredPaint[i]->m_iBrowser == iBrowser ) + { + // Decode + CHTMLProtoBufMsg cmdMerge( eHTMLCommands_NeedsPaint ); + DbgVerify( cmdMerge.BDeserializeCrossProc( &vecDeferredPaint[i]->m_Buffer ) ); + CMsgNeedsPaint &bodyMerge = cmdMerge.Body(); + + if ( body.browser_handle() == bodyMerge.browser_handle() ) + { + ReleaseCommandBuffer( vecDeferredPaint[i] ); + vecDeferredPaint.Remove( i ); + + // Merge update region + if ( bodyMerge.updatewide() && bodyMerge.updatetall() ) + { + region.MarkDirtyRect( bodyMerge.updatex(), bodyMerge.updatey(), bodyMerge.updatex() + bodyMerge.updatewide(), bodyMerge.updatey() + bodyMerge.updatetall() ); + } + else + { + region.MarkAllDirty(); + } + + // Send response to the skipped paint update to free up the texture slot + pCmd = GetFreeCommandBuffer( eHTMLCommands_NeedsPaintResponse, bodyMerge.browser_handle() ); + CHTMLProtoBufMsg cmdResponse( eHTMLCommands_NeedsPaintResponse ); + cmdResponse.Body().set_browser_handle( bodyMerge.browser_handle() ); + cmdResponse.Body().set_textureid( bodyMerge.textureid() ); + cmdResponse.SerializeCrossProc( &pCmd->m_Buffer ); + PushCommand( pCmd ); + } + } + } + + // Dispatch the merged update + int idxBrowser = m_mapBrowsers.Find( body.browser_handle() ); + if ( idxBrowser != m_mapBrowsers.InvalidIndex() ) + { + if ( m_mapBrowsers[idxBrowser] ) + { + int updateWide = region.GetUpdateWide( body.wide() ); + int updateTall = region.GetUpdateTall( body.tall() ); + if ( updateWide != body.wide() || updateTall != body.tall() ) + { + body.set_updatex( region.GetUpdateX( body.wide() ) ); + body.set_updatey( region.GetUpdateY( body.tall() ) ); + body.set_updatewide( updateWide ); + body.set_updatetall( updateTall ); + } + else + { + body.clear_updatex(); + body.clear_updatey(); + body.clear_updatewide(); + body.clear_updatetall(); + } + m_mapBrowsers[idxBrowser]->BrowserNeedsPaint( &body ); + } + } + } + + return bDidwork; +} + +//----------------------------------------------------------------------------- +// Purpose: set a cef cookie +//----------------------------------------------------------------------------- +void CHTMLController::SetWebCookie( const char *pchHostname, const char *pchKey, const char *pchValue, const char *pchPath, RTime32 nExpires ) +{ +#if !defined(WIN64) && !defined(STATIC_TIER0) + ChromeSetWebCookie( pchHostname, pchKey, pchValue, pchPath, nExpires ); +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: set the buildid to report +//----------------------------------------------------------------------------- +void CHTMLController::SetClientBuildID( uint64 ulBuildID ) +{ +#if !defined(WIN64) && !defined(STATIC_TIER0) + ChromeSetClientBuildID( ulBuildID ); +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: get the cef cookies for a url +//----------------------------------------------------------------------------- +void CHTMLController::GetWebCookiesForURL( CUtlString *pstrValue, const char *pchURL, const char *pchName ) +{ +#if !defined(WIN64) && !defined(STATIC_TIER0) + ChromeGetWebCookiesForURL( pstrValue, pchURL, pchName ); +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: true if any pending html message in the queue +//----------------------------------------------------------------------------- +bool CHTMLController::BHasPendingMessages() +{ + return AccessHTMLWrapper().BHasPendingMessages(); +} + + +//----------------------------------------------------------------------------- +// Purpose: tell the cef thread the frame rate to use if it changes +//----------------------------------------------------------------------------- +void CHTMLController::SetCefThreadTargetFrameRate( uint32 nFPS ) +{ + if ( nFPS != m_nCefTargetFrameRate ) + { + m_nCefTargetFrameRate = nFPS; + CHTMLProtoBufMsg cmd( eHTMLCommands_SetTargetFrameRate ); + cmd.Body().set_ntargetframerate( nFPS ); + HTMLCommandBuffer_t *pBuf = AccessHTMLWrapper().GetFreeCommandBuffer( eHTMLCommands_SetTargetFrameRate, -1 ); + cmd.SerializeCrossProc( &pBuf->m_Buffer ); + AccessHTMLWrapper().PushCommand( pBuf ); + } +} + +#ifdef DBGFLAG_VALIDATE +//----------------------------------------------------------------------------- +// Purpose: validate mem +//----------------------------------------------------------------------------- +void CHTMLController::Validate( CValidator &validator, const char *pchName ) +{ + ChromeValidate( validator, "ChromeValidate" ); + + validator.Push( "CHTMLController::ValidateStatics", NULL, pchName ); + ValidateObj( m_mapBrowserRequests ); + ValidateObj( m_mapBrowsers ); + validator.Pop(); +} + +bool CHTMLController::ChromeResumeFromValidate() +{ + return ::ChromeResumeFromValidate(); +} + +bool CHTMLController::ChromePrepareForValidate() +{ + return ::ChromePrepareForValidate(); +} +#endif // DBGFLAG_VALIDATE + + + diff --git a/mp/src/vgui2/chromehtml/html_chrome.cpp b/mp/src/vgui2/chromehtml/html_chrome.cpp index 0af67f82..89874400 100644 --- a/mp/src/vgui2/chromehtml/html_chrome.cpp +++ b/mp/src/vgui2/chromehtml/html_chrome.cpp @@ -1,3508 +1,3508 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -//=============================================================================// - -#include "winlite.h" -#include "html_chrome.h" -#include "cef/include/cef_cookie.h" -#include "imageloadwrapper.h" -#include "html/ichromehtmlwrapper.h" -#include "input/mousecursors.h" -//#include "rtime.h" -#ifdef OSX -extern "C" void *CreateAutoReleasePool(); -extern "C" void ReleaseAutoReleasePool( void *pool ); -#endif -#ifdef WIN32 -#include -#endif - -// memdbgon must be the last include file in a .cpp file!!! -#include - -static uint64 sm_ulBuildID = 0; // will be appended to the user agent -static CCEFThread g_CEFThread; // main thread for CEF to do its thinking - -// access for main thread to get to the html object -CCEFThread &AccessHTMLWrapper() -{ - return g_CEFThread; -} - -// helper macro to send a command to the CEF thread -#define DISPATCH_MESSAGE( eCmd ) \ - cmd.Body().set_browser_handle( m_iBrowser ); \ - HTMLCommandBuffer_t *pBuf = g_CEFThread.GetFreeCommandBuffer( eCmd, m_iBrowser ); \ - cmd.SerializeCrossProc( &pBuf->m_Buffer ); \ - g_CEFThread.PushResponse( pBuf ); \ - -// wrappers for our handle to list index/serial, lower 16 bits are list index, top 16 bits are the serial number -#define BROWSER_HANDLE_FROM_INDEX_SERIAL( iList, nSerial ) iList + ((int)nSerial<<16) -#define BROWSER_SERIAL_FROM_HANDLE( nHandle ) (uint32)nHandle>>16 - -const int k_ScreenshotQuality = 95; // quality value used when making a jpeg image of a web page -const float k_flMaxScreenshotWaitTime = 5.0; // wait up to 5 sec before taking a screenshot - -//----------------------------------------------------------------------------- -// Purpose: thread to manage pumping the CEF message loop -//----------------------------------------------------------------------------- -CCEFThread::CCEFThread() -{ - m_bExit = false; - m_nTargetFrameRate = 60; // 60 Hz by default - m_nBrowserSerial = 0; - m_bFullScreenFlashVisible = false; - m_bSawUserInputThisFrame = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: destructor -//----------------------------------------------------------------------------- -CCEFThread::~CCEFThread() -{ - // This can get called by the main thread before the actual thread exits, - // if there is a forced process exit due to some error. In this case, - // force the thread to exit before destroying the member variables it - // is depending on. - if ( !m_bExit ) - { - TriggerShutdown(); - Join( 20 * k_nThousand ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: tell cef where to place its state dirs -//----------------------------------------------------------------------------- -void CCEFThread::SetCEFPaths( const char *pchHTMLCacheDir, const char *pchCookiePath ) -{ - Assert( !IsAlive() ); - m_sHTMLCacheDir = pchHTMLCacheDir; - m_sCookiePath = pchCookiePath; -} - - -//----------------------------------------------------------------------------- -// Purpose: set the exit event on the cef thread -//----------------------------------------------------------------------------- -void CCEFThread::TriggerShutdown() -{ - m_bExit = true; - m_WakeEvent.Set(); - m_evWaitingForCommand.Set(); - m_eventDidExit.Wait( k_nThousand ); // wait 1 second at most for it to go away -} - - -//----------------------------------------------------------------------------- -// Purpose: tickle the run loop of the thread to run more commands -//----------------------------------------------------------------------------- -void CCEFThread::WakeThread() -{ - m_WakeEvent.Set(); -} - - -//----------------------------------------------------------------------------- -// Purpose: helper macro for message dispatch -//----------------------------------------------------------------------------- -#define HTML_MSG_FUNC( eHTMLCommand, bodyType, commandFunc ) \ - case eHTMLCommand: \ - { \ - CHTMLProtoBufMsg< bodyType > cmd( pCmd->m_eCmd ); \ - if ( !cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ) \ - { \ - bError = true; \ - } \ - else \ - { \ - cmd.Body().set_browser_handle( pCmd->m_iBrowser ); \ - g_CEFThread.commandFunc( cmd ); \ - } \ - } \ - break; - - -#define HTML_MSG_FUNC_NOHANDLE( eHTMLCommand, bodyType, commandFunc ) \ - case eHTMLCommand: \ - { \ - CHTMLProtoBufMsg< bodyType > cmd( pCmd->m_eCmd ); \ - if ( !cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ) \ - { \ - bError = true; \ - } \ - else \ - { \ - g_CEFThread.commandFunc( cmd ); \ - } \ - } \ - break; - - -//----------------------------------------------------------------------------- -// Purpose: clear any pending commands from the main thread -//----------------------------------------------------------------------------- -void CCEFThread::RunCurrentCommands() -{ - bool bError = false; - HTMLCommandBuffer_t *pCmd =NULL; - - while( GetCEFThreadCommand( &pCmd ) ) - { - //Msg( "Got command %d\n", pCmd->m_eCmd ); - switch( pCmd->m_eCmd ) - { - HTML_MSG_FUNC_NOHANDLE( eHTMLCommands_BrowserCreate, CMsgBrowserCreate, ThreadCreateBrowser ); - HTML_MSG_FUNC( eHTMLCommands_BrowserRemove, CMsgBrowserRemove, ThreadRemoveBrowser ); - HTML_MSG_FUNC( eHTMLCommands_BrowserSize, CMsgBrowserSize, ThreadBrowserSize ); - HTML_MSG_FUNC( eHTMLCommands_BrowserPosition, CMsgBrowserPosition, ThreadBrowserPosition ); - HTML_MSG_FUNC( eHTMLCommands_PostURL, CMsgPostURL, ThreadBrowserPostURL ); - HTML_MSG_FUNC( eHTMLCommands_StopLoad, CMsgStopLoad, ThreadBrowserStopLoad ); - HTML_MSG_FUNC( eHTMLCommands_Reload, CMsgReload, ThreadBrowserReload ); - HTML_MSG_FUNC( eHTMLCommands_GoForward, CMsgGoForward, ThreadBrowserGoForward ); - HTML_MSG_FUNC( eHTMLCommands_GoBack, CMsgGoBack, ThreadBrowserGoBack ); - HTML_MSG_FUNC( eHTMLCommands_Copy, CMsgCopy, ThreadBrowserCopy ); - HTML_MSG_FUNC( eHTMLCommands_Paste, CMsgPaste, ThreadBrowserPaste ); - HTML_MSG_FUNC( eHTMLCommands_ExecuteJavaScript, CMsgExecuteJavaScript, ThreadBrowserExecuteJavascript ); - HTML_MSG_FUNC( eHTMLCommands_SetFocus, CMsgSetFocus, ThreadBrowserSetFocus ); - HTML_MSG_FUNC( eHTMLCommands_HorizontalScrollBarSize, CMsgHorizontalScrollBarSize, ThreadBrowserHorizontalScrollBarSize ); - HTML_MSG_FUNC( eHTMLCommands_VerticalScrollBarSize, CMsgVerticalScrollBarSize, ThreadBrowserVerticalScrollBarSize ); - HTML_MSG_FUNC( eHTMLCommands_Find, CMsgFind, ThreadBrowserFind ); - HTML_MSG_FUNC( eHTMLCommands_StopFind, CMsgStopFind, ThreadBrowserStopFind ); - HTML_MSG_FUNC( eHTMLCommands_SetHorizontalScroll, CMsgSetHorizontalScroll, ThreadBrowserSetHorizontalScroll ); - HTML_MSG_FUNC( eHTMLCommands_SetVerticalScroll, CMsgSetVerticalScroll, ThreadBrowserSetVerticalScroll ); - HTML_MSG_FUNC( eHTMLCommands_SetZoomLevel, CMsgSetZoomLevel, ThreadBrowserSetZoomLevel ); - HTML_MSG_FUNC( eHTMLCommands_ViewSource, CMsgViewSource, ThreadBrowserViewSource ); - HTML_MSG_FUNC( eHTMLCommands_NeedsPaintResponse, CMsgNeedsPaintResponse, ThreadNeedsPaintResponse ); - HTML_MSG_FUNC( eHTMLCommands_BrowserErrorStrings, CMsgBrowserErrorStrings, ThreadBrowserErrorStrings ); - HTML_MSG_FUNC( eHTMLCommands_AddHeader, CMsgAddHeader, ThreadAddHeader ); - HTML_MSG_FUNC( eHTMLCommands_GetZoom, CMsgGetZoom, ThreadGetZoom ); - HTML_MSG_FUNC_NOHANDLE( eHTMLCommands_SetCookie, CMsgSetCookie, ThreadSetCookie ); - HTML_MSG_FUNC_NOHANDLE( eHTMLCommands_SetTargetFrameRate, CMsgSetTargetFrameRate, ThreadSetTargetFrameRate ); - HTML_MSG_FUNC( eHTMLCommands_HidePopup, CMsgHidePopup, ThreadHidePopup ); - HTML_MSG_FUNC( eHTMLCommands_FullRepaint, CMsgFullRepaint, ThreadFullRepaint ); - HTML_MSG_FUNC( eHTMLCommands_GetCookiesForURL, CMsgGetCookiesForURL, ThreadGetCookiesForURL ); - HTML_MSG_FUNC( eHTMLCommands_ZoomToCurrentlyFocusedNode, CMsgZoomToFocusedElement, ThreadZoomToFocusedElement ); - HTML_MSG_FUNC( eHTMLCommands_GetFocusedNodeValue, CMsgFocusedNodeText, ThreadGetFocusedNodeText ); - - HTML_MSG_FUNC( eHTMLCommands_MouseDown, CMsgMouseDown, ThreadMouseButtonDown ); - HTML_MSG_FUNC( eHTMLCommands_MouseUp, CMsgMouseUp, ThreadMouseButtonUp ); - HTML_MSG_FUNC( eHTMLCommands_MouseDblClick, CMsgMouseDblClick, ThreadMouseButtonDlbClick ); - HTML_MSG_FUNC( eHTMLCommands_MouseWheel, CMsgMouseWheel, ThreadMouseWheel ); - HTML_MSG_FUNC( eHTMLCommands_KeyDown, CMsgKeyDown, ThreadKeyDown ); - HTML_MSG_FUNC( eHTMLCommands_KeyUp, CMsgKeyUp, ThreadKeyUp ); - HTML_MSG_FUNC( eHTMLCommands_KeyChar, CMsgKeyChar, ThreadKeyTyped ); - HTML_MSG_FUNC( eHTMLCommands_MouseMove, CMsgMouseMove, ThreadMouseMove ); - HTML_MSG_FUNC( eHTMLCommands_MouseLeave, CMsgMouseLeave, ThreadMouseLeave ); - HTML_MSG_FUNC( eHTMLCommands_LinkAtPosition, CMsgLinkAtPosition, ThreadLinkAtPosition ); - HTML_MSG_FUNC( eHTMLCommands_ZoomToElementAtPosition, CMsgZoomToElementAtPosition, ThreadZoomToElementAtPosition ); - HTML_MSG_FUNC( eHTMLCommands_SavePageToJPEG, CMsgSavePageToJPEG, ThreadSavePageToJPEG ); - HTML_MSG_FUNC( eHTMLCommands_SetPageScale, CMsgScalePageToValue, ThreadSetPageScale ); - HTML_MSG_FUNC( eHTMLCommands_ExitFullScreen, CMsgExitFullScreen, ThreadExitFullScreen ); - HTML_MSG_FUNC( eHTMLCommands_CloseFullScreenFlashIfOpen, CMsgCloseFullScreenFlashIfOpen, ThreadCloseFullScreenFlashIfOpen ); - HTML_MSG_FUNC( eHTMLCommands_PauseFullScreenFlashMovieIfOpen, CMsgPauseFullScreenFlashMovieIfOpen, ThreadPauseFullScreenFlashMovieIfOpen ); - - default: - bError = true; - AssertMsg1( false, "Invalid message in browser stream (%d)", pCmd->m_eCmd ); - break; - } - - if ( pCmd->m_eCmd == eHTMLCommands_MouseDown || pCmd->m_eCmd == eHTMLCommands_MouseDblClick || pCmd->m_eCmd == eHTMLCommands_KeyDown ) - m_bSawUserInputThisFrame = true; - - ReleaseCommandBuffer( pCmd ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: done with this buffer, put it in the free queue -//----------------------------------------------------------------------------- -void CCEFThread::ReleaseCommandBuffer( HTMLCommandBuffer_t *pBuf ) -{ - pBuf->m_eCmd = eHTMLCommands_None; - pBuf->m_iBrowser = -1; - pBuf->m_Buffer.Clear(); - m_tslUnsedBuffers.PushItem( pBuf ); -} - - -//----------------------------------------------------------------------------- -// Purpose: get the next free cef thread command buffer to write into -//----------------------------------------------------------------------------- -HTMLCommandBuffer_t *CCEFThread::GetFreeCommandBuffer( EHTMLCommands eCmd, int iBrowser ) -{ - HTMLCommandBuffer_t *pBuf; - if ( !m_tslUnsedBuffers.PopItem( &pBuf ) ) // if nothing in the free queue just make a new one - pBuf = new HTMLCommandBuffer_t; - - pBuf->m_eCmd = eCmd; - pBuf->m_iBrowser = iBrowser; - return pBuf; -} - - -//----------------------------------------------------------------------------- -// Purpose: wait for a command of this type on the cef thread -//----------------------------------------------------------------------------- -HTMLCommandBuffer_t *CCEFThread::BWaitForCommand( EHTMLCommands eCmd, int iBrowser ) -{ - while ( !m_bExit ) - { - if ( m_bSleepForValidate ) - { - m_bSleepingForValidate = true; - ThreadSleep( 100 ); - continue; - } - m_bSleepingForValidate = false; - - HTMLCommandBuffer_t *pBuf = NULL; - while ( m_tslCommandBuffers.PopItem( &pBuf ) && pBuf ) - { - if ( pBuf->m_iBrowser == iBrowser ) - { - if ( pBuf->m_eCmd == eCmd ) // it is what we have been waiting for - return pBuf; - if ( pBuf->m_eCmd == eHTMLCommands_BrowserRemove ) // check to see if this is being deleted while its launching - return NULL; - } - - m_vecQueueCommands.AddToTail( pBuf ); - pBuf = NULL; - } - Assert( pBuf == NULL ); - m_evWaitingForCommand.Wait(); - } - return NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: wait for a command of this type on the cef thread -//----------------------------------------------------------------------------- -HTMLCommandBuffer_t *CCEFThread::BWaitForResponse( EHTMLCommands eCmd, int iBrowser ) -{ - while ( !m_bExit ) - { - if ( m_bSleepForValidate ) - { - m_bSleepingForValidate = true; - ThreadSleep( 100 ); - continue; - } - m_bSleepingForValidate = false; - - HTMLCommandBuffer_t *pBuf = NULL; - while ( m_tslResponseBuffers.PopItem( &pBuf ) && pBuf ) - { - if ( pBuf->m_iBrowser == iBrowser ) - { - if ( pBuf->m_eCmd == eCmd ) // it is what we have been waiting for - return pBuf; - if ( pBuf->m_eCmd == eHTMLCommands_BrowserRemove ) // check to see if this is being deleted while its launching - return NULL; - } - - m_vecQueueResponses.AddToTail( pBuf ); - pBuf = NULL; - } - Assert( pBuf == NULL ); - m_evWaitingForResponse.Wait(); - } - return NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: add a command for the cef thread -//----------------------------------------------------------------------------- -void CCEFThread::PushCommand( HTMLCommandBuffer_t *pBuf ) -{ - m_tslCommandBuffers.PushItem( pBuf ); - m_evWaitingForCommand.Set(); -} - - -//----------------------------------------------------------------------------- -// Purpose: add a command for the main thread -//----------------------------------------------------------------------------- -void CCEFThread::PushResponse( HTMLCommandBuffer_t *pBuf ) -{ - m_tslResponseBuffers.PushItem( pBuf ); - m_evWaitingForResponse.Set(); -} - - - -//----------------------------------------------------------------------------- -// Purpose: get any cef responses on the main thread, returns false if there are none -//----------------------------------------------------------------------------- -bool CCEFThread::GetMainThreadCommand( HTMLCommandBuffer_t **pBuf ) -{ - if ( m_vecQueueResponses.Count() ) - { - *pBuf = m_vecQueueResponses[0]; - m_vecQueueResponses.Remove(0); - return true; - } - else - return m_tslResponseBuffers.PopItem( pBuf ); -} - - -//----------------------------------------------------------------------------- -// Purpose: get the next cef thread protobuf command to run, return false if there are none -//----------------------------------------------------------------------------- -bool CCEFThread::GetCEFThreadCommand( HTMLCommandBuffer_t **pBuf ) -{ - if ( m_vecQueueCommands.Count() ) - { - *pBuf = m_vecQueueCommands[0]; - m_vecQueueCommands.Remove(0); - return true; - } - else - return m_tslCommandBuffers.PopItem( pBuf ); -} - - -//----------------------------------------------------------------------------- -// Purpose: the user agent to report when using this browser control -//----------------------------------------------------------------------------- -const char *CCEFThread::PchWebkitUserAgent() -{ - return "Mozilla/5.0 (%s; U; %s; en-US; %s/%llu; %s) AppleWebKit/535.15 (KHTML, like Gecko) Chrome/18.0.989.0 Safari/535.11"; -} - - -//----------------------------------------------------------------------------- -// Purpose: make a new browser object -//----------------------------------------------------------------------------- -void CCEFThread::ThreadCreateBrowser( const CHTMLProtoBufMsg &htmlCommand ) -{ - int idx = m_listClientHandlers.AddToTail(); - ++m_nBrowserSerial; - CClientHandler *pBrowser = new CClientHandler( BROWSER_HANDLE_FROM_INDEX_SERIAL( idx, m_nBrowserSerial ), PchWebkitUserAgent(), m_nBrowserSerial ); - pBrowser->SetUserAgentIdentifier( htmlCommand.BodyConst().useragent().c_str() ); - - // send the handle info back to the main thread, needs to be before the CreateBrowser below - // to stop a race condition of the browser being ready before the main thread knows its handle - CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserCreateResponse ); - cmd.Body().set_browser_handle( BROWSER_HANDLE_FROM_INDEX_SERIAL( idx, m_nBrowserSerial ) ); - cmd.Body().set_request_id( htmlCommand.BodyConst().request_id() ); - HTMLCommandBuffer_t *pBuf = GetFreeCommandBuffer( eHTMLCommands_BrowserCreateResponse, idx ); - cmd.SerializeCrossProc( &pBuf->m_Buffer ); - PushResponse( pBuf ); - - m_listClientHandlers[idx] = pBrowser; - pBrowser->AddRef(); - - CefWindowInfo info; - info.SetAsOffScreen( NULL ); - info.m_bPopupWindow = htmlCommand.BodyConst().popup(); - - CefBrowserSettings settings; - settings.fullscreen_enabled = true; - settings.threaded_compositing_enabled = true; - settings.java_disabled = true; - //settings.accelerated_compositing_enabled = true; // not supported when going into fullscreen mode - - // Drag and drop is supposed to be disabled automatically for offscreen views, but - // ports for Mac and Linux have bugs where it is not really disabled, causing havoc - settings.drag_drop_disabled = true; - -#ifdef LINUX - // Turn off web features here that don't work on Linux - settings.webgl_disabled = true; -#endif - - CefBrowser::CreateBrowserSync( info, pBrowser, "", settings ); - CefDoMessageLoopWork(); -} - - -//----------------------------------------------------------------------------- -// Purpose: return true if this browser handle resolves into a currently valid browser handler object -//----------------------------------------------------------------------------- -bool CCEFThread::BIsValidBrowserHandle( uint32 nHandle, int &iClient ) -{ - iClient = nHandle & 0xffff; - if ( m_listClientHandlers.IsValidIndex( nHandle & 0xffff ) ) - { - if ( m_listClientHandlers[ iClient ]->NSerial() == BROWSER_SERIAL_FROM_HANDLE( nHandle ) ) - return true; - } - iClient = -1; - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: delete this browser object, we are done with it -//----------------------------------------------------------------------------- -void CCEFThread::ThreadRemoveBrowser( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - m_listClientHandlers[iClient]->CloseBrowser(); - m_listClientHandlers[iClient]->Release(); - m_listClientHandlers[iClient] = NULL; - m_listClientHandlers.Remove( iClient ); - } -} - - -// helper macro to call browser functions if you have a valid handle -#define GET_BROSWER_FUNC( msg, cmd ) \ - int iClient; \ - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) \ - { \ - if ( m_listClientHandlers[iClient]->GetBrowser() ) \ - m_listClientHandlers[iClient]->GetBrowser()->cmd; \ - } \ - - //else \ - // Assert( false ); - - -//----------------------------------------------------------------------------- -// Purpose: make the web page this many pixels wide&tall -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserSize( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, SetSize( PET_VIEW, htmlCommand.BodyConst().width(), htmlCommand.BodyConst().height() ) ); - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - m_listClientHandlers[iClient]->SetSize( htmlCommand.BodyConst().width(), htmlCommand.BodyConst().height() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: set the global position of the browser to these co-ords, used by some activeX controls -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserPosition( const CHTMLProtoBufMsg &htmlCommand ) -{ - // no longer used - BUGBUG remove me -} - - -//----------------------------------------------------------------------------- -// Purpose: load this url in the browser with optional post data -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserPostURL( const CHTMLProtoBufMsg &htmlCommand ) -{ - if ( htmlCommand.BodyConst().url().empty() ) - return; // they asked us to load nothing, ignore them - - const char *pchPostData = htmlCommand.BodyConst().post().c_str(); - if ( !pchPostData || !pchPostData[0] ) - { - GET_BROSWER_FUNC( htmlCommand, GetMainFrame()->LoadURL( std::wstring( CStrAutoEncode( htmlCommand.BodyConst().url().c_str() ).ToWString() ) ) ); - } - else - { - // Create a new request - CefRefPtr request(CefRequest::CreateRequest()); - - // Set the request URL - request->SetURL( CStrAutoEncode( htmlCommand.BodyConst().url().c_str() ).ToWString() ); - - // Add post data to the request. The correct method and content- - // type headers will be set by CEF. - CefRefPtr postDataElement( CefPostDataElement::CreatePostDataElement() ); - - std::string data = pchPostData; - - postDataElement->SetToBytes(data.length(), data.c_str()); - - CefRefPtr postData(CefPostData::CreatePostData()); - - postData->AddElement(postDataElement); - request->SetPostData(postData); - - GET_BROSWER_FUNC( htmlCommand, GetMainFrame()->LoadRequest( request ) ); - } - - int iClient; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - m_listClientHandlers[iClient]->SetPendingPageSerial( htmlCommand.BodyConst().pageserial() ); - - CefDoMessageLoopWork(); // make sure CEF processes this load before we do anything else (like delete the browser control) -} - - -//----------------------------------------------------------------------------- -// Purpose: stop loading the page we are on -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserStopLoad( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, StopLoad() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: reload the current page -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserReload( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, Reload() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: go forward one page in its history -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserGoForward( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, GoForward() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: go back one page in its history -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserGoBack( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, GoBack() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: copy selected text to the clipboard -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserCopy( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, GetMainFrame()->Copy() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: paste from the clipboard into the web page -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserPaste( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, GetMainFrame()->Paste() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: run this javascript on the current page -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserExecuteJavascript( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, GetMainFrame()->ExecuteJavaScript( - CStrAutoEncode( htmlCommand.BodyConst().script().c_str() ).ToWString(), L"", 0 ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: tell the browser it has key focus -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserSetFocus( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, SetFocus( htmlCommand.BodyConst().focus() ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: get the size of the horizontal scroll bar -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserHorizontalScrollBarSize( const CHTMLProtoBufMsg &htmlCommand ) -{ - ThreadBrowserHorizontalScrollBarSizeHelper( htmlCommand.BodyConst().browser_handle(), true ); -} - - -//----------------------------------------------------------------------------- -// Purpose: tell the main thread details of the horiztonal scrollbar -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserHorizontalScrollBarSizeHelper( int iBrowser, bool bForceSendUpdate ) -{ - CClientHandler::CachedScrollBarState_t scroll = { }; - float flZoom = 0.0f; - int iClient = 0; - if ( BIsValidBrowserHandle( iBrowser, iClient ) ) - { - CClientHandler *pHandler = m_listClientHandlers[iClient]; - - if ( pHandler->GetBrowser() ) - { - scroll.m_nMax = pHandler->GetBrowser()->HorizontalScrollMax(); - scroll.m_nScroll = pHandler->GetBrowser()->HorizontalScroll(); - scroll.m_bVisible = pHandler->GetBrowser()->IsHorizontalScrollBarVisible(); - pHandler->GetBrowser()->HorizontalScrollBarSize( scroll.m_nX, scroll.m_nY, scroll.m_nWide, scroll.m_nTall ); - flZoom = pHandler->GetBrowser()->GetZoomLevel(); - if ( scroll.m_nMax < 0 ) - scroll.m_nMax = 0; - if ( flZoom == 0.0f ) - flZoom = 1.0f; - } - - if ( bForceSendUpdate || 0 != memcmp( &pHandler->m_CachedHScroll, &scroll, sizeof( scroll ) ) ) - { - pHandler->m_CachedHScroll = scroll; - - CHTMLProtoBufMsg cmd( eHTMLCommands_HorizontalScrollBarSizeResponse ); - cmd.Body().set_x( scroll.m_nX ); - cmd.Body().set_y( scroll.m_nY ); - cmd.Body().set_wide( scroll.m_nWide ); - cmd.Body().set_tall( scroll.m_nTall ); - cmd.Body().set_scroll_max( scroll.m_nMax ); - cmd.Body().set_scroll( scroll.m_nScroll ); - cmd.Body().set_visible( scroll.m_bVisible != 0 ); - cmd.Body().set_zoom( flZoom ); - int m_iBrowser = iBrowser; - DISPATCH_MESSAGE( eHTMLCommands_HorizontalScrollBarSizeResponse ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: get the size of the verical scrollbar -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserVerticalScrollBarSize( const CHTMLProtoBufMsg &htmlCommand ) -{ - ThreadBrowserVerticalScrollBarSizeHelper( htmlCommand.BodyConst().browser_handle(), true ); -} - - -//----------------------------------------------------------------------------- -// Purpose: tell the main thread details of the vertical scrollbar -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserVerticalScrollBarSizeHelper( int iBrowser, bool bForceSendUpdate ) -{ - CClientHandler::CachedScrollBarState_t scroll = { }; - float flZoom = 0.0f; - int iClient = 0; - if ( BIsValidBrowserHandle( iBrowser, iClient ) ) - { - CClientHandler *pHandler = m_listClientHandlers[iClient]; - - if ( pHandler->GetBrowser() ) - { - scroll.m_nMax = pHandler->GetBrowser()->VerticalScrollMax(); - scroll.m_nScroll = pHandler->GetBrowser()->VerticalScroll(); - scroll.m_bVisible = pHandler->GetBrowser()->IsVeritcalScrollBarVisible(); - pHandler->GetBrowser()->VerticalScrollBarSize( scroll.m_nX, scroll.m_nY, scroll.m_nWide, scroll.m_nTall ); - flZoom = pHandler->GetBrowser()->GetZoomLevel(); - if ( scroll.m_nMax < 0 ) - scroll.m_nMax = 0; - if ( flZoom == 0.0f ) - flZoom = 1.0f; - } - - if ( bForceSendUpdate || 0 != memcmp( &pHandler->m_CachedVScroll, &scroll, sizeof( scroll ) ) ) - { - pHandler->m_CachedVScroll = scroll; - - CHTMLProtoBufMsg cmd( eHTMLCommands_VerticalScrollBarSizeResponse ); - cmd.Body().set_x( scroll.m_nX ); - cmd.Body().set_y( scroll.m_nY ); - cmd.Body().set_wide( scroll.m_nWide ); - cmd.Body().set_tall( scroll.m_nTall ); - cmd.Body().set_scroll_max( scroll.m_nMax ); - cmd.Body().set_scroll( scroll.m_nScroll ); - cmd.Body().set_visible( scroll.m_bVisible != 0 ); - cmd.Body().set_zoom( flZoom ); - int m_iBrowser = iBrowser; - DISPATCH_MESSAGE( eHTMLCommands_VerticalScrollBarSizeResponse ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: start a find in a web page -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserFind( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, Find( 1, htmlCommand.BodyConst().find().c_str(), !htmlCommand.BodyConst().reverse(), false, htmlCommand.BodyConst().infind() ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: dismiss a current find -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserStopFind( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, StopFinding( 1 ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: scroll the page horizontally to this pos -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserSetHorizontalScroll( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, SetHorizontalScroll( htmlCommand.BodyConst().scroll() ) ); - - ThreadBrowserHorizontalScrollBarSizeHelper( htmlCommand.BodyConst().browser_handle(), true ); -} - - -//----------------------------------------------------------------------------- -// Purpose: scroll the page vertically to this pos -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserSetVerticalScroll( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, SetVerticalScroll( htmlCommand.BodyConst().scroll() ) ); - - ThreadBrowserVerticalScrollBarSizeHelper( htmlCommand.BodyConst().browser_handle(), true ); -} - - -//----------------------------------------------------------------------------- -// Purpose: set the zoom to this level, 100% means normal size, 200% double size -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserSetZoomLevel( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, SetZoomLevel( htmlCommand.BodyConst().zoom() ) ); - - CefDoMessageLoopWork(); // tell cef to think one frame, Zoom is in a work queue and we want it to apply right away so think now - - // now tell if about the scrollbars we now have - ThreadBrowserHorizontalScrollBarSizeHelper( htmlCommand.BodyConst().browser_handle(), true ); - ThreadBrowserVerticalScrollBarSizeHelper( htmlCommand.BodyConst().browser_handle(), true ); -} - - -//----------------------------------------------------------------------------- -// Purpose: show the page source in notepad -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserViewSource( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, GetMainFrame()->ViewSource() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: add a header to any requests we make -//----------------------------------------------------------------------------- -void CCEFThread::ThreadAddHeader( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - m_listClientHandlers[iClient]->AddHeader( htmlCommand.BodyConst().key().c_str(), htmlCommand.BodyConst().value().c_str() ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: main thread is done with a paint buffer, tell our handler -//----------------------------------------------------------------------------- -void CCEFThread::ThreadNeedsPaintResponse( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - m_listClientHandlers[iClient]->SetTextureUploaded( htmlCommand.BodyConst().textureid() ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: set the error strings to display on page load -//----------------------------------------------------------------------------- -void CCEFThread::ThreadBrowserErrorStrings( const CHTMLProtoBufMsg &cmd ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( cmd.BodyConst().browser_handle(), iClient ) ) - { - m_listClientHandlers[iClient]->SetErrorStrings( cmd.BodyConst().title().c_str(), cmd.BodyConst().header().c_str(), - cmd.BodyConst().cache_miss().c_str(), cmd.BodyConst().bad_url().c_str(), - cmd.BodyConst().connection_problem().c_str(), - cmd.BodyConst().proxy_problem().c_str(), cmd.BodyConst().unknown().c_str() ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: return the zoom level for the current page, 100% means actual size, 200% double size -//----------------------------------------------------------------------------- -void CCEFThread::ThreadGetZoom( const CHTMLProtoBufMsg &htmlCmd ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCmd.BodyConst().browser_handle(), iClient ) ) - { - float flZoom = 0.0f; - if ( m_listClientHandlers[iClient]->GetBrowser() ) - flZoom = m_listClientHandlers[iClient]->GetBrowser()->GetZoomLevel(); - if ( flZoom == 0.0f ) - flZoom = 1.0f; - if ( flZoom > 100.0f ) - flZoom /= 100.0f; - CHTMLProtoBufMsg cmd( eHTMLCommands_GetZoomResponse ); - cmd.Body().set_zoom( flZoom ); - int m_iBrowser = htmlCmd.BodyConst().browser_handle(); - DISPATCH_MESSAGE( eHTMLCommands_GetZoomResponse ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: handler func to throw the cookie call to the cef IO thread -//----------------------------------------------------------------------------- -void IOT_SetCookie(const CefString& url, CefCookie* cookie, CThreadEvent *pEvent ) -{ - CefSetCookie(url, *cookie); - pEvent->Set(); -} - - -//----------------------------------------------------------------------------- -// Purpose: set this cookie into the cef instance -//----------------------------------------------------------------------------- -void CCEFThread::ThreadSetCookie( const CHTMLProtoBufMsg &htmlCommand ) -{ - CefCookie cookie; - cef_string_utf8_copy( htmlCommand.BodyConst().key().c_str(), htmlCommand.BodyConst().key().size(), &cookie.name ); - cef_string_utf8_copy( htmlCommand.BodyConst().value().c_str(), htmlCommand.BodyConst().value().size(), &cookie.value ); - cef_string_utf8_copy( htmlCommand.BodyConst().path().c_str(), htmlCommand.BodyConst().path().size(), &cookie.path ); - if ( htmlCommand.BodyConst().has_expires() ) - { - AssertMsg( false, "Cookie expiration not implemented -- rtime.cpp needs to be ported." ); - /* - cookie.has_expires = true; - CRTime rtExpire( (RTime32)htmlCommand.BodyConst().expires() ); - cookie.expires.year = rtExpire.GetLocalYear(); - cookie.expires.month = rtExpire.GetLocalMonth(); -#if !defined(OS_MACOSX) - cookie.expires.day_of_week = rtExpire.GetLocalDayOfWeek(); -#endif - cookie.expires.day_of_month = rtExpire.GetLocalMonth(); - cookie.expires.hour = rtExpire.GetLocalHour(); - cookie.expires.minute = rtExpire.GetLocalMinute(); - cookie.expires.second = rtExpire.GetLocalSecond(); - */ - } - - CThreadEvent event; - CefPostTask(TID_IO, NewCefRunnableFunction( IOT_SetCookie, htmlCommand.BodyConst().host().c_str(), &cookie, &event)); - event.Wait(); -} - - -struct CCefCookie -{ - CUtlString sValue; - CUtlString sName; - CUtlString sDomain; - CUtlString sPath; -}; -//----------------------------------------------------------------------------- -// Purpose: helper class to iterator cookies -//----------------------------------------------------------------------------- -class CookieVisitor : public CefCookieVisitor { -public: - CookieVisitor( CUtlVector *pVec, CThreadEvent *pEvent ) - { - m_pVecCookies = pVec; - m_pEvent = pEvent; - } - ~CookieVisitor() - { - m_pEvent->Set(); - } - - virtual bool Visit(const CefCookie& cookie, int count, int total, - bool& deleteCookie) { - - CCefCookie cookieCopy; - cookieCopy.sValue = cookie.value.str; - cookieCopy.sName = cookie.name.str; - cookieCopy.sDomain = cookie.domain.str; - cookieCopy.sPath = cookie.path.str; - m_pVecCookies->AddToTail( cookieCopy ); - return true; - } - -private: - CUtlVector *m_pVecCookies; - CThreadEvent *m_pEvent; - - IMPLEMENT_REFCOUNTING(CookieVisitor); -}; - - -//----------------------------------------------------------------------------- -// Purpose: handler func to throw the cookie call to the cef IO thread -//----------------------------------------------------------------------------- -void IOT_CookiesForURL(const CefString& url, CThreadEvent *pEvent, CUtlVector *pVecCookies ) -{ - CefVisitUrlCookies( url, false, new CookieVisitor( pVecCookies, pEvent ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: get all the cookies for this URL -//----------------------------------------------------------------------------- -void CCEFThread::ThreadGetCookiesForURL( const CHTMLProtoBufMsg &htmlCommand ) -{ - CUtlVector vecCookies; - CThreadEvent event; - CefPostTask(TID_IO, NewCefRunnableFunction( IOT_CookiesForURL, htmlCommand.BodyConst().url().c_str(), &event, &vecCookies )); - event.Wait(); - - CHTMLProtoBufMsg cmd( eHTMLCommands_GetCookiesForURLResponse ); - int m_iBrowser = htmlCommand.BodyConst().browser_handle(); - cmd.Body().set_url( htmlCommand.BodyConst().url() ); - - FOR_EACH_VEC( vecCookies, i ) - { - CCookie *pCookie = cmd.Body().add_cookies(); - pCookie->set_name( vecCookies[i].sName ); - pCookie->set_value( vecCookies[i].sValue ); - pCookie->set_domain( vecCookies[i].sDomain ); - pCookie->set_path( vecCookies[i].sPath ); - } - - DISPATCH_MESSAGE( eHTMLCommands_GetCookiesForURLResponse ); -} - - -//----------------------------------------------------------------------------- -// Purpose: set the framerate to run CEF at -//----------------------------------------------------------------------------- -void CCEFThread::ThreadSetTargetFrameRate( const CHTMLProtoBufMsg &htmlCommand ) -{ - m_nTargetFrameRate = htmlCommand.BodyConst().ntargetframerate(); -} - - -//----------------------------------------------------------------------------- -// Purpose: hide any showing popup for this browser -//----------------------------------------------------------------------------- -void CCEFThread::ThreadHidePopup( const CHTMLProtoBufMsg &htmlCommand ) -{ - GET_BROSWER_FUNC( htmlCommand, HidePopup() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: request a full redraw of the client -//----------------------------------------------------------------------------- -void CCEFThread::ThreadFullRepaint( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - if ( m_listClientHandlers[iClient]->GetBrowser()->IsVisuallyNonEmpty() ) - { - int wide, tall; - m_listClientHandlers[iClient]->GetExpectedSize( wide, tall); - CefRect rect; - rect.x = rect.y = 0; - rect.width = wide; - rect.height = tall; - m_listClientHandlers[iClient]->GetBrowser()->Invalidate( rect ); - } - else - m_listClientHandlers[iClient]->GetBrowser()->Reload(); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: configure the options we want for cef -//----------------------------------------------------------------------------- -void CCEFThread::AppGetSettings(CefSettings& settings, CefRefPtr& app) -{ - settings.multi_threaded_message_loop = false; -#if defined(OS_WIN) - settings.auto_detect_proxy_settings_enabled = true; -#endif - - CefString(&settings.cache_path) = m_sHTMLCacheDir; - CefString(&settings.product_version) = "Steam"; -// CefString(&settings.log_file) = -/*#ifdef WIN32 - settings.graphics_implementation = ANGLE_IN_PROCESS_COMMAND_BUFFER; -#else*/ - settings.graphics_implementation = DESKTOP_IN_PROCESS_COMMAND_BUFFER; -//#endif -} - - -//----------------------------------------------------------------------------- -// Purpose: clean up the temp folders cef can leave around on crash -//----------------------------------------------------------------------------- -void CCEFThread::CleanupTempFolders() -{ - /* - // Temporarily commented out to avoid bringing in additional code. -#if defined( WIN32 ) - char rgchPath[MAX_PATH]; - if ( GetTempPathA( Q_ARRAYSIZE( rgchPath ), rgchPath ) == 0 ) - return; - - CUtlString strPath = rgchPath; -#elif defined( LINUX ) || defined( OSX ) - // TMPDIR first, T_tmpdir next, /tmp last. - char *pszDir = getenv( "TMPDIR" ); - if ( pszDir == NULL ) - { - pszDir = P_tmpdir; - if ( pszDir == NULL ) - pszDir = "/tmp"; - } - if ( pszDir == NULL ) - return; - - CUtlString strPath = pszDir; -#endif - - if ( strPath[strPath.Length()-1] != CORRECT_PATH_SEPARATOR ) - strPath += CORRECT_PATH_SEPARATOR_S; - - CUtlString strSearch = strPath; - strSearch += "scoped_dir*"; - CDirIterator tempDirIterator( strSearch.String() ); - - while ( tempDirIterator.BNextFile() ) - { - if ( tempDirIterator.BCurrentIsDir() ) - { - CUtlString sStrDir = strPath; - sStrDir += tempDirIterator.CurrentFileName(); - BRemoveDirectoryRecursive( sStrDir ); - } - } - */ -} - - -//----------------------------------------------------------------------------- -// Purpose: main thread for pumping CEF, get commands from the main thread and dispatches responses to it -//----------------------------------------------------------------------------- -int CCEFThread::Run() -{ - { // scope to trigger destructors before setting the we have exited event - CefSettings settings; - CefRefPtr app; - - // blow away any temp folders CEF left lying around if we crashed last - CleanupTempFolders(); - - // Populate the settings based on command line arguments. - AppGetSettings(settings, app); - - // Initialize CEF. - CefInitialize(settings, app, ""); - - #if defined( VPROF_ENABLED ) - //CVProfile *pProfile = GetVProfProfileForCurrentThread(); - #endif - - CLimitTimer timer; - CLimitTimer timerLastCefThink; // track when we think cef so we can do it at a minimum of 10hz - timerLastCefThink.SetLimit( k_nMillion ); // 1Hz min think time -#ifdef WIN32 - CLimitTimer timerLastFlashFullscreenThink; // track when we think cef so we can do it at a minimum of 10hz - timerLastFlashFullscreenThink.SetLimit( k_nMillion * k_nMillion ); // think again in the distance future - bool bInitialThinkAfterInput = false; -#endif - while( !m_bExit ) - { - #ifdef OSX - void *pool = CreateAutoReleasePool(); - #endif - - if ( m_bSleepForValidate ) - { - m_bSleepingForValidate = true; - ThreadSleep( 100 ); - continue; - } - m_bSleepingForValidate = false; - - m_bSawUserInputThisFrame = false; - - #if defined( VPROF_ENABLED ) - //if ( pProfile ) - // pProfile->MarkFrame( "UI CEF HTML Thread" ); - #endif - // Limit animation frame rate - timer.SetLimit( k_nMillion/m_nTargetFrameRate ); - - // run any pending commands, get ack'd paint buffers - { - VPROF_BUDGET( "CCEFThread - RunCurrentCommands()", VPROF_BUDGETGROUP_VGUI ); - RunCurrentCommands(); - } - - // now let cef think - if ( m_listClientHandlers.Count() || timerLastCefThink.BLimitReached() ) - { - VPROF_BUDGET( "CCEFThread - CefDoMessageLoopWork()", VPROF_BUDGETGROUP_TENFOOT ); - CefDoMessageLoopWork(); - timerLastCefThink.SetLimit( k_nMillion ); // 1Hz min think time - } - #ifdef OSX - ReleaseAutoReleasePool( pool ); - #endif - - // push any changes to scrollbar data and refresh HTML element hover states - { - VPROF_BUDGET( "CCEFThread - Scroll", VPROF_BUDGETGROUP_VGUI ); - FOR_EACH_LL( m_listClientHandlers, i ) - { - int nBrowser = BROWSER_HANDLE_FROM_INDEX_SERIAL( i, m_listClientHandlers[i]->NSerial() ); - ThreadBrowserHorizontalScrollBarSizeHelper( nBrowser, false ); - ThreadBrowserVerticalScrollBarSizeHelper( nBrowser, false ); - - // workaround a CEF issue where mouse hover states don't update after scrolling - m_listClientHandlers[i]->RefreshCEFHoverStatesAfterScroll(); - } - } - - { - VPROF_BUDGET( "CCEFThread - SendSizeChangeEvents", VPROF_BUDGETGROUP_VGUI ); - SendSizeChangeEvents(); - } - - //see if we need a paint - { - VPROF_BUDGET( "CCEFThread - Paint", VPROF_BUDGETGROUP_VGUI ); - FOR_EACH_LL( m_listClientHandlers, i ) - { - if ( !m_listClientHandlers[i]->GetBrowser() ) - continue; - - m_listClientHandlers[i]->Paint(); - if ( m_listClientHandlers[i]->BPaintBufferReady() && m_listClientHandlers[i]->BNeedsPaint() ) - { - uint32 textureID = m_listClientHandlers[i]->FlipTexture(); - const byte *pRGBA = m_listClientHandlers[i]->PComposedTextureData( textureID ); - if ( pRGBA ) - { - CClientHandler *pHandler = m_listClientHandlers[i]; - pHandler->Lock(); - if ( pHandler->BPendingScreenShot() ) - { - pHandler->SavePageToJPEGIfNeeded( pHandler->GetBrowser(), pRGBA, pHandler->GetTextureWide(), pHandler->GetTextureTall() ); - } - CHTMLProtoBufMsg cmd( eHTMLCommands_NeedsPaint ); - cmd.Body().set_browser_handle( BROWSER_HANDLE_FROM_INDEX_SERIAL( i, pHandler->NSerial() ) ); - cmd.Body().set_wide( pHandler->GetTextureWide() ); - cmd.Body().set_tall( pHandler->GetTextureTall() ); - cmd.Body().set_rgba( (uint64)pRGBA ); - cmd.Body().set_pageserial( pHandler->GetPageSerial() ); - cmd.Body().set_textureid( textureID ); - cmd.Body().set_updatex( pHandler->GetUpdateX() ); - cmd.Body().set_updatey( pHandler->GetUpdateY() ); - cmd.Body().set_updatewide( pHandler->GetUpdateWide() ); - cmd.Body().set_updatetall( pHandler->GetUpdateTall() ); - cmd.Body().set_scrollx( pHandler->GetBrowser()->HorizontalScroll() ); - cmd.Body().set_scrolly( pHandler->GetBrowser()->VerticalScroll() ); - - if ( pHandler->BPopupVisibleAndPainted() ) // add in the combo box's texture data if visible - { - int x,y,wide,tall; // popup sizes - pHandler->PopupRect( x, y, wide, tall ); - cmd.Body().set_combobox_rgba( (uint64)pHandler->PPopupTextureDataCached() ); - cmd.Body().set_combobox_wide( wide ); - cmd.Body().set_combobox_tall( tall ); - } - - // Texture update rect has now been pushed to main thread - pHandler->ClearUpdateRect(); - - HTMLCommandBuffer_t *pBuf = g_CEFThread.GetFreeCommandBuffer( eHTMLCommands_NeedsPaint, i ); - cmd.SerializeCrossProc( &pBuf->m_Buffer ); - pHandler->Unlock(); - - g_CEFThread.PushResponse( pBuf ); - } - else - { - m_listClientHandlers[i]->SetTextureUploaded( textureID ); - } - } - } - } - -#ifdef WIN32 - if ( m_bSawUserInputThisFrame ) - { - if ( timerLastFlashFullscreenThink.CMicroSecLeft() > k_nMillion/10 || timerLastFlashFullscreenThink.BLimitReached() ) - { - timerLastFlashFullscreenThink.SetLimit( k_nMillion/10 ); // check in 100msec - } - bInitialThinkAfterInput = true; - } - - if ( m_listClientHandlers.Count() && timerLastFlashFullscreenThink.BLimitReached() ) - { - CheckForFullScreenFlashControl(); - if ( ( !m_bFullScreenFlashVisible && bInitialThinkAfterInput ) || m_bFullScreenFlashVisible ) - { - timerLastFlashFullscreenThink.SetLimit( k_nMillion ); // could be a slow machine, check again in 1 sec - } - else - { - timerLastFlashFullscreenThink.SetLimit( k_nMillion * k_nMillion ); // think again in the distance future - } - - bInitialThinkAfterInput= false; - } -#endif - - { - VPROF_BUDGET( "Sleep - FPS Limiting", VPROF_BUDGETGROUP_TENFOOT ); - if ( timer.BLimitReached() ) - ThreadSleep( 1 ); - else - m_WakeEvent.Wait( timer.CMicroSecLeft() / 1000 ); - } - } - - FOR_EACH_LL( m_listClientHandlers, i ) - { - if ( m_listClientHandlers[i] ) - m_listClientHandlers[i]->CloseBrowser(); - m_listClientHandlers[i] = NULL;; - } - m_listClientHandlers.RemoveAll(); - - CefDoMessageLoopWork(); // pump the message loop to clear the close browser calls from above - - CefShutdown(); - } - m_eventDidExit.Set(); - return 0; -} - - -//----------------------------------------------------------------------------- -// Purpose: special code to sniff for flash doing its usual naughty things -//----------------------------------------------------------------------------- -void CCEFThread::CheckForFullScreenFlashControl() -{ -#ifdef WIN32 - VPROF_BUDGET( "Searching for Flash fullscreen - FindWindowEx", VPROF_BUDGETGROUP_TENFOOT ); - - // see if we need to drag the flash fullscreen window to front - HWND flashfullscreenHWND = ::FindWindowEx( NULL, NULL, "ShockwaveFlashFullScreen", NULL ); - if ( flashfullscreenHWND ) - { - DWORD proccess_id; - GetWindowThreadProcessId( flashfullscreenHWND, &proccess_id); - TCHAR exe_path[MAX_PATH]; - GetModuleFileName( GetModuleHandle(NULL), exe_path, MAX_PATH); - HANDLE hmodule = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, proccess_id); - MODULEENTRY32 mod = { sizeof(MODULEENTRY32) }; - if ( Module32First( hmodule, &mod) ) - { - if ( Q_stricmp(mod.szExePath, exe_path) == 0 ) - { - if ( !m_bFullScreenFlashVisible ) - { - m_bFullScreenFlashVisible = true; - m_flashfullscreenHWND = flashfullscreenHWND; - - FOR_EACH_LL( m_listClientHandlers, i ) - { - if ( m_listClientHandlers[i] ) - m_listClientHandlers[i]->GetRenderHandler()->OnEnterFullScreen( m_listClientHandlers[i]->GetBrowser() ); - } - - SetForegroundWindow( m_flashfullscreenHWND ); - } - } - else - { - if ( m_bFullScreenFlashVisible ) - { - m_bFullScreenFlashVisible = false; - m_flashfullscreenHWND = NULL; - FOR_EACH_LL( m_listClientHandlers, i ) - { - if ( m_listClientHandlers[i] ) - m_listClientHandlers[i]->GetRenderHandler()->OnExitFullScreen( m_listClientHandlers[i]->GetBrowser() ); - } - } - } - } - CloseHandle( hmodule ); - } - else - { - if ( m_bFullScreenFlashVisible ) - { - m_bFullScreenFlashVisible = false; - m_flashfullscreenHWND = NULL; - FOR_EACH_LL( m_listClientHandlers, i ) - { - if ( m_listClientHandlers[i] ) - m_listClientHandlers[i]->GetRenderHandler()->OnExitFullScreen( m_listClientHandlers[i]->GetBrowser() ); - } - } - } -#else -#warning "Do we need to sniff for fullscreen flash and it breaking us?" -#endif -} - - -#ifdef DBGFLAG_VALIDATE -//----------------------------------------------------------------------------- -// Purpose: validate mem -//----------------------------------------------------------------------------- -void CCEFThread::Validate( CValidator &validator, const tchar *pchName ) -{ - // hacky but reliable way to avoid both vgui and panorama validating all this stuff twice - if ( !validator.IsClaimed( m_sHTMLCacheDir.Access() ) ) - { - VALIDATE_SCOPE(); - ValidateObj( m_sHTMLCacheDir ); - ValidateObj( m_sCookiePath ); - ValidateObj( m_listClientHandlers ); - FOR_EACH_LL( m_listClientHandlers, i ) - { - ValidatePtr( m_listClientHandlers[i] ); - } - ValidateObj( m_vecQueueCommands ); - FOR_EACH_VEC( m_vecQueueCommands, i ) - { - ValidatePtr( m_vecQueueCommands[i] ); - } - ValidateObj( m_vecQueueResponses ); - FOR_EACH_VEC( m_vecQueueResponses, i ) - { - ValidatePtr( m_vecQueueResponses[i] ); - } - - ValidateObj( m_tslUnsedBuffers ); - { - CTSList::Node_t *pNode = m_tslUnsedBuffers.Detach(); - while ( pNode ) - { - CTSList::Node_t *pNext = (CTSList::Node_t *)pNode->Next; - ValidatePtr( pNode->elem ); - m_tslUnsedBuffers.Push( pNode ); - pNode = pNext; - } - } - - ValidateObj( m_tslCommandBuffers ); - ValidateObj( m_tslResponseBuffers ); - } -} -#endif - - -//----------------------------------------------------------------------------- -// Purpose: turn on CEF and its supporting thread -//----------------------------------------------------------------------------- -void ChromeInit( const char *pchHTMLCacheDir, const char *pchCookiePath ) -{ - Assert( !g_CEFThread.IsAlive() ); - g_CEFThread.SetCEFPaths( pchHTMLCacheDir, pchCookiePath ); - g_CEFThread.SetName( "UICEFThread" ); - g_CEFThread.Start(); -} - - -//----------------------------------------------------------------------------- -// Purpose: turn off CEF -//----------------------------------------------------------------------------- -void ChromeShutdown() -{ - g_CEFThread.TriggerShutdown(); - g_CEFThread.Join( 20 *k_nThousand ); -} - - -#ifdef DBGFLAG_VALIDATE -//----------------------------------------------------------------------------- -// Purpose: suspend the cef thread so we can validate mem -//----------------------------------------------------------------------------- -bool ChromePrepareForValidate() -{ - g_CEFThread.SleepForValidate(); - while ( !g_CEFThread.BSleepingForValidate() ) - ThreadSleep( 100 ); - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: wake the cef thread back up -//----------------------------------------------------------------------------- -bool ChromeResumeFromValidate() -{ - g_CEFThread.WakeFromValidate(); - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ChromeValidate( CValidator &validator, const char *pchName ) -{ - g_CEFThread.Validate( validator, "g_CEFThread" ); -} -#endif - - -//----------------------------------------------------------------------------- -// Purpose: set this cookie to be used by cef -//----------------------------------------------------------------------------- -bool ChromeSetWebCookie( const char *pchHostname, const char *pchName, const char *pchValue, const char *pchPath, RTime32 nExpires ) -{ - CHTMLProtoBufMsg< CMsgSetCookie > cmd( eHTMLCommands_SetCookie ); - cmd.Body().set_value( pchValue ); - cmd.Body().set_key( pchName ); - cmd.Body().set_path( pchPath ); - cmd.Body().set_host( pchHostname ); - if ( nExpires ) - cmd.Body().set_expires( nExpires ); - - HTMLCommandBuffer_t *pBuf = g_CEFThread.GetFreeCommandBuffer( eHTMLCommands_SetCookie, -1 ); - cmd.SerializeCrossProc( &pBuf->m_Buffer ); - g_CEFThread.PushCommand( pBuf ); - g_CEFThread.WakeThread(); - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: set this cookie to be used by cef -//----------------------------------------------------------------------------- -bool ChromeGetWebCookiesForURL( CUtlString *pstrValue, const char *pchURL, const char *pchName ) -{ - pstrValue->Clear(); - - { - CHTMLProtoBufMsg cmd( eHTMLCommands_GetCookiesForURL ); - cmd.Body().set_url( pchURL ); - - HTMLCommandBuffer_t *pCmd = g_CEFThread.GetFreeCommandBuffer( eHTMLCommands_GetCookiesForURL, -1 ); - cmd.SerializeCrossProc( &pCmd->m_Buffer ); - g_CEFThread.PushCommand( pCmd ); - } - - HTMLCommandBuffer_t *pBuf = g_CEFThread.BWaitForResponse( eHTMLCommands_GetCookiesForURLResponse, -1 ); - if ( pBuf ) - { - CHTMLProtoBufMsg< CMsgGetCookiesForURLResponse > cmdResponse( eHTMLCommands_GetCookiesForURLResponse ); - if ( cmdResponse.BDeserializeCrossProc( &pBuf->m_Buffer ) ) - { - for ( int i = 0; i < cmdResponse.BodyConst().cookies_size(); i++ ) - { - const CCookie &cookie = cmdResponse.BodyConst().cookies(i); - if ( cookie.name() == pchName ) - pstrValue->Set( cookie.value().c_str() ); - } - } - g_CEFThread.ReleaseCommandBuffer( pBuf ); - } - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: set the build number to report in our user agent -//----------------------------------------------------------------------------- -void ChromeSetClientBuildID( uint64 ulBuildID ) -{ - sm_ulBuildID = ulBuildID; -} - - -//----------------------------------------------------------------------------- -// Purpose: constructor -//----------------------------------------------------------------------------- -CChromePainter::CChromePainter( CClientHandler *pParent ) -{ - m_iNextTexture = 0; - m_iTexturesInFlightBits = 0; - m_pParent = pParent; - m_bUpdated = false; - m_bPopupVisible = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: destructor -//----------------------------------------------------------------------------- -CChromePainter::~CChromePainter() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: cef is calling us back and saying it updated the html texture -//----------------------------------------------------------------------------- -void CChromePainter::OnPaint(CefRefPtr browser, PaintElementType type, const RectList& dirtyRects, const void* buffer) -{ - VPROF_BUDGET( "CChromePainter::DrawSubTextureRGBA", VPROF_BUDGETGROUP_VGUI ); - - int wide, tall; - browser->GetSize( type, wide, tall ); - - if ( wide <= 0 || tall <= 0 ) - return; - - if ( type == PET_POPUP ) - { - m_nPopupWide = wide; - m_nPopupTall = tall; - - m_PopupTexture.EnsureCount( tall*wide*4 ); - Q_memcpy( m_PopupTexture.Base(), buffer, tall*wide*4 ); - - // force a recomposition + display whenever painting a popup - m_bUpdated = true; - } - else - { - // main browser painting - - if ( !m_pParent->IsVisuallyNonEmpty() ) - { - return; - } - - // If there were no dirty regions (unlikely), perhaps due to a bug, be conservative and paint all - if ( dirtyRects.empty() ) - { - m_MainTexture.MarkAllDirty(); - } - else - { - for ( RectList::const_iterator iter = dirtyRects.begin(); iter != dirtyRects.end(); ++iter ) - { - m_MainTexture.MarkDirtyRect( iter->x, iter->y, iter->x + iter->width, iter->y + iter->height ); - } - } - - // Refresh all dirty main texture pixels from the chromium rendering buffer - if ( m_MainTexture.BUpdatePixels( (byte*)buffer, wide, tall ) ) - { - // at least one pixel in the main texture has changed - m_bUpdated = true; - - // Notify the main thread that this newly painted region is dirty - m_UpdateRect.MarkDirtyRect( m_MainTexture ); - - // Merge update region into all buffer textures so that at composition time, - // they know to copy the union of all updates since the last composition - for ( size_t i = 0; i < Q_ARRAYSIZE(m_Texture); ++i ) - { - m_Texture[i].MarkDirtyRect( m_MainTexture ); - } - } - - // The main texture is now a clean copy of chromium's canvas backing - m_MainTexture.MarkAllClean(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: true if we have had a paint call from cef -//----------------------------------------------------------------------------- -bool CChromePainter::BUpdated() -{ - return m_bUpdated; -} - - -//----------------------------------------------------------------------------- -// Purpose: force the updated state -//----------------------------------------------------------------------------- -void CChromePainter::SetUpdated( bool state ) -{ - m_bUpdated = state; -} - - -//----------------------------------------------------------------------------- -// Purpose: move to the next html texture to render into -//----------------------------------------------------------------------------- -uint32 CChromePainter::FlipTexture() -{ - int iTex = m_iNextTexture; - m_iTexturesInFlightBits |= ( 1< 0 && m_MainTexture.GetTall() > 0) && !( m_iTexturesInFlightBits & (1< parentBrowser, - const CefPopupFeatures& popupFeatures, - CefWindowInfo& windowInfo, - const CefString& url, - bool bForeground, - CefRefPtr& client, - CefBrowserSettings& settings ) -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_OpenNewTab ); - cmd.Body().set_url( url.c_str() ); - cmd.Body().set_bforeground( bForeground ); - - DISPATCH_MESSAGE( eHTMLCommands_OpenNewTab ); - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Called before a new popup window is created. The |parentBrowser| parameter -// will point to the parent browser window. The |popupFeatures| parameter will -// contain information about the style of popup window requested. Return false -// to have the framework create the new popup window based on the parameters -// in |windowInfo|. Return true to cancel creation of the popup window. By -// default, a newly created popup window will have the same client and -// settings as the parent window. To change the client for the new window -// modify the object that |client| points to. To change the settings for the -// new window modify the |settings| structure. -//----------------------------------------------------------------------------- -bool CClientHandler::OnBeforePopup(CefRefPtr parentBrowser, - const CefPopupFeatures& popupFeatures, - CefWindowInfo& windowInfo, - const CefString& url, - CefRefPtr& client, - CefBrowserSettings& settings) -{ - // If it's a steam:// url we already have a scheme handler installed, however that's too late to block the frame navigating - // and we'll end up loading a blank new window. So preempt that happening here and handle early returning that we've handled so - // chromium won't actually change URLs or navigate at all. - if ( url.size() > 0 && ( Q_stristr( url.c_str(), "steam://" ) || Q_stristr( url.c_str(), "steambeta://" ) ) && Q_stristr( url.c_str(), "/close" ) == NULL ) - { - CStrAutoEncode urlString( url.c_str() ); - CHTMLProtoBufMsg cmd( eHTMLCommands_OpenSteamURL ); - cmd.Body().set_url( urlString.ToString() ); - DISPATCH_MESSAGE( eHTMLCommands_OpenSteamURL ); - - return true; - } - - CStrAutoEncode urlString( url.c_str() ); - static bool bInPopup = false; - // if we get an empty url string when loading a new popup page just don't load it, we don't support the make a - // new popup and .write() into the buffer to make the contents because we want to make a whole new VGUI HTML object - // that contains the webkit widget for that popup, not allow this inline one - if ( urlString.ToString() && Q_strlen( urlString.ToString() ) > 0 ) - { - if ( !bInPopup ) - { - bInPopup = true; - CHTMLProtoBufMsg cmd( eHTMLCommands_PopupHTMLWindow ); - cmd.Body().set_url( urlString.ToString() ); - if ( popupFeatures.xSet ) - cmd.Body().set_x( popupFeatures.x ); - if ( popupFeatures.ySet ) - cmd.Body().set_y( popupFeatures.y ); - if ( popupFeatures.widthSet ) - cmd.Body().set_wide( popupFeatures.width ); - if ( popupFeatures.heightSet ) - cmd.Body().set_tall( popupFeatures.height ); - - DISPATCH_MESSAGE( eHTMLCommands_PopupHTMLWindow ); - bInPopup = false; - return true; - } - } - if ( !bInPopup ) - { - return true; - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Event called after a new window is created. The return value is currently -// ignored. -//----------------------------------------------------------------------------- -void CClientHandler::OnAfterCreated(CefRefPtr browser) -{ - Lock(); - if ( !m_Browser ) - { - // We need to keep the main child window, but not popup windows - m_Browser = browser; - browser->SetShowScrollBars( false ); - SetBrowserAgent( browser ); - if ( m_nExpectedWide > 0 && m_nExpectedTall > 0 ) - browser->SetSize( PET_VIEW, m_nExpectedWide, m_nExpectedTall ); - - CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserReady ); - DISPATCH_MESSAGE( eHTMLCommands_BrowserReady ); - } - Unlock(); -} - - - -//----------------------------------------------------------------------------- -// Purpose: -// Event called when the page title changes. The return value is currently -// ignored. -//----------------------------------------------------------------------------- -void CClientHandler::OnTitleChange(CefRefPtr browser, const CefString& title) -{ - if ( !title.empty() ) - { - CHTMLProtoBufMsg cmd( eHTMLCommands_SetHTMLTitle ); - cmd.Body().set_title( title ); - - DISPATCH_MESSAGE( eHTMLCommands_SetHTMLTitle ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Event called before browser navigation. The client has an opportunity to -// modify the |request| object if desired. Return RV_HANDLED to cancel -// navigation. -//----------------------------------------------------------------------------- -bool CClientHandler::OnBeforeBrowse(CefRefPtr browser, - CefRefPtr frame, - CefRefPtr request, - NavType navType, - bool isRedirect, - bool isNewTabRequest - ) -{ - std::string sURL = request->GetURL(); - - // If it's a steam:// url we already have a scheme handler installed, however that's too late to block the frame navigating - // and we'll end up loading a blank page. So preempt that happening here and handle early returning that we've handled so - // chromium won't actually change URLs or navigate at all. - if ( sURL.size() > 0 && (sURL.find( "steam://" ) == 0 || sURL.find( "steambeta://" ) == 0) && sURL.find( "/close" ) == std::wstring::npos ) - { - CHTMLProtoBufMsg cmd( eHTMLCommands_OpenSteamURL ); - cmd.Body().set_url( CStrAutoEncode( request->GetURL().c_str() ).ToString() ); - DISPATCH_MESSAGE( eHTMLCommands_OpenSteamURL ); - - return true ; - } - - if ( isNewTabRequest ) - return false; - - // We only care about these on the main frame - if( !frame.get() || !frame->IsMain() ) - return false; - - if ( request->GetPostData() ) - { - CefPostData::ElementVector elements; - request->GetPostData()->GetElements( elements ); - CefPostData::ElementVector::const_iterator it = elements.begin(); - m_strPostData = ""; - CUtlVector vecPostBytes; - while ( it != elements.end() ) - { - if ( it->get()->GetType() == PDE_TYPE_BYTES ) - { - size_t nBytes = it->get()->GetBytesCount(); - int curInsertPos = vecPostBytes.Count(); - vecPostBytes.EnsureCount( curInsertPos + nBytes + 1 ); - it->get()->GetBytes( nBytes, vecPostBytes.Base() + curInsertPos ); - vecPostBytes[ curInsertPos + nBytes ] = 0; - } - it++; - } - - m_strPostData = vecPostBytes.Base(); - } - else - m_strPostData = ""; - - CStrAutoEncode strURL( sURL.c_str() ); - - if ( isRedirect ) - m_strLastRedirectURL = strURL.ToString(); - - bool rv = false; - - { - // scope this so the wait below doesn't keep this allocation on the stack - CHTMLProtoBufMsg cmd( eHTMLCommands_StartRequest ); - cmd.Body().set_url( strURL.ToString() ); - CefString frameName = frame->GetName(); - if ( !frameName.empty() ) - cmd.Body().set_target( frameName.c_str() ); - cmd.Body().set_postdata( m_strPostData ); - cmd.Body().set_bisredirect( isRedirect ); - - DISPATCH_MESSAGE( eHTMLCommands_StartRequest ); - } - - HTMLCommandBuffer_t *pBuf = g_CEFThread.BWaitForCommand( eHTMLCommands_StartRequestResponse, m_iBrowser ); - if ( pBuf ) - { - CHTMLProtoBufMsg< CMsgStartRequestResponse > cmd( eHTMLCommands_StartRequestResponse ); - if ( cmd.BDeserializeCrossProc( &pBuf->m_Buffer ) ) - { - if ( !cmd.BodyConst().ballow() ) - rv = true ; - } - g_CEFThread.ReleaseCommandBuffer( pBuf ); - } - - if ( m_Snapshot.m_sURLSnapshot.IsValid() ) - m_Snapshot.m_flRequestTimeout = Plat_FloatTime(); // before we change URL lets queue up a snapshot for the next paint - - return rv; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Event called when the browser begins loading a page. The |frame| pointer -// will be empty if the event represents the overall load status and not the -// load status for a particular frame. The return value is currently ignored. -//----------------------------------------------------------------------------- -void CClientHandler::OnLoadStart(CefRefPtr browser, - CefRefPtr frame, bool bIsNewNavigation ) -{ - if ( !frame.get() ) - return; - - { - if ( !frame->IsMain() ) - return; - - std::wstring sURL = frame->GetURL(); - if ( sURL.empty() ) - return; - - CStrAutoEncode url( sURL.c_str() ); - m_strCurrentUrl = url.ToString(); - - if ( m_strCurrentUrl.IsEmpty() ) - return; - - bool bIsRedirect = false; - if ( m_strCurrentUrl == m_strLastRedirectURL ) - bIsRedirect = true; - - CHTMLProtoBufMsg cmd( eHTMLCommands_URLChanged ); - cmd.Body().set_url( url.ToString() ); - cmd.Body().set_bnewnavigation( bIsNewNavigation ); - - if ( !m_strPostData.IsEmpty() ) - cmd.Body().set_postdata( m_strPostData.String() ); - - cmd.Body().set_bisredirect( bIsRedirect ); - CefString frameName = frame->GetName(); - if ( !frameName.empty() ) - cmd.Body().set_pagetitle( frameName.c_str() ); - - DISPATCH_MESSAGE( eHTMLCommands_URLChanged ); - } - - { - CHTMLProtoBufMsg cmd( eHTMLCommands_CanGoBackandForward ); - cmd.Body().set_bgoback( browser->CanGoBack() ); - cmd.Body().set_bgoforward( browser->CanGoForward() ); - DISPATCH_MESSAGE( eHTMLCommands_CanGoBackandForward ); - } - - m_nPageSerial = m_nPendingPageSerial; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Event called when the browser is done loading a page. The |frame| pointer -// will be empty if the event represents the overall load status and not the -// load status for a particular frame. This event will be generated -// irrespective of whether the request completes successfully. The return -// value is currently ignored. -//----------------------------------------------------------------------------- -void CClientHandler::OnLoadEnd(CefRefPtr browser, - CefRefPtr frame, - int httpStatusCode, CefRefPtr request ) -{ - // We only care about these on the main frame - if( !frame.get() || !frame->IsMain() ) - return; - - { - CHTMLProtoBufMsg cmd( eHTMLCommands_FinishedRequest ); - cmd.Body().set_url( CStrAutoEncode( browser->GetMainFrame()->GetURL().c_str() ).ToString() ); - CefString frameName = browser->GetMainFrame()->GetName(); - if ( !frameName.empty() ) - cmd.Body().set_pagetitle( frameName.c_str() ); - - if ( request.get() ) - { - CefRequest::HeaderMap headerMap; - request->GetHeaderMap( headerMap ); - - CefRequest::HeaderMap::const_iterator it; - for(it = headerMap.begin(); it != headerMap.end(); ++it) - { - CHTMLHeader *pHeader = cmd.Body().add_headers(); - if ( !it->first.empty() ) - pHeader->set_key( it->first.c_str() ); - if ( !it->second.empty() ) - pHeader->set_value( it->second.c_str() ); - } - - CefRefPtr pRefSecurityDetails = request->SecurityDetails(); - CHTMLPageSecurityInfo *pSecurityInfo = cmd.Body().mutable_security_info(); - if ( pRefSecurityDetails.get() ) - { - pSecurityInfo->set_bissecure( pRefSecurityDetails->BIsSecure() ); - pSecurityInfo->set_bhascerterror( pRefSecurityDetails->BHasCertError() ); - pSecurityInfo->set_issuername( pRefSecurityDetails->PchCertIssuer() ); - pSecurityInfo->set_certname( pRefSecurityDetails->PchCertCommonName() ); - pSecurityInfo->set_certexpiry( pRefSecurityDetails->TCertExpiry() ); - pSecurityInfo->set_bisevcert( pRefSecurityDetails->BIsEVCert() ); - pSecurityInfo->set_ncertbits( pRefSecurityDetails->NCertBits() ); - } - else - { - pSecurityInfo->set_bissecure( false ); - } - } - - DISPATCH_MESSAGE( eHTMLCommands_FinishedRequest ); - } - - { - CHTMLProtoBufMsg cmd( eHTMLCommands_CanGoBackandForward ); - cmd.Body().set_bgoback( browser->CanGoBack() ); - cmd.Body().set_bgoforward( browser->CanGoForward() ); - DISPATCH_MESSAGE( eHTMLCommands_CanGoBackandForward ); - } - - if ( m_Snapshot.m_sURLSnapshot.IsValid() ) - m_Snapshot.m_flRequestTimeout = Plat_FloatTime(); // finished page load, lets queue up a snapshot for the next paint -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Called when the browser fails to load a resource. |errorCode| is the -// error code number and |failedUrl| is the URL that failed to load. To -// provide custom error text assign the text to |errorText| and return -// RV_HANDLED. Otherwise, return RV_CONTINUE for the default error text. -//----------------------------------------------------------------------------- -bool CClientHandler::OnLoadError(CefRefPtr browser, - CefRefPtr frame, - ErrorCode errorCode, - const CefString& failedUrl, - CefString& errorText) -{ - // If it's a steam:// url we always get an error, but we handle it ok internally already, just ignore - if ( failedUrl.size() > 0 && ( Q_stristr( failedUrl.c_str(), "steam://" ) || Q_stristr( failedUrl.c_str(), "steambeta://" ) ) ) - return false; - - const char *pchDetail = NULL; - switch ( errorCode ) - { - case ERR_ABORTED: - // We'll get this in cases where we just start another URL before finishing a previous and such, don't show it. - return false; - break; - case ERR_CACHE_MISS: - pchDetail = m_sErrorCacheMiss; - break; - case ERR_UNKNOWN_URL_SCHEME: - case ERR_INVALID_URL: - pchDetail = m_sErrorBadURL; - break; - case ERR_CONNECTION_CLOSED: - case ERR_CONNECTION_RESET: - case ERR_CONNECTION_REFUSED: - case ERR_CONNECTION_ABORTED: - case ERR_CONNECTION_FAILED: - case ERR_NAME_NOT_RESOLVED: - case ERR_INTERNET_DISCONNECTED: - case ERR_CONNECTION_TIMED_OUT: - pchDetail = m_sErrorConnectionProblem; - break; - case ERR_UNEXPECTED_PROXY_AUTH: - case ERR_EMPTY_PROXY_LIST: - pchDetail = m_sErrorProxyProblem; - break; - default: - pchDetail = m_sErrorUnknown; - break; - } - - char rgchError[4096]; - Q_snprintf( rgchError, Q_ARRAYSIZE( rgchError ), - "" - "" - "%s" - "" - "" - "

%s%d

" - "

" - "%s" - "

" - "" - "", - m_sErrorTitle.String(), - m_sErrorHeader.String(), - errorCode, - pchDetail ); - - errorText = rgchError; - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Event called before a resource is loaded. To allow the resource to load -// normally return RV_CONTINUE. To redirect the resource to a new url -// populate the |redirectUrl| value and return RV_CONTINUE. To specify -// data for the resource return a CefStream object in |resourceStream|, set -// 'mimeType| to the resource stream's mime type, and return RV_CONTINUE. -// To cancel loading of the resource return RV_HANDLED. -//----------------------------------------------------------------------------- -bool CClientHandler::OnBeforeResourceLoad(CefRefPtr browser, - CefRefPtr request, - CefString& redirectUrl, - CefRefPtr& resourceStream, - CefRefPtr response, - int loadFlags) -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_LoadingResource ); - cmd.Body().set_url( CStrAutoEncode( request->GetURL().c_str() ).ToString() ); - DISPATCH_MESSAGE( eHTMLCommands_LoadingResource ); - - // insert custom headers - CefRequest::HeaderMap headerMap; - request->GetHeaderMap( headerMap ); - FOR_EACH_VEC( m_vecHeaders, i ) - { - headerMap.insert( m_vecHeaders[i] ); - } - - request->SetHeaderMap( headerMap ); - - return false; -} - - - -//----------------------------------------------------------------------------- -// Purpose: -// Run a JS alert message. Return RV_CONTINUE to display the default alert -// or RV_HANDLED if you displayed a custom alert. -//----------------------------------------------------------------------------- -bool CClientHandler::OnJSAlert(CefRefPtr browser, - CefRefPtr frame, - const CefString& message) -{ - { - // scope this so the wait below doesn't keep this allocation on the stack - CHTMLProtoBufMsg cmd( eHTMLCommands_JSAlert ); - cmd.Body().set_message( message.c_str() ); - DISPATCH_MESSAGE( eHTMLCommands_JSAlert ); - } - - HTMLCommandBuffer_t *pBuf = g_CEFThread.BWaitForCommand( eHTMLCommands_JSDialogResponse, m_iBrowser ); - if ( pBuf ) - { - CHTMLProtoBufMsg< CMsgJSDialogResponse > cmd( eHTMLCommands_JSDialogResponse ); - g_CEFThread.ReleaseCommandBuffer( pBuf ); - } - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Run a JS confirm request. Return RV_CONTINUE to display the default alert -// or RV_HANDLED if you displayed a custom alert. If you handled the alert -// set |CefHandler::RetVal| to true if the user accepted the confirmation. -//----------------------------------------------------------------------------- -bool CClientHandler::OnJSConfirm(CefRefPtr browser, - CefRefPtr frame, - const CefString& message, - bool& retval) -{ - { - // scope this so the wait below doesn't keep this allocation on the stack - CHTMLProtoBufMsg cmd( eHTMLCommands_JSConfirm ); - cmd.Body().set_message( message.c_str() ); - DISPATCH_MESSAGE( eHTMLCommands_JSConfirm ); - } - - retval = false; - - HTMLCommandBuffer_t *pBuf = g_CEFThread.BWaitForCommand( eHTMLCommands_JSDialogResponse, m_iBrowser ); - if ( pBuf ) - { - CHTMLProtoBufMsg< CMsgJSDialogResponse > cmd( eHTMLCommands_JSDialogResponse ); - if ( cmd.BDeserializeCrossProc( &pBuf->m_Buffer ) ) - { - if ( cmd.BodyConst().result() ) - retval = true ; - } - - g_CEFThread.ReleaseCommandBuffer( pBuf ); - } - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Run a JS prompt request. Return RV_CONTINUE to display the default prompt -// or RV_HANDLED if you displayed a custom prompt. If you handled the prompt -// set |CefHandler::RetVal| to true if the user accepted the prompt and request and -// |result| to the resulting value. -//----------------------------------------------------------------------------- -bool CClientHandler::OnJSPrompt(CefRefPtr browser, - CefRefPtr frame, - const CefString& message, - const CefString& defaultValue, - bool& retval, - CefString& result) -{ - retval = false; // just don't pop JS prompts for now - result = defaultValue; - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Called just before a window is closed. -//----------------------------------------------------------------------------- -void CClientHandler::OnBeforeClose(CefRefPtr browser) -{ - Lock(); - if ( m_Browser ) - { - // Free the browser pointer so that the browser can be destroyed - m_Browser = NULL; - - if ( !m_bBrowserClosing ) - { - CHTMLProtoBufMsg cmd( eHTMLCommands_Close ); - DISPATCH_MESSAGE( eHTMLCommands_Close ); - } - } - Unlock(); -} - - -//----------------------------------------------------------------------------- -// Purpose: show a html popup, a pulldown menu or the like -//----------------------------------------------------------------------------- -void CChromePainter::OnPopupShow(CefRefPtr browser, bool show) -{ - m_bPopupVisible = show; - int m_iBrowser = m_pParent->m_iBrowser; - if ( show ) - { - CHTMLProtoBufMsg cmd( eHTMLCommands_ShowPopup ); - DISPATCH_MESSAGE( eHTMLCommands_ShowPopup ); - } - else - { - CHTMLProtoBufMsg cmd( eHTMLCommands_HidePopup ); - DISPATCH_MESSAGE( eHTMLCommands_HidePopup ); - - // redraw the buffered texture behind the rectangle that was previously composited - for ( size_t i = 0; i < Q_ARRAYSIZE( m_Texture ); ++i ) - { - m_Texture[i].MarkDirtyRect( m_nPopupX, m_nPopupY, m_nPopupX + m_nPopupWide, m_nPopupY + m_nPopupTall ); - } - - // and notify the main thread to redraw the previously composited area as well - m_UpdateRect.MarkDirtyRect( m_nPopupX, m_nPopupY, m_nPopupX + m_nPopupWide, m_nPopupY + m_nPopupTall ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: make the popup this big -//----------------------------------------------------------------------------- -void CChromePainter::OnPopupSize(CefRefPtr browser, const CefRect& rect ) -{ - if ( m_bPopupVisible ) - { - // redraw the buffered texture behind the rectangle that was previously composited - for ( size_t i = 0; i < Q_ARRAYSIZE( m_Texture ); ++i ) - { - m_Texture[i].MarkDirtyRect( m_nPopupX, m_nPopupY, m_nPopupX + m_nPopupWide, m_nPopupY + m_nPopupTall ); - } - - // and notify the main thread to redraw the previously composited area as well - m_UpdateRect.MarkDirtyRect( m_nPopupX, m_nPopupY, m_nPopupX + m_nPopupWide, m_nPopupY + m_nPopupTall ); - } - - m_bPopupVisible = true; - m_nPopupX = rect.x; - m_nPopupWide = rect.width; - m_nPopupY = rect.y; - m_nPopupTall = rect.height; - - int m_iBrowser = m_pParent->m_iBrowser; - CHTMLProtoBufMsg cmd( eHTMLCommands_SizePopup ); - cmd.Body().set_x( m_nPopupX ); - cmd.Body().set_y( m_nPopupY ); - cmd.Body().set_wide( m_nPopupWide ); - cmd.Body().set_tall( m_nPopupTall ); - DISPATCH_MESSAGE( eHTMLCommands_SizePopup ); -} - - -//----------------------------------------------------------------------------- -// Purpose: cef has status text for us -//----------------------------------------------------------------------------- -void CClientHandler::OnStatusMessage(CefRefPtr browser, const CefString& value, StatusType type) -{ - if ( !value.empty() ) - { - CHTMLProtoBufMsg cmd( eHTMLCommands_StatusText ); - cmd.Body().set_text( value.c_str() ); - DISPATCH_MESSAGE( eHTMLCommands_StatusText ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: show tooltip please -//----------------------------------------------------------------------------- -bool CClientHandler::OnTooltip(CefRefPtr browser, CefString& text) -{ - if ( !m_bShowingToolTip && !text.empty() ) - { - m_bShowingToolTip = true; - m_strToolTip = text.c_str(); - - CHTMLProtoBufMsg cmd( eHTMLCommands_ShowToolTip ); - cmd.Body().set_text( m_strToolTip ); - DISPATCH_MESSAGE( eHTMLCommands_ShowToolTip ); - } - else if ( m_bShowingToolTip && !text.empty() ) - { - if ( m_strToolTip != text.c_str() ) - { - m_strToolTip = text.c_str(); - - CHTMLProtoBufMsg cmd( eHTMLCommands_UpdateToolTip ); - cmd.Body().set_text( m_strToolTip ); - DISPATCH_MESSAGE( eHTMLCommands_UpdateToolTip ); - } - } - else if ( m_bShowingToolTip ) - { - CHTMLProtoBufMsg cmd( eHTMLCommands_HideToolTip ); - DISPATCH_MESSAGE( eHTMLCommands_HideToolTip ); - - m_bShowingToolTip = false; - m_strToolTip.Clear(); - } - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: set the mouse cursor to this image -//----------------------------------------------------------------------------- -bool CChromePainter::OnSetCursor( CefRefPtr browser, const CursorType type, const void *pchIconData, int iWide, int iTall, int xHotSpot, int yHotSpot ) -{ - int m_iBrowser = m_pParent->m_iBrowser; - CHTMLProtoBufMsg cmd( eHTMLCommands_SetCursor ); - - EMouseCursor cursor; - switch( type ) - { - case TypeCustom: - cursor = dc_last; - break; - case TypeCross: - cursor = dc_crosshair; - break; - case TypeHand: - cursor = dc_hand; - break; - case TypeIBeam: - cursor = dc_ibeam; - break; - case TypeWait: - cursor = dc_hourglass; - break; - case TypeHelp: - cursor = dc_help; - break; - case TypeEastResize: - cursor = dc_sizee; - break; - case TypeNorthResize: - cursor = dc_sizen; - break; - case TypeNorthEastResize: - cursor = dc_sizene; - break; - case TypeNorthWestResize: - cursor = dc_sizenw; - break; - case TypeSouthResize: - cursor = dc_sizes; - break; - case TypeSouthEastResize: - cursor = dc_sizese; - break; - case TypeSouthWestResize: - cursor = dc_sizesw; - break; - case TypeNorthSouthResize: - cursor = dc_sizes; - break; - case TypeEastWestResize: - cursor = dc_sizew; - break; - case TypeNorthEastSouthWestResize: - cursor = dc_sizeall; - break; - case TypeColumnResize: - cursor = dc_colresize; - break; - case TypeRowResize: - cursor = dc_rowresize; - break; - case TypeMiddlePanning: - cursor = dc_middle_pan; - break; - case TypeEastPanning: - cursor = dc_east_pan; - break; - case TypeNorthPanning: - cursor = dc_north_pan; - break; - case TypeNorthEastPanning: - cursor = dc_north_east_pan; - break; - case TypeNorthWestPanning: - cursor = dc_north_west_pan; - break; - case TypeSouthPanning: - cursor = dc_south_pan; - break; - case TypeSouthEastPanning: - cursor = dc_south_east_pan; - break; - case TypeSouthWestPanning: - cursor = dc_south_west_pan; - break; - case TypeWestPanning: - cursor = dc_west_pan; - break; - case TypeMove: - cursor = dc_sizeall; - break; - case TypeVerticalText: - cursor = dc_verticaltext; - break; - case TypeCell: - cursor = dc_cell; - break; - case TypeContextMenu: - cursor = dc_none; - break; - case TypeAlias: - cursor = dc_alias; - break; - case TypeProgress: - cursor = dc_waitarrow; - break; - case TypeNoDrop: - cursor = dc_no; - break; - case TypeCopy: - cursor = dc_copycur; - break; - case TypeNone: - cursor = dc_none; - break; - case TypeNotAllowed: - cursor = dc_no; - break; - case TypeZoomIn: - cursor = dc_zoomin; - break; - case TypeZoomOut: - cursor = dc_zoomout; - break; - case TypePointer: - default: - cursor = dc_arrow; - } - cmd.Body().set_cursor( cursor ); - cmd.Body().set_data( (uint32)pchIconData ); // we are relying on chrome keeping around the cursor data after this call completes, it does right now. - cmd.Body().set_wide( iWide ); - cmd.Body().set_tall( iTall ); - cmd.Body().set_xhotspot( xHotSpot ); - cmd.Body().set_yhotspot( yHotSpot ); - DISPATCH_MESSAGE( eHTMLCommands_SetCursor ); - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: file open dialog to be shown -//----------------------------------------------------------------------------- -bool CChromePainter::OnFileOpenDialog( CefRefPtr browser, bool bMultiSelect, const CefString &default_title, const CefString &default_file, CefWebFileChooserCallback *pCallback ) -{ - if ( !pCallback ) - return true; - - int m_iBrowser = m_pParent->m_iBrowser; - { - // scope this so this allocation doesn't stay on the stack during validate - CHTMLProtoBufMsg cmd( eHTMLCommands_FileLoadDialog ); - if ( !default_title.empty() ) - cmd.Body().set_title( default_title ); - if ( !default_file.empty() ) - cmd.Body().set_initialfile( default_file ); - DISPATCH_MESSAGE( eHTMLCommands_FileLoadDialog ); - } - - HTMLCommandBuffer_t *pBuf = g_CEFThread.BWaitForCommand( eHTMLCommands_FileLoadDialogResponse, m_iBrowser ); - if ( pBuf ) - { - CHTMLProtoBufMsg< CMsgFileLoadDialogResponse > cmd( eHTMLCommands_FileLoadDialogResponse ); - if ( cmd.BDeserializeCrossProc( &pBuf->m_Buffer ) ) - { - std::vector files; - for ( int i = 0; i < cmd.BodyConst().files_size(); i++ ) - { - if ( !cmd.BodyConst().files(i).empty() ) - { - CPathString path( cmd.BodyConst().files(i).c_str() ); - files.push_back( path.GetWCharPathPrePended() ); - } - } - - // if you have a DEBUG build and are crashing here it is because - // Chrome is a release library and the std::vector iterator isn't crossing - // the interface happyily. Build release and you will run fine. -#if defined(DEBUG) && defined(WIN32) - Assert( !"File select dialog not available in debug due to STL debug/release issues\n" ); -#else - pCallback->OnFileChoose( files ); -#endif - } - g_CEFThread.ReleaseCommandBuffer( pBuf ); - } - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: CEF is asking if it can show itself fullscreen -//----------------------------------------------------------------------------- -bool CChromePainter::OnEnterFullScreen( CefRefPtr browser ) -{ - int m_iBrowser = m_pParent->m_iBrowser; - { - // scope this so this allocation doesn't stay on the stack during validate - CHTMLProtoBufMsg cmd( eHTMLCommands_RequestFullScreen ); - DISPATCH_MESSAGE( eHTMLCommands_RequestFullScreen ); - } - - HTMLCommandBuffer_t *pBuf = g_CEFThread.BWaitForCommand( eHTMLCommands_RequestFullScreenResponse, m_iBrowser ); - if ( pBuf ) - { - CHTMLProtoBufMsg< CMsgRequestFullScreenResponse > cmd( eHTMLCommands_RequestFullScreenResponse ); - if ( cmd.BDeserializeCrossProc( &pBuf->m_Buffer ) ) - { - return cmd.BodyConst().ballow(); - } - g_CEFThread.ReleaseCommandBuffer( pBuf ); - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: cef is spewing to its console, print it -//----------------------------------------------------------------------------- -bool CChromePainter::OnExitFullScreen( CefRefPtr browser ) -{ - int m_iBrowser = m_pParent->m_iBrowser; - { - // scope this so this allocation doesn't stay on the stack during validate - // tell the main thread we are exiting fullscreen - CHTMLProtoBufMsg cmd( eHTMLCommands_ExitFullScreen ); - DISPATCH_MESSAGE( eHTMLCommands_ExitFullScreen ); - } - - // BUGUBG - add a request/response here so you can disallow leaving fullscreen?? - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: cef is spewing to its console, print it -//----------------------------------------------------------------------------- -bool CClientHandler::OnConsoleMessage(CefRefPtr browser, - const CefString& message, - const CefString& source, - int line) -{ - // the console is very chatty and doesn't provide useful information for us app developers, just for html/css editors, so lets ignore this for now - //Msg( "Browser Message: %s - %s:%d\n", message.c_str(), source.c_str(), line ); - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: helper function to recurvisely search down the DOM for any input nodes and an input button name -//----------------------------------------------------------------------------- -void SearchForInputButtonAndOtherInputs_R( CefRefPtr root, CefRefPtr focusNode, bool &bHasMultipleTextInputNodes, CUtlString &sSearchButtonName, int nMaxRecurse ) -{ - CefRefPtr nodeChildren = root->GetFirstChild(); - while ( nodeChildren.get() ) - { - if ( !nodeChildren->IsSame( focusNode ) && nodeChildren->IsElement() ) - { - CUtlString sElementType = nodeChildren->GetElementTagName().c_str(); - if ( !Q_stricmp( "input", sElementType ) ) - { - CUtlString sChildControlType = nodeChildren->GetFormControlElementType().c_str(); - if ( sSearchButtonName.IsEmpty() && !Q_stricmp( sChildControlType, "submit" ) ) - { - if ( nodeChildren->HasElementAttribute( "value" ) ) - sSearchButtonName = nodeChildren->GetElementAttribute( "value" ).c_str(); - } - else if ( !Q_stricmp( "text", sChildControlType ) || !Q_stricmp( "password", sChildControlType ) || !Q_stricmp( "email", sChildControlType ) ) - { - //CefDOMNode::AttributeMap attrMap; - //nodeChildren->GetElementAttributes( attrMap ); - if ( !nodeChildren->HasElementAttribute( "disabled" ) ) - bHasMultipleTextInputNodes = true; - } - } - else if ( !Q_stricmp( "textarea", sElementType ) ) - { - if ( !nodeChildren->HasElementAttribute( "disabled" ) ) - bHasMultipleTextInputNodes = true; - } - else if ( nMaxRecurse > 0 /*&& !Q_stricmp( "div", sElementType ) || !Q_stricmp( "tr", sElementType ) || !Q_stricmp( "td", sElementType ) || !Q_stricmp( "table", sElementType ) || !Q_stricmp( "tbody", sElementType )*/ ) - { - SearchForInputButtonAndOtherInputs_R( nodeChildren, focusNode, bHasMultipleTextInputNodes, sSearchButtonName, nMaxRecurse - 1 ); - } - } - nodeChildren = nodeChildren->GetNextSibling(); - - if ( bHasMultipleTextInputNodes && sSearchButtonName.IsValid() ) - break; // if we found both multiple nodes and a search button name we can bail early - } -} - - -//----------------------------------------------------------------------------- -// Purpose: a new node in the DOM has focus now -//----------------------------------------------------------------------------- -void CClientHandler::OnFocusedNodeChanged(CefRefPtr browser, CefRefPtr frame, CefRefPtr node) -{ - VPROF_BUDGET( "CCEFThread - CClientHandler::OnFocusedNodeChanged()", VPROF_BUDGETGROUP_VGUI ); - - bool bIsInputNode = false; - CUtlString sElementType; - if ( node.get() ) - sElementType = node->GetElementTagName().c_str(); - - CUtlString sName; - if ( node.get() ) - sName = node->GetName().c_str(); - - CUtlString sSearchButtonName; - CUtlString sControlType; - bool bInputNode = !Q_stricmp( "input", sElementType ); - bool bHasMultipleInputNodes = false; - if ( sElementType.IsValid() && ( bInputNode || !Q_stricmp( "textarea", sElementType ) ) ) - { - sControlType = node->GetFormControlElementType().c_str(); - - // lets go searching for the submit button and grab the text it shows - bIsInputNode = true; - CefRefPtr nodeParent = node->GetParent(); - while( nodeParent.get() ) - { - CUtlString sParentElementType = nodeParent->GetElementTagName().c_str(); - if ( !Q_stricmp( "form", sParentElementType ) ) - break; - nodeParent = nodeParent->GetParent(); - } - - if ( nodeParent.get() ) - SearchForInputButtonAndOtherInputs_R( nodeParent, node, bHasMultipleInputNodes, sSearchButtonName, 32 ); - } - - if ( sElementType.IsValid() && !Q_stricmp( "div", sElementType ) ) - { - if ( node->HasElementAttribute( "contenteditable" ) ) - bIsInputNode = true; - } - - if ( sSearchButtonName.IsEmpty() ) - sSearchButtonName = "#Web_FormSubmit"; - - CHTMLProtoBufMsg cmd( eHTMLCommands_NodeGotFocus ); - cmd.Body().set_binput( bIsInputNode ); - if ( sName.IsValid() ) - cmd.Body().set_name( sName ); - if ( sElementType.IsValid() ) - cmd.Body().set_elementtagname( sElementType ); - if ( sSearchButtonName.IsValid() ) - cmd.Body().set_searchbuttontext( sSearchButtonName ); - cmd.Body().set_bhasmultipleinputs( bHasMultipleInputNodes ); - if ( sControlType.IsValid() ) - cmd.Body().set_input_type( sControlType ); - - DISPATCH_MESSAGE( eHTMLCommands_NodeGotFocus ); -} - - -//----------------------------------------------------------------------------- -// Purpose: a find has found matches on the page, feed back that info -//----------------------------------------------------------------------------- -void CClientHandler::OnFindResult(CefRefPtr browser, - int identifier, - int count, - const CefRect& selectionRect, - int activeMatchOrdinal, - bool finalUpdate) -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_SearchResults ); - cmd.Body().set_activematch( activeMatchOrdinal ); - cmd.Body().set_results( count ); - DISPATCH_MESSAGE( eHTMLCommands_SearchResults ); -} - - -//----------------------------------------------------------------------------- -// Purpose: return a pointer to the CEF browser object -//----------------------------------------------------------------------------- -CefRefPtr CClientHandler::GetBrowser() -{ - return m_Browser; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CClientHandler::CloseBrowser() -{ - if ( m_Browser ) - { - m_bBrowserClosing = true; - m_Browser->CloseBrowser(); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CClientHandler::SetMouseLocation( int nMouseX, int nMouseY ) -{ - m_nMouseX = nMouseX; - m_nMouseY = nMouseY; - if ( m_Browser.get() ) - { - m_nMouseScrolledX = nMouseX + m_Browser->VerticalScroll(); - m_nMouseScrolledY = nMouseY + m_Browser->HorizontalScroll(); - m_bMouseFocus = true; - m_Browser->SendMouseMoveEvent( m_nMouseX, m_nMouseY, false ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: check if window has scrolled and generate a fake mouse move event -// to force CEF to check for hover state changes (seems like a CEF bug...) -//----------------------------------------------------------------------------- -void CClientHandler::RefreshCEFHoverStatesAfterScroll() -{ - if ( m_Browser.get() && m_bMouseFocus ) - { - int nScrolledX = m_nMouseX + m_Browser->VerticalScroll(); - int nScrolledY = m_nMouseY + m_Browser->HorizontalScroll(); - if ( nScrolledX != m_nMouseScrolledX || nScrolledY != m_nMouseScrolledY ) - { - m_nMouseScrolledX = nScrolledX; - m_nMouseScrolledY = nScrolledY; - m_Browser->SendMouseMoveEvent( m_nMouseX, m_nMouseY, false ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: make the user agent for this browser -//----------------------------------------------------------------------------- -void CClientHandler::SetBrowserAgent( CefRefPtr browser ) -{ - static bool bGotIEVersion = false; - static char rgchWindowsVersion[64] = "Windows NT 5.1"; // XP SP1 - static char *rgchOS = ""; - - if ( !bGotIEVersion ) - { -#ifdef WIN32 - rgchOS = "Windows"; - // First get windows version - OSVERSIONINFO verInfo; - memset( &verInfo, 0, sizeof(OSVERSIONINFO) ); - verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - - if ( ::GetVersionEx( &verInfo ) ) - { - // We only run on "Windows NT" os's, so we just need to append the major/minor version dynamically - Q_snprintf( rgchWindowsVersion, sizeof(rgchWindowsVersion), "Windows NT %u.%u", verInfo.dwMajorVersion, verInfo.dwMinorVersion ); - //Log( "Windows Version is: %u.%u\n", verInfo.dwMajorVersion, verInfo.dwMinorVersion ); - } - -#elif defined(OSX) - Q_snprintf( rgchWindowsVersion, sizeof(rgchWindowsVersion), "Macintosh" ); - rgchOS = "Macintosh"; -#elif defined(LINUX) - Q_snprintf( rgchWindowsVersion, sizeof(rgchWindowsVersion), "X11" );// strange, but that's what Firefox uses - rgchOS = "Linux"; -#endif - } - - // user agent is process wide for Chrome so you can only set it once and it appplies to everything you open - { - char szAgent[ 2048 ]; - Q_snprintf( szAgent, sizeof(szAgent), m_strUserAgent.String(), rgchOS, rgchWindowsVersion, m_strUserAgentIdentifier.String(), sm_ulBuildID, m_szUserAgentExtras ); - browser->SetUserAgent( szAgent ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: paint the cef control -//----------------------------------------------------------------------------- -void CClientHandler::Paint() -{ - Lock(); - if ( m_Browser ) - { - m_bPendingPaint |= m_Painter.BUpdated(); - m_Painter.SetUpdated( false ); - } - Unlock(); -} - - -//----------------------------------------------------------------------------- -// Purpose: did we have a paint that updated the texture buffer? -//----------------------------------------------------------------------------- -bool CClientHandler::BNeedsPaint() -{ - bool bVal = m_bPendingPaint; - m_bPendingPaint = false; - return bVal; -} - - -//----------------------------------------------------------------------------- -// Purpose: get the texture data for the control -//----------------------------------------------------------------------------- -const byte *CClientHandler::PComposedTextureData( uint32 iTexture ) -{ - VPROF_BUDGET( "CClientHandler::PTextureData", VPROF_BUDGETGROUP_VGUI ); - - return m_Painter.PComposedTextureData( iTexture ); -} - - -//----------------------------------------------------------------------------- -// Purpose: true if something valid has rendered (i.e not the blank page) -//----------------------------------------------------------------------------- -bool CClientHandler::IsVisuallyNonEmpty() -{ - if ( m_Browser ) - return m_Browser->IsVisuallyNonEmpty(); - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: set the loc strings to display on error -//----------------------------------------------------------------------------- -void CClientHandler::SetErrorStrings( const char *pchTitle, const char *pchHeader, const char *pchCacheMiss, const char *pchBadURL, const char *pchConnectionProblem, - const char *pchProxyProblem, const char *pchUnknown ) -{ - m_sErrorTitle = pchTitle; - m_sErrorHeader = pchHeader; - m_sErrorCacheMiss = pchCacheMiss; - m_sErrorBadURL = pchBadURL; - m_sErrorConnectionProblem = pchConnectionProblem; - m_sErrorProxyProblem = pchProxyProblem; - m_sErrorUnknown = pchUnknown; -} - - -//----------------------------------------------------------------------------- -// Purpose: the user wants us to take a screenshot of a page -//----------------------------------------------------------------------------- -void CClientHandler::RequestScreenShot( const CHTMLProtoBufMsg &cmd ) -{ - m_Snapshot.m_sURLSnapshot = cmd.BodyConst().url().c_str(); - m_Snapshot.m_sFileNameSnapshot = cmd.BodyConst().filename().c_str(); - m_Snapshot.m_nWide = cmd.BodyConst().width(); - m_Snapshot.m_nTall = cmd.BodyConst().height(); - m_Snapshot.m_flRequestTimeout = Plat_FloatTime() + k_flMaxScreenshotWaitTime; -} - - -//----------------------------------------------------------------------------- -// Purpose: save a screenshot of the current html page to this file with this size -//----------------------------------------------------------------------------- -void CClientHandler::SavePageToJPEGIfNeeded( CefRefPtr browser, const byte *pRGBA, int wide, int tall ) -{ - if ( m_Snapshot.m_sURLSnapshot.IsValid() && wide && tall - && m_Snapshot.m_sURLSnapshot == CStrAutoEncode( browser->GetMainFrame()->GetURL().c_str() ).ToString() ) - { - VPROF_BUDGET( "CClientHandler::SavePageToJPEGIfNeeded", VPROF_BUDGETGROUP_TENFOOT ); - - CUtlBuffer bufRGB; - - bufRGB.Put( pRGBA, wide * tall *4 ); - if ( !BConvertRGBAToRGB( bufRGB, wide, tall ) ) - return; - - BResizeImageRGB( bufRGB, wide, tall, m_Snapshot.m_nWide, m_Snapshot.m_nTall ); - // input format is actually BGRA so now swizzle to rgb - byte *pBGR = (byte *)bufRGB.Base(); - for ( int i = 0; i < m_Snapshot.m_nTall; i++ ) - { - for ( int j = 0; j < m_Snapshot.m_nWide; j++ ) - { - char cR = pBGR[0]; - pBGR[0] = pBGR[2]; - pBGR[2] = cR; - pBGR += 3; - } - } - - if ( !ConvertRGBToJpeg( m_Snapshot.m_sFileNameSnapshot, k_ScreenshotQuality, m_Snapshot.m_nWide, m_Snapshot.m_nTall, bufRGB ) ) - return; - - CHTMLProtoBufMsg cmd( eHTMLCommands_SavePageToJPEGResponse ); - cmd.Body().set_url( m_Snapshot.m_sURLSnapshot ); - cmd.Body().set_filename( m_Snapshot.m_sFileNameSnapshot ); - DISPATCH_MESSAGE( eHTMLCommands_SavePageToJPEGResponse ); - - m_Snapshot.m_sURLSnapshot.Clear(); - m_Snapshot.m_flRequestTimeout = 0.0f; - - } -} - - -//----------------------------------------------------------------------------- -// Purpose: return the url located at this position if there is one -//----------------------------------------------------------------------------- -void CCEFThread::ThreadLinkAtPosition( const CHTMLProtoBufMsg &htmlCommand ) -{ - CefString pchURL; - int iClient = 0; - int bLiveLink = false; - int bInput = false; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - if ( m_listClientHandlers[iClient]->GetBrowser() ) - pchURL = m_listClientHandlers[iClient]->GetBrowser()->GetLinkAtPosition( htmlCommand.BodyConst().x(), htmlCommand.BodyConst().y(), bLiveLink, bInput ); - } - - CHTMLProtoBufMsg cmd( eHTMLCommands_LinkAtPositionResponse ); - cmd.Body().set_x( htmlCommand.BodyConst().x() ); - cmd.Body().set_y( htmlCommand.BodyConst().y() ); - cmd.Body().set_blivelink( bLiveLink>0 ? true: false ); - cmd.Body().set_binput( bInput>0 ? true: false ); - if ( !pchURL.empty() ) - cmd.Body().set_url( pchURL ); - int m_iBrowser = htmlCommand.BodyConst().browser_handle(); - DISPATCH_MESSAGE( eHTMLCommands_LinkAtPositionResponse ); -} - - -//----------------------------------------------------------------------------- -// Purpose: zoom the screen to the element at this position -//----------------------------------------------------------------------------- -void CCEFThread::ThreadZoomToElementAtPosition( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - if ( m_listClientHandlers[iClient]->GetBrowser() ) - { - CefRect initialRect, finalRect; - float zoomLevel = m_listClientHandlers[iClient]->GetBrowser()->scalePageToFitElementAt( - htmlCommand.BodyConst().x(), htmlCommand.BodyConst().y(), - initialRect, finalRect ); - int m_iBrowser = htmlCommand.BodyConst().browser_handle(); - ThreadBrowserVerticalScrollBarSizeHelper( m_iBrowser, true ); - ThreadBrowserHorizontalScrollBarSizeHelper( m_iBrowser, true ); - { - CHTMLProtoBufMsg cmd( eHTMLCommands_ZoomToElementAtPositionResponse ); - cmd.Body().set_zoom( zoomLevel ); - cmd.Body().set_initial_x( initialRect.x ); - cmd.Body().set_initial_y( initialRect.y ); - cmd.Body().set_initial_width( initialRect.width ); - cmd.Body().set_initial_height( initialRect.height ); - cmd.Body().set_final_x( finalRect.x ); - cmd.Body().set_final_y( finalRect.y ); - cmd.Body().set_final_width( finalRect.width ); - cmd.Body().set_final_height( finalRect.height ); - DISPATCH_MESSAGE( eHTMLCommands_ZoomToElementAtPositionResponse ); - } - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: zoom the screen to the element at this position -//----------------------------------------------------------------------------- -void CCEFThread::ThreadZoomToFocusedElement( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - if ( m_listClientHandlers[iClient]->GetBrowser() ) - { - CefRect initialRect, finalRect; - float zoomLevel = m_listClientHandlers[iClient]->GetBrowser()->scalePageToFocusedElement( htmlCommand.BodyConst().leftoffset(), htmlCommand.BodyConst().topoffset(), initialRect, finalRect ); - int m_iBrowser = htmlCommand.BodyConst().browser_handle(); - ThreadBrowserVerticalScrollBarSizeHelper( m_iBrowser, true ); - ThreadBrowserHorizontalScrollBarSizeHelper( m_iBrowser, true ); - { - CHTMLProtoBufMsg cmd( eHTMLCommands_ZoomToElementAtPositionResponse ); - cmd.Body().set_zoom( zoomLevel ); - cmd.Body().set_initial_x( initialRect.x ); - cmd.Body().set_initial_y( initialRect.y ); - cmd.Body().set_initial_width( initialRect.width ); - cmd.Body().set_initial_height( initialRect.height ); - cmd.Body().set_final_x( finalRect.x ); - cmd.Body().set_final_y( finalRect.y ); - cmd.Body().set_final_width( finalRect.width ); - cmd.Body().set_final_height( finalRect.height ); - DISPATCH_MESSAGE( eHTMLCommands_ZoomToElementAtPositionResponse ); - } - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: increment the scale factor on the page by an increment -//----------------------------------------------------------------------------- -void CCEFThread::ThreadSetPageScale( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - CClientHandler *pHandler = m_listClientHandlers[iClient]; - if ( pHandler->GetBrowser() ) - { - int nPageHeightBefore = pHandler->GetBrowser()->VerticalScrollMax(); - int nPageWidthBefore = pHandler->GetBrowser()->HorizontalScrollMax(); - - float zoomLevel = pHandler->GetBrowser()->setPageScaleFactor( htmlCommand.BodyConst().scale(), htmlCommand.BodyConst().x(), htmlCommand.BodyConst().y() ); - - int idx = m_mapSizeChangesPending.Find( htmlCommand.BodyConst().browser_handle() ); - if ( idx == m_mapSizeChangesPending.InvalidIndex() ) - { - SizeChange_t &sizeChange = m_mapSizeChangesPending [ m_mapSizeChangesPending.Insert( htmlCommand.BodyConst().browser_handle() ) ]; - sizeChange.iBrowser = htmlCommand.BodyConst().browser_handle(); - sizeChange.nBeforeHeight = nPageHeightBefore; - sizeChange.nBeforeWidth = nPageWidthBefore; - sizeChange.flNewZoom = zoomLevel; - } - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: fire off the details of any page scale changes we had pending from last frame -//----------------------------------------------------------------------------- -void CCEFThread::SendSizeChangeEvents() -{ - FOR_EACH_MAP_FAST( m_mapSizeChangesPending, i ) - { - int iClient = 0; - if ( BIsValidBrowserHandle( m_mapSizeChangesPending[i].iBrowser, iClient ) ) - { - CClientHandler *pHandler = m_listClientHandlers[iClient]; - if ( pHandler->GetBrowser() ) - { - int nPageHeightAfter = pHandler->GetBrowser()->VerticalScrollMax(); - int nPageWidthAfter = pHandler->GetBrowser()->HorizontalScrollMax(); - - int m_iBrowser = m_mapSizeChangesPending[i].iBrowser; - - ThreadBrowserVerticalScrollBarSizeHelper( m_iBrowser, true ); - ThreadBrowserHorizontalScrollBarSizeHelper( m_iBrowser, true ); - { - CHTMLProtoBufMsg cmd( eHTMLCommands_ScaleToValueResponse ); - cmd.Body().set_zoom( m_mapSizeChangesPending[i].flNewZoom ); - cmd.Body().set_width_delta( nPageWidthAfter - m_mapSizeChangesPending[i].nBeforeWidth ); - cmd.Body().set_height_delta( nPageHeightAfter - m_mapSizeChangesPending[i].nBeforeHeight ); - DISPATCH_MESSAGE( eHTMLCommands_ScaleToValueResponse ); - } - } - } - } - m_mapSizeChangesPending.RemoveAll(); -} - - -//----------------------------------------------------------------------------- -// Purpose: exit from fullscreen if in it -//----------------------------------------------------------------------------- -void CCEFThread::ThreadExitFullScreen( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - m_listClientHandlers[ iClient ]->GetBrowser()->ExitFullScreen(); - } - -} - - -//----------------------------------------------------------------------------- -// Purpose: the user has requested we save this url to a file on local disk -//----------------------------------------------------------------------------- -void CCEFThread::ThreadSavePageToJPEG( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - m_listClientHandlers[ iClient ]->RequestScreenShot( htmlCommand ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: mouse moved to this x,y on the page -//----------------------------------------------------------------------------- -void CCEFThread::ThreadMouseMove( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - m_listClientHandlers[ iClient ]->SetMouseLocation( htmlCommand.BodyConst().x(), htmlCommand.BodyConst().y() ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: mouse left the control, tell cef -//----------------------------------------------------------------------------- -void CCEFThread::ThreadMouseLeave( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); - if ( !browser.get() ) - return; - - m_listClientHandlers[ iClient ]->SetMouseFocus( false ); - - int mx, my; - m_listClientHandlers[ iClient ]->GetMouseLocation( mx, my ); - browser->SendMouseMoveEvent( mx, my, true ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: helper to convert UI mouse codes to CEF ones -//----------------------------------------------------------------------------- -CefBrowser::MouseButtonType ConvertMouseCodeToCEFCode( int code ) -{ - // BUGBUG - switch( code ) - { - case 0: - return MBT_LEFT; - break; - case 1: - return MBT_RIGHT; - break; - case 2: - return MBT_MIDDLE; - break; - default: - return MBT_LEFT; - break; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: mouse button pressed -//----------------------------------------------------------------------------- -void CCEFThread::ThreadMouseButtonDown( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - - CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); - if ( !browser.get() ) - return; - int nMouseX, nMouseY; - m_listClientHandlers[ iClient ]->GetMouseLocation( nMouseX, nMouseY ); - - browser->SendMouseClickEvent( nMouseX, nMouseY, ConvertMouseCodeToCEFCode( htmlCommand.BodyConst().mouse_button() ), false, 1 ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: mouse button released -//----------------------------------------------------------------------------- -void CCEFThread::ThreadMouseButtonUp( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); - if ( !browser.get() ) - return; - int nMouseX, nMouseY; - m_listClientHandlers[ iClient ]->GetMouseLocation( nMouseX, nMouseY ); - - browser->SendMouseClickEvent( nMouseX, nMouseY, ConvertMouseCodeToCEFCode( htmlCommand.BodyConst().mouse_button() ), true, 1 ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: mouse button double pressed -//----------------------------------------------------------------------------- -void CCEFThread::ThreadMouseButtonDlbClick( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); - if ( !browser.get() ) - return; - int nMouseX, nMouseY; - m_listClientHandlers[ iClient ]->GetMouseLocation( nMouseX, nMouseY ); - - browser->SendMouseClickEvent( nMouseX, nMouseY, ConvertMouseCodeToCEFCode( htmlCommand.BodyConst().mouse_button() ), false, 2 ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: mouse was wheeled -//----------------------------------------------------------------------------- -void CCEFThread::ThreadMouseWheel( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); - if ( !browser.get() ) - return; - int nMouseX, nMouseY; - m_listClientHandlers[ iClient ]->GetMouseLocation( nMouseX, nMouseY ); - - browser->SendMouseWheelEvent( nMouseX, nMouseY, htmlCommand.BodyConst().delta() ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: unicode character was typed -//----------------------------------------------------------------------------- -void CCEFThread::ThreadKeyTyped( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); - if ( !browser.get() ) - return; - - browser->SendKeyEvent( KT_CHAR, htmlCommand.BodyConst().unichar(), 0, false, false ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: raw key was pressed -//----------------------------------------------------------------------------- -void CCEFThread::ThreadKeyDown( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); - if ( !browser.get() ) - return; - - browser->SendKeyEvent( KT_KEYDOWN, htmlCommand.BodyConst().keycode(), htmlCommand.BodyConst().modifiers(), false, false ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: raw key was released -//----------------------------------------------------------------------------- -void CCEFThread::ThreadKeyUp( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); - if ( !browser.get() ) - return; - - browser->SendKeyEvent( KT_KEYUP, htmlCommand.BodyConst().keycode(), htmlCommand.BodyConst().modifiers(), false, false ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: please close any fullscreen flash controls you see -//----------------------------------------------------------------------------- -void CCEFThread::ThreadCloseFullScreenFlashIfOpen( const CHTMLProtoBufMsg &htmlCommand ) -{ - CheckForFullScreenFlashControl(); - if ( m_bFullScreenFlashVisible ) - { -#ifdef WIN32 - ::PostMessageA( m_flashfullscreenHWND, WM_KEYDOWN, VK_ESCAPE, 0 ); - ::PostMessageA( m_flashfullscreenHWND, WM_KEYUP, VK_ESCAPE, 0 ); -#endif - } -} - - -//----------------------------------------------------------------------------- -// Purpose: please close any fullscreen flash controls you see -//----------------------------------------------------------------------------- -void CCEFThread::ThreadPauseFullScreenFlashMovieIfOpen( const CHTMLProtoBufMsg &htmlCommand ) -{ - CheckForFullScreenFlashControl(); - if ( m_bFullScreenFlashVisible ) - { -#ifdef WIN32 - ::PostMessageA( m_flashfullscreenHWND, WM_KEYDOWN, VK_SPACE, 0 ); - ::PostMessageA( m_flashfullscreenHWND, WM_KEYUP, VK_SPACE, 0 ); -#endif - } -} - - - -//----------------------------------------------------------------------------- -// Purpose: helper class to get the focused node in the dom -//----------------------------------------------------------------------------- -class CVisitor : public CefDOMVisitor -{ -public: - CVisitor( CThreadEvent *pEvent, CUtlString *psValue ) - { - m_pEvent = pEvent; - m_psValue = psValue; - } - - ~CVisitor() - { - m_pEvent->Set(); - } - - virtual void Visit(CefRefPtr document) { - CefRefPtr focusedNode = document->GetFocusedNode(); - *m_psValue = focusedNode->GetValue().c_str(); - } - - -private: - CThreadEvent *m_pEvent; - CUtlString *m_psValue; - - IMPLEMENT_REFCOUNTING(CVisitor); -}; - - -//----------------------------------------------------------------------------- -// Purpose: get the text out of the current dom field that has focus -//----------------------------------------------------------------------------- -void CCEFThread::ThreadGetFocusedNodeText( const CHTMLProtoBufMsg &htmlCommand ) -{ - int iClient = 0; - if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) - { - CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); - if ( !browser.get() ) - return; - - CThreadEvent event; - CUtlString sValue; - browser->GetFocusedFrame()->VisitDOM( new CVisitor( &event, &sValue ) ); - do - { - CefDoMessageLoopWork(); - } - while ( !event.Wait( 100 ) ); // keep pumping CEF until it has done our walk - - { - int m_iBrowser = htmlCommand.BodyConst().browser_handle(); - CHTMLProtoBufMsg cmd( eHTMLCommands_GetFocusedNodeValueResponse ); - cmd.Body().set_value( sValue ); - DISPATCH_MESSAGE( eHTMLCommands_GetFocusedNodeValueResponse ); - } - } -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +//=============================================================================// + +#include "winlite.h" +#include "html_chrome.h" +#include "cef/include/cef_cookie.h" +#include "imageloadwrapper.h" +#include "html/ichromehtmlwrapper.h" +#include "input/mousecursors.h" +//#include "rtime.h" +#ifdef OSX +extern "C" void *CreateAutoReleasePool(); +extern "C" void ReleaseAutoReleasePool( void *pool ); +#endif +#ifdef WIN32 +#include +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include + +static uint64 sm_ulBuildID = 0; // will be appended to the user agent +static CCEFThread g_CEFThread; // main thread for CEF to do its thinking + +// access for main thread to get to the html object +CCEFThread &AccessHTMLWrapper() +{ + return g_CEFThread; +} + +// helper macro to send a command to the CEF thread +#define DISPATCH_MESSAGE( eCmd ) \ + cmd.Body().set_browser_handle( m_iBrowser ); \ + HTMLCommandBuffer_t *pBuf = g_CEFThread.GetFreeCommandBuffer( eCmd, m_iBrowser ); \ + cmd.SerializeCrossProc( &pBuf->m_Buffer ); \ + g_CEFThread.PushResponse( pBuf ); \ + +// wrappers for our handle to list index/serial, lower 16 bits are list index, top 16 bits are the serial number +#define BROWSER_HANDLE_FROM_INDEX_SERIAL( iList, nSerial ) iList + ((int)nSerial<<16) +#define BROWSER_SERIAL_FROM_HANDLE( nHandle ) (uint32)nHandle>>16 + +const int k_ScreenshotQuality = 95; // quality value used when making a jpeg image of a web page +const float k_flMaxScreenshotWaitTime = 5.0; // wait up to 5 sec before taking a screenshot + +//----------------------------------------------------------------------------- +// Purpose: thread to manage pumping the CEF message loop +//----------------------------------------------------------------------------- +CCEFThread::CCEFThread() +{ + m_bExit = false; + m_nTargetFrameRate = 60; // 60 Hz by default + m_nBrowserSerial = 0; + m_bFullScreenFlashVisible = false; + m_bSawUserInputThisFrame = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: destructor +//----------------------------------------------------------------------------- +CCEFThread::~CCEFThread() +{ + // This can get called by the main thread before the actual thread exits, + // if there is a forced process exit due to some error. In this case, + // force the thread to exit before destroying the member variables it + // is depending on. + if ( !m_bExit ) + { + TriggerShutdown(); + Join( 20 * k_nThousand ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: tell cef where to place its state dirs +//----------------------------------------------------------------------------- +void CCEFThread::SetCEFPaths( const char *pchHTMLCacheDir, const char *pchCookiePath ) +{ + Assert( !IsAlive() ); + m_sHTMLCacheDir = pchHTMLCacheDir; + m_sCookiePath = pchCookiePath; +} + + +//----------------------------------------------------------------------------- +// Purpose: set the exit event on the cef thread +//----------------------------------------------------------------------------- +void CCEFThread::TriggerShutdown() +{ + m_bExit = true; + m_WakeEvent.Set(); + m_evWaitingForCommand.Set(); + m_eventDidExit.Wait( k_nThousand ); // wait 1 second at most for it to go away +} + + +//----------------------------------------------------------------------------- +// Purpose: tickle the run loop of the thread to run more commands +//----------------------------------------------------------------------------- +void CCEFThread::WakeThread() +{ + m_WakeEvent.Set(); +} + + +//----------------------------------------------------------------------------- +// Purpose: helper macro for message dispatch +//----------------------------------------------------------------------------- +#define HTML_MSG_FUNC( eHTMLCommand, bodyType, commandFunc ) \ + case eHTMLCommand: \ + { \ + CHTMLProtoBufMsg< bodyType > cmd( pCmd->m_eCmd ); \ + if ( !cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ) \ + { \ + bError = true; \ + } \ + else \ + { \ + cmd.Body().set_browser_handle( pCmd->m_iBrowser ); \ + g_CEFThread.commandFunc( cmd ); \ + } \ + } \ + break; + + +#define HTML_MSG_FUNC_NOHANDLE( eHTMLCommand, bodyType, commandFunc ) \ + case eHTMLCommand: \ + { \ + CHTMLProtoBufMsg< bodyType > cmd( pCmd->m_eCmd ); \ + if ( !cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ) \ + { \ + bError = true; \ + } \ + else \ + { \ + g_CEFThread.commandFunc( cmd ); \ + } \ + } \ + break; + + +//----------------------------------------------------------------------------- +// Purpose: clear any pending commands from the main thread +//----------------------------------------------------------------------------- +void CCEFThread::RunCurrentCommands() +{ + bool bError = false; + HTMLCommandBuffer_t *pCmd =NULL; + + while( GetCEFThreadCommand( &pCmd ) ) + { + //Msg( "Got command %d\n", pCmd->m_eCmd ); + switch( pCmd->m_eCmd ) + { + HTML_MSG_FUNC_NOHANDLE( eHTMLCommands_BrowserCreate, CMsgBrowserCreate, ThreadCreateBrowser ); + HTML_MSG_FUNC( eHTMLCommands_BrowserRemove, CMsgBrowserRemove, ThreadRemoveBrowser ); + HTML_MSG_FUNC( eHTMLCommands_BrowserSize, CMsgBrowserSize, ThreadBrowserSize ); + HTML_MSG_FUNC( eHTMLCommands_BrowserPosition, CMsgBrowserPosition, ThreadBrowserPosition ); + HTML_MSG_FUNC( eHTMLCommands_PostURL, CMsgPostURL, ThreadBrowserPostURL ); + HTML_MSG_FUNC( eHTMLCommands_StopLoad, CMsgStopLoad, ThreadBrowserStopLoad ); + HTML_MSG_FUNC( eHTMLCommands_Reload, CMsgReload, ThreadBrowserReload ); + HTML_MSG_FUNC( eHTMLCommands_GoForward, CMsgGoForward, ThreadBrowserGoForward ); + HTML_MSG_FUNC( eHTMLCommands_GoBack, CMsgGoBack, ThreadBrowserGoBack ); + HTML_MSG_FUNC( eHTMLCommands_Copy, CMsgCopy, ThreadBrowserCopy ); + HTML_MSG_FUNC( eHTMLCommands_Paste, CMsgPaste, ThreadBrowserPaste ); + HTML_MSG_FUNC( eHTMLCommands_ExecuteJavaScript, CMsgExecuteJavaScript, ThreadBrowserExecuteJavascript ); + HTML_MSG_FUNC( eHTMLCommands_SetFocus, CMsgSetFocus, ThreadBrowserSetFocus ); + HTML_MSG_FUNC( eHTMLCommands_HorizontalScrollBarSize, CMsgHorizontalScrollBarSize, ThreadBrowserHorizontalScrollBarSize ); + HTML_MSG_FUNC( eHTMLCommands_VerticalScrollBarSize, CMsgVerticalScrollBarSize, ThreadBrowserVerticalScrollBarSize ); + HTML_MSG_FUNC( eHTMLCommands_Find, CMsgFind, ThreadBrowserFind ); + HTML_MSG_FUNC( eHTMLCommands_StopFind, CMsgStopFind, ThreadBrowserStopFind ); + HTML_MSG_FUNC( eHTMLCommands_SetHorizontalScroll, CMsgSetHorizontalScroll, ThreadBrowserSetHorizontalScroll ); + HTML_MSG_FUNC( eHTMLCommands_SetVerticalScroll, CMsgSetVerticalScroll, ThreadBrowserSetVerticalScroll ); + HTML_MSG_FUNC( eHTMLCommands_SetZoomLevel, CMsgSetZoomLevel, ThreadBrowserSetZoomLevel ); + HTML_MSG_FUNC( eHTMLCommands_ViewSource, CMsgViewSource, ThreadBrowserViewSource ); + HTML_MSG_FUNC( eHTMLCommands_NeedsPaintResponse, CMsgNeedsPaintResponse, ThreadNeedsPaintResponse ); + HTML_MSG_FUNC( eHTMLCommands_BrowserErrorStrings, CMsgBrowserErrorStrings, ThreadBrowserErrorStrings ); + HTML_MSG_FUNC( eHTMLCommands_AddHeader, CMsgAddHeader, ThreadAddHeader ); + HTML_MSG_FUNC( eHTMLCommands_GetZoom, CMsgGetZoom, ThreadGetZoom ); + HTML_MSG_FUNC_NOHANDLE( eHTMLCommands_SetCookie, CMsgSetCookie, ThreadSetCookie ); + HTML_MSG_FUNC_NOHANDLE( eHTMLCommands_SetTargetFrameRate, CMsgSetTargetFrameRate, ThreadSetTargetFrameRate ); + HTML_MSG_FUNC( eHTMLCommands_HidePopup, CMsgHidePopup, ThreadHidePopup ); + HTML_MSG_FUNC( eHTMLCommands_FullRepaint, CMsgFullRepaint, ThreadFullRepaint ); + HTML_MSG_FUNC( eHTMLCommands_GetCookiesForURL, CMsgGetCookiesForURL, ThreadGetCookiesForURL ); + HTML_MSG_FUNC( eHTMLCommands_ZoomToCurrentlyFocusedNode, CMsgZoomToFocusedElement, ThreadZoomToFocusedElement ); + HTML_MSG_FUNC( eHTMLCommands_GetFocusedNodeValue, CMsgFocusedNodeText, ThreadGetFocusedNodeText ); + + HTML_MSG_FUNC( eHTMLCommands_MouseDown, CMsgMouseDown, ThreadMouseButtonDown ); + HTML_MSG_FUNC( eHTMLCommands_MouseUp, CMsgMouseUp, ThreadMouseButtonUp ); + HTML_MSG_FUNC( eHTMLCommands_MouseDblClick, CMsgMouseDblClick, ThreadMouseButtonDlbClick ); + HTML_MSG_FUNC( eHTMLCommands_MouseWheel, CMsgMouseWheel, ThreadMouseWheel ); + HTML_MSG_FUNC( eHTMLCommands_KeyDown, CMsgKeyDown, ThreadKeyDown ); + HTML_MSG_FUNC( eHTMLCommands_KeyUp, CMsgKeyUp, ThreadKeyUp ); + HTML_MSG_FUNC( eHTMLCommands_KeyChar, CMsgKeyChar, ThreadKeyTyped ); + HTML_MSG_FUNC( eHTMLCommands_MouseMove, CMsgMouseMove, ThreadMouseMove ); + HTML_MSG_FUNC( eHTMLCommands_MouseLeave, CMsgMouseLeave, ThreadMouseLeave ); + HTML_MSG_FUNC( eHTMLCommands_LinkAtPosition, CMsgLinkAtPosition, ThreadLinkAtPosition ); + HTML_MSG_FUNC( eHTMLCommands_ZoomToElementAtPosition, CMsgZoomToElementAtPosition, ThreadZoomToElementAtPosition ); + HTML_MSG_FUNC( eHTMLCommands_SavePageToJPEG, CMsgSavePageToJPEG, ThreadSavePageToJPEG ); + HTML_MSG_FUNC( eHTMLCommands_SetPageScale, CMsgScalePageToValue, ThreadSetPageScale ); + HTML_MSG_FUNC( eHTMLCommands_ExitFullScreen, CMsgExitFullScreen, ThreadExitFullScreen ); + HTML_MSG_FUNC( eHTMLCommands_CloseFullScreenFlashIfOpen, CMsgCloseFullScreenFlashIfOpen, ThreadCloseFullScreenFlashIfOpen ); + HTML_MSG_FUNC( eHTMLCommands_PauseFullScreenFlashMovieIfOpen, CMsgPauseFullScreenFlashMovieIfOpen, ThreadPauseFullScreenFlashMovieIfOpen ); + + default: + bError = true; + AssertMsg1( false, "Invalid message in browser stream (%d)", pCmd->m_eCmd ); + break; + } + + if ( pCmd->m_eCmd == eHTMLCommands_MouseDown || pCmd->m_eCmd == eHTMLCommands_MouseDblClick || pCmd->m_eCmd == eHTMLCommands_KeyDown ) + m_bSawUserInputThisFrame = true; + + ReleaseCommandBuffer( pCmd ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: done with this buffer, put it in the free queue +//----------------------------------------------------------------------------- +void CCEFThread::ReleaseCommandBuffer( HTMLCommandBuffer_t *pBuf ) +{ + pBuf->m_eCmd = eHTMLCommands_None; + pBuf->m_iBrowser = -1; + pBuf->m_Buffer.Clear(); + m_tslUnsedBuffers.PushItem( pBuf ); +} + + +//----------------------------------------------------------------------------- +// Purpose: get the next free cef thread command buffer to write into +//----------------------------------------------------------------------------- +HTMLCommandBuffer_t *CCEFThread::GetFreeCommandBuffer( EHTMLCommands eCmd, int iBrowser ) +{ + HTMLCommandBuffer_t *pBuf; + if ( !m_tslUnsedBuffers.PopItem( &pBuf ) ) // if nothing in the free queue just make a new one + pBuf = new HTMLCommandBuffer_t; + + pBuf->m_eCmd = eCmd; + pBuf->m_iBrowser = iBrowser; + return pBuf; +} + + +//----------------------------------------------------------------------------- +// Purpose: wait for a command of this type on the cef thread +//----------------------------------------------------------------------------- +HTMLCommandBuffer_t *CCEFThread::BWaitForCommand( EHTMLCommands eCmd, int iBrowser ) +{ + while ( !m_bExit ) + { + if ( m_bSleepForValidate ) + { + m_bSleepingForValidate = true; + ThreadSleep( 100 ); + continue; + } + m_bSleepingForValidate = false; + + HTMLCommandBuffer_t *pBuf = NULL; + while ( m_tslCommandBuffers.PopItem( &pBuf ) && pBuf ) + { + if ( pBuf->m_iBrowser == iBrowser ) + { + if ( pBuf->m_eCmd == eCmd ) // it is what we have been waiting for + return pBuf; + if ( pBuf->m_eCmd == eHTMLCommands_BrowserRemove ) // check to see if this is being deleted while its launching + return NULL; + } + + m_vecQueueCommands.AddToTail( pBuf ); + pBuf = NULL; + } + Assert( pBuf == NULL ); + m_evWaitingForCommand.Wait(); + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: wait for a command of this type on the cef thread +//----------------------------------------------------------------------------- +HTMLCommandBuffer_t *CCEFThread::BWaitForResponse( EHTMLCommands eCmd, int iBrowser ) +{ + while ( !m_bExit ) + { + if ( m_bSleepForValidate ) + { + m_bSleepingForValidate = true; + ThreadSleep( 100 ); + continue; + } + m_bSleepingForValidate = false; + + HTMLCommandBuffer_t *pBuf = NULL; + while ( m_tslResponseBuffers.PopItem( &pBuf ) && pBuf ) + { + if ( pBuf->m_iBrowser == iBrowser ) + { + if ( pBuf->m_eCmd == eCmd ) // it is what we have been waiting for + return pBuf; + if ( pBuf->m_eCmd == eHTMLCommands_BrowserRemove ) // check to see if this is being deleted while its launching + return NULL; + } + + m_vecQueueResponses.AddToTail( pBuf ); + pBuf = NULL; + } + Assert( pBuf == NULL ); + m_evWaitingForResponse.Wait(); + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: add a command for the cef thread +//----------------------------------------------------------------------------- +void CCEFThread::PushCommand( HTMLCommandBuffer_t *pBuf ) +{ + m_tslCommandBuffers.PushItem( pBuf ); + m_evWaitingForCommand.Set(); +} + + +//----------------------------------------------------------------------------- +// Purpose: add a command for the main thread +//----------------------------------------------------------------------------- +void CCEFThread::PushResponse( HTMLCommandBuffer_t *pBuf ) +{ + m_tslResponseBuffers.PushItem( pBuf ); + m_evWaitingForResponse.Set(); +} + + + +//----------------------------------------------------------------------------- +// Purpose: get any cef responses on the main thread, returns false if there are none +//----------------------------------------------------------------------------- +bool CCEFThread::GetMainThreadCommand( HTMLCommandBuffer_t **pBuf ) +{ + if ( m_vecQueueResponses.Count() ) + { + *pBuf = m_vecQueueResponses[0]; + m_vecQueueResponses.Remove(0); + return true; + } + else + return m_tslResponseBuffers.PopItem( pBuf ); +} + + +//----------------------------------------------------------------------------- +// Purpose: get the next cef thread protobuf command to run, return false if there are none +//----------------------------------------------------------------------------- +bool CCEFThread::GetCEFThreadCommand( HTMLCommandBuffer_t **pBuf ) +{ + if ( m_vecQueueCommands.Count() ) + { + *pBuf = m_vecQueueCommands[0]; + m_vecQueueCommands.Remove(0); + return true; + } + else + return m_tslCommandBuffers.PopItem( pBuf ); +} + + +//----------------------------------------------------------------------------- +// Purpose: the user agent to report when using this browser control +//----------------------------------------------------------------------------- +const char *CCEFThread::PchWebkitUserAgent() +{ + return "Mozilla/5.0 (%s; U; %s; en-US; %s/%llu; %s) AppleWebKit/535.15 (KHTML, like Gecko) Chrome/18.0.989.0 Safari/535.11"; +} + + +//----------------------------------------------------------------------------- +// Purpose: make a new browser object +//----------------------------------------------------------------------------- +void CCEFThread::ThreadCreateBrowser( const CHTMLProtoBufMsg &htmlCommand ) +{ + int idx = m_listClientHandlers.AddToTail(); + ++m_nBrowserSerial; + CClientHandler *pBrowser = new CClientHandler( BROWSER_HANDLE_FROM_INDEX_SERIAL( idx, m_nBrowserSerial ), PchWebkitUserAgent(), m_nBrowserSerial ); + pBrowser->SetUserAgentIdentifier( htmlCommand.BodyConst().useragent().c_str() ); + + // send the handle info back to the main thread, needs to be before the CreateBrowser below + // to stop a race condition of the browser being ready before the main thread knows its handle + CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserCreateResponse ); + cmd.Body().set_browser_handle( BROWSER_HANDLE_FROM_INDEX_SERIAL( idx, m_nBrowserSerial ) ); + cmd.Body().set_request_id( htmlCommand.BodyConst().request_id() ); + HTMLCommandBuffer_t *pBuf = GetFreeCommandBuffer( eHTMLCommands_BrowserCreateResponse, idx ); + cmd.SerializeCrossProc( &pBuf->m_Buffer ); + PushResponse( pBuf ); + + m_listClientHandlers[idx] = pBrowser; + pBrowser->AddRef(); + + CefWindowInfo info; + info.SetAsOffScreen( NULL ); + info.m_bPopupWindow = htmlCommand.BodyConst().popup(); + + CefBrowserSettings settings; + settings.fullscreen_enabled = true; + settings.threaded_compositing_enabled = true; + settings.java_disabled = true; + //settings.accelerated_compositing_enabled = true; // not supported when going into fullscreen mode + + // Drag and drop is supposed to be disabled automatically for offscreen views, but + // ports for Mac and Linux have bugs where it is not really disabled, causing havoc + settings.drag_drop_disabled = true; + +#ifdef LINUX + // Turn off web features here that don't work on Linux + settings.webgl_disabled = true; +#endif + + CefBrowser::CreateBrowserSync( info, pBrowser, "", settings ); + CefDoMessageLoopWork(); +} + + +//----------------------------------------------------------------------------- +// Purpose: return true if this browser handle resolves into a currently valid browser handler object +//----------------------------------------------------------------------------- +bool CCEFThread::BIsValidBrowserHandle( uint32 nHandle, int &iClient ) +{ + iClient = nHandle & 0xffff; + if ( m_listClientHandlers.IsValidIndex( nHandle & 0xffff ) ) + { + if ( m_listClientHandlers[ iClient ]->NSerial() == BROWSER_SERIAL_FROM_HANDLE( nHandle ) ) + return true; + } + iClient = -1; + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: delete this browser object, we are done with it +//----------------------------------------------------------------------------- +void CCEFThread::ThreadRemoveBrowser( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + m_listClientHandlers[iClient]->CloseBrowser(); + m_listClientHandlers[iClient]->Release(); + m_listClientHandlers[iClient] = NULL; + m_listClientHandlers.Remove( iClient ); + } +} + + +// helper macro to call browser functions if you have a valid handle +#define GET_BROSWER_FUNC( msg, cmd ) \ + int iClient; \ + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) \ + { \ + if ( m_listClientHandlers[iClient]->GetBrowser() ) \ + m_listClientHandlers[iClient]->GetBrowser()->cmd; \ + } \ + + //else \ + // Assert( false ); + + +//----------------------------------------------------------------------------- +// Purpose: make the web page this many pixels wide&tall +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserSize( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, SetSize( PET_VIEW, htmlCommand.BodyConst().width(), htmlCommand.BodyConst().height() ) ); + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + m_listClientHandlers[iClient]->SetSize( htmlCommand.BodyConst().width(), htmlCommand.BodyConst().height() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: set the global position of the browser to these co-ords, used by some activeX controls +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserPosition( const CHTMLProtoBufMsg &htmlCommand ) +{ + // no longer used - BUGBUG remove me +} + + +//----------------------------------------------------------------------------- +// Purpose: load this url in the browser with optional post data +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserPostURL( const CHTMLProtoBufMsg &htmlCommand ) +{ + if ( htmlCommand.BodyConst().url().empty() ) + return; // they asked us to load nothing, ignore them + + const char *pchPostData = htmlCommand.BodyConst().post().c_str(); + if ( !pchPostData || !pchPostData[0] ) + { + GET_BROSWER_FUNC( htmlCommand, GetMainFrame()->LoadURL( std::wstring( CStrAutoEncode( htmlCommand.BodyConst().url().c_str() ).ToWString() ) ) ); + } + else + { + // Create a new request + CefRefPtr request(CefRequest::CreateRequest()); + + // Set the request URL + request->SetURL( CStrAutoEncode( htmlCommand.BodyConst().url().c_str() ).ToWString() ); + + // Add post data to the request. The correct method and content- + // type headers will be set by CEF. + CefRefPtr postDataElement( CefPostDataElement::CreatePostDataElement() ); + + std::string data = pchPostData; + + postDataElement->SetToBytes(data.length(), data.c_str()); + + CefRefPtr postData(CefPostData::CreatePostData()); + + postData->AddElement(postDataElement); + request->SetPostData(postData); + + GET_BROSWER_FUNC( htmlCommand, GetMainFrame()->LoadRequest( request ) ); + } + + int iClient; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + m_listClientHandlers[iClient]->SetPendingPageSerial( htmlCommand.BodyConst().pageserial() ); + + CefDoMessageLoopWork(); // make sure CEF processes this load before we do anything else (like delete the browser control) +} + + +//----------------------------------------------------------------------------- +// Purpose: stop loading the page we are on +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserStopLoad( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, StopLoad() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: reload the current page +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserReload( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, Reload() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: go forward one page in its history +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserGoForward( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, GoForward() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: go back one page in its history +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserGoBack( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, GoBack() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: copy selected text to the clipboard +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserCopy( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, GetMainFrame()->Copy() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: paste from the clipboard into the web page +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserPaste( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, GetMainFrame()->Paste() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: run this javascript on the current page +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserExecuteJavascript( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, GetMainFrame()->ExecuteJavaScript( + CStrAutoEncode( htmlCommand.BodyConst().script().c_str() ).ToWString(), L"", 0 ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: tell the browser it has key focus +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserSetFocus( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, SetFocus( htmlCommand.BodyConst().focus() ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: get the size of the horizontal scroll bar +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserHorizontalScrollBarSize( const CHTMLProtoBufMsg &htmlCommand ) +{ + ThreadBrowserHorizontalScrollBarSizeHelper( htmlCommand.BodyConst().browser_handle(), true ); +} + + +//----------------------------------------------------------------------------- +// Purpose: tell the main thread details of the horiztonal scrollbar +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserHorizontalScrollBarSizeHelper( int iBrowser, bool bForceSendUpdate ) +{ + CClientHandler::CachedScrollBarState_t scroll = { }; + float flZoom = 0.0f; + int iClient = 0; + if ( BIsValidBrowserHandle( iBrowser, iClient ) ) + { + CClientHandler *pHandler = m_listClientHandlers[iClient]; + + if ( pHandler->GetBrowser() ) + { + scroll.m_nMax = pHandler->GetBrowser()->HorizontalScrollMax(); + scroll.m_nScroll = pHandler->GetBrowser()->HorizontalScroll(); + scroll.m_bVisible = pHandler->GetBrowser()->IsHorizontalScrollBarVisible(); + pHandler->GetBrowser()->HorizontalScrollBarSize( scroll.m_nX, scroll.m_nY, scroll.m_nWide, scroll.m_nTall ); + flZoom = pHandler->GetBrowser()->GetZoomLevel(); + if ( scroll.m_nMax < 0 ) + scroll.m_nMax = 0; + if ( flZoom == 0.0f ) + flZoom = 1.0f; + } + + if ( bForceSendUpdate || 0 != memcmp( &pHandler->m_CachedHScroll, &scroll, sizeof( scroll ) ) ) + { + pHandler->m_CachedHScroll = scroll; + + CHTMLProtoBufMsg cmd( eHTMLCommands_HorizontalScrollBarSizeResponse ); + cmd.Body().set_x( scroll.m_nX ); + cmd.Body().set_y( scroll.m_nY ); + cmd.Body().set_wide( scroll.m_nWide ); + cmd.Body().set_tall( scroll.m_nTall ); + cmd.Body().set_scroll_max( scroll.m_nMax ); + cmd.Body().set_scroll( scroll.m_nScroll ); + cmd.Body().set_visible( scroll.m_bVisible != 0 ); + cmd.Body().set_zoom( flZoom ); + int m_iBrowser = iBrowser; + DISPATCH_MESSAGE( eHTMLCommands_HorizontalScrollBarSizeResponse ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: get the size of the verical scrollbar +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserVerticalScrollBarSize( const CHTMLProtoBufMsg &htmlCommand ) +{ + ThreadBrowserVerticalScrollBarSizeHelper( htmlCommand.BodyConst().browser_handle(), true ); +} + + +//----------------------------------------------------------------------------- +// Purpose: tell the main thread details of the vertical scrollbar +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserVerticalScrollBarSizeHelper( int iBrowser, bool bForceSendUpdate ) +{ + CClientHandler::CachedScrollBarState_t scroll = { }; + float flZoom = 0.0f; + int iClient = 0; + if ( BIsValidBrowserHandle( iBrowser, iClient ) ) + { + CClientHandler *pHandler = m_listClientHandlers[iClient]; + + if ( pHandler->GetBrowser() ) + { + scroll.m_nMax = pHandler->GetBrowser()->VerticalScrollMax(); + scroll.m_nScroll = pHandler->GetBrowser()->VerticalScroll(); + scroll.m_bVisible = pHandler->GetBrowser()->IsVeritcalScrollBarVisible(); + pHandler->GetBrowser()->VerticalScrollBarSize( scroll.m_nX, scroll.m_nY, scroll.m_nWide, scroll.m_nTall ); + flZoom = pHandler->GetBrowser()->GetZoomLevel(); + if ( scroll.m_nMax < 0 ) + scroll.m_nMax = 0; + if ( flZoom == 0.0f ) + flZoom = 1.0f; + } + + if ( bForceSendUpdate || 0 != memcmp( &pHandler->m_CachedVScroll, &scroll, sizeof( scroll ) ) ) + { + pHandler->m_CachedVScroll = scroll; + + CHTMLProtoBufMsg cmd( eHTMLCommands_VerticalScrollBarSizeResponse ); + cmd.Body().set_x( scroll.m_nX ); + cmd.Body().set_y( scroll.m_nY ); + cmd.Body().set_wide( scroll.m_nWide ); + cmd.Body().set_tall( scroll.m_nTall ); + cmd.Body().set_scroll_max( scroll.m_nMax ); + cmd.Body().set_scroll( scroll.m_nScroll ); + cmd.Body().set_visible( scroll.m_bVisible != 0 ); + cmd.Body().set_zoom( flZoom ); + int m_iBrowser = iBrowser; + DISPATCH_MESSAGE( eHTMLCommands_VerticalScrollBarSizeResponse ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: start a find in a web page +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserFind( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, Find( 1, htmlCommand.BodyConst().find().c_str(), !htmlCommand.BodyConst().reverse(), false, htmlCommand.BodyConst().infind() ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: dismiss a current find +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserStopFind( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, StopFinding( 1 ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: scroll the page horizontally to this pos +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserSetHorizontalScroll( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, SetHorizontalScroll( htmlCommand.BodyConst().scroll() ) ); + + ThreadBrowserHorizontalScrollBarSizeHelper( htmlCommand.BodyConst().browser_handle(), true ); +} + + +//----------------------------------------------------------------------------- +// Purpose: scroll the page vertically to this pos +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserSetVerticalScroll( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, SetVerticalScroll( htmlCommand.BodyConst().scroll() ) ); + + ThreadBrowserVerticalScrollBarSizeHelper( htmlCommand.BodyConst().browser_handle(), true ); +} + + +//----------------------------------------------------------------------------- +// Purpose: set the zoom to this level, 100% means normal size, 200% double size +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserSetZoomLevel( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, SetZoomLevel( htmlCommand.BodyConst().zoom() ) ); + + CefDoMessageLoopWork(); // tell cef to think one frame, Zoom is in a work queue and we want it to apply right away so think now + + // now tell if about the scrollbars we now have + ThreadBrowserHorizontalScrollBarSizeHelper( htmlCommand.BodyConst().browser_handle(), true ); + ThreadBrowserVerticalScrollBarSizeHelper( htmlCommand.BodyConst().browser_handle(), true ); +} + + +//----------------------------------------------------------------------------- +// Purpose: show the page source in notepad +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserViewSource( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, GetMainFrame()->ViewSource() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: add a header to any requests we make +//----------------------------------------------------------------------------- +void CCEFThread::ThreadAddHeader( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + m_listClientHandlers[iClient]->AddHeader( htmlCommand.BodyConst().key().c_str(), htmlCommand.BodyConst().value().c_str() ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: main thread is done with a paint buffer, tell our handler +//----------------------------------------------------------------------------- +void CCEFThread::ThreadNeedsPaintResponse( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + m_listClientHandlers[iClient]->SetTextureUploaded( htmlCommand.BodyConst().textureid() ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: set the error strings to display on page load +//----------------------------------------------------------------------------- +void CCEFThread::ThreadBrowserErrorStrings( const CHTMLProtoBufMsg &cmd ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( cmd.BodyConst().browser_handle(), iClient ) ) + { + m_listClientHandlers[iClient]->SetErrorStrings( cmd.BodyConst().title().c_str(), cmd.BodyConst().header().c_str(), + cmd.BodyConst().cache_miss().c_str(), cmd.BodyConst().bad_url().c_str(), + cmd.BodyConst().connection_problem().c_str(), + cmd.BodyConst().proxy_problem().c_str(), cmd.BodyConst().unknown().c_str() ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: return the zoom level for the current page, 100% means actual size, 200% double size +//----------------------------------------------------------------------------- +void CCEFThread::ThreadGetZoom( const CHTMLProtoBufMsg &htmlCmd ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCmd.BodyConst().browser_handle(), iClient ) ) + { + float flZoom = 0.0f; + if ( m_listClientHandlers[iClient]->GetBrowser() ) + flZoom = m_listClientHandlers[iClient]->GetBrowser()->GetZoomLevel(); + if ( flZoom == 0.0f ) + flZoom = 1.0f; + if ( flZoom > 100.0f ) + flZoom /= 100.0f; + CHTMLProtoBufMsg cmd( eHTMLCommands_GetZoomResponse ); + cmd.Body().set_zoom( flZoom ); + int m_iBrowser = htmlCmd.BodyConst().browser_handle(); + DISPATCH_MESSAGE( eHTMLCommands_GetZoomResponse ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: handler func to throw the cookie call to the cef IO thread +//----------------------------------------------------------------------------- +void IOT_SetCookie(const CefString& url, CefCookie* cookie, CThreadEvent *pEvent ) +{ + CefSetCookie(url, *cookie); + pEvent->Set(); +} + + +//----------------------------------------------------------------------------- +// Purpose: set this cookie into the cef instance +//----------------------------------------------------------------------------- +void CCEFThread::ThreadSetCookie( const CHTMLProtoBufMsg &htmlCommand ) +{ + CefCookie cookie; + cef_string_utf8_copy( htmlCommand.BodyConst().key().c_str(), htmlCommand.BodyConst().key().size(), &cookie.name ); + cef_string_utf8_copy( htmlCommand.BodyConst().value().c_str(), htmlCommand.BodyConst().value().size(), &cookie.value ); + cef_string_utf8_copy( htmlCommand.BodyConst().path().c_str(), htmlCommand.BodyConst().path().size(), &cookie.path ); + if ( htmlCommand.BodyConst().has_expires() ) + { + AssertMsg( false, "Cookie expiration not implemented -- rtime.cpp needs to be ported." ); + /* + cookie.has_expires = true; + CRTime rtExpire( (RTime32)htmlCommand.BodyConst().expires() ); + cookie.expires.year = rtExpire.GetLocalYear(); + cookie.expires.month = rtExpire.GetLocalMonth(); +#if !defined(OS_MACOSX) + cookie.expires.day_of_week = rtExpire.GetLocalDayOfWeek(); +#endif + cookie.expires.day_of_month = rtExpire.GetLocalMonth(); + cookie.expires.hour = rtExpire.GetLocalHour(); + cookie.expires.minute = rtExpire.GetLocalMinute(); + cookie.expires.second = rtExpire.GetLocalSecond(); + */ + } + + CThreadEvent event; + CefPostTask(TID_IO, NewCefRunnableFunction( IOT_SetCookie, htmlCommand.BodyConst().host().c_str(), &cookie, &event)); + event.Wait(); +} + + +struct CCefCookie +{ + CUtlString sValue; + CUtlString sName; + CUtlString sDomain; + CUtlString sPath; +}; +//----------------------------------------------------------------------------- +// Purpose: helper class to iterator cookies +//----------------------------------------------------------------------------- +class CookieVisitor : public CefCookieVisitor { +public: + CookieVisitor( CUtlVector *pVec, CThreadEvent *pEvent ) + { + m_pVecCookies = pVec; + m_pEvent = pEvent; + } + ~CookieVisitor() + { + m_pEvent->Set(); + } + + virtual bool Visit(const CefCookie& cookie, int count, int total, + bool& deleteCookie) { + + CCefCookie cookieCopy; + cookieCopy.sValue = cookie.value.str; + cookieCopy.sName = cookie.name.str; + cookieCopy.sDomain = cookie.domain.str; + cookieCopy.sPath = cookie.path.str; + m_pVecCookies->AddToTail( cookieCopy ); + return true; + } + +private: + CUtlVector *m_pVecCookies; + CThreadEvent *m_pEvent; + + IMPLEMENT_REFCOUNTING(CookieVisitor); +}; + + +//----------------------------------------------------------------------------- +// Purpose: handler func to throw the cookie call to the cef IO thread +//----------------------------------------------------------------------------- +void IOT_CookiesForURL(const CefString& url, CThreadEvent *pEvent, CUtlVector *pVecCookies ) +{ + CefVisitUrlCookies( url, false, new CookieVisitor( pVecCookies, pEvent ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: get all the cookies for this URL +//----------------------------------------------------------------------------- +void CCEFThread::ThreadGetCookiesForURL( const CHTMLProtoBufMsg &htmlCommand ) +{ + CUtlVector vecCookies; + CThreadEvent event; + CefPostTask(TID_IO, NewCefRunnableFunction( IOT_CookiesForURL, htmlCommand.BodyConst().url().c_str(), &event, &vecCookies )); + event.Wait(); + + CHTMLProtoBufMsg cmd( eHTMLCommands_GetCookiesForURLResponse ); + int m_iBrowser = htmlCommand.BodyConst().browser_handle(); + cmd.Body().set_url( htmlCommand.BodyConst().url() ); + + FOR_EACH_VEC( vecCookies, i ) + { + CCookie *pCookie = cmd.Body().add_cookies(); + pCookie->set_name( vecCookies[i].sName ); + pCookie->set_value( vecCookies[i].sValue ); + pCookie->set_domain( vecCookies[i].sDomain ); + pCookie->set_path( vecCookies[i].sPath ); + } + + DISPATCH_MESSAGE( eHTMLCommands_GetCookiesForURLResponse ); +} + + +//----------------------------------------------------------------------------- +// Purpose: set the framerate to run CEF at +//----------------------------------------------------------------------------- +void CCEFThread::ThreadSetTargetFrameRate( const CHTMLProtoBufMsg &htmlCommand ) +{ + m_nTargetFrameRate = htmlCommand.BodyConst().ntargetframerate(); +} + + +//----------------------------------------------------------------------------- +// Purpose: hide any showing popup for this browser +//----------------------------------------------------------------------------- +void CCEFThread::ThreadHidePopup( const CHTMLProtoBufMsg &htmlCommand ) +{ + GET_BROSWER_FUNC( htmlCommand, HidePopup() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: request a full redraw of the client +//----------------------------------------------------------------------------- +void CCEFThread::ThreadFullRepaint( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + if ( m_listClientHandlers[iClient]->GetBrowser()->IsVisuallyNonEmpty() ) + { + int wide, tall; + m_listClientHandlers[iClient]->GetExpectedSize( wide, tall); + CefRect rect; + rect.x = rect.y = 0; + rect.width = wide; + rect.height = tall; + m_listClientHandlers[iClient]->GetBrowser()->Invalidate( rect ); + } + else + m_listClientHandlers[iClient]->GetBrowser()->Reload(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: configure the options we want for cef +//----------------------------------------------------------------------------- +void CCEFThread::AppGetSettings(CefSettings& settings, CefRefPtr& app) +{ + settings.multi_threaded_message_loop = false; +#if defined(OS_WIN) + settings.auto_detect_proxy_settings_enabled = true; +#endif + + CefString(&settings.cache_path) = m_sHTMLCacheDir; + CefString(&settings.product_version) = "Steam"; +// CefString(&settings.log_file) = +/*#ifdef WIN32 + settings.graphics_implementation = ANGLE_IN_PROCESS_COMMAND_BUFFER; +#else*/ + settings.graphics_implementation = DESKTOP_IN_PROCESS_COMMAND_BUFFER; +//#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: clean up the temp folders cef can leave around on crash +//----------------------------------------------------------------------------- +void CCEFThread::CleanupTempFolders() +{ + /* + // Temporarily commented out to avoid bringing in additional code. +#if defined( WIN32 ) + char rgchPath[MAX_PATH]; + if ( GetTempPathA( Q_ARRAYSIZE( rgchPath ), rgchPath ) == 0 ) + return; + + CUtlString strPath = rgchPath; +#elif defined( LINUX ) || defined( OSX ) + // TMPDIR first, T_tmpdir next, /tmp last. + char *pszDir = getenv( "TMPDIR" ); + if ( pszDir == NULL ) + { + pszDir = P_tmpdir; + if ( pszDir == NULL ) + pszDir = "/tmp"; + } + if ( pszDir == NULL ) + return; + + CUtlString strPath = pszDir; +#endif + + if ( strPath[strPath.Length()-1] != CORRECT_PATH_SEPARATOR ) + strPath += CORRECT_PATH_SEPARATOR_S; + + CUtlString strSearch = strPath; + strSearch += "scoped_dir*"; + CDirIterator tempDirIterator( strSearch.String() ); + + while ( tempDirIterator.BNextFile() ) + { + if ( tempDirIterator.BCurrentIsDir() ) + { + CUtlString sStrDir = strPath; + sStrDir += tempDirIterator.CurrentFileName(); + BRemoveDirectoryRecursive( sStrDir ); + } + } + */ +} + + +//----------------------------------------------------------------------------- +// Purpose: main thread for pumping CEF, get commands from the main thread and dispatches responses to it +//----------------------------------------------------------------------------- +int CCEFThread::Run() +{ + { // scope to trigger destructors before setting the we have exited event + CefSettings settings; + CefRefPtr app; + + // blow away any temp folders CEF left lying around if we crashed last + CleanupTempFolders(); + + // Populate the settings based on command line arguments. + AppGetSettings(settings, app); + + // Initialize CEF. + CefInitialize(settings, app, ""); + + #if defined( VPROF_ENABLED ) + //CVProfile *pProfile = GetVProfProfileForCurrentThread(); + #endif + + CLimitTimer timer; + CLimitTimer timerLastCefThink; // track when we think cef so we can do it at a minimum of 10hz + timerLastCefThink.SetLimit( k_nMillion ); // 1Hz min think time +#ifdef WIN32 + CLimitTimer timerLastFlashFullscreenThink; // track when we think cef so we can do it at a minimum of 10hz + timerLastFlashFullscreenThink.SetLimit( k_nMillion * k_nMillion ); // think again in the distance future + bool bInitialThinkAfterInput = false; +#endif + while( !m_bExit ) + { + #ifdef OSX + void *pool = CreateAutoReleasePool(); + #endif + + if ( m_bSleepForValidate ) + { + m_bSleepingForValidate = true; + ThreadSleep( 100 ); + continue; + } + m_bSleepingForValidate = false; + + m_bSawUserInputThisFrame = false; + + #if defined( VPROF_ENABLED ) + //if ( pProfile ) + // pProfile->MarkFrame( "UI CEF HTML Thread" ); + #endif + // Limit animation frame rate + timer.SetLimit( k_nMillion/m_nTargetFrameRate ); + + // run any pending commands, get ack'd paint buffers + { + VPROF_BUDGET( "CCEFThread - RunCurrentCommands()", VPROF_BUDGETGROUP_VGUI ); + RunCurrentCommands(); + } + + // now let cef think + if ( m_listClientHandlers.Count() || timerLastCefThink.BLimitReached() ) + { + VPROF_BUDGET( "CCEFThread - CefDoMessageLoopWork()", VPROF_BUDGETGROUP_TENFOOT ); + CefDoMessageLoopWork(); + timerLastCefThink.SetLimit( k_nMillion ); // 1Hz min think time + } + #ifdef OSX + ReleaseAutoReleasePool( pool ); + #endif + + // push any changes to scrollbar data and refresh HTML element hover states + { + VPROF_BUDGET( "CCEFThread - Scroll", VPROF_BUDGETGROUP_VGUI ); + FOR_EACH_LL( m_listClientHandlers, i ) + { + int nBrowser = BROWSER_HANDLE_FROM_INDEX_SERIAL( i, m_listClientHandlers[i]->NSerial() ); + ThreadBrowserHorizontalScrollBarSizeHelper( nBrowser, false ); + ThreadBrowserVerticalScrollBarSizeHelper( nBrowser, false ); + + // workaround a CEF issue where mouse hover states don't update after scrolling + m_listClientHandlers[i]->RefreshCEFHoverStatesAfterScroll(); + } + } + + { + VPROF_BUDGET( "CCEFThread - SendSizeChangeEvents", VPROF_BUDGETGROUP_VGUI ); + SendSizeChangeEvents(); + } + + //see if we need a paint + { + VPROF_BUDGET( "CCEFThread - Paint", VPROF_BUDGETGROUP_VGUI ); + FOR_EACH_LL( m_listClientHandlers, i ) + { + if ( !m_listClientHandlers[i]->GetBrowser() ) + continue; + + m_listClientHandlers[i]->Paint(); + if ( m_listClientHandlers[i]->BPaintBufferReady() && m_listClientHandlers[i]->BNeedsPaint() ) + { + uint32 textureID = m_listClientHandlers[i]->FlipTexture(); + const byte *pRGBA = m_listClientHandlers[i]->PComposedTextureData( textureID ); + if ( pRGBA ) + { + CClientHandler *pHandler = m_listClientHandlers[i]; + pHandler->Lock(); + if ( pHandler->BPendingScreenShot() ) + { + pHandler->SavePageToJPEGIfNeeded( pHandler->GetBrowser(), pRGBA, pHandler->GetTextureWide(), pHandler->GetTextureTall() ); + } + CHTMLProtoBufMsg cmd( eHTMLCommands_NeedsPaint ); + cmd.Body().set_browser_handle( BROWSER_HANDLE_FROM_INDEX_SERIAL( i, pHandler->NSerial() ) ); + cmd.Body().set_wide( pHandler->GetTextureWide() ); + cmd.Body().set_tall( pHandler->GetTextureTall() ); + cmd.Body().set_rgba( (uint64)pRGBA ); + cmd.Body().set_pageserial( pHandler->GetPageSerial() ); + cmd.Body().set_textureid( textureID ); + cmd.Body().set_updatex( pHandler->GetUpdateX() ); + cmd.Body().set_updatey( pHandler->GetUpdateY() ); + cmd.Body().set_updatewide( pHandler->GetUpdateWide() ); + cmd.Body().set_updatetall( pHandler->GetUpdateTall() ); + cmd.Body().set_scrollx( pHandler->GetBrowser()->HorizontalScroll() ); + cmd.Body().set_scrolly( pHandler->GetBrowser()->VerticalScroll() ); + + if ( pHandler->BPopupVisibleAndPainted() ) // add in the combo box's texture data if visible + { + int x,y,wide,tall; // popup sizes + pHandler->PopupRect( x, y, wide, tall ); + cmd.Body().set_combobox_rgba( (uint64)pHandler->PPopupTextureDataCached() ); + cmd.Body().set_combobox_wide( wide ); + cmd.Body().set_combobox_tall( tall ); + } + + // Texture update rect has now been pushed to main thread + pHandler->ClearUpdateRect(); + + HTMLCommandBuffer_t *pBuf = g_CEFThread.GetFreeCommandBuffer( eHTMLCommands_NeedsPaint, i ); + cmd.SerializeCrossProc( &pBuf->m_Buffer ); + pHandler->Unlock(); + + g_CEFThread.PushResponse( pBuf ); + } + else + { + m_listClientHandlers[i]->SetTextureUploaded( textureID ); + } + } + } + } + +#ifdef WIN32 + if ( m_bSawUserInputThisFrame ) + { + if ( timerLastFlashFullscreenThink.CMicroSecLeft() > k_nMillion/10 || timerLastFlashFullscreenThink.BLimitReached() ) + { + timerLastFlashFullscreenThink.SetLimit( k_nMillion/10 ); // check in 100msec + } + bInitialThinkAfterInput = true; + } + + if ( m_listClientHandlers.Count() && timerLastFlashFullscreenThink.BLimitReached() ) + { + CheckForFullScreenFlashControl(); + if ( ( !m_bFullScreenFlashVisible && bInitialThinkAfterInput ) || m_bFullScreenFlashVisible ) + { + timerLastFlashFullscreenThink.SetLimit( k_nMillion ); // could be a slow machine, check again in 1 sec + } + else + { + timerLastFlashFullscreenThink.SetLimit( k_nMillion * k_nMillion ); // think again in the distance future + } + + bInitialThinkAfterInput= false; + } +#endif + + { + VPROF_BUDGET( "Sleep - FPS Limiting", VPROF_BUDGETGROUP_TENFOOT ); + if ( timer.BLimitReached() ) + ThreadSleep( 1 ); + else + m_WakeEvent.Wait( timer.CMicroSecLeft() / 1000 ); + } + } + + FOR_EACH_LL( m_listClientHandlers, i ) + { + if ( m_listClientHandlers[i] ) + m_listClientHandlers[i]->CloseBrowser(); + m_listClientHandlers[i] = NULL;; + } + m_listClientHandlers.RemoveAll(); + + CefDoMessageLoopWork(); // pump the message loop to clear the close browser calls from above + + CefShutdown(); + } + m_eventDidExit.Set(); + return 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: special code to sniff for flash doing its usual naughty things +//----------------------------------------------------------------------------- +void CCEFThread::CheckForFullScreenFlashControl() +{ +#ifdef WIN32 + VPROF_BUDGET( "Searching for Flash fullscreen - FindWindowEx", VPROF_BUDGETGROUP_TENFOOT ); + + // see if we need to drag the flash fullscreen window to front + HWND flashfullscreenHWND = ::FindWindowEx( NULL, NULL, "ShockwaveFlashFullScreen", NULL ); + if ( flashfullscreenHWND ) + { + DWORD proccess_id; + GetWindowThreadProcessId( flashfullscreenHWND, &proccess_id); + TCHAR exe_path[MAX_PATH]; + GetModuleFileName( GetModuleHandle(NULL), exe_path, MAX_PATH); + HANDLE hmodule = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, proccess_id); + MODULEENTRY32 mod = { sizeof(MODULEENTRY32) }; + if ( Module32First( hmodule, &mod) ) + { + if ( Q_stricmp(mod.szExePath, exe_path) == 0 ) + { + if ( !m_bFullScreenFlashVisible ) + { + m_bFullScreenFlashVisible = true; + m_flashfullscreenHWND = flashfullscreenHWND; + + FOR_EACH_LL( m_listClientHandlers, i ) + { + if ( m_listClientHandlers[i] ) + m_listClientHandlers[i]->GetRenderHandler()->OnEnterFullScreen( m_listClientHandlers[i]->GetBrowser() ); + } + + SetForegroundWindow( m_flashfullscreenHWND ); + } + } + else + { + if ( m_bFullScreenFlashVisible ) + { + m_bFullScreenFlashVisible = false; + m_flashfullscreenHWND = NULL; + FOR_EACH_LL( m_listClientHandlers, i ) + { + if ( m_listClientHandlers[i] ) + m_listClientHandlers[i]->GetRenderHandler()->OnExitFullScreen( m_listClientHandlers[i]->GetBrowser() ); + } + } + } + } + CloseHandle( hmodule ); + } + else + { + if ( m_bFullScreenFlashVisible ) + { + m_bFullScreenFlashVisible = false; + m_flashfullscreenHWND = NULL; + FOR_EACH_LL( m_listClientHandlers, i ) + { + if ( m_listClientHandlers[i] ) + m_listClientHandlers[i]->GetRenderHandler()->OnExitFullScreen( m_listClientHandlers[i]->GetBrowser() ); + } + } + } +#else +#warning "Do we need to sniff for fullscreen flash and it breaking us?" +#endif +} + + +#ifdef DBGFLAG_VALIDATE +//----------------------------------------------------------------------------- +// Purpose: validate mem +//----------------------------------------------------------------------------- +void CCEFThread::Validate( CValidator &validator, const tchar *pchName ) +{ + // hacky but reliable way to avoid both vgui and panorama validating all this stuff twice + if ( !validator.IsClaimed( m_sHTMLCacheDir.Access() ) ) + { + VALIDATE_SCOPE(); + ValidateObj( m_sHTMLCacheDir ); + ValidateObj( m_sCookiePath ); + ValidateObj( m_listClientHandlers ); + FOR_EACH_LL( m_listClientHandlers, i ) + { + ValidatePtr( m_listClientHandlers[i] ); + } + ValidateObj( m_vecQueueCommands ); + FOR_EACH_VEC( m_vecQueueCommands, i ) + { + ValidatePtr( m_vecQueueCommands[i] ); + } + ValidateObj( m_vecQueueResponses ); + FOR_EACH_VEC( m_vecQueueResponses, i ) + { + ValidatePtr( m_vecQueueResponses[i] ); + } + + ValidateObj( m_tslUnsedBuffers ); + { + CTSList::Node_t *pNode = m_tslUnsedBuffers.Detach(); + while ( pNode ) + { + CTSList::Node_t *pNext = (CTSList::Node_t *)pNode->Next; + ValidatePtr( pNode->elem ); + m_tslUnsedBuffers.Push( pNode ); + pNode = pNext; + } + } + + ValidateObj( m_tslCommandBuffers ); + ValidateObj( m_tslResponseBuffers ); + } +} +#endif + + +//----------------------------------------------------------------------------- +// Purpose: turn on CEF and its supporting thread +//----------------------------------------------------------------------------- +void ChromeInit( const char *pchHTMLCacheDir, const char *pchCookiePath ) +{ + Assert( !g_CEFThread.IsAlive() ); + g_CEFThread.SetCEFPaths( pchHTMLCacheDir, pchCookiePath ); + g_CEFThread.SetName( "UICEFThread" ); + g_CEFThread.Start(); +} + + +//----------------------------------------------------------------------------- +// Purpose: turn off CEF +//----------------------------------------------------------------------------- +void ChromeShutdown() +{ + g_CEFThread.TriggerShutdown(); + g_CEFThread.Join( 20 *k_nThousand ); +} + + +#ifdef DBGFLAG_VALIDATE +//----------------------------------------------------------------------------- +// Purpose: suspend the cef thread so we can validate mem +//----------------------------------------------------------------------------- +bool ChromePrepareForValidate() +{ + g_CEFThread.SleepForValidate(); + while ( !g_CEFThread.BSleepingForValidate() ) + ThreadSleep( 100 ); + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: wake the cef thread back up +//----------------------------------------------------------------------------- +bool ChromeResumeFromValidate() +{ + g_CEFThread.WakeFromValidate(); + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ChromeValidate( CValidator &validator, const char *pchName ) +{ + g_CEFThread.Validate( validator, "g_CEFThread" ); +} +#endif + + +//----------------------------------------------------------------------------- +// Purpose: set this cookie to be used by cef +//----------------------------------------------------------------------------- +bool ChromeSetWebCookie( const char *pchHostname, const char *pchName, const char *pchValue, const char *pchPath, RTime32 nExpires ) +{ + CHTMLProtoBufMsg< CMsgSetCookie > cmd( eHTMLCommands_SetCookie ); + cmd.Body().set_value( pchValue ); + cmd.Body().set_key( pchName ); + cmd.Body().set_path( pchPath ); + cmd.Body().set_host( pchHostname ); + if ( nExpires ) + cmd.Body().set_expires( nExpires ); + + HTMLCommandBuffer_t *pBuf = g_CEFThread.GetFreeCommandBuffer( eHTMLCommands_SetCookie, -1 ); + cmd.SerializeCrossProc( &pBuf->m_Buffer ); + g_CEFThread.PushCommand( pBuf ); + g_CEFThread.WakeThread(); + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: set this cookie to be used by cef +//----------------------------------------------------------------------------- +bool ChromeGetWebCookiesForURL( CUtlString *pstrValue, const char *pchURL, const char *pchName ) +{ + pstrValue->Clear(); + + { + CHTMLProtoBufMsg cmd( eHTMLCommands_GetCookiesForURL ); + cmd.Body().set_url( pchURL ); + + HTMLCommandBuffer_t *pCmd = g_CEFThread.GetFreeCommandBuffer( eHTMLCommands_GetCookiesForURL, -1 ); + cmd.SerializeCrossProc( &pCmd->m_Buffer ); + g_CEFThread.PushCommand( pCmd ); + } + + HTMLCommandBuffer_t *pBuf = g_CEFThread.BWaitForResponse( eHTMLCommands_GetCookiesForURLResponse, -1 ); + if ( pBuf ) + { + CHTMLProtoBufMsg< CMsgGetCookiesForURLResponse > cmdResponse( eHTMLCommands_GetCookiesForURLResponse ); + if ( cmdResponse.BDeserializeCrossProc( &pBuf->m_Buffer ) ) + { + for ( int i = 0; i < cmdResponse.BodyConst().cookies_size(); i++ ) + { + const CCookie &cookie = cmdResponse.BodyConst().cookies(i); + if ( cookie.name() == pchName ) + pstrValue->Set( cookie.value().c_str() ); + } + } + g_CEFThread.ReleaseCommandBuffer( pBuf ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: set the build number to report in our user agent +//----------------------------------------------------------------------------- +void ChromeSetClientBuildID( uint64 ulBuildID ) +{ + sm_ulBuildID = ulBuildID; +} + + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CChromePainter::CChromePainter( CClientHandler *pParent ) +{ + m_iNextTexture = 0; + m_iTexturesInFlightBits = 0; + m_pParent = pParent; + m_bUpdated = false; + m_bPopupVisible = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: destructor +//----------------------------------------------------------------------------- +CChromePainter::~CChromePainter() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: cef is calling us back and saying it updated the html texture +//----------------------------------------------------------------------------- +void CChromePainter::OnPaint(CefRefPtr browser, PaintElementType type, const RectList& dirtyRects, const void* buffer) +{ + VPROF_BUDGET( "CChromePainter::DrawSubTextureRGBA", VPROF_BUDGETGROUP_VGUI ); + + int wide, tall; + browser->GetSize( type, wide, tall ); + + if ( wide <= 0 || tall <= 0 ) + return; + + if ( type == PET_POPUP ) + { + m_nPopupWide = wide; + m_nPopupTall = tall; + + m_PopupTexture.EnsureCount( tall*wide*4 ); + Q_memcpy( m_PopupTexture.Base(), buffer, tall*wide*4 ); + + // force a recomposition + display whenever painting a popup + m_bUpdated = true; + } + else + { + // main browser painting + + if ( !m_pParent->IsVisuallyNonEmpty() ) + { + return; + } + + // If there were no dirty regions (unlikely), perhaps due to a bug, be conservative and paint all + if ( dirtyRects.empty() ) + { + m_MainTexture.MarkAllDirty(); + } + else + { + for ( RectList::const_iterator iter = dirtyRects.begin(); iter != dirtyRects.end(); ++iter ) + { + m_MainTexture.MarkDirtyRect( iter->x, iter->y, iter->x + iter->width, iter->y + iter->height ); + } + } + + // Refresh all dirty main texture pixels from the chromium rendering buffer + if ( m_MainTexture.BUpdatePixels( (byte*)buffer, wide, tall ) ) + { + // at least one pixel in the main texture has changed + m_bUpdated = true; + + // Notify the main thread that this newly painted region is dirty + m_UpdateRect.MarkDirtyRect( m_MainTexture ); + + // Merge update region into all buffer textures so that at composition time, + // they know to copy the union of all updates since the last composition + for ( size_t i = 0; i < Q_ARRAYSIZE(m_Texture); ++i ) + { + m_Texture[i].MarkDirtyRect( m_MainTexture ); + } + } + + // The main texture is now a clean copy of chromium's canvas backing + m_MainTexture.MarkAllClean(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: true if we have had a paint call from cef +//----------------------------------------------------------------------------- +bool CChromePainter::BUpdated() +{ + return m_bUpdated; +} + + +//----------------------------------------------------------------------------- +// Purpose: force the updated state +//----------------------------------------------------------------------------- +void CChromePainter::SetUpdated( bool state ) +{ + m_bUpdated = state; +} + + +//----------------------------------------------------------------------------- +// Purpose: move to the next html texture to render into +//----------------------------------------------------------------------------- +uint32 CChromePainter::FlipTexture() +{ + int iTex = m_iNextTexture; + m_iTexturesInFlightBits |= ( 1< 0 && m_MainTexture.GetTall() > 0) && !( m_iTexturesInFlightBits & (1< parentBrowser, + const CefPopupFeatures& popupFeatures, + CefWindowInfo& windowInfo, + const CefString& url, + bool bForeground, + CefRefPtr& client, + CefBrowserSettings& settings ) +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_OpenNewTab ); + cmd.Body().set_url( url.c_str() ); + cmd.Body().set_bforeground( bForeground ); + + DISPATCH_MESSAGE( eHTMLCommands_OpenNewTab ); + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Called before a new popup window is created. The |parentBrowser| parameter +// will point to the parent browser window. The |popupFeatures| parameter will +// contain information about the style of popup window requested. Return false +// to have the framework create the new popup window based on the parameters +// in |windowInfo|. Return true to cancel creation of the popup window. By +// default, a newly created popup window will have the same client and +// settings as the parent window. To change the client for the new window +// modify the object that |client| points to. To change the settings for the +// new window modify the |settings| structure. +//----------------------------------------------------------------------------- +bool CClientHandler::OnBeforePopup(CefRefPtr parentBrowser, + const CefPopupFeatures& popupFeatures, + CefWindowInfo& windowInfo, + const CefString& url, + CefRefPtr& client, + CefBrowserSettings& settings) +{ + // If it's a steam:// url we already have a scheme handler installed, however that's too late to block the frame navigating + // and we'll end up loading a blank new window. So preempt that happening here and handle early returning that we've handled so + // chromium won't actually change URLs or navigate at all. + if ( url.size() > 0 && ( Q_stristr( url.c_str(), "steam://" ) || Q_stristr( url.c_str(), "steambeta://" ) ) && Q_stristr( url.c_str(), "/close" ) == NULL ) + { + CStrAutoEncode urlString( url.c_str() ); + CHTMLProtoBufMsg cmd( eHTMLCommands_OpenSteamURL ); + cmd.Body().set_url( urlString.ToString() ); + DISPATCH_MESSAGE( eHTMLCommands_OpenSteamURL ); + + return true; + } + + CStrAutoEncode urlString( url.c_str() ); + static bool bInPopup = false; + // if we get an empty url string when loading a new popup page just don't load it, we don't support the make a + // new popup and .write() into the buffer to make the contents because we want to make a whole new VGUI HTML object + // that contains the webkit widget for that popup, not allow this inline one + if ( urlString.ToString() && Q_strlen( urlString.ToString() ) > 0 ) + { + if ( !bInPopup ) + { + bInPopup = true; + CHTMLProtoBufMsg cmd( eHTMLCommands_PopupHTMLWindow ); + cmd.Body().set_url( urlString.ToString() ); + if ( popupFeatures.xSet ) + cmd.Body().set_x( popupFeatures.x ); + if ( popupFeatures.ySet ) + cmd.Body().set_y( popupFeatures.y ); + if ( popupFeatures.widthSet ) + cmd.Body().set_wide( popupFeatures.width ); + if ( popupFeatures.heightSet ) + cmd.Body().set_tall( popupFeatures.height ); + + DISPATCH_MESSAGE( eHTMLCommands_PopupHTMLWindow ); + bInPopup = false; + return true; + } + } + if ( !bInPopup ) + { + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Event called after a new window is created. The return value is currently +// ignored. +//----------------------------------------------------------------------------- +void CClientHandler::OnAfterCreated(CefRefPtr browser) +{ + Lock(); + if ( !m_Browser ) + { + // We need to keep the main child window, but not popup windows + m_Browser = browser; + browser->SetShowScrollBars( false ); + SetBrowserAgent( browser ); + if ( m_nExpectedWide > 0 && m_nExpectedTall > 0 ) + browser->SetSize( PET_VIEW, m_nExpectedWide, m_nExpectedTall ); + + CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserReady ); + DISPATCH_MESSAGE( eHTMLCommands_BrowserReady ); + } + Unlock(); +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Event called when the page title changes. The return value is currently +// ignored. +//----------------------------------------------------------------------------- +void CClientHandler::OnTitleChange(CefRefPtr browser, const CefString& title) +{ + if ( !title.empty() ) + { + CHTMLProtoBufMsg cmd( eHTMLCommands_SetHTMLTitle ); + cmd.Body().set_title( title ); + + DISPATCH_MESSAGE( eHTMLCommands_SetHTMLTitle ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Event called before browser navigation. The client has an opportunity to +// modify the |request| object if desired. Return RV_HANDLED to cancel +// navigation. +//----------------------------------------------------------------------------- +bool CClientHandler::OnBeforeBrowse(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + NavType navType, + bool isRedirect, + bool isNewTabRequest + ) +{ + std::string sURL = request->GetURL(); + + // If it's a steam:// url we already have a scheme handler installed, however that's too late to block the frame navigating + // and we'll end up loading a blank page. So preempt that happening here and handle early returning that we've handled so + // chromium won't actually change URLs or navigate at all. + if ( sURL.size() > 0 && (sURL.find( "steam://" ) == 0 || sURL.find( "steambeta://" ) == 0) && sURL.find( "/close" ) == std::wstring::npos ) + { + CHTMLProtoBufMsg cmd( eHTMLCommands_OpenSteamURL ); + cmd.Body().set_url( CStrAutoEncode( request->GetURL().c_str() ).ToString() ); + DISPATCH_MESSAGE( eHTMLCommands_OpenSteamURL ); + + return true ; + } + + if ( isNewTabRequest ) + return false; + + // We only care about these on the main frame + if( !frame.get() || !frame->IsMain() ) + return false; + + if ( request->GetPostData() ) + { + CefPostData::ElementVector elements; + request->GetPostData()->GetElements( elements ); + CefPostData::ElementVector::const_iterator it = elements.begin(); + m_strPostData = ""; + CUtlVector vecPostBytes; + while ( it != elements.end() ) + { + if ( it->get()->GetType() == PDE_TYPE_BYTES ) + { + size_t nBytes = it->get()->GetBytesCount(); + int curInsertPos = vecPostBytes.Count(); + vecPostBytes.EnsureCount( curInsertPos + nBytes + 1 ); + it->get()->GetBytes( nBytes, vecPostBytes.Base() + curInsertPos ); + vecPostBytes[ curInsertPos + nBytes ] = 0; + } + it++; + } + + m_strPostData = vecPostBytes.Base(); + } + else + m_strPostData = ""; + + CStrAutoEncode strURL( sURL.c_str() ); + + if ( isRedirect ) + m_strLastRedirectURL = strURL.ToString(); + + bool rv = false; + + { + // scope this so the wait below doesn't keep this allocation on the stack + CHTMLProtoBufMsg cmd( eHTMLCommands_StartRequest ); + cmd.Body().set_url( strURL.ToString() ); + CefString frameName = frame->GetName(); + if ( !frameName.empty() ) + cmd.Body().set_target( frameName.c_str() ); + cmd.Body().set_postdata( m_strPostData ); + cmd.Body().set_bisredirect( isRedirect ); + + DISPATCH_MESSAGE( eHTMLCommands_StartRequest ); + } + + HTMLCommandBuffer_t *pBuf = g_CEFThread.BWaitForCommand( eHTMLCommands_StartRequestResponse, m_iBrowser ); + if ( pBuf ) + { + CHTMLProtoBufMsg< CMsgStartRequestResponse > cmd( eHTMLCommands_StartRequestResponse ); + if ( cmd.BDeserializeCrossProc( &pBuf->m_Buffer ) ) + { + if ( !cmd.BodyConst().ballow() ) + rv = true ; + } + g_CEFThread.ReleaseCommandBuffer( pBuf ); + } + + if ( m_Snapshot.m_sURLSnapshot.IsValid() ) + m_Snapshot.m_flRequestTimeout = Plat_FloatTime(); // before we change URL lets queue up a snapshot for the next paint + + return rv; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Event called when the browser begins loading a page. The |frame| pointer +// will be empty if the event represents the overall load status and not the +// load status for a particular frame. The return value is currently ignored. +//----------------------------------------------------------------------------- +void CClientHandler::OnLoadStart(CefRefPtr browser, + CefRefPtr frame, bool bIsNewNavigation ) +{ + if ( !frame.get() ) + return; + + { + if ( !frame->IsMain() ) + return; + + std::wstring sURL = frame->GetURL(); + if ( sURL.empty() ) + return; + + CStrAutoEncode url( sURL.c_str() ); + m_strCurrentUrl = url.ToString(); + + if ( m_strCurrentUrl.IsEmpty() ) + return; + + bool bIsRedirect = false; + if ( m_strCurrentUrl == m_strLastRedirectURL ) + bIsRedirect = true; + + CHTMLProtoBufMsg cmd( eHTMLCommands_URLChanged ); + cmd.Body().set_url( url.ToString() ); + cmd.Body().set_bnewnavigation( bIsNewNavigation ); + + if ( !m_strPostData.IsEmpty() ) + cmd.Body().set_postdata( m_strPostData.String() ); + + cmd.Body().set_bisredirect( bIsRedirect ); + CefString frameName = frame->GetName(); + if ( !frameName.empty() ) + cmd.Body().set_pagetitle( frameName.c_str() ); + + DISPATCH_MESSAGE( eHTMLCommands_URLChanged ); + } + + { + CHTMLProtoBufMsg cmd( eHTMLCommands_CanGoBackandForward ); + cmd.Body().set_bgoback( browser->CanGoBack() ); + cmd.Body().set_bgoforward( browser->CanGoForward() ); + DISPATCH_MESSAGE( eHTMLCommands_CanGoBackandForward ); + } + + m_nPageSerial = m_nPendingPageSerial; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Event called when the browser is done loading a page. The |frame| pointer +// will be empty if the event represents the overall load status and not the +// load status for a particular frame. This event will be generated +// irrespective of whether the request completes successfully. The return +// value is currently ignored. +//----------------------------------------------------------------------------- +void CClientHandler::OnLoadEnd(CefRefPtr browser, + CefRefPtr frame, + int httpStatusCode, CefRefPtr request ) +{ + // We only care about these on the main frame + if( !frame.get() || !frame->IsMain() ) + return; + + { + CHTMLProtoBufMsg cmd( eHTMLCommands_FinishedRequest ); + cmd.Body().set_url( CStrAutoEncode( browser->GetMainFrame()->GetURL().c_str() ).ToString() ); + CefString frameName = browser->GetMainFrame()->GetName(); + if ( !frameName.empty() ) + cmd.Body().set_pagetitle( frameName.c_str() ); + + if ( request.get() ) + { + CefRequest::HeaderMap headerMap; + request->GetHeaderMap( headerMap ); + + CefRequest::HeaderMap::const_iterator it; + for(it = headerMap.begin(); it != headerMap.end(); ++it) + { + CHTMLHeader *pHeader = cmd.Body().add_headers(); + if ( !it->first.empty() ) + pHeader->set_key( it->first.c_str() ); + if ( !it->second.empty() ) + pHeader->set_value( it->second.c_str() ); + } + + CefRefPtr pRefSecurityDetails = request->SecurityDetails(); + CHTMLPageSecurityInfo *pSecurityInfo = cmd.Body().mutable_security_info(); + if ( pRefSecurityDetails.get() ) + { + pSecurityInfo->set_bissecure( pRefSecurityDetails->BIsSecure() ); + pSecurityInfo->set_bhascerterror( pRefSecurityDetails->BHasCertError() ); + pSecurityInfo->set_issuername( pRefSecurityDetails->PchCertIssuer() ); + pSecurityInfo->set_certname( pRefSecurityDetails->PchCertCommonName() ); + pSecurityInfo->set_certexpiry( pRefSecurityDetails->TCertExpiry() ); + pSecurityInfo->set_bisevcert( pRefSecurityDetails->BIsEVCert() ); + pSecurityInfo->set_ncertbits( pRefSecurityDetails->NCertBits() ); + } + else + { + pSecurityInfo->set_bissecure( false ); + } + } + + DISPATCH_MESSAGE( eHTMLCommands_FinishedRequest ); + } + + { + CHTMLProtoBufMsg cmd( eHTMLCommands_CanGoBackandForward ); + cmd.Body().set_bgoback( browser->CanGoBack() ); + cmd.Body().set_bgoforward( browser->CanGoForward() ); + DISPATCH_MESSAGE( eHTMLCommands_CanGoBackandForward ); + } + + if ( m_Snapshot.m_sURLSnapshot.IsValid() ) + m_Snapshot.m_flRequestTimeout = Plat_FloatTime(); // finished page load, lets queue up a snapshot for the next paint +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Called when the browser fails to load a resource. |errorCode| is the +// error code number and |failedUrl| is the URL that failed to load. To +// provide custom error text assign the text to |errorText| and return +// RV_HANDLED. Otherwise, return RV_CONTINUE for the default error text. +//----------------------------------------------------------------------------- +bool CClientHandler::OnLoadError(CefRefPtr browser, + CefRefPtr frame, + ErrorCode errorCode, + const CefString& failedUrl, + CefString& errorText) +{ + // If it's a steam:// url we always get an error, but we handle it ok internally already, just ignore + if ( failedUrl.size() > 0 && ( Q_stristr( failedUrl.c_str(), "steam://" ) || Q_stristr( failedUrl.c_str(), "steambeta://" ) ) ) + return false; + + const char *pchDetail = NULL; + switch ( errorCode ) + { + case ERR_ABORTED: + // We'll get this in cases where we just start another URL before finishing a previous and such, don't show it. + return false; + break; + case ERR_CACHE_MISS: + pchDetail = m_sErrorCacheMiss; + break; + case ERR_UNKNOWN_URL_SCHEME: + case ERR_INVALID_URL: + pchDetail = m_sErrorBadURL; + break; + case ERR_CONNECTION_CLOSED: + case ERR_CONNECTION_RESET: + case ERR_CONNECTION_REFUSED: + case ERR_CONNECTION_ABORTED: + case ERR_CONNECTION_FAILED: + case ERR_NAME_NOT_RESOLVED: + case ERR_INTERNET_DISCONNECTED: + case ERR_CONNECTION_TIMED_OUT: + pchDetail = m_sErrorConnectionProblem; + break; + case ERR_UNEXPECTED_PROXY_AUTH: + case ERR_EMPTY_PROXY_LIST: + pchDetail = m_sErrorProxyProblem; + break; + default: + pchDetail = m_sErrorUnknown; + break; + } + + char rgchError[4096]; + Q_snprintf( rgchError, Q_ARRAYSIZE( rgchError ), + "" + "" + "%s" + "" + "" + "

%s%d

" + "

" + "%s" + "

" + "" + "", + m_sErrorTitle.String(), + m_sErrorHeader.String(), + errorCode, + pchDetail ); + + errorText = rgchError; + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Event called before a resource is loaded. To allow the resource to load +// normally return RV_CONTINUE. To redirect the resource to a new url +// populate the |redirectUrl| value and return RV_CONTINUE. To specify +// data for the resource return a CefStream object in |resourceStream|, set +// 'mimeType| to the resource stream's mime type, and return RV_CONTINUE. +// To cancel loading of the resource return RV_HANDLED. +//----------------------------------------------------------------------------- +bool CClientHandler::OnBeforeResourceLoad(CefRefPtr browser, + CefRefPtr request, + CefString& redirectUrl, + CefRefPtr& resourceStream, + CefRefPtr response, + int loadFlags) +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_LoadingResource ); + cmd.Body().set_url( CStrAutoEncode( request->GetURL().c_str() ).ToString() ); + DISPATCH_MESSAGE( eHTMLCommands_LoadingResource ); + + // insert custom headers + CefRequest::HeaderMap headerMap; + request->GetHeaderMap( headerMap ); + FOR_EACH_VEC( m_vecHeaders, i ) + { + headerMap.insert( m_vecHeaders[i] ); + } + + request->SetHeaderMap( headerMap ); + + return false; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Run a JS alert message. Return RV_CONTINUE to display the default alert +// or RV_HANDLED if you displayed a custom alert. +//----------------------------------------------------------------------------- +bool CClientHandler::OnJSAlert(CefRefPtr browser, + CefRefPtr frame, + const CefString& message) +{ + { + // scope this so the wait below doesn't keep this allocation on the stack + CHTMLProtoBufMsg cmd( eHTMLCommands_JSAlert ); + cmd.Body().set_message( message.c_str() ); + DISPATCH_MESSAGE( eHTMLCommands_JSAlert ); + } + + HTMLCommandBuffer_t *pBuf = g_CEFThread.BWaitForCommand( eHTMLCommands_JSDialogResponse, m_iBrowser ); + if ( pBuf ) + { + CHTMLProtoBufMsg< CMsgJSDialogResponse > cmd( eHTMLCommands_JSDialogResponse ); + g_CEFThread.ReleaseCommandBuffer( pBuf ); + } + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Run a JS confirm request. Return RV_CONTINUE to display the default alert +// or RV_HANDLED if you displayed a custom alert. If you handled the alert +// set |CefHandler::RetVal| to true if the user accepted the confirmation. +//----------------------------------------------------------------------------- +bool CClientHandler::OnJSConfirm(CefRefPtr browser, + CefRefPtr frame, + const CefString& message, + bool& retval) +{ + { + // scope this so the wait below doesn't keep this allocation on the stack + CHTMLProtoBufMsg cmd( eHTMLCommands_JSConfirm ); + cmd.Body().set_message( message.c_str() ); + DISPATCH_MESSAGE( eHTMLCommands_JSConfirm ); + } + + retval = false; + + HTMLCommandBuffer_t *pBuf = g_CEFThread.BWaitForCommand( eHTMLCommands_JSDialogResponse, m_iBrowser ); + if ( pBuf ) + { + CHTMLProtoBufMsg< CMsgJSDialogResponse > cmd( eHTMLCommands_JSDialogResponse ); + if ( cmd.BDeserializeCrossProc( &pBuf->m_Buffer ) ) + { + if ( cmd.BodyConst().result() ) + retval = true ; + } + + g_CEFThread.ReleaseCommandBuffer( pBuf ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Run a JS prompt request. Return RV_CONTINUE to display the default prompt +// or RV_HANDLED if you displayed a custom prompt. If you handled the prompt +// set |CefHandler::RetVal| to true if the user accepted the prompt and request and +// |result| to the resulting value. +//----------------------------------------------------------------------------- +bool CClientHandler::OnJSPrompt(CefRefPtr browser, + CefRefPtr frame, + const CefString& message, + const CefString& defaultValue, + bool& retval, + CefString& result) +{ + retval = false; // just don't pop JS prompts for now + result = defaultValue; + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Called just before a window is closed. +//----------------------------------------------------------------------------- +void CClientHandler::OnBeforeClose(CefRefPtr browser) +{ + Lock(); + if ( m_Browser ) + { + // Free the browser pointer so that the browser can be destroyed + m_Browser = NULL; + + if ( !m_bBrowserClosing ) + { + CHTMLProtoBufMsg cmd( eHTMLCommands_Close ); + DISPATCH_MESSAGE( eHTMLCommands_Close ); + } + } + Unlock(); +} + + +//----------------------------------------------------------------------------- +// Purpose: show a html popup, a pulldown menu or the like +//----------------------------------------------------------------------------- +void CChromePainter::OnPopupShow(CefRefPtr browser, bool show) +{ + m_bPopupVisible = show; + int m_iBrowser = m_pParent->m_iBrowser; + if ( show ) + { + CHTMLProtoBufMsg cmd( eHTMLCommands_ShowPopup ); + DISPATCH_MESSAGE( eHTMLCommands_ShowPopup ); + } + else + { + CHTMLProtoBufMsg cmd( eHTMLCommands_HidePopup ); + DISPATCH_MESSAGE( eHTMLCommands_HidePopup ); + + // redraw the buffered texture behind the rectangle that was previously composited + for ( size_t i = 0; i < Q_ARRAYSIZE( m_Texture ); ++i ) + { + m_Texture[i].MarkDirtyRect( m_nPopupX, m_nPopupY, m_nPopupX + m_nPopupWide, m_nPopupY + m_nPopupTall ); + } + + // and notify the main thread to redraw the previously composited area as well + m_UpdateRect.MarkDirtyRect( m_nPopupX, m_nPopupY, m_nPopupX + m_nPopupWide, m_nPopupY + m_nPopupTall ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: make the popup this big +//----------------------------------------------------------------------------- +void CChromePainter::OnPopupSize(CefRefPtr browser, const CefRect& rect ) +{ + if ( m_bPopupVisible ) + { + // redraw the buffered texture behind the rectangle that was previously composited + for ( size_t i = 0; i < Q_ARRAYSIZE( m_Texture ); ++i ) + { + m_Texture[i].MarkDirtyRect( m_nPopupX, m_nPopupY, m_nPopupX + m_nPopupWide, m_nPopupY + m_nPopupTall ); + } + + // and notify the main thread to redraw the previously composited area as well + m_UpdateRect.MarkDirtyRect( m_nPopupX, m_nPopupY, m_nPopupX + m_nPopupWide, m_nPopupY + m_nPopupTall ); + } + + m_bPopupVisible = true; + m_nPopupX = rect.x; + m_nPopupWide = rect.width; + m_nPopupY = rect.y; + m_nPopupTall = rect.height; + + int m_iBrowser = m_pParent->m_iBrowser; + CHTMLProtoBufMsg cmd( eHTMLCommands_SizePopup ); + cmd.Body().set_x( m_nPopupX ); + cmd.Body().set_y( m_nPopupY ); + cmd.Body().set_wide( m_nPopupWide ); + cmd.Body().set_tall( m_nPopupTall ); + DISPATCH_MESSAGE( eHTMLCommands_SizePopup ); +} + + +//----------------------------------------------------------------------------- +// Purpose: cef has status text for us +//----------------------------------------------------------------------------- +void CClientHandler::OnStatusMessage(CefRefPtr browser, const CefString& value, StatusType type) +{ + if ( !value.empty() ) + { + CHTMLProtoBufMsg cmd( eHTMLCommands_StatusText ); + cmd.Body().set_text( value.c_str() ); + DISPATCH_MESSAGE( eHTMLCommands_StatusText ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: show tooltip please +//----------------------------------------------------------------------------- +bool CClientHandler::OnTooltip(CefRefPtr browser, CefString& text) +{ + if ( !m_bShowingToolTip && !text.empty() ) + { + m_bShowingToolTip = true; + m_strToolTip = text.c_str(); + + CHTMLProtoBufMsg cmd( eHTMLCommands_ShowToolTip ); + cmd.Body().set_text( m_strToolTip ); + DISPATCH_MESSAGE( eHTMLCommands_ShowToolTip ); + } + else if ( m_bShowingToolTip && !text.empty() ) + { + if ( m_strToolTip != text.c_str() ) + { + m_strToolTip = text.c_str(); + + CHTMLProtoBufMsg cmd( eHTMLCommands_UpdateToolTip ); + cmd.Body().set_text( m_strToolTip ); + DISPATCH_MESSAGE( eHTMLCommands_UpdateToolTip ); + } + } + else if ( m_bShowingToolTip ) + { + CHTMLProtoBufMsg cmd( eHTMLCommands_HideToolTip ); + DISPATCH_MESSAGE( eHTMLCommands_HideToolTip ); + + m_bShowingToolTip = false; + m_strToolTip.Clear(); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: set the mouse cursor to this image +//----------------------------------------------------------------------------- +bool CChromePainter::OnSetCursor( CefRefPtr browser, const CursorType type, const void *pchIconData, int iWide, int iTall, int xHotSpot, int yHotSpot ) +{ + int m_iBrowser = m_pParent->m_iBrowser; + CHTMLProtoBufMsg cmd( eHTMLCommands_SetCursor ); + + EMouseCursor cursor; + switch( type ) + { + case TypeCustom: + cursor = dc_last; + break; + case TypeCross: + cursor = dc_crosshair; + break; + case TypeHand: + cursor = dc_hand; + break; + case TypeIBeam: + cursor = dc_ibeam; + break; + case TypeWait: + cursor = dc_hourglass; + break; + case TypeHelp: + cursor = dc_help; + break; + case TypeEastResize: + cursor = dc_sizee; + break; + case TypeNorthResize: + cursor = dc_sizen; + break; + case TypeNorthEastResize: + cursor = dc_sizene; + break; + case TypeNorthWestResize: + cursor = dc_sizenw; + break; + case TypeSouthResize: + cursor = dc_sizes; + break; + case TypeSouthEastResize: + cursor = dc_sizese; + break; + case TypeSouthWestResize: + cursor = dc_sizesw; + break; + case TypeNorthSouthResize: + cursor = dc_sizes; + break; + case TypeEastWestResize: + cursor = dc_sizew; + break; + case TypeNorthEastSouthWestResize: + cursor = dc_sizeall; + break; + case TypeColumnResize: + cursor = dc_colresize; + break; + case TypeRowResize: + cursor = dc_rowresize; + break; + case TypeMiddlePanning: + cursor = dc_middle_pan; + break; + case TypeEastPanning: + cursor = dc_east_pan; + break; + case TypeNorthPanning: + cursor = dc_north_pan; + break; + case TypeNorthEastPanning: + cursor = dc_north_east_pan; + break; + case TypeNorthWestPanning: + cursor = dc_north_west_pan; + break; + case TypeSouthPanning: + cursor = dc_south_pan; + break; + case TypeSouthEastPanning: + cursor = dc_south_east_pan; + break; + case TypeSouthWestPanning: + cursor = dc_south_west_pan; + break; + case TypeWestPanning: + cursor = dc_west_pan; + break; + case TypeMove: + cursor = dc_sizeall; + break; + case TypeVerticalText: + cursor = dc_verticaltext; + break; + case TypeCell: + cursor = dc_cell; + break; + case TypeContextMenu: + cursor = dc_none; + break; + case TypeAlias: + cursor = dc_alias; + break; + case TypeProgress: + cursor = dc_waitarrow; + break; + case TypeNoDrop: + cursor = dc_no; + break; + case TypeCopy: + cursor = dc_copycur; + break; + case TypeNone: + cursor = dc_none; + break; + case TypeNotAllowed: + cursor = dc_no; + break; + case TypeZoomIn: + cursor = dc_zoomin; + break; + case TypeZoomOut: + cursor = dc_zoomout; + break; + case TypePointer: + default: + cursor = dc_arrow; + } + cmd.Body().set_cursor( cursor ); + cmd.Body().set_data( (uint32)pchIconData ); // we are relying on chrome keeping around the cursor data after this call completes, it does right now. + cmd.Body().set_wide( iWide ); + cmd.Body().set_tall( iTall ); + cmd.Body().set_xhotspot( xHotSpot ); + cmd.Body().set_yhotspot( yHotSpot ); + DISPATCH_MESSAGE( eHTMLCommands_SetCursor ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: file open dialog to be shown +//----------------------------------------------------------------------------- +bool CChromePainter::OnFileOpenDialog( CefRefPtr browser, bool bMultiSelect, const CefString &default_title, const CefString &default_file, CefWebFileChooserCallback *pCallback ) +{ + if ( !pCallback ) + return true; + + int m_iBrowser = m_pParent->m_iBrowser; + { + // scope this so this allocation doesn't stay on the stack during validate + CHTMLProtoBufMsg cmd( eHTMLCommands_FileLoadDialog ); + if ( !default_title.empty() ) + cmd.Body().set_title( default_title ); + if ( !default_file.empty() ) + cmd.Body().set_initialfile( default_file ); + DISPATCH_MESSAGE( eHTMLCommands_FileLoadDialog ); + } + + HTMLCommandBuffer_t *pBuf = g_CEFThread.BWaitForCommand( eHTMLCommands_FileLoadDialogResponse, m_iBrowser ); + if ( pBuf ) + { + CHTMLProtoBufMsg< CMsgFileLoadDialogResponse > cmd( eHTMLCommands_FileLoadDialogResponse ); + if ( cmd.BDeserializeCrossProc( &pBuf->m_Buffer ) ) + { + std::vector files; + for ( int i = 0; i < cmd.BodyConst().files_size(); i++ ) + { + if ( !cmd.BodyConst().files(i).empty() ) + { + CPathString path( cmd.BodyConst().files(i).c_str() ); + files.push_back( path.GetWCharPathPrePended() ); + } + } + + // if you have a DEBUG build and are crashing here it is because + // Chrome is a release library and the std::vector iterator isn't crossing + // the interface happyily. Build release and you will run fine. +#if defined(DEBUG) && defined(WIN32) + Assert( !"File select dialog not available in debug due to STL debug/release issues\n" ); +#else + pCallback->OnFileChoose( files ); +#endif + } + g_CEFThread.ReleaseCommandBuffer( pBuf ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: CEF is asking if it can show itself fullscreen +//----------------------------------------------------------------------------- +bool CChromePainter::OnEnterFullScreen( CefRefPtr browser ) +{ + int m_iBrowser = m_pParent->m_iBrowser; + { + // scope this so this allocation doesn't stay on the stack during validate + CHTMLProtoBufMsg cmd( eHTMLCommands_RequestFullScreen ); + DISPATCH_MESSAGE( eHTMLCommands_RequestFullScreen ); + } + + HTMLCommandBuffer_t *pBuf = g_CEFThread.BWaitForCommand( eHTMLCommands_RequestFullScreenResponse, m_iBrowser ); + if ( pBuf ) + { + CHTMLProtoBufMsg< CMsgRequestFullScreenResponse > cmd( eHTMLCommands_RequestFullScreenResponse ); + if ( cmd.BDeserializeCrossProc( &pBuf->m_Buffer ) ) + { + return cmd.BodyConst().ballow(); + } + g_CEFThread.ReleaseCommandBuffer( pBuf ); + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: cef is spewing to its console, print it +//----------------------------------------------------------------------------- +bool CChromePainter::OnExitFullScreen( CefRefPtr browser ) +{ + int m_iBrowser = m_pParent->m_iBrowser; + { + // scope this so this allocation doesn't stay on the stack during validate + // tell the main thread we are exiting fullscreen + CHTMLProtoBufMsg cmd( eHTMLCommands_ExitFullScreen ); + DISPATCH_MESSAGE( eHTMLCommands_ExitFullScreen ); + } + + // BUGUBG - add a request/response here so you can disallow leaving fullscreen?? + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: cef is spewing to its console, print it +//----------------------------------------------------------------------------- +bool CClientHandler::OnConsoleMessage(CefRefPtr browser, + const CefString& message, + const CefString& source, + int line) +{ + // the console is very chatty and doesn't provide useful information for us app developers, just for html/css editors, so lets ignore this for now + //Msg( "Browser Message: %s - %s:%d\n", message.c_str(), source.c_str(), line ); + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: helper function to recurvisely search down the DOM for any input nodes and an input button name +//----------------------------------------------------------------------------- +void SearchForInputButtonAndOtherInputs_R( CefRefPtr root, CefRefPtr focusNode, bool &bHasMultipleTextInputNodes, CUtlString &sSearchButtonName, int nMaxRecurse ) +{ + CefRefPtr nodeChildren = root->GetFirstChild(); + while ( nodeChildren.get() ) + { + if ( !nodeChildren->IsSame( focusNode ) && nodeChildren->IsElement() ) + { + CUtlString sElementType = nodeChildren->GetElementTagName().c_str(); + if ( !Q_stricmp( "input", sElementType ) ) + { + CUtlString sChildControlType = nodeChildren->GetFormControlElementType().c_str(); + if ( sSearchButtonName.IsEmpty() && !Q_stricmp( sChildControlType, "submit" ) ) + { + if ( nodeChildren->HasElementAttribute( "value" ) ) + sSearchButtonName = nodeChildren->GetElementAttribute( "value" ).c_str(); + } + else if ( !Q_stricmp( "text", sChildControlType ) || !Q_stricmp( "password", sChildControlType ) || !Q_stricmp( "email", sChildControlType ) ) + { + //CefDOMNode::AttributeMap attrMap; + //nodeChildren->GetElementAttributes( attrMap ); + if ( !nodeChildren->HasElementAttribute( "disabled" ) ) + bHasMultipleTextInputNodes = true; + } + } + else if ( !Q_stricmp( "textarea", sElementType ) ) + { + if ( !nodeChildren->HasElementAttribute( "disabled" ) ) + bHasMultipleTextInputNodes = true; + } + else if ( nMaxRecurse > 0 /*&& !Q_stricmp( "div", sElementType ) || !Q_stricmp( "tr", sElementType ) || !Q_stricmp( "td", sElementType ) || !Q_stricmp( "table", sElementType ) || !Q_stricmp( "tbody", sElementType )*/ ) + { + SearchForInputButtonAndOtherInputs_R( nodeChildren, focusNode, bHasMultipleTextInputNodes, sSearchButtonName, nMaxRecurse - 1 ); + } + } + nodeChildren = nodeChildren->GetNextSibling(); + + if ( bHasMultipleTextInputNodes && sSearchButtonName.IsValid() ) + break; // if we found both multiple nodes and a search button name we can bail early + } +} + + +//----------------------------------------------------------------------------- +// Purpose: a new node in the DOM has focus now +//----------------------------------------------------------------------------- +void CClientHandler::OnFocusedNodeChanged(CefRefPtr browser, CefRefPtr frame, CefRefPtr node) +{ + VPROF_BUDGET( "CCEFThread - CClientHandler::OnFocusedNodeChanged()", VPROF_BUDGETGROUP_VGUI ); + + bool bIsInputNode = false; + CUtlString sElementType; + if ( node.get() ) + sElementType = node->GetElementTagName().c_str(); + + CUtlString sName; + if ( node.get() ) + sName = node->GetName().c_str(); + + CUtlString sSearchButtonName; + CUtlString sControlType; + bool bInputNode = !Q_stricmp( "input", sElementType ); + bool bHasMultipleInputNodes = false; + if ( sElementType.IsValid() && ( bInputNode || !Q_stricmp( "textarea", sElementType ) ) ) + { + sControlType = node->GetFormControlElementType().c_str(); + + // lets go searching for the submit button and grab the text it shows + bIsInputNode = true; + CefRefPtr nodeParent = node->GetParent(); + while( nodeParent.get() ) + { + CUtlString sParentElementType = nodeParent->GetElementTagName().c_str(); + if ( !Q_stricmp( "form", sParentElementType ) ) + break; + nodeParent = nodeParent->GetParent(); + } + + if ( nodeParent.get() ) + SearchForInputButtonAndOtherInputs_R( nodeParent, node, bHasMultipleInputNodes, sSearchButtonName, 32 ); + } + + if ( sElementType.IsValid() && !Q_stricmp( "div", sElementType ) ) + { + if ( node->HasElementAttribute( "contenteditable" ) ) + bIsInputNode = true; + } + + if ( sSearchButtonName.IsEmpty() ) + sSearchButtonName = "#Web_FormSubmit"; + + CHTMLProtoBufMsg cmd( eHTMLCommands_NodeGotFocus ); + cmd.Body().set_binput( bIsInputNode ); + if ( sName.IsValid() ) + cmd.Body().set_name( sName ); + if ( sElementType.IsValid() ) + cmd.Body().set_elementtagname( sElementType ); + if ( sSearchButtonName.IsValid() ) + cmd.Body().set_searchbuttontext( sSearchButtonName ); + cmd.Body().set_bhasmultipleinputs( bHasMultipleInputNodes ); + if ( sControlType.IsValid() ) + cmd.Body().set_input_type( sControlType ); + + DISPATCH_MESSAGE( eHTMLCommands_NodeGotFocus ); +} + + +//----------------------------------------------------------------------------- +// Purpose: a find has found matches on the page, feed back that info +//----------------------------------------------------------------------------- +void CClientHandler::OnFindResult(CefRefPtr browser, + int identifier, + int count, + const CefRect& selectionRect, + int activeMatchOrdinal, + bool finalUpdate) +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_SearchResults ); + cmd.Body().set_activematch( activeMatchOrdinal ); + cmd.Body().set_results( count ); + DISPATCH_MESSAGE( eHTMLCommands_SearchResults ); +} + + +//----------------------------------------------------------------------------- +// Purpose: return a pointer to the CEF browser object +//----------------------------------------------------------------------------- +CefRefPtr CClientHandler::GetBrowser() +{ + return m_Browser; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CClientHandler::CloseBrowser() +{ + if ( m_Browser ) + { + m_bBrowserClosing = true; + m_Browser->CloseBrowser(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CClientHandler::SetMouseLocation( int nMouseX, int nMouseY ) +{ + m_nMouseX = nMouseX; + m_nMouseY = nMouseY; + if ( m_Browser.get() ) + { + m_nMouseScrolledX = nMouseX + m_Browser->VerticalScroll(); + m_nMouseScrolledY = nMouseY + m_Browser->HorizontalScroll(); + m_bMouseFocus = true; + m_Browser->SendMouseMoveEvent( m_nMouseX, m_nMouseY, false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: check if window has scrolled and generate a fake mouse move event +// to force CEF to check for hover state changes (seems like a CEF bug...) +//----------------------------------------------------------------------------- +void CClientHandler::RefreshCEFHoverStatesAfterScroll() +{ + if ( m_Browser.get() && m_bMouseFocus ) + { + int nScrolledX = m_nMouseX + m_Browser->VerticalScroll(); + int nScrolledY = m_nMouseY + m_Browser->HorizontalScroll(); + if ( nScrolledX != m_nMouseScrolledX || nScrolledY != m_nMouseScrolledY ) + { + m_nMouseScrolledX = nScrolledX; + m_nMouseScrolledY = nScrolledY; + m_Browser->SendMouseMoveEvent( m_nMouseX, m_nMouseY, false ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: make the user agent for this browser +//----------------------------------------------------------------------------- +void CClientHandler::SetBrowserAgent( CefRefPtr browser ) +{ + static bool bGotIEVersion = false; + static char rgchWindowsVersion[64] = "Windows NT 5.1"; // XP SP1 + static char *rgchOS = ""; + + if ( !bGotIEVersion ) + { +#ifdef WIN32 + rgchOS = "Windows"; + // First get windows version + OSVERSIONINFO verInfo; + memset( &verInfo, 0, sizeof(OSVERSIONINFO) ); + verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + if ( ::GetVersionEx( &verInfo ) ) + { + // We only run on "Windows NT" os's, so we just need to append the major/minor version dynamically + Q_snprintf( rgchWindowsVersion, sizeof(rgchWindowsVersion), "Windows NT %u.%u", verInfo.dwMajorVersion, verInfo.dwMinorVersion ); + //Log( "Windows Version is: %u.%u\n", verInfo.dwMajorVersion, verInfo.dwMinorVersion ); + } + +#elif defined(OSX) + Q_snprintf( rgchWindowsVersion, sizeof(rgchWindowsVersion), "Macintosh" ); + rgchOS = "Macintosh"; +#elif defined(LINUX) + Q_snprintf( rgchWindowsVersion, sizeof(rgchWindowsVersion), "X11" );// strange, but that's what Firefox uses + rgchOS = "Linux"; +#endif + } + + // user agent is process wide for Chrome so you can only set it once and it appplies to everything you open + { + char szAgent[ 2048 ]; + Q_snprintf( szAgent, sizeof(szAgent), m_strUserAgent.String(), rgchOS, rgchWindowsVersion, m_strUserAgentIdentifier.String(), sm_ulBuildID, m_szUserAgentExtras ); + browser->SetUserAgent( szAgent ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: paint the cef control +//----------------------------------------------------------------------------- +void CClientHandler::Paint() +{ + Lock(); + if ( m_Browser ) + { + m_bPendingPaint |= m_Painter.BUpdated(); + m_Painter.SetUpdated( false ); + } + Unlock(); +} + + +//----------------------------------------------------------------------------- +// Purpose: did we have a paint that updated the texture buffer? +//----------------------------------------------------------------------------- +bool CClientHandler::BNeedsPaint() +{ + bool bVal = m_bPendingPaint; + m_bPendingPaint = false; + return bVal; +} + + +//----------------------------------------------------------------------------- +// Purpose: get the texture data for the control +//----------------------------------------------------------------------------- +const byte *CClientHandler::PComposedTextureData( uint32 iTexture ) +{ + VPROF_BUDGET( "CClientHandler::PTextureData", VPROF_BUDGETGROUP_VGUI ); + + return m_Painter.PComposedTextureData( iTexture ); +} + + +//----------------------------------------------------------------------------- +// Purpose: true if something valid has rendered (i.e not the blank page) +//----------------------------------------------------------------------------- +bool CClientHandler::IsVisuallyNonEmpty() +{ + if ( m_Browser ) + return m_Browser->IsVisuallyNonEmpty(); + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: set the loc strings to display on error +//----------------------------------------------------------------------------- +void CClientHandler::SetErrorStrings( const char *pchTitle, const char *pchHeader, const char *pchCacheMiss, const char *pchBadURL, const char *pchConnectionProblem, + const char *pchProxyProblem, const char *pchUnknown ) +{ + m_sErrorTitle = pchTitle; + m_sErrorHeader = pchHeader; + m_sErrorCacheMiss = pchCacheMiss; + m_sErrorBadURL = pchBadURL; + m_sErrorConnectionProblem = pchConnectionProblem; + m_sErrorProxyProblem = pchProxyProblem; + m_sErrorUnknown = pchUnknown; +} + + +//----------------------------------------------------------------------------- +// Purpose: the user wants us to take a screenshot of a page +//----------------------------------------------------------------------------- +void CClientHandler::RequestScreenShot( const CHTMLProtoBufMsg &cmd ) +{ + m_Snapshot.m_sURLSnapshot = cmd.BodyConst().url().c_str(); + m_Snapshot.m_sFileNameSnapshot = cmd.BodyConst().filename().c_str(); + m_Snapshot.m_nWide = cmd.BodyConst().width(); + m_Snapshot.m_nTall = cmd.BodyConst().height(); + m_Snapshot.m_flRequestTimeout = Plat_FloatTime() + k_flMaxScreenshotWaitTime; +} + + +//----------------------------------------------------------------------------- +// Purpose: save a screenshot of the current html page to this file with this size +//----------------------------------------------------------------------------- +void CClientHandler::SavePageToJPEGIfNeeded( CefRefPtr browser, const byte *pRGBA, int wide, int tall ) +{ + if ( m_Snapshot.m_sURLSnapshot.IsValid() && wide && tall + && m_Snapshot.m_sURLSnapshot == CStrAutoEncode( browser->GetMainFrame()->GetURL().c_str() ).ToString() ) + { + VPROF_BUDGET( "CClientHandler::SavePageToJPEGIfNeeded", VPROF_BUDGETGROUP_TENFOOT ); + + CUtlBuffer bufRGB; + + bufRGB.Put( pRGBA, wide * tall *4 ); + if ( !BConvertRGBAToRGB( bufRGB, wide, tall ) ) + return; + + BResizeImageRGB( bufRGB, wide, tall, m_Snapshot.m_nWide, m_Snapshot.m_nTall ); + // input format is actually BGRA so now swizzle to rgb + byte *pBGR = (byte *)bufRGB.Base(); + for ( int i = 0; i < m_Snapshot.m_nTall; i++ ) + { + for ( int j = 0; j < m_Snapshot.m_nWide; j++ ) + { + char cR = pBGR[0]; + pBGR[0] = pBGR[2]; + pBGR[2] = cR; + pBGR += 3; + } + } + + if ( !ConvertRGBToJpeg( m_Snapshot.m_sFileNameSnapshot, k_ScreenshotQuality, m_Snapshot.m_nWide, m_Snapshot.m_nTall, bufRGB ) ) + return; + + CHTMLProtoBufMsg cmd( eHTMLCommands_SavePageToJPEGResponse ); + cmd.Body().set_url( m_Snapshot.m_sURLSnapshot ); + cmd.Body().set_filename( m_Snapshot.m_sFileNameSnapshot ); + DISPATCH_MESSAGE( eHTMLCommands_SavePageToJPEGResponse ); + + m_Snapshot.m_sURLSnapshot.Clear(); + m_Snapshot.m_flRequestTimeout = 0.0f; + + } +} + + +//----------------------------------------------------------------------------- +// Purpose: return the url located at this position if there is one +//----------------------------------------------------------------------------- +void CCEFThread::ThreadLinkAtPosition( const CHTMLProtoBufMsg &htmlCommand ) +{ + CefString pchURL; + int iClient = 0; + int bLiveLink = false; + int bInput = false; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + if ( m_listClientHandlers[iClient]->GetBrowser() ) + pchURL = m_listClientHandlers[iClient]->GetBrowser()->GetLinkAtPosition( htmlCommand.BodyConst().x(), htmlCommand.BodyConst().y(), bLiveLink, bInput ); + } + + CHTMLProtoBufMsg cmd( eHTMLCommands_LinkAtPositionResponse ); + cmd.Body().set_x( htmlCommand.BodyConst().x() ); + cmd.Body().set_y( htmlCommand.BodyConst().y() ); + cmd.Body().set_blivelink( bLiveLink>0 ? true: false ); + cmd.Body().set_binput( bInput>0 ? true: false ); + if ( !pchURL.empty() ) + cmd.Body().set_url( pchURL ); + int m_iBrowser = htmlCommand.BodyConst().browser_handle(); + DISPATCH_MESSAGE( eHTMLCommands_LinkAtPositionResponse ); +} + + +//----------------------------------------------------------------------------- +// Purpose: zoom the screen to the element at this position +//----------------------------------------------------------------------------- +void CCEFThread::ThreadZoomToElementAtPosition( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + if ( m_listClientHandlers[iClient]->GetBrowser() ) + { + CefRect initialRect, finalRect; + float zoomLevel = m_listClientHandlers[iClient]->GetBrowser()->scalePageToFitElementAt( + htmlCommand.BodyConst().x(), htmlCommand.BodyConst().y(), + initialRect, finalRect ); + int m_iBrowser = htmlCommand.BodyConst().browser_handle(); + ThreadBrowserVerticalScrollBarSizeHelper( m_iBrowser, true ); + ThreadBrowserHorizontalScrollBarSizeHelper( m_iBrowser, true ); + { + CHTMLProtoBufMsg cmd( eHTMLCommands_ZoomToElementAtPositionResponse ); + cmd.Body().set_zoom( zoomLevel ); + cmd.Body().set_initial_x( initialRect.x ); + cmd.Body().set_initial_y( initialRect.y ); + cmd.Body().set_initial_width( initialRect.width ); + cmd.Body().set_initial_height( initialRect.height ); + cmd.Body().set_final_x( finalRect.x ); + cmd.Body().set_final_y( finalRect.y ); + cmd.Body().set_final_width( finalRect.width ); + cmd.Body().set_final_height( finalRect.height ); + DISPATCH_MESSAGE( eHTMLCommands_ZoomToElementAtPositionResponse ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: zoom the screen to the element at this position +//----------------------------------------------------------------------------- +void CCEFThread::ThreadZoomToFocusedElement( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + if ( m_listClientHandlers[iClient]->GetBrowser() ) + { + CefRect initialRect, finalRect; + float zoomLevel = m_listClientHandlers[iClient]->GetBrowser()->scalePageToFocusedElement( htmlCommand.BodyConst().leftoffset(), htmlCommand.BodyConst().topoffset(), initialRect, finalRect ); + int m_iBrowser = htmlCommand.BodyConst().browser_handle(); + ThreadBrowserVerticalScrollBarSizeHelper( m_iBrowser, true ); + ThreadBrowserHorizontalScrollBarSizeHelper( m_iBrowser, true ); + { + CHTMLProtoBufMsg cmd( eHTMLCommands_ZoomToElementAtPositionResponse ); + cmd.Body().set_zoom( zoomLevel ); + cmd.Body().set_initial_x( initialRect.x ); + cmd.Body().set_initial_y( initialRect.y ); + cmd.Body().set_initial_width( initialRect.width ); + cmd.Body().set_initial_height( initialRect.height ); + cmd.Body().set_final_x( finalRect.x ); + cmd.Body().set_final_y( finalRect.y ); + cmd.Body().set_final_width( finalRect.width ); + cmd.Body().set_final_height( finalRect.height ); + DISPATCH_MESSAGE( eHTMLCommands_ZoomToElementAtPositionResponse ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: increment the scale factor on the page by an increment +//----------------------------------------------------------------------------- +void CCEFThread::ThreadSetPageScale( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + CClientHandler *pHandler = m_listClientHandlers[iClient]; + if ( pHandler->GetBrowser() ) + { + int nPageHeightBefore = pHandler->GetBrowser()->VerticalScrollMax(); + int nPageWidthBefore = pHandler->GetBrowser()->HorizontalScrollMax(); + + float zoomLevel = pHandler->GetBrowser()->setPageScaleFactor( htmlCommand.BodyConst().scale(), htmlCommand.BodyConst().x(), htmlCommand.BodyConst().y() ); + + int idx = m_mapSizeChangesPending.Find( htmlCommand.BodyConst().browser_handle() ); + if ( idx == m_mapSizeChangesPending.InvalidIndex() ) + { + SizeChange_t &sizeChange = m_mapSizeChangesPending [ m_mapSizeChangesPending.Insert( htmlCommand.BodyConst().browser_handle() ) ]; + sizeChange.iBrowser = htmlCommand.BodyConst().browser_handle(); + sizeChange.nBeforeHeight = nPageHeightBefore; + sizeChange.nBeforeWidth = nPageWidthBefore; + sizeChange.flNewZoom = zoomLevel; + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: fire off the details of any page scale changes we had pending from last frame +//----------------------------------------------------------------------------- +void CCEFThread::SendSizeChangeEvents() +{ + FOR_EACH_MAP_FAST( m_mapSizeChangesPending, i ) + { + int iClient = 0; + if ( BIsValidBrowserHandle( m_mapSizeChangesPending[i].iBrowser, iClient ) ) + { + CClientHandler *pHandler = m_listClientHandlers[iClient]; + if ( pHandler->GetBrowser() ) + { + int nPageHeightAfter = pHandler->GetBrowser()->VerticalScrollMax(); + int nPageWidthAfter = pHandler->GetBrowser()->HorizontalScrollMax(); + + int m_iBrowser = m_mapSizeChangesPending[i].iBrowser; + + ThreadBrowserVerticalScrollBarSizeHelper( m_iBrowser, true ); + ThreadBrowserHorizontalScrollBarSizeHelper( m_iBrowser, true ); + { + CHTMLProtoBufMsg cmd( eHTMLCommands_ScaleToValueResponse ); + cmd.Body().set_zoom( m_mapSizeChangesPending[i].flNewZoom ); + cmd.Body().set_width_delta( nPageWidthAfter - m_mapSizeChangesPending[i].nBeforeWidth ); + cmd.Body().set_height_delta( nPageHeightAfter - m_mapSizeChangesPending[i].nBeforeHeight ); + DISPATCH_MESSAGE( eHTMLCommands_ScaleToValueResponse ); + } + } + } + } + m_mapSizeChangesPending.RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// Purpose: exit from fullscreen if in it +//----------------------------------------------------------------------------- +void CCEFThread::ThreadExitFullScreen( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + m_listClientHandlers[ iClient ]->GetBrowser()->ExitFullScreen(); + } + +} + + +//----------------------------------------------------------------------------- +// Purpose: the user has requested we save this url to a file on local disk +//----------------------------------------------------------------------------- +void CCEFThread::ThreadSavePageToJPEG( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + m_listClientHandlers[ iClient ]->RequestScreenShot( htmlCommand ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: mouse moved to this x,y on the page +//----------------------------------------------------------------------------- +void CCEFThread::ThreadMouseMove( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + m_listClientHandlers[ iClient ]->SetMouseLocation( htmlCommand.BodyConst().x(), htmlCommand.BodyConst().y() ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: mouse left the control, tell cef +//----------------------------------------------------------------------------- +void CCEFThread::ThreadMouseLeave( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); + if ( !browser.get() ) + return; + + m_listClientHandlers[ iClient ]->SetMouseFocus( false ); + + int mx, my; + m_listClientHandlers[ iClient ]->GetMouseLocation( mx, my ); + browser->SendMouseMoveEvent( mx, my, true ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: helper to convert UI mouse codes to CEF ones +//----------------------------------------------------------------------------- +CefBrowser::MouseButtonType ConvertMouseCodeToCEFCode( int code ) +{ + // BUGBUG + switch( code ) + { + case 0: + return MBT_LEFT; + break; + case 1: + return MBT_RIGHT; + break; + case 2: + return MBT_MIDDLE; + break; + default: + return MBT_LEFT; + break; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: mouse button pressed +//----------------------------------------------------------------------------- +void CCEFThread::ThreadMouseButtonDown( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + + CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); + if ( !browser.get() ) + return; + int nMouseX, nMouseY; + m_listClientHandlers[ iClient ]->GetMouseLocation( nMouseX, nMouseY ); + + browser->SendMouseClickEvent( nMouseX, nMouseY, ConvertMouseCodeToCEFCode( htmlCommand.BodyConst().mouse_button() ), false, 1 ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: mouse button released +//----------------------------------------------------------------------------- +void CCEFThread::ThreadMouseButtonUp( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); + if ( !browser.get() ) + return; + int nMouseX, nMouseY; + m_listClientHandlers[ iClient ]->GetMouseLocation( nMouseX, nMouseY ); + + browser->SendMouseClickEvent( nMouseX, nMouseY, ConvertMouseCodeToCEFCode( htmlCommand.BodyConst().mouse_button() ), true, 1 ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: mouse button double pressed +//----------------------------------------------------------------------------- +void CCEFThread::ThreadMouseButtonDlbClick( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); + if ( !browser.get() ) + return; + int nMouseX, nMouseY; + m_listClientHandlers[ iClient ]->GetMouseLocation( nMouseX, nMouseY ); + + browser->SendMouseClickEvent( nMouseX, nMouseY, ConvertMouseCodeToCEFCode( htmlCommand.BodyConst().mouse_button() ), false, 2 ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: mouse was wheeled +//----------------------------------------------------------------------------- +void CCEFThread::ThreadMouseWheel( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); + if ( !browser.get() ) + return; + int nMouseX, nMouseY; + m_listClientHandlers[ iClient ]->GetMouseLocation( nMouseX, nMouseY ); + + browser->SendMouseWheelEvent( nMouseX, nMouseY, htmlCommand.BodyConst().delta() ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: unicode character was typed +//----------------------------------------------------------------------------- +void CCEFThread::ThreadKeyTyped( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); + if ( !browser.get() ) + return; + + browser->SendKeyEvent( KT_CHAR, htmlCommand.BodyConst().unichar(), 0, false, false ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: raw key was pressed +//----------------------------------------------------------------------------- +void CCEFThread::ThreadKeyDown( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); + if ( !browser.get() ) + return; + + browser->SendKeyEvent( KT_KEYDOWN, htmlCommand.BodyConst().keycode(), htmlCommand.BodyConst().modifiers(), false, false ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: raw key was released +//----------------------------------------------------------------------------- +void CCEFThread::ThreadKeyUp( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); + if ( !browser.get() ) + return; + + browser->SendKeyEvent( KT_KEYUP, htmlCommand.BodyConst().keycode(), htmlCommand.BodyConst().modifiers(), false, false ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: please close any fullscreen flash controls you see +//----------------------------------------------------------------------------- +void CCEFThread::ThreadCloseFullScreenFlashIfOpen( const CHTMLProtoBufMsg &htmlCommand ) +{ + CheckForFullScreenFlashControl(); + if ( m_bFullScreenFlashVisible ) + { +#ifdef WIN32 + ::PostMessageA( m_flashfullscreenHWND, WM_KEYDOWN, VK_ESCAPE, 0 ); + ::PostMessageA( m_flashfullscreenHWND, WM_KEYUP, VK_ESCAPE, 0 ); +#endif + } +} + + +//----------------------------------------------------------------------------- +// Purpose: please close any fullscreen flash controls you see +//----------------------------------------------------------------------------- +void CCEFThread::ThreadPauseFullScreenFlashMovieIfOpen( const CHTMLProtoBufMsg &htmlCommand ) +{ + CheckForFullScreenFlashControl(); + if ( m_bFullScreenFlashVisible ) + { +#ifdef WIN32 + ::PostMessageA( m_flashfullscreenHWND, WM_KEYDOWN, VK_SPACE, 0 ); + ::PostMessageA( m_flashfullscreenHWND, WM_KEYUP, VK_SPACE, 0 ); +#endif + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: helper class to get the focused node in the dom +//----------------------------------------------------------------------------- +class CVisitor : public CefDOMVisitor +{ +public: + CVisitor( CThreadEvent *pEvent, CUtlString *psValue ) + { + m_pEvent = pEvent; + m_psValue = psValue; + } + + ~CVisitor() + { + m_pEvent->Set(); + } + + virtual void Visit(CefRefPtr document) { + CefRefPtr focusedNode = document->GetFocusedNode(); + *m_psValue = focusedNode->GetValue().c_str(); + } + + +private: + CThreadEvent *m_pEvent; + CUtlString *m_psValue; + + IMPLEMENT_REFCOUNTING(CVisitor); +}; + + +//----------------------------------------------------------------------------- +// Purpose: get the text out of the current dom field that has focus +//----------------------------------------------------------------------------- +void CCEFThread::ThreadGetFocusedNodeText( const CHTMLProtoBufMsg &htmlCommand ) +{ + int iClient = 0; + if ( BIsValidBrowserHandle( htmlCommand.BodyConst().browser_handle(), iClient ) ) + { + CefRefPtr browser = m_listClientHandlers[ iClient ]->GetBrowser(); + if ( !browser.get() ) + return; + + CThreadEvent event; + CUtlString sValue; + browser->GetFocusedFrame()->VisitDOM( new CVisitor( &event, &sValue ) ); + do + { + CefDoMessageLoopWork(); + } + while ( !event.Wait( 100 ) ); // keep pumping CEF until it has done our walk + + { + int m_iBrowser = htmlCommand.BodyConst().browser_handle(); + CHTMLProtoBufMsg cmd( eHTMLCommands_GetFocusedNodeValueResponse ); + cmd.Body().set_value( sValue ); + DISPATCH_MESSAGE( eHTMLCommands_GetFocusedNodeValueResponse ); + } + } +} diff --git a/mp/src/vgui2/chromehtml/html_chrome.h b/mp/src/vgui2/chromehtml/html_chrome.h index 5611180d..d6ae5495 100644 --- a/mp/src/vgui2/chromehtml/html_chrome.h +++ b/mp/src/vgui2/chromehtml/html_chrome.h @@ -1,637 +1,637 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -//=============================================================================// - -#ifndef HTML_CHROME_H -#define HTML_CHROME_H - -#ifdef _WIN32 -#pragma once -#endif - -#include "cef/include/cef_render_handler.h" -#include "cef/include/cef_client.h" -#include "cef/include/cef_app.h" -#include "cef/include/cef_browser.h" -#include "cef/include/cef_command_line.h" -#include "cef/include/cef_frame.h" -#include "cef/include/cef_runnable.h" -#include "cef/include/cef_web_urlrequest.h" -#include "cef/include/cef_request_handler.h" -#include "cef/include/cef_load_handler.h" -#include "cef/include/cef_display_handler.h" -#include "cef/include/cef_life_span_handler.h" -#include "cef/include/cef_render_handler.h" - -#include "tier0/platform.h" -#include "tier0/vprof.h" -#include "tier1/utlarray.h" -#include "tier1/utlbuffer.h" -#include "fileio.h" -//#include "constants.h" -#include "tier0/validator.h" -#include "tier1/utlmap.h" -#include "tier1/utlstring.h" -#include "tier0/tslist.h" -#include "html/ihtmlchrome.h" -#include "html/htmlprotobuf.h" -// These must be undefed so that the MacOS build will work -- their STL -// fails if min and max are defined. I'm undefing them in all builds to -// avoid having a Windows dependency on min/max creep in. -#undef min -#undef max -#include "htmlmessages.pb.h" - -class CClientHandler; -class CChromePainter; -class ICookieCallback; - -bool ChromeSetWebCookie( const char *pchHostname, const char *pchName, const char *pchValue, const char *pchPath, RTime32 nExpires ); -bool ChromeGetWebCookiesForURL( CUtlString *pstrValue, const char *pchURL, const char *pchName ); - -//----------------------------------------------------------------------------- -// Purpose: track dirty rects, shuttle bits between renderer and main thread -//----------------------------------------------------------------------------- -class CChromeUpdateRegion -{ -public: - CChromeUpdateRegion() { MarkAllClean(); } - int GetUpdateX( int clampWide ) const { return clamp( m_nUpdateX0, 0, clampWide ); } - int GetUpdateY( int clampTall ) const { return clamp( m_nUpdateY0, 0, clampTall ); } - int GetUpdateWide( int clampWide ) const { return clamp( m_nUpdateX1, 0, clampWide ) - GetUpdateX( clampWide ); } - int GetUpdateTall( int clampTall ) const { return clamp( m_nUpdateY1, 0, clampTall ) - GetUpdateY( clampTall ); } - - void MarkAllClean() { m_nUpdateX0 = m_nUpdateY0 = INT_MAX; m_nUpdateX1 = m_nUpdateY1 = 0; } - void MarkAllDirty() { m_nUpdateX0 = m_nUpdateY0 = 0; m_nUpdateX1 = m_nUpdateY1 = INT_MAX; } - void MarkDirtyRect( int x0, int y0, int x1, int y1 ) - { - if ( x0 >= x1 || y0 >= y1 ) return; - if ( m_nUpdateX0 > x0 ) m_nUpdateX0 = x0; - if ( m_nUpdateY0 > y0 ) m_nUpdateY0 = y0; - if ( m_nUpdateX1 < x1 ) m_nUpdateX1 = x1; - if ( m_nUpdateY1 < y1 ) m_nUpdateY1 = y1; - } - void MarkDirtyRect( const CChromeUpdateRegion& other ) - { - MarkDirtyRect( other.m_nUpdateX0, other.m_nUpdateY0, other.m_nUpdateX1, other.m_nUpdateY1 ); - } - -protected: - int m_nUpdateX0; - int m_nUpdateY0; - int m_nUpdateX1; - int m_nUpdateY1; -}; - -class CChromeRenderBuffer : public CChromeUpdateRegion -{ -public: - CChromeRenderBuffer() { m_nWide = m_nTall = 0; } - void SetSize( int wide, int tall ) - { - if ( wide != m_nWide || tall != m_nTall ) - { - m_nWide = wide; - m_nTall = tall; - MarkAllDirty(); - m_Texture.EnsureCapacity( wide * tall * 4 ); - } - } - int GetWide() const { return m_nWide; } - int GetTall() const { return m_nTall; } - byte *GetPixels() { return m_Texture.Base(); } - - bool BUpdatePixels( byte *pOther, int wide, int tall ) - { - SetSize( wide, tall ); - int x0 = clamp( m_nUpdateX0, 0, wide ); - int y0 = clamp( m_nUpdateY0, 0, tall ); - int x1 = clamp( m_nUpdateX1, 0, wide ); - int y1 = clamp( m_nUpdateY1, 0, tall ); - if ( x0 >= x1 || y0 >= y1 ) - return false; - - if ( x0 == 0 && x1 == wide ) - { - byte *pDst = GetPixels() + y0*wide*4; - byte *pSrc = pOther + y0*wide*4; - Q_memcpy( pDst, pSrc, wide * ( y1 - y0 ) * 4 ); - } - else - { - byte *pDst = GetPixels() + y0*wide*4 + x0*4; - byte *pSrc = pOther + y0*wide*4 + x0*4; - int nCopyBytesPerRow = (x1 - x0)*4; - for ( int rows = y1 - y0; rows > 0; --rows ) - { - Q_memcpy( pDst, pSrc, nCopyBytesPerRow ); - pSrc += wide * 4; - pDst += wide * 4; - } - } - return true; - } - -#ifdef DBGFLAG_VALIDATE - virtual void Validate( CValidator &validator, const char *pchName ) - { - VALIDATE_SCOPE(); - ValidateObj( m_Texture ); - } -#endif - -protected: - int m_nWide; // dimensions of the buffer - int m_nTall; - CUtlVector m_Texture; // rgba data -}; - -//----------------------------------------------------------------------------- -// Purpose: interface Chrome uses to send up paint messages -//----------------------------------------------------------------------------- -class CChromePainter : public CefRenderHandler -{ -public: - CChromePainter( CClientHandler *pParent ); - ~CChromePainter(); - - virtual bool GetViewRect(CefRefPtr browser, CefRect& rect) OVERRIDE { return false; } - virtual bool GetScreenRect(CefRefPtr browser, CefRect& rect) OVERRIDE { return false; } - virtual bool GetScreenPoint(CefRefPtr browser, int viewX, int viewY, int& screenX, int& screenY) OVERRIDE { return false; } - - virtual void OnPopupShow(CefRefPtr browser, bool show) OVERRIDE; - virtual void OnPopupSize(CefRefPtr browser, const CefRect& rect) OVERRIDE; - - virtual void OnPaint(CefRefPtr browser, PaintElementType type, const RectList& dirtyRects, const void* buffer) OVERRIDE; - virtual void OnCursorChange(CefRefPtr browser, CefCursorHandle cursor) OVERRIDE {} - virtual bool OnSetCursor( CefRefPtr browser, const CursorType type, const void *pchIconData, int iWide, int iTall, int xHotSpot, int yHotSpot ) OVERRIDE; - virtual bool OnFileOpenDialog( CefRefPtr browser, bool bMultiSelect, const CefString &default_title, const CefString &default_file, CefWebFileChooserCallback *pCallback ) OVERRIDE; - virtual bool OnEnterFullScreen( CefRefPtr browser ) OVERRIDE; - virtual bool OnExitFullScreen( CefRefPtr browser ) OVERRIDE; - - bool BUpdated(); - void SetUpdated( bool state ); - - int GetPopupTall() const { return m_nPopupTall; } - int GetPopupWide() const { return m_nPopupWide; } - int GetPopupX() const { return m_nPopupX; } - int GetPopupY() const { return m_nPopupY; } - const byte *PPopupTextureData() - { - return m_PopupTexture.Base(); - } - - uint32 FlipTexture(); - void SetTextureUploaded( uint32 id ); - bool BPaintBufferAvailable(); - - byte *PComposedTextureData( uint32 iTexture ); - int GetTall() const { return m_MainTexture.GetTall(); } - int GetWide() const { return m_MainTexture.GetWide(); } - int GetUpdateX() const { return m_UpdateRect.GetUpdateX( GetWide() ); } - int GetUpdateY() const { return m_UpdateRect.GetUpdateY( GetTall() ); } - int GetUpdateWide() const { return m_UpdateRect.GetUpdateWide( GetWide() ); } - int GetUpdateTall() const { return m_UpdateRect.GetUpdateTall( GetTall() ); } - void ClearUpdateRect() { m_UpdateRect.MarkAllClean(); } - - // XXX not shuttled safely between threads, should use real buffering - const byte *PPopupTextureDataCached(); - - bool BPopupVisible() const { return m_bPopupVisible; } - // WebKit will show popups before they have been given a size and painted and we may do - // a drawing pass during that time, so we want to make sure to only do something - // with the popup when it has an actual size and content. - bool BPopupVisibleAndPainted() const - { - return m_bPopupVisible && - m_nPopupWide != 0 && - m_nPopupTall != 0 && - m_PopupTexture.Count() >= m_nPopupWide * m_nPopupTall * 4; - } - void PopupRect( int &x, int &y, int &wide, int &tall ) const { x = m_nPopupX; y = m_nPopupY; wide = m_nPopupWide; tall = m_nPopupTall; } - -#ifdef DBGFLAG_VALIDATE - virtual void Validate( CValidator &validator, const char *pchName ) - { - VALIDATE_SCOPE(); - for ( int i = 0; i < Q_ARRAYSIZE(m_Texture); i++ ) - ValidateObj( m_Texture[i] ); - - ValidateObj( m_PopupTextureCache ); - ValidateObj( m_PopupTexture ); - ValidateObj( m_MainTexture ); - } -#endif - IMPLEMENT_REFCOUNTING(CChromePainter); - -private: - uint32 m_iNextTexture; - uint32 m_iTexturesInFlightBits; - int m_nPopupWide; - int m_nPopupTall; - CChromeRenderBuffer m_MainTexture; - CChromeRenderBuffer m_Texture[2]; // buffering -- XXX should use atomic swap, not push multiple buffers to command buffer - CChromeUpdateRegion m_UpdateRect; - CUtlVector m_PopupTextureCache; - CUtlVector m_PopupTexture; - bool m_bUpdated; - CClientHandler *m_pParent; - bool m_bPopupVisible; - int m_nPopupX, m_nPopupY; -}; - - - -//----------------------------------------------------------------------------- -// Purpose: CEF callback object -//----------------------------------------------------------------------------- -class CClientHandler : public CefClient, - public CefLifeSpanHandler, - public CefLoadHandler, - public CefRequestHandler, - public CefDisplayHandler, - - public CefJSDialogHandler, - public CefFindHandler, - public CefMenuHandler, - public CefFocusHandler, - public CefPermissionHandler -{ -public: - CClientHandler( int iBrowser, const char *pchUserAgent, uint16 nSerial ); - ~CClientHandler(); - - // CefClient methods - virtual CefRefPtr GetLifeSpanHandler() OVERRIDE { - return this; - } - virtual CefRefPtr GetLoadHandler() OVERRIDE { - return this; - } - virtual CefRefPtr GetRequestHandler() OVERRIDE { - return this; - } - virtual CefRefPtr GetDisplayHandler() OVERRIDE { - return this; - } - virtual CefRefPtr GetRenderHandler() OVERRIDE { - return &m_Painter; - } - - virtual CefRefPtr GetFocusHandler() OVERRIDE { - return this; - } - - virtual CefRefPtr GetMenuHandler() OVERRIDE { - return this; - } - virtual CefRefPtr GetPermissionHandler() OVERRIDE { - return this; - } - - virtual CefRefPtr GetFindHandler() OVERRIDE { - return this; - } - virtual CefRefPtr GetJSDialogHandler() OVERRIDE { - return this; - } - - - // CefLifeSpanHandler methods - - virtual bool OnBeforePopup(CefRefPtr parentBrowser, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, const CefString& url, CefRefPtr& client, CefBrowserSettings& settings) OVERRIDE; - virtual void OnAfterCreated(CefRefPtr browser) OVERRIDE; - virtual void OnBeforeClose(CefRefPtr browser) OVERRIDE; - virtual bool DoClose(CefRefPtr browser) { return false; } - virtual bool OnNewTab(CefRefPtr parentBrowser, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, const CefString& url, bool bForeground, CefRefPtr& client, CefBrowserSettings& settings ); - - // CefLoadHandler methods - virtual void OnLoadStart(CefRefPtr browser, CefRefPtr frame, bool bIsNewNavigation ) OVERRIDE; - - virtual void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode, CefRefPtr request ) OVERRIDE; - virtual bool OnLoadError(CefRefPtr browser, CefRefPtr frame, ErrorCode errorCode, const CefString& failedUrl, CefString& errorText); - - // CefRequestHandler methods - virtual bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, CefRefPtr request, NavType navType, bool isRedirect, bool isNewTabRequest ); - virtual bool OnBeforeResourceLoad(CefRefPtr browser, CefRefPtr request, CefString& redirectUrl, CefRefPtr& resourceStream, CefRefPtr response, int loadFlags); - - // CefDisplayHandler methods - virtual void OnNavStateChange(CefRefPtr browser, bool canGoBack, bool canGoForward) OVERRIDE {} - virtual void OnAddressChange(CefRefPtr browser, CefRefPtr frame, const CefString& url) OVERRIDE { } - virtual void OnTitleChange(CefRefPtr browser, const CefString& title) OVERRIDE; - - virtual bool OnTooltip(CefRefPtr browser, CefString& text); - virtual void OnStatusMessage(CefRefPtr browser, const CefString& value, StatusType type); - virtual bool OnConsoleMessage(CefRefPtr browser, const CefString& message, const CefString& source, int line); - - virtual void OnTakeFocus(CefRefPtr browser, bool next) {} - virtual bool OnSetFocus(CefRefPtr browser, FocusSource source) { return true; } - virtual void OnFocusedNodeChanged(CefRefPtr browser, CefRefPtr frame, CefRefPtr node); - - virtual bool OnBeforeMenu(CefRefPtr browser, const CefMenuInfo& menuInfo) { return true; } - virtual void GetMenuLabel(CefRefPtr browser, MenuId menuId, CefString& label) {} - virtual bool OnMenuAction(CefRefPtr browser, MenuId menuId) { return true; } - virtual bool OnBeforeScriptExtensionLoad(CefRefPtr browser, CefRefPtr frame, const CefString& extensionName) { return false; } - - virtual void OnFindResult(CefRefPtr browser, int identifier, int count, const CefRect& selectionRect, int activeMatchOrdinal, bool finalUpdate); - virtual bool OnJSAlert(CefRefPtr browser, CefRefPtr frame, const CefString& message); - virtual bool OnJSConfirm(CefRefPtr browser, CefRefPtr frame, const CefString& message, bool& retval); - virtual bool OnJSPrompt(CefRefPtr browser, CefRefPtr frame, const CefString& message, const CefString& defaultValue, bool& retval, CefString& result); - - void FileOpenDialogResult( const char *pchFileName ); - - - // paint helpers - void Paint(); - bool BNeedsPaint(); - - int GetTextureTall() const { return m_Painter.GetTall(); } - int GetTextureWide() const { return m_Painter.GetWide(); } - int GetUpdateX() const { return m_Painter.GetUpdateX(); } - int GetUpdateY() const { return m_Painter.GetUpdateY(); } - int GetUpdateWide() const { return m_Painter.GetUpdateWide(); } - int GetUpdateTall() const { return m_Painter.GetUpdateTall(); } - - const byte *PPopupTextureDataCached() { return m_Painter.PPopupTextureDataCached(); } - bool BPopupVisible() const { return m_Painter.BPopupVisible(); } - bool BPopupVisibleAndPainted() const { return m_Painter.BPopupVisibleAndPainted(); } - void PopupRect( int &x, int &y, int &wide, int &tall ) const { m_Painter.PopupRect( x, y, wide, tall ); } - - const byte *PComposedTextureData( uint32 iTexture ); - uint32 FlipTexture() { return m_Painter.FlipTexture(); } - void SetTextureUploaded( uint32 iTexture ) { m_Painter.SetTextureUploaded( iTexture ); } - void ClearUpdateRect() { m_Painter.ClearUpdateRect(); } - bool BPaintBufferReady() { return m_Painter.BPaintBufferAvailable(); } - - // Helpers - CefRefPtr GetBrowser(); - void CloseBrowser(); - uint32 GetPageSerial() { return m_nPageSerial; } - void SetPendingPageSerial( uint32 nPageSerial ) - { - m_nPendingPageSerial = nPageSerial; - } - - - void SetUserAgent( const char *pchAgent ) { Q_strncpy( m_szUserAgentExtras, pchAgent, sizeof(m_szUserAgentExtras) ); } - const char *PchCurrentURL() { return m_strCurrentUrl.String(); } - bool IsVisuallyNonEmpty(); - void SetErrorStrings( const char *pchTitle, const char *pchHeader, const char *pchCacheMiss, const char *pchBadURL, const char *pchConnectionProblem, - const char *pchProxyProblem, const char *pchUnknown ); - void SetMouseLocation( int x, int y ); - void GetMouseLocation( int &x, int &y ) { x = m_nMouseX; y = m_nMouseY; } - void SetMouseFocus( bool bFocus ) { m_bMouseFocus = bFocus; } - void SetSize( int wide, int tall ) { m_nExpectedWide = wide; m_nExpectedTall = tall; } - void GetExpectedSize( int &wide, int &tall ) { wide = m_nExpectedWide; tall =m_nExpectedTall; } - - //----------------------------------------------------------------------------- - // Purpose: Adds a custom header to all requests - //----------------------------------------------------------------------------- - void AddHeader( const char *pchHeader, const char *pchValue ) - { - m_vecHeaders.AddToTail( std::make_pair( CStrAutoEncode( pchHeader ).ToWString(), CStrAutoEncode( pchValue ).ToWString() ) ); - } - - void RequestScreenShot( const CHTMLProtoBufMsg &cmd ); - void SavePageToJPEGIfNeeded( CefRefPtr browser, const byte *pRGBA, int wide, int tall ); - bool BPendingScreenShot() - { - return m_Snapshot.m_flRequestTimeout > 0.0f && m_Snapshot.m_sURLSnapshot.IsValid() && m_Snapshot.m_flRequestTimeout < Plat_FloatTime(); - } - - void SetUserAgentIdentifier( const char *pchIdent ) - { - m_strUserAgentIdentifier = pchIdent; - } - - uint16 NSerial() { return m_nSerial; } - - void RefreshCEFHoverStatesAfterScroll(); - -#ifdef DBGFLAG_VALIDATE - virtual void Validate( CValidator &validator, const char *pchName ) - { - VALIDATE_SCOPE(); - ValidateObj( m_strCurrentUrl ); - ValidateObj( m_strPostData ); - ValidateObj( m_strLastRedirectURL ); - ValidateObj( m_vecHeaders ); - ValidateObj( m_strUserAgent ); - ValidateObj( m_Painter ); - ValidateObj( m_sErrorTitle ); - ValidateObj( m_sErrorHeader ); - ValidateObj( m_sErrorCacheMiss ); - ValidateObj( m_sErrorBadURL ); - ValidateObj( m_sErrorConnectionProblem ); - ValidateObj( m_sErrorProxyProblem ); - ValidateObj( m_sErrorUnknown ); - ValidateObj( m_strUserAgentIdentifier ); - // ValidateObj( m_vecHCursor ); - } -#endif - - IMPLEMENT_REFCOUNTING(CClientHandler); - IMPLEMENT_LOCKING(CClientHandler); -private: - friend class CChromePainter; - friend class CCEFThread; - - void SetBrowserAgent( CefRefPtr browser ); - - // The child browser window - CefRefPtr m_Browser; - - CefRenderHandler::CefWebFileChooserCallback *m_pOpenFileCallback; - - uint16 m_nSerial; - char m_szUserAgentExtras[ 256 ]; - CUtlString m_strCurrentUrl; - CUtlString m_strPostData; - CUtlString m_strLastRedirectURL; - CUtlString m_strUserAgent; - CUtlString m_strUserAgentIdentifier; - CUtlString m_strToolTip; - bool m_bShowingToolTip; - bool m_bPendingPaint; - bool m_bBrowserClosing; - bool m_bMouseFocus; - CChromePainter m_Painter; - CUtlVector< std::pair< std::wstring, std::wstring > > m_vecHeaders; - int m_iBrowser; - int m_nExpectedWide; - int m_nExpectedTall; - uint32 m_nPageSerial; - uint32 m_nPendingPageSerial; - - CUtlString m_sErrorTitle; - CUtlString m_sErrorHeader; - CUtlString m_sErrorCacheMiss; - CUtlString m_sErrorBadURL; - CUtlString m_sErrorConnectionProblem; - CUtlString m_sErrorProxyProblem; - CUtlString m_sErrorUnknown; - int m_nMouseX; - int m_nMouseY; - int m_nMouseScrolledX; - int m_nMouseScrolledY; - - struct SnapshotData_t - { - CUtlString m_sURLSnapshot; - CUtlString m_sFileNameSnapshot; - int m_nWide, m_nTall; - float m_flRequestTimeout; - }; - - SnapshotData_t m_Snapshot; - - // Scroll bar state is cached and compared to CEF's current values every frame; - // any changes are passed on to the client handler as serialized update messages. - struct CachedScrollBarState_t - { - int m_nX; - int m_nY; - int m_nWide; - int m_nTall; - int m_nMax; - int m_nScroll; - int m_bVisible; // using 'int' to avoid padding bytes so memcmp can be used - }; - CachedScrollBarState_t m_CachedVScroll; - CachedScrollBarState_t m_CachedHScroll; -}; - - - -//----------------------------------------------------------------------------- -// Purpose: CEF thinking thread -//----------------------------------------------------------------------------- -class CCEFThread : public CValidatableThread -{ -public: - CCEFThread(); - ~CCEFThread(); - void SetCEFPaths( const char *pchHTMLCacheDir, const char *pchCookiePath ); - void TriggerShutdown(); - void WakeThread(); - virtual int Run(); - - bool BHasPendingMessages() { return m_tslResponseBuffers.Count() > 0; } - - HTMLCommandBuffer_t *GetFreeCommandBuffer( EHTMLCommands eCmd, int iBrowser ); - void ReleaseCommandBuffer( HTMLCommandBuffer_t *pBuf ); - void PushCommand( HTMLCommandBuffer_t * ); - void PushResponse( HTMLCommandBuffer_t * ); - bool GetMainThreadCommand( HTMLCommandBuffer_t ** ); - HTMLCommandBuffer_t *BWaitForCommand( EHTMLCommands eCmd, int iBrowser ); - HTMLCommandBuffer_t *BWaitForResponse( EHTMLCommands eCmd, int iBrowser ); - bool GetCEFThreadCommand( HTMLCommandBuffer_t **pBuf ); - -#ifdef DBGFLAG_VALIDATE - virtual void SleepForValidate() { CValidatableThread::SleepForValidate(); m_evWaitingForCommand.Set(); WakeThread(); } - virtual void Validate( CValidator &validator, const tchar *pchName ); -#endif - - -private: - void RunCurrentCommands(); - const char *PchWebkitUserAgent(); - void AppGetSettings(CefSettings& settings, CefRefPtr& app); - void CleanupTempFolders(); - - void ThreadCreateBrowser( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadRemoveBrowser( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserSize( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserPosition( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserPostURL( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserStopLoad( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserReload( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserGoForward( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserGoBack( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserCopy( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserPaste( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserExecuteJavascript( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserSetFocus( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserHorizontalScrollBarSize( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserVerticalScrollBarSize( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserFind( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserStopFind( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserSetHorizontalScroll( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserSetVerticalScroll( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserSetZoomLevel( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserViewSource( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadNeedsPaintResponse( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadBrowserErrorStrings( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadAddHeader( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadGetZoom( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadHidePopup( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadGetCookiesForURL( const CHTMLProtoBufMsg &htmlCommand ); - - void ThreadKeyDown( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadKeyUp( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadKeyTyped( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadMouseButtonDown( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadMouseButtonUp( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadMouseButtonDlbClick( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadMouseWheel( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadMouseMove( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadMouseLeave( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadLinkAtPosition( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadZoomToElementAtPosition( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadZoomToFocusedElement( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadSavePageToJPEG( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadSetCookie( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadSetTargetFrameRate( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadFullRepaint( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadSetPageScale( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadExitFullScreen( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadCloseFullScreenFlashIfOpen( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadPauseFullScreenFlashMovieIfOpen( const CHTMLProtoBufMsg &htmlCommand ); - void ThreadGetFocusedNodeText( const CHTMLProtoBufMsg &htmlCommand ); - - void ThreadBrowserVerticalScrollBarSizeHelper( int iBrowser, bool bForceSendUpdate ); - void ThreadBrowserHorizontalScrollBarSizeHelper( int iBrowser, bool bForceSendUpdate ); - - bool BIsValidBrowserHandle( uint32 nHandle, int &iClient ); - void SendSizeChangeEvents(); - void CheckForFullScreenFlashControl(); - - CThreadEvent m_WakeEvent; - CUtlString m_sHTMLCacheDir; - CUtlString m_sCookiePath; - bool m_bExit; - CThreadEvent m_eventDidExit; - - CUtlLinkedList m_listClientHandlers; - - CTSQueue m_tslCommandBuffers; - CUtlVector m_vecQueueCommands; - CTSQueue m_tslResponseBuffers; - CUtlVector m_vecQueueResponses; - - CTSList m_tslUnsedBuffers; - - CThreadEvent m_evWaitingForCommand; - CThreadEvent m_evWaitingForResponse; - int m_nTargetFrameRate; - uint16 m_nBrowserSerial; - bool m_bFullScreenFlashVisible; -#ifdef WIN32 - HWND m_flashfullscreenHWND; -#endif - bool m_bSawUserInputThisFrame; - - struct SizeChange_t - { - int iBrowser; - int nBeforeWidth; - int nBeforeHeight; - float flNewZoom; - }; - CUtlMap< int, SizeChange_t, int > m_mapSizeChangesPending; -}; - -CCEFThread &AccessHTMLWrapper(); - -#endif // HTML_CHROME_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +//=============================================================================// + +#ifndef HTML_CHROME_H +#define HTML_CHROME_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "cef/include/cef_render_handler.h" +#include "cef/include/cef_client.h" +#include "cef/include/cef_app.h" +#include "cef/include/cef_browser.h" +#include "cef/include/cef_command_line.h" +#include "cef/include/cef_frame.h" +#include "cef/include/cef_runnable.h" +#include "cef/include/cef_web_urlrequest.h" +#include "cef/include/cef_request_handler.h" +#include "cef/include/cef_load_handler.h" +#include "cef/include/cef_display_handler.h" +#include "cef/include/cef_life_span_handler.h" +#include "cef/include/cef_render_handler.h" + +#include "tier0/platform.h" +#include "tier0/vprof.h" +#include "tier1/utlarray.h" +#include "tier1/utlbuffer.h" +#include "fileio.h" +//#include "constants.h" +#include "tier0/validator.h" +#include "tier1/utlmap.h" +#include "tier1/utlstring.h" +#include "tier0/tslist.h" +#include "html/ihtmlchrome.h" +#include "html/htmlprotobuf.h" +// These must be undefed so that the MacOS build will work -- their STL +// fails if min and max are defined. I'm undefing them in all builds to +// avoid having a Windows dependency on min/max creep in. +#undef min +#undef max +#include "htmlmessages.pb.h" + +class CClientHandler; +class CChromePainter; +class ICookieCallback; + +bool ChromeSetWebCookie( const char *pchHostname, const char *pchName, const char *pchValue, const char *pchPath, RTime32 nExpires ); +bool ChromeGetWebCookiesForURL( CUtlString *pstrValue, const char *pchURL, const char *pchName ); + +//----------------------------------------------------------------------------- +// Purpose: track dirty rects, shuttle bits between renderer and main thread +//----------------------------------------------------------------------------- +class CChromeUpdateRegion +{ +public: + CChromeUpdateRegion() { MarkAllClean(); } + int GetUpdateX( int clampWide ) const { return clamp( m_nUpdateX0, 0, clampWide ); } + int GetUpdateY( int clampTall ) const { return clamp( m_nUpdateY0, 0, clampTall ); } + int GetUpdateWide( int clampWide ) const { return clamp( m_nUpdateX1, 0, clampWide ) - GetUpdateX( clampWide ); } + int GetUpdateTall( int clampTall ) const { return clamp( m_nUpdateY1, 0, clampTall ) - GetUpdateY( clampTall ); } + + void MarkAllClean() { m_nUpdateX0 = m_nUpdateY0 = INT_MAX; m_nUpdateX1 = m_nUpdateY1 = 0; } + void MarkAllDirty() { m_nUpdateX0 = m_nUpdateY0 = 0; m_nUpdateX1 = m_nUpdateY1 = INT_MAX; } + void MarkDirtyRect( int x0, int y0, int x1, int y1 ) + { + if ( x0 >= x1 || y0 >= y1 ) return; + if ( m_nUpdateX0 > x0 ) m_nUpdateX0 = x0; + if ( m_nUpdateY0 > y0 ) m_nUpdateY0 = y0; + if ( m_nUpdateX1 < x1 ) m_nUpdateX1 = x1; + if ( m_nUpdateY1 < y1 ) m_nUpdateY1 = y1; + } + void MarkDirtyRect( const CChromeUpdateRegion& other ) + { + MarkDirtyRect( other.m_nUpdateX0, other.m_nUpdateY0, other.m_nUpdateX1, other.m_nUpdateY1 ); + } + +protected: + int m_nUpdateX0; + int m_nUpdateY0; + int m_nUpdateX1; + int m_nUpdateY1; +}; + +class CChromeRenderBuffer : public CChromeUpdateRegion +{ +public: + CChromeRenderBuffer() { m_nWide = m_nTall = 0; } + void SetSize( int wide, int tall ) + { + if ( wide != m_nWide || tall != m_nTall ) + { + m_nWide = wide; + m_nTall = tall; + MarkAllDirty(); + m_Texture.EnsureCapacity( wide * tall * 4 ); + } + } + int GetWide() const { return m_nWide; } + int GetTall() const { return m_nTall; } + byte *GetPixels() { return m_Texture.Base(); } + + bool BUpdatePixels( byte *pOther, int wide, int tall ) + { + SetSize( wide, tall ); + int x0 = clamp( m_nUpdateX0, 0, wide ); + int y0 = clamp( m_nUpdateY0, 0, tall ); + int x1 = clamp( m_nUpdateX1, 0, wide ); + int y1 = clamp( m_nUpdateY1, 0, tall ); + if ( x0 >= x1 || y0 >= y1 ) + return false; + + if ( x0 == 0 && x1 == wide ) + { + byte *pDst = GetPixels() + y0*wide*4; + byte *pSrc = pOther + y0*wide*4; + Q_memcpy( pDst, pSrc, wide * ( y1 - y0 ) * 4 ); + } + else + { + byte *pDst = GetPixels() + y0*wide*4 + x0*4; + byte *pSrc = pOther + y0*wide*4 + x0*4; + int nCopyBytesPerRow = (x1 - x0)*4; + for ( int rows = y1 - y0; rows > 0; --rows ) + { + Q_memcpy( pDst, pSrc, nCopyBytesPerRow ); + pSrc += wide * 4; + pDst += wide * 4; + } + } + return true; + } + +#ifdef DBGFLAG_VALIDATE + virtual void Validate( CValidator &validator, const char *pchName ) + { + VALIDATE_SCOPE(); + ValidateObj( m_Texture ); + } +#endif + +protected: + int m_nWide; // dimensions of the buffer + int m_nTall; + CUtlVector m_Texture; // rgba data +}; + +//----------------------------------------------------------------------------- +// Purpose: interface Chrome uses to send up paint messages +//----------------------------------------------------------------------------- +class CChromePainter : public CefRenderHandler +{ +public: + CChromePainter( CClientHandler *pParent ); + ~CChromePainter(); + + virtual bool GetViewRect(CefRefPtr browser, CefRect& rect) OVERRIDE { return false; } + virtual bool GetScreenRect(CefRefPtr browser, CefRect& rect) OVERRIDE { return false; } + virtual bool GetScreenPoint(CefRefPtr browser, int viewX, int viewY, int& screenX, int& screenY) OVERRIDE { return false; } + + virtual void OnPopupShow(CefRefPtr browser, bool show) OVERRIDE; + virtual void OnPopupSize(CefRefPtr browser, const CefRect& rect) OVERRIDE; + + virtual void OnPaint(CefRefPtr browser, PaintElementType type, const RectList& dirtyRects, const void* buffer) OVERRIDE; + virtual void OnCursorChange(CefRefPtr browser, CefCursorHandle cursor) OVERRIDE {} + virtual bool OnSetCursor( CefRefPtr browser, const CursorType type, const void *pchIconData, int iWide, int iTall, int xHotSpot, int yHotSpot ) OVERRIDE; + virtual bool OnFileOpenDialog( CefRefPtr browser, bool bMultiSelect, const CefString &default_title, const CefString &default_file, CefWebFileChooserCallback *pCallback ) OVERRIDE; + virtual bool OnEnterFullScreen( CefRefPtr browser ) OVERRIDE; + virtual bool OnExitFullScreen( CefRefPtr browser ) OVERRIDE; + + bool BUpdated(); + void SetUpdated( bool state ); + + int GetPopupTall() const { return m_nPopupTall; } + int GetPopupWide() const { return m_nPopupWide; } + int GetPopupX() const { return m_nPopupX; } + int GetPopupY() const { return m_nPopupY; } + const byte *PPopupTextureData() + { + return m_PopupTexture.Base(); + } + + uint32 FlipTexture(); + void SetTextureUploaded( uint32 id ); + bool BPaintBufferAvailable(); + + byte *PComposedTextureData( uint32 iTexture ); + int GetTall() const { return m_MainTexture.GetTall(); } + int GetWide() const { return m_MainTexture.GetWide(); } + int GetUpdateX() const { return m_UpdateRect.GetUpdateX( GetWide() ); } + int GetUpdateY() const { return m_UpdateRect.GetUpdateY( GetTall() ); } + int GetUpdateWide() const { return m_UpdateRect.GetUpdateWide( GetWide() ); } + int GetUpdateTall() const { return m_UpdateRect.GetUpdateTall( GetTall() ); } + void ClearUpdateRect() { m_UpdateRect.MarkAllClean(); } + + // XXX not shuttled safely between threads, should use real buffering + const byte *PPopupTextureDataCached(); + + bool BPopupVisible() const { return m_bPopupVisible; } + // WebKit will show popups before they have been given a size and painted and we may do + // a drawing pass during that time, so we want to make sure to only do something + // with the popup when it has an actual size and content. + bool BPopupVisibleAndPainted() const + { + return m_bPopupVisible && + m_nPopupWide != 0 && + m_nPopupTall != 0 && + m_PopupTexture.Count() >= m_nPopupWide * m_nPopupTall * 4; + } + void PopupRect( int &x, int &y, int &wide, int &tall ) const { x = m_nPopupX; y = m_nPopupY; wide = m_nPopupWide; tall = m_nPopupTall; } + +#ifdef DBGFLAG_VALIDATE + virtual void Validate( CValidator &validator, const char *pchName ) + { + VALIDATE_SCOPE(); + for ( int i = 0; i < Q_ARRAYSIZE(m_Texture); i++ ) + ValidateObj( m_Texture[i] ); + + ValidateObj( m_PopupTextureCache ); + ValidateObj( m_PopupTexture ); + ValidateObj( m_MainTexture ); + } +#endif + IMPLEMENT_REFCOUNTING(CChromePainter); + +private: + uint32 m_iNextTexture; + uint32 m_iTexturesInFlightBits; + int m_nPopupWide; + int m_nPopupTall; + CChromeRenderBuffer m_MainTexture; + CChromeRenderBuffer m_Texture[2]; // buffering -- XXX should use atomic swap, not push multiple buffers to command buffer + CChromeUpdateRegion m_UpdateRect; + CUtlVector m_PopupTextureCache; + CUtlVector m_PopupTexture; + bool m_bUpdated; + CClientHandler *m_pParent; + bool m_bPopupVisible; + int m_nPopupX, m_nPopupY; +}; + + + +//----------------------------------------------------------------------------- +// Purpose: CEF callback object +//----------------------------------------------------------------------------- +class CClientHandler : public CefClient, + public CefLifeSpanHandler, + public CefLoadHandler, + public CefRequestHandler, + public CefDisplayHandler, + + public CefJSDialogHandler, + public CefFindHandler, + public CefMenuHandler, + public CefFocusHandler, + public CefPermissionHandler +{ +public: + CClientHandler( int iBrowser, const char *pchUserAgent, uint16 nSerial ); + ~CClientHandler(); + + // CefClient methods + virtual CefRefPtr GetLifeSpanHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetLoadHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetRequestHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetDisplayHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetRenderHandler() OVERRIDE { + return &m_Painter; + } + + virtual CefRefPtr GetFocusHandler() OVERRIDE { + return this; + } + + virtual CefRefPtr GetMenuHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetPermissionHandler() OVERRIDE { + return this; + } + + virtual CefRefPtr GetFindHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetJSDialogHandler() OVERRIDE { + return this; + } + + + // CefLifeSpanHandler methods + + virtual bool OnBeforePopup(CefRefPtr parentBrowser, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, const CefString& url, CefRefPtr& client, CefBrowserSettings& settings) OVERRIDE; + virtual void OnAfterCreated(CefRefPtr browser) OVERRIDE; + virtual void OnBeforeClose(CefRefPtr browser) OVERRIDE; + virtual bool DoClose(CefRefPtr browser) { return false; } + virtual bool OnNewTab(CefRefPtr parentBrowser, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, const CefString& url, bool bForeground, CefRefPtr& client, CefBrowserSettings& settings ); + + // CefLoadHandler methods + virtual void OnLoadStart(CefRefPtr browser, CefRefPtr frame, bool bIsNewNavigation ) OVERRIDE; + + virtual void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode, CefRefPtr request ) OVERRIDE; + virtual bool OnLoadError(CefRefPtr browser, CefRefPtr frame, ErrorCode errorCode, const CefString& failedUrl, CefString& errorText); + + // CefRequestHandler methods + virtual bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, CefRefPtr request, NavType navType, bool isRedirect, bool isNewTabRequest ); + virtual bool OnBeforeResourceLoad(CefRefPtr browser, CefRefPtr request, CefString& redirectUrl, CefRefPtr& resourceStream, CefRefPtr response, int loadFlags); + + // CefDisplayHandler methods + virtual void OnNavStateChange(CefRefPtr browser, bool canGoBack, bool canGoForward) OVERRIDE {} + virtual void OnAddressChange(CefRefPtr browser, CefRefPtr frame, const CefString& url) OVERRIDE { } + virtual void OnTitleChange(CefRefPtr browser, const CefString& title) OVERRIDE; + + virtual bool OnTooltip(CefRefPtr browser, CefString& text); + virtual void OnStatusMessage(CefRefPtr browser, const CefString& value, StatusType type); + virtual bool OnConsoleMessage(CefRefPtr browser, const CefString& message, const CefString& source, int line); + + virtual void OnTakeFocus(CefRefPtr browser, bool next) {} + virtual bool OnSetFocus(CefRefPtr browser, FocusSource source) { return true; } + virtual void OnFocusedNodeChanged(CefRefPtr browser, CefRefPtr frame, CefRefPtr node); + + virtual bool OnBeforeMenu(CefRefPtr browser, const CefMenuInfo& menuInfo) { return true; } + virtual void GetMenuLabel(CefRefPtr browser, MenuId menuId, CefString& label) {} + virtual bool OnMenuAction(CefRefPtr browser, MenuId menuId) { return true; } + virtual bool OnBeforeScriptExtensionLoad(CefRefPtr browser, CefRefPtr frame, const CefString& extensionName) { return false; } + + virtual void OnFindResult(CefRefPtr browser, int identifier, int count, const CefRect& selectionRect, int activeMatchOrdinal, bool finalUpdate); + virtual bool OnJSAlert(CefRefPtr browser, CefRefPtr frame, const CefString& message); + virtual bool OnJSConfirm(CefRefPtr browser, CefRefPtr frame, const CefString& message, bool& retval); + virtual bool OnJSPrompt(CefRefPtr browser, CefRefPtr frame, const CefString& message, const CefString& defaultValue, bool& retval, CefString& result); + + void FileOpenDialogResult( const char *pchFileName ); + + + // paint helpers + void Paint(); + bool BNeedsPaint(); + + int GetTextureTall() const { return m_Painter.GetTall(); } + int GetTextureWide() const { return m_Painter.GetWide(); } + int GetUpdateX() const { return m_Painter.GetUpdateX(); } + int GetUpdateY() const { return m_Painter.GetUpdateY(); } + int GetUpdateWide() const { return m_Painter.GetUpdateWide(); } + int GetUpdateTall() const { return m_Painter.GetUpdateTall(); } + + const byte *PPopupTextureDataCached() { return m_Painter.PPopupTextureDataCached(); } + bool BPopupVisible() const { return m_Painter.BPopupVisible(); } + bool BPopupVisibleAndPainted() const { return m_Painter.BPopupVisibleAndPainted(); } + void PopupRect( int &x, int &y, int &wide, int &tall ) const { m_Painter.PopupRect( x, y, wide, tall ); } + + const byte *PComposedTextureData( uint32 iTexture ); + uint32 FlipTexture() { return m_Painter.FlipTexture(); } + void SetTextureUploaded( uint32 iTexture ) { m_Painter.SetTextureUploaded( iTexture ); } + void ClearUpdateRect() { m_Painter.ClearUpdateRect(); } + bool BPaintBufferReady() { return m_Painter.BPaintBufferAvailable(); } + + // Helpers + CefRefPtr GetBrowser(); + void CloseBrowser(); + uint32 GetPageSerial() { return m_nPageSerial; } + void SetPendingPageSerial( uint32 nPageSerial ) + { + m_nPendingPageSerial = nPageSerial; + } + + + void SetUserAgent( const char *pchAgent ) { Q_strncpy( m_szUserAgentExtras, pchAgent, sizeof(m_szUserAgentExtras) ); } + const char *PchCurrentURL() { return m_strCurrentUrl.String(); } + bool IsVisuallyNonEmpty(); + void SetErrorStrings( const char *pchTitle, const char *pchHeader, const char *pchCacheMiss, const char *pchBadURL, const char *pchConnectionProblem, + const char *pchProxyProblem, const char *pchUnknown ); + void SetMouseLocation( int x, int y ); + void GetMouseLocation( int &x, int &y ) { x = m_nMouseX; y = m_nMouseY; } + void SetMouseFocus( bool bFocus ) { m_bMouseFocus = bFocus; } + void SetSize( int wide, int tall ) { m_nExpectedWide = wide; m_nExpectedTall = tall; } + void GetExpectedSize( int &wide, int &tall ) { wide = m_nExpectedWide; tall =m_nExpectedTall; } + + //----------------------------------------------------------------------------- + // Purpose: Adds a custom header to all requests + //----------------------------------------------------------------------------- + void AddHeader( const char *pchHeader, const char *pchValue ) + { + m_vecHeaders.AddToTail( std::make_pair( CStrAutoEncode( pchHeader ).ToWString(), CStrAutoEncode( pchValue ).ToWString() ) ); + } + + void RequestScreenShot( const CHTMLProtoBufMsg &cmd ); + void SavePageToJPEGIfNeeded( CefRefPtr browser, const byte *pRGBA, int wide, int tall ); + bool BPendingScreenShot() + { + return m_Snapshot.m_flRequestTimeout > 0.0f && m_Snapshot.m_sURLSnapshot.IsValid() && m_Snapshot.m_flRequestTimeout < Plat_FloatTime(); + } + + void SetUserAgentIdentifier( const char *pchIdent ) + { + m_strUserAgentIdentifier = pchIdent; + } + + uint16 NSerial() { return m_nSerial; } + + void RefreshCEFHoverStatesAfterScroll(); + +#ifdef DBGFLAG_VALIDATE + virtual void Validate( CValidator &validator, const char *pchName ) + { + VALIDATE_SCOPE(); + ValidateObj( m_strCurrentUrl ); + ValidateObj( m_strPostData ); + ValidateObj( m_strLastRedirectURL ); + ValidateObj( m_vecHeaders ); + ValidateObj( m_strUserAgent ); + ValidateObj( m_Painter ); + ValidateObj( m_sErrorTitle ); + ValidateObj( m_sErrorHeader ); + ValidateObj( m_sErrorCacheMiss ); + ValidateObj( m_sErrorBadURL ); + ValidateObj( m_sErrorConnectionProblem ); + ValidateObj( m_sErrorProxyProblem ); + ValidateObj( m_sErrorUnknown ); + ValidateObj( m_strUserAgentIdentifier ); + // ValidateObj( m_vecHCursor ); + } +#endif + + IMPLEMENT_REFCOUNTING(CClientHandler); + IMPLEMENT_LOCKING(CClientHandler); +private: + friend class CChromePainter; + friend class CCEFThread; + + void SetBrowserAgent( CefRefPtr browser ); + + // The child browser window + CefRefPtr m_Browser; + + CefRenderHandler::CefWebFileChooserCallback *m_pOpenFileCallback; + + uint16 m_nSerial; + char m_szUserAgentExtras[ 256 ]; + CUtlString m_strCurrentUrl; + CUtlString m_strPostData; + CUtlString m_strLastRedirectURL; + CUtlString m_strUserAgent; + CUtlString m_strUserAgentIdentifier; + CUtlString m_strToolTip; + bool m_bShowingToolTip; + bool m_bPendingPaint; + bool m_bBrowserClosing; + bool m_bMouseFocus; + CChromePainter m_Painter; + CUtlVector< std::pair< std::wstring, std::wstring > > m_vecHeaders; + int m_iBrowser; + int m_nExpectedWide; + int m_nExpectedTall; + uint32 m_nPageSerial; + uint32 m_nPendingPageSerial; + + CUtlString m_sErrorTitle; + CUtlString m_sErrorHeader; + CUtlString m_sErrorCacheMiss; + CUtlString m_sErrorBadURL; + CUtlString m_sErrorConnectionProblem; + CUtlString m_sErrorProxyProblem; + CUtlString m_sErrorUnknown; + int m_nMouseX; + int m_nMouseY; + int m_nMouseScrolledX; + int m_nMouseScrolledY; + + struct SnapshotData_t + { + CUtlString m_sURLSnapshot; + CUtlString m_sFileNameSnapshot; + int m_nWide, m_nTall; + float m_flRequestTimeout; + }; + + SnapshotData_t m_Snapshot; + + // Scroll bar state is cached and compared to CEF's current values every frame; + // any changes are passed on to the client handler as serialized update messages. + struct CachedScrollBarState_t + { + int m_nX; + int m_nY; + int m_nWide; + int m_nTall; + int m_nMax; + int m_nScroll; + int m_bVisible; // using 'int' to avoid padding bytes so memcmp can be used + }; + CachedScrollBarState_t m_CachedVScroll; + CachedScrollBarState_t m_CachedHScroll; +}; + + + +//----------------------------------------------------------------------------- +// Purpose: CEF thinking thread +//----------------------------------------------------------------------------- +class CCEFThread : public CValidatableThread +{ +public: + CCEFThread(); + ~CCEFThread(); + void SetCEFPaths( const char *pchHTMLCacheDir, const char *pchCookiePath ); + void TriggerShutdown(); + void WakeThread(); + virtual int Run(); + + bool BHasPendingMessages() { return m_tslResponseBuffers.Count() > 0; } + + HTMLCommandBuffer_t *GetFreeCommandBuffer( EHTMLCommands eCmd, int iBrowser ); + void ReleaseCommandBuffer( HTMLCommandBuffer_t *pBuf ); + void PushCommand( HTMLCommandBuffer_t * ); + void PushResponse( HTMLCommandBuffer_t * ); + bool GetMainThreadCommand( HTMLCommandBuffer_t ** ); + HTMLCommandBuffer_t *BWaitForCommand( EHTMLCommands eCmd, int iBrowser ); + HTMLCommandBuffer_t *BWaitForResponse( EHTMLCommands eCmd, int iBrowser ); + bool GetCEFThreadCommand( HTMLCommandBuffer_t **pBuf ); + +#ifdef DBGFLAG_VALIDATE + virtual void SleepForValidate() { CValidatableThread::SleepForValidate(); m_evWaitingForCommand.Set(); WakeThread(); } + virtual void Validate( CValidator &validator, const tchar *pchName ); +#endif + + +private: + void RunCurrentCommands(); + const char *PchWebkitUserAgent(); + void AppGetSettings(CefSettings& settings, CefRefPtr& app); + void CleanupTempFolders(); + + void ThreadCreateBrowser( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadRemoveBrowser( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserSize( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserPosition( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserPostURL( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserStopLoad( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserReload( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserGoForward( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserGoBack( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserCopy( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserPaste( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserExecuteJavascript( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserSetFocus( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserHorizontalScrollBarSize( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserVerticalScrollBarSize( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserFind( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserStopFind( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserSetHorizontalScroll( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserSetVerticalScroll( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserSetZoomLevel( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserViewSource( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadNeedsPaintResponse( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadBrowserErrorStrings( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadAddHeader( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadGetZoom( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadHidePopup( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadGetCookiesForURL( const CHTMLProtoBufMsg &htmlCommand ); + + void ThreadKeyDown( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadKeyUp( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadKeyTyped( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadMouseButtonDown( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadMouseButtonUp( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadMouseButtonDlbClick( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadMouseWheel( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadMouseMove( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadMouseLeave( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadLinkAtPosition( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadZoomToElementAtPosition( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadZoomToFocusedElement( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadSavePageToJPEG( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadSetCookie( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadSetTargetFrameRate( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadFullRepaint( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadSetPageScale( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadExitFullScreen( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadCloseFullScreenFlashIfOpen( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadPauseFullScreenFlashMovieIfOpen( const CHTMLProtoBufMsg &htmlCommand ); + void ThreadGetFocusedNodeText( const CHTMLProtoBufMsg &htmlCommand ); + + void ThreadBrowserVerticalScrollBarSizeHelper( int iBrowser, bool bForceSendUpdate ); + void ThreadBrowserHorizontalScrollBarSizeHelper( int iBrowser, bool bForceSendUpdate ); + + bool BIsValidBrowserHandle( uint32 nHandle, int &iClient ); + void SendSizeChangeEvents(); + void CheckForFullScreenFlashControl(); + + CThreadEvent m_WakeEvent; + CUtlString m_sHTMLCacheDir; + CUtlString m_sCookiePath; + bool m_bExit; + CThreadEvent m_eventDidExit; + + CUtlLinkedList m_listClientHandlers; + + CTSQueue m_tslCommandBuffers; + CUtlVector m_vecQueueCommands; + CTSQueue m_tslResponseBuffers; + CUtlVector m_vecQueueResponses; + + CTSList m_tslUnsedBuffers; + + CThreadEvent m_evWaitingForCommand; + CThreadEvent m_evWaitingForResponse; + int m_nTargetFrameRate; + uint16 m_nBrowserSerial; + bool m_bFullScreenFlashVisible; +#ifdef WIN32 + HWND m_flashfullscreenHWND; +#endif + bool m_bSawUserInputThisFrame; + + struct SizeChange_t + { + int iBrowser; + int nBeforeWidth; + int nBeforeHeight; + float flNewZoom; + }; + CUtlMap< int, SizeChange_t, int > m_mapSizeChangesPending; +}; + +CCEFThread &AccessHTMLWrapper(); + +#endif // HTML_CHROME_H diff --git a/mp/src/vgui2/chromehtml/htmlmanager.h b/mp/src/vgui2/chromehtml/htmlmanager.h index 02b3728f..3aaa551c 100644 --- a/mp/src/vgui2/chromehtml/htmlmanager.h +++ b/mp/src/vgui2/chromehtml/htmlmanager.h @@ -1,30 +1,30 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -//=============================================================================// - -#ifndef HTMLMANAGER_H -#define HTMLMANAGER_H - -#ifdef _WIN32 -#pragma once -#endif - -//----------------------------------------------------------------------------- -// Purpose: helper funcs to pump chrome -//----------------------------------------------------------------------------- -void ChromeInit( const char *pchHTMLCacheDir, const char *pchCookiePath ); -void ChromeShutdown(); - -#ifdef DBGFLAG_VALIDATE -void ChromeValidate( CValidator &validator, const char *pchName ); -bool ChromeResumeFromValidate(); -bool ChromePrepareForValidate(); -#endif - -bool ChromeSetWebCookie( const char *pchHostname, const char *pchName, const char *pchValue, const char *pchPath ); -void ChromeSetClientBuildID( uint64 ulBuildID ); - -enum EMouseState { UP,DOWN,MOVE,DBLCLICK }; - -#endif // HTMLMANAGER_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +//=============================================================================// + +#ifndef HTMLMANAGER_H +#define HTMLMANAGER_H + +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Purpose: helper funcs to pump chrome +//----------------------------------------------------------------------------- +void ChromeInit( const char *pchHTMLCacheDir, const char *pchCookiePath ); +void ChromeShutdown(); + +#ifdef DBGFLAG_VALIDATE +void ChromeValidate( CValidator &validator, const char *pchName ); +bool ChromeResumeFromValidate(); +bool ChromePrepareForValidate(); +#endif + +bool ChromeSetWebCookie( const char *pchHostname, const char *pchName, const char *pchValue, const char *pchPath ); +void ChromeSetClientBuildID( uint64 ulBuildID ); + +enum EMouseState { UP,DOWN,MOVE,DBLCLICK }; + +#endif // HTMLMANAGER_H diff --git a/mp/src/vgui2/chromehtml/stdafx.h b/mp/src/vgui2/chromehtml/stdafx.h index 9746e141..9f38e71f 100644 --- a/mp/src/vgui2/chromehtml/stdafx.h +++ b/mp/src/vgui2/chromehtml/stdafx.h @@ -1,19 +1,19 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -#ifndef STDAFX_H -#define STDAFX_H - -#include "tier0/platform.h" -#include "tier0/vprof.h" -#include "tier1/utlarray.h" -#include "tier1/utlbuffer.h" -#include "fileio.h" -//#include "constants.h" -#include "steam/steamtypes.h" -#include "tier0/validator.h" -#include "tier1/utlmap.h" -#include "tier1/utlstring.h" -#include "tier0/tslist.h" -#include "html/ihtmlchrome.h" -#include "html/htmlprotobuf.h" - -#endif +//========= Copyright Valve Corporation, All rights reserved. ============// +#ifndef STDAFX_H +#define STDAFX_H + +#include "tier0/platform.h" +#include "tier0/vprof.h" +#include "tier1/utlarray.h" +#include "tier1/utlbuffer.h" +#include "fileio.h" +//#include "constants.h" +#include "steam/steamtypes.h" +#include "tier0/validator.h" +#include "tier1/utlmap.h" +#include "tier1/utlstring.h" +#include "tier0/tslist.h" +#include "html/ihtmlchrome.h" +#include "html/htmlprotobuf.h" + +#endif diff --git a/mp/src/vgui2/src/vgui_key_translation.cpp b/mp/src/vgui2/src/vgui_key_translation.cpp index 1a3d6d73..0377a5d8 100644 --- a/mp/src/vgui2/src/vgui_key_translation.cpp +++ b/mp/src/vgui2/src/vgui_key_translation.cpp @@ -1,42 +1,42 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//===========================================================================// - -#if defined( WIN32 ) && !defined( _X360 ) -#include -#include -#include "xbox/xboxstubs.h" -#endif -#include "tier0/dbg.h" -#include "vgui_key_translation.h" -#if defined( _X360 ) -#include "xbox/xbox_win32stubs.h" -#endif -#ifdef POSIX -#define VK_RETURN -1 -#endif - -#include "tier2/tier2.h" -#include "inputsystem/iinputsystem.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -vgui::KeyCode KeyCode_VirtualKeyToVGUI( int key ) -{ - // Some tools load vgui for localization and never use input - if ( !g_pInputSystem ) - return KEY_NONE; - return g_pInputSystem->VirtualKeyToButtonCode( key ); -} - -int KeyCode_VGUIToVirtualKey( vgui::KeyCode code ) -{ - // Some tools load vgui for localization and never use input - if ( !g_pInputSystem ) - return VK_RETURN; - - return g_pInputSystem->ButtonCodeToVirtualKey( code ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#if defined( WIN32 ) && !defined( _X360 ) +#include +#include +#include "xbox/xboxstubs.h" +#endif +#include "tier0/dbg.h" +#include "vgui_key_translation.h" +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif +#ifdef POSIX +#define VK_RETURN -1 +#endif + +#include "tier2/tier2.h" +#include "inputsystem/iinputsystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +vgui::KeyCode KeyCode_VirtualKeyToVGUI( int key ) +{ + // Some tools load vgui for localization and never use input + if ( !g_pInputSystem ) + return KEY_NONE; + return g_pInputSystem->VirtualKeyToButtonCode( key ); +} + +int KeyCode_VGUIToVirtualKey( vgui::KeyCode code ) +{ + // Some tools load vgui for localization and never use input + if ( !g_pInputSystem ) + return VK_RETURN; + + return g_pInputSystem->ButtonCodeToVirtualKey( code ); +} diff --git a/mp/src/vgui2/src/vgui_key_translation.h b/mp/src/vgui2/src/vgui_key_translation.h index 288acd94..3d084357 100644 --- a/mp/src/vgui2/src/vgui_key_translation.h +++ b/mp/src/vgui2/src/vgui_key_translation.h @@ -1,20 +1,20 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//===========================================================================// - -#ifndef VGUI_KEY_TRANSLATION_H -#define VGUI_KEY_TRANSLATION_H -#ifdef _WIN32 -#pragma once -#endif - -#include - -// Convert from Windows scan codes to VGUI key codes. -vgui::KeyCode KeyCode_VirtualKeyToVGUI( int key ); -int KeyCode_VGUIToVirtualKey( vgui::KeyCode keycode ); - - -#endif // VGUI_KEY_TRANSLATION_H +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#ifndef VGUI_KEY_TRANSLATION_H +#define VGUI_KEY_TRANSLATION_H +#ifdef _WIN32 +#pragma once +#endif + +#include + +// Convert from Windows scan codes to VGUI key codes. +vgui::KeyCode KeyCode_VirtualKeyToVGUI( int key ); +int KeyCode_VGUIToVirtualKey( vgui::KeyCode keycode ); + + +#endif // VGUI_KEY_TRANSLATION_H diff --git a/mp/src/vgui2/vgui_controls/AnalogBar.cpp b/mp/src/vgui2/vgui_controls/AnalogBar.cpp index 49be39d4..68e525da 100644 --- a/mp/src/vgui2/vgui_controls/AnalogBar.cpp +++ b/mp/src/vgui2/vgui_controls/AnalogBar.cpp @@ -1,460 +1,460 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY( AnalogBar ); - - -#define ANALOG_BAR_HOME_SIZE 4 -#define ANALOG_BAR_HOME_GAP 2 -#define ANALOG_BAR_LESS_TALL ( ANALOG_BAR_HOME_SIZE + ANALOG_BAR_HOME_GAP ) - - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -AnalogBar::AnalogBar(Panel *parent, const char *panelName) : Panel(parent, panelName) -{ - _analogValue = 0.0f; - m_pszDialogVar = NULL; - SetSegmentInfo( 2, 6 ); - SetBarInset( 0 ); - m_iAnalogValueDirection = PROGRESS_EAST; - - m_fHomeValue = 2.0f; - m_HomeColor = GetFgColor(); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -AnalogBar::~AnalogBar() -{ - delete [] m_pszDialogVar; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void AnalogBar::SetSegmentInfo( int gap, int width ) -{ - _segmentGap = gap; - _segmentWide = width; -} - -//----------------------------------------------------------------------------- -// Purpose: returns the number of segment blocks drawn -//----------------------------------------------------------------------------- -int AnalogBar::GetDrawnSegmentCount() -{ - int wide, tall; - GetSize(wide, tall); - int segmentTotal = wide / (_segmentGap + _segmentWide); - return (int)(segmentTotal * _analogValue); -} - -//----------------------------------------------------------------------------- -// Purpose: returns the total number of segment blocks drawn (active and inactive) -//----------------------------------------------------------------------------- -int AnalogBar::GetTotalSegmentCount() -{ - int wide, tall; - GetSize(wide, tall); - int segmentTotal = wide / (_segmentGap + _segmentWide); - return segmentTotal; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void AnalogBar::PaintBackground() -{ - // Don't draw a background -} - -void AnalogBar::PaintSegment( int &x, int &y, int tall, int wide, Color color, bool bHome ) -{ - switch( m_iAnalogValueDirection ) - { - case PROGRESS_EAST: - x += _segmentGap; - - if ( bHome ) - { - surface()->DrawSetColor( GetHomeColor() ); - surface()->DrawFilledRect(x, y, x + _segmentWide, y + ANALOG_BAR_HOME_SIZE ); - surface()->DrawFilledRect(x, y + tall - (y * 2) - ANALOG_BAR_HOME_SIZE, x + _segmentWide, y + tall - (y * 2) ); - } - - surface()->DrawSetColor( color ); - surface()->DrawFilledRect(x, y + ANALOG_BAR_LESS_TALL, x + _segmentWide, y + tall - (y * 2) - ANALOG_BAR_LESS_TALL ); - x += _segmentWide; - break; - - case PROGRESS_WEST: - x -= _segmentGap + _segmentWide; - - if ( bHome ) - { - surface()->DrawSetColor( GetHomeColor() ); - surface()->DrawFilledRect(x, y, x + _segmentWide, y + ANALOG_BAR_HOME_SIZE ); - surface()->DrawFilledRect(x, y + tall - (y * 2) - ANALOG_BAR_HOME_SIZE, x + _segmentWide, y + tall - (y * 2) ); - } - - surface()->DrawSetColor( color ); - surface()->DrawFilledRect(x, y + ANALOG_BAR_LESS_TALL, x + _segmentWide, y + tall - (y * 2) - ANALOG_BAR_LESS_TALL ); - break; - - case PROGRESS_NORTH: - y -= _segmentGap + _segmentWide; - - if ( bHome ) - { - surface()->DrawSetColor( GetHomeColor() ); - surface()->DrawFilledRect(x, y, x + ANALOG_BAR_HOME_SIZE, y + _segmentWide ); - surface()->DrawFilledRect(x + wide - (x * 2) - ANALOG_BAR_HOME_SIZE, y, x + wide - (x * 2), y + _segmentWide ); - } - - surface()->DrawSetColor( color ); - surface()->DrawFilledRect(x + ANALOG_BAR_LESS_TALL, y, x + wide - (x * 2) - ANALOG_BAR_LESS_TALL, y + _segmentWide); - break; - - case PROGRESS_SOUTH: - y += _segmentGap; - - if ( bHome ) - { - surface()->DrawSetColor( GetHomeColor() ); - surface()->DrawFilledRect(x, y, x + ANALOG_BAR_HOME_SIZE, y + _segmentWide ); - surface()->DrawFilledRect(x + wide - (x * 2) - ANALOG_BAR_HOME_SIZE, y, x + wide - (x * 2), y + _segmentWide ); - } - - surface()->DrawSetColor( color ); - surface()->DrawFilledRect(x + ANALOG_BAR_LESS_TALL, y, x + wide - (x * 2) - ANALOG_BAR_LESS_TALL, y + _segmentWide); - y += _segmentWide; - break; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void AnalogBar::Paint() -{ - int wide, tall; - GetSize(wide, tall); - - // gaps - int segmentTotal = 0, segmentsDrawn = 0; - int x = 0, y = 0; - - switch( m_iAnalogValueDirection ) - { - case PROGRESS_WEST: - x = wide; - y = m_iBarInset; - segmentTotal = wide / (_segmentGap + _segmentWide); - segmentsDrawn = (int)(segmentTotal * _analogValue + 0.5f); - break; - - case PROGRESS_EAST: - x = 0; - y = m_iBarInset; - segmentTotal = wide / (_segmentGap + _segmentWide); - segmentsDrawn = (int)(segmentTotal * _analogValue + 0.5f); - break; - - case PROGRESS_NORTH: - x = m_iBarInset; - y = tall; - segmentTotal = tall / (_segmentGap + _segmentWide); - segmentsDrawn = (int)(segmentTotal * _analogValue + 0.5f); - break; - - case PROGRESS_SOUTH: - x = m_iBarInset; - y = 0; - segmentTotal = tall / (_segmentGap + _segmentWide); - segmentsDrawn = (int)(segmentTotal * _analogValue + 0.5f); - break; - } - - int iHomeIndex = (int)( segmentTotal * m_fHomeValue + 0.5f ) - 1; - if ( iHomeIndex < 0 ) - iHomeIndex = 0; - - for (int i = 0; i < segmentsDrawn; i++) - PaintSegment( x, y, tall, wide, GetFgColor(), i == iHomeIndex ); - - for (int i = segmentsDrawn; i < segmentTotal; i++) - PaintSegment( x, y, tall, wide, GetBgColor(), i == iHomeIndex ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void AnalogBar::SetAnalogValue(float analogValue) -{ - if (analogValue != _analogValue) - { - // clamp the analogValue value within the range - if (analogValue < 0.0f) - { - analogValue = 0.0f; - } - else if (analogValue > 1.0f) - { - analogValue = 1.0f; - } - - _analogValue = analogValue; - Repaint(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -float AnalogBar::GetAnalogValue() -{ - return _analogValue; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void AnalogBar::ApplySchemeSettings(IScheme *pScheme) -{ - Panel::ApplySchemeSettings(pScheme); - - SetBgColor( Color( 255 - GetFgColor().r(), 255 - GetFgColor().g(), 255 - GetFgColor().b(), GetFgColor().a() ) ); -} - -//----------------------------------------------------------------------------- -// Purpose: utility function for calculating a time remaining string -//----------------------------------------------------------------------------- -bool AnalogBar::ConstructTimeRemainingString(wchar_t *output, int outputBufferSizeInBytes, float startTime, float currentTime, float currentAnalogValue, float lastAnalogValueUpdateTime, bool addRemainingSuffix) -{ - Assert( outputBufferSizeInBytes >= sizeof(output[0]) ); - Assert(lastAnalogValueUpdateTime <= currentTime); - output[0] = 0; - - // calculate pre-extrapolation values - float timeElapsed = lastAnalogValueUpdateTime - startTime; - float totalTime = timeElapsed / currentAnalogValue; - - // calculate seconds - int secondsRemaining = (int)(totalTime - timeElapsed); - if (lastAnalogValueUpdateTime < currentTime) - { - // old update, extrapolate - float analogValueRate = currentAnalogValue / timeElapsed; - float extrapolatedAnalogValue = analogValueRate * (currentTime - startTime); - float extrapolatedTotalTime = (currentTime - startTime) / extrapolatedAnalogValue; - secondsRemaining = (int)(extrapolatedTotalTime - timeElapsed); - } - // if there's some time, make sure it's at least one second left - if ( secondsRemaining == 0 && ( ( totalTime - timeElapsed ) > 0 ) ) - { - secondsRemaining = 1; - } - - // calculate minutes - int minutesRemaining = 0; - while (secondsRemaining >= 60) - { - minutesRemaining++; - secondsRemaining -= 60; - } - - char minutesBuf[16]; - Q_snprintf(minutesBuf, sizeof( minutesBuf ), "%d", minutesRemaining); - char secondsBuf[16]; - Q_snprintf(secondsBuf, sizeof( secondsBuf ), "%d", secondsRemaining); - - if (minutesRemaining > 0) - { - wchar_t unicodeMinutes[16]; - g_pVGuiLocalize->ConvertANSIToUnicode(minutesBuf, unicodeMinutes, sizeof( unicodeMinutes )); - wchar_t unicodeSeconds[16]; - g_pVGuiLocalize->ConvertANSIToUnicode(secondsBuf, unicodeSeconds, sizeof( unicodeSeconds )); - - const char *unlocalizedString = "#vgui_TimeLeftMinutesSeconds"; - if (minutesRemaining == 1 && secondsRemaining == 1) - { - unlocalizedString = "#vgui_TimeLeftMinuteSecond"; - } - else if (minutesRemaining == 1) - { - unlocalizedString = "#vgui_TimeLeftMinuteSeconds"; - } - else if (secondsRemaining == 1) - { - unlocalizedString = "#vgui_TimeLeftMinutesSecond"; - } - - char unlocString[64]; - Q_strncpy(unlocString, unlocalizedString,sizeof( unlocString )); - if (addRemainingSuffix) - { - Q_strncat(unlocString, "Remaining", sizeof(unlocString ), COPY_ALL_CHARACTERS); - } - g_pVGuiLocalize->ConstructString(output, outputBufferSizeInBytes, g_pVGuiLocalize->Find(unlocString), 2, unicodeMinutes, unicodeSeconds); - - } - else if (secondsRemaining > 0) - { - wchar_t unicodeSeconds[16]; - g_pVGuiLocalize->ConvertANSIToUnicode(secondsBuf, unicodeSeconds, sizeof( unicodeSeconds )); - - const char *unlocalizedString = "#vgui_TimeLeftSeconds"; - if (secondsRemaining == 1) - { - unlocalizedString = "#vgui_TimeLeftSecond"; - } - char unlocString[64]; - Q_strncpy(unlocString, unlocalizedString,sizeof(unlocString)); - if (addRemainingSuffix) - { - Q_strncat(unlocString, "Remaining",sizeof(unlocString), COPY_ALL_CHARACTERS); - } - g_pVGuiLocalize->ConstructString(output, outputBufferSizeInBytes, g_pVGuiLocalize->Find(unlocString), 1, unicodeSeconds); - } - else - { - return false; - } - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void AnalogBar::SetBarInset( int pixels ) -{ - m_iBarInset = pixels; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -int AnalogBar::GetBarInset( void ) -{ - return m_iBarInset; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void AnalogBar::ApplySettings(KeyValues *inResourceData) -{ - _analogValue = inResourceData->GetFloat("analogValue", 0.0f); - - const char *dialogVar = inResourceData->GetString("variable", ""); - if (dialogVar && *dialogVar) - { - m_pszDialogVar = new char[strlen(dialogVar) + 1]; - strcpy(m_pszDialogVar, dialogVar); - } - - BaseClass::ApplySettings(inResourceData); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void AnalogBar::GetSettings(KeyValues *outResourceData) -{ - BaseClass::GetSettings(outResourceData); - outResourceData->SetFloat("analogValue", _analogValue ); - - if (m_pszDialogVar) - { - outResourceData->SetString("variable", m_pszDialogVar); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Returns a string description of the panel fields for use in the UI -//----------------------------------------------------------------------------- -const char *AnalogBar::GetDescription( void ) -{ - static char buf[1024]; - _snprintf(buf, sizeof(buf), "%s, string analogValue, string variable", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: updates analogValue bar bases on values -//----------------------------------------------------------------------------- -void AnalogBar::OnDialogVariablesChanged(KeyValues *dialogVariables) -{ - if (m_pszDialogVar) - { - int val = dialogVariables->GetInt(m_pszDialogVar, -1); - if (val >= 0.0f) - { - SetAnalogValue(val / 100.0f); - } - } -} - - -DECLARE_BUILD_FACTORY( ContinuousAnalogBar ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -ContinuousAnalogBar::ContinuousAnalogBar(Panel *parent, const char *panelName) : AnalogBar(parent, panelName) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ContinuousAnalogBar::Paint() -{ - int x = 0, y = 0; - int wide, tall; - GetSize(wide, tall); - - surface()->DrawSetColor(GetFgColor()); - - switch( m_iAnalogValueDirection ) - { - case PROGRESS_EAST: - surface()->DrawFilledRect( x, y, x + (int)( wide * _analogValue ), y + tall ); - break; - - case PROGRESS_WEST: - surface()->DrawFilledRect( x + (int)( wide * ( 1.0f - _analogValue ) ), y, x + wide, y + tall ); - break; - - case PROGRESS_NORTH: - surface()->DrawFilledRect( x, y + (int)( tall * ( 1.0f - _analogValue ) ), x + wide, y + tall ); - break; - - case PROGRESS_SOUTH: - surface()->DrawFilledRect( x, y, x + wide, y + (int)( tall * _analogValue ) ); - break; - } +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY( AnalogBar ); + + +#define ANALOG_BAR_HOME_SIZE 4 +#define ANALOG_BAR_HOME_GAP 2 +#define ANALOG_BAR_LESS_TALL ( ANALOG_BAR_HOME_SIZE + ANALOG_BAR_HOME_GAP ) + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +AnalogBar::AnalogBar(Panel *parent, const char *panelName) : Panel(parent, panelName) +{ + _analogValue = 0.0f; + m_pszDialogVar = NULL; + SetSegmentInfo( 2, 6 ); + SetBarInset( 0 ); + m_iAnalogValueDirection = PROGRESS_EAST; + + m_fHomeValue = 2.0f; + m_HomeColor = GetFgColor(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +AnalogBar::~AnalogBar() +{ + delete [] m_pszDialogVar; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void AnalogBar::SetSegmentInfo( int gap, int width ) +{ + _segmentGap = gap; + _segmentWide = width; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the number of segment blocks drawn +//----------------------------------------------------------------------------- +int AnalogBar::GetDrawnSegmentCount() +{ + int wide, tall; + GetSize(wide, tall); + int segmentTotal = wide / (_segmentGap + _segmentWide); + return (int)(segmentTotal * _analogValue); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the total number of segment blocks drawn (active and inactive) +//----------------------------------------------------------------------------- +int AnalogBar::GetTotalSegmentCount() +{ + int wide, tall; + GetSize(wide, tall); + int segmentTotal = wide / (_segmentGap + _segmentWide); + return segmentTotal; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void AnalogBar::PaintBackground() +{ + // Don't draw a background +} + +void AnalogBar::PaintSegment( int &x, int &y, int tall, int wide, Color color, bool bHome ) +{ + switch( m_iAnalogValueDirection ) + { + case PROGRESS_EAST: + x += _segmentGap; + + if ( bHome ) + { + surface()->DrawSetColor( GetHomeColor() ); + surface()->DrawFilledRect(x, y, x + _segmentWide, y + ANALOG_BAR_HOME_SIZE ); + surface()->DrawFilledRect(x, y + tall - (y * 2) - ANALOG_BAR_HOME_SIZE, x + _segmentWide, y + tall - (y * 2) ); + } + + surface()->DrawSetColor( color ); + surface()->DrawFilledRect(x, y + ANALOG_BAR_LESS_TALL, x + _segmentWide, y + tall - (y * 2) - ANALOG_BAR_LESS_TALL ); + x += _segmentWide; + break; + + case PROGRESS_WEST: + x -= _segmentGap + _segmentWide; + + if ( bHome ) + { + surface()->DrawSetColor( GetHomeColor() ); + surface()->DrawFilledRect(x, y, x + _segmentWide, y + ANALOG_BAR_HOME_SIZE ); + surface()->DrawFilledRect(x, y + tall - (y * 2) - ANALOG_BAR_HOME_SIZE, x + _segmentWide, y + tall - (y * 2) ); + } + + surface()->DrawSetColor( color ); + surface()->DrawFilledRect(x, y + ANALOG_BAR_LESS_TALL, x + _segmentWide, y + tall - (y * 2) - ANALOG_BAR_LESS_TALL ); + break; + + case PROGRESS_NORTH: + y -= _segmentGap + _segmentWide; + + if ( bHome ) + { + surface()->DrawSetColor( GetHomeColor() ); + surface()->DrawFilledRect(x, y, x + ANALOG_BAR_HOME_SIZE, y + _segmentWide ); + surface()->DrawFilledRect(x + wide - (x * 2) - ANALOG_BAR_HOME_SIZE, y, x + wide - (x * 2), y + _segmentWide ); + } + + surface()->DrawSetColor( color ); + surface()->DrawFilledRect(x + ANALOG_BAR_LESS_TALL, y, x + wide - (x * 2) - ANALOG_BAR_LESS_TALL, y + _segmentWide); + break; + + case PROGRESS_SOUTH: + y += _segmentGap; + + if ( bHome ) + { + surface()->DrawSetColor( GetHomeColor() ); + surface()->DrawFilledRect(x, y, x + ANALOG_BAR_HOME_SIZE, y + _segmentWide ); + surface()->DrawFilledRect(x + wide - (x * 2) - ANALOG_BAR_HOME_SIZE, y, x + wide - (x * 2), y + _segmentWide ); + } + + surface()->DrawSetColor( color ); + surface()->DrawFilledRect(x + ANALOG_BAR_LESS_TALL, y, x + wide - (x * 2) - ANALOG_BAR_LESS_TALL, y + _segmentWide); + y += _segmentWide; + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void AnalogBar::Paint() +{ + int wide, tall; + GetSize(wide, tall); + + // gaps + int segmentTotal = 0, segmentsDrawn = 0; + int x = 0, y = 0; + + switch( m_iAnalogValueDirection ) + { + case PROGRESS_WEST: + x = wide; + y = m_iBarInset; + segmentTotal = wide / (_segmentGap + _segmentWide); + segmentsDrawn = (int)(segmentTotal * _analogValue + 0.5f); + break; + + case PROGRESS_EAST: + x = 0; + y = m_iBarInset; + segmentTotal = wide / (_segmentGap + _segmentWide); + segmentsDrawn = (int)(segmentTotal * _analogValue + 0.5f); + break; + + case PROGRESS_NORTH: + x = m_iBarInset; + y = tall; + segmentTotal = tall / (_segmentGap + _segmentWide); + segmentsDrawn = (int)(segmentTotal * _analogValue + 0.5f); + break; + + case PROGRESS_SOUTH: + x = m_iBarInset; + y = 0; + segmentTotal = tall / (_segmentGap + _segmentWide); + segmentsDrawn = (int)(segmentTotal * _analogValue + 0.5f); + break; + } + + int iHomeIndex = (int)( segmentTotal * m_fHomeValue + 0.5f ) - 1; + if ( iHomeIndex < 0 ) + iHomeIndex = 0; + + for (int i = 0; i < segmentsDrawn; i++) + PaintSegment( x, y, tall, wide, GetFgColor(), i == iHomeIndex ); + + for (int i = segmentsDrawn; i < segmentTotal; i++) + PaintSegment( x, y, tall, wide, GetBgColor(), i == iHomeIndex ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void AnalogBar::SetAnalogValue(float analogValue) +{ + if (analogValue != _analogValue) + { + // clamp the analogValue value within the range + if (analogValue < 0.0f) + { + analogValue = 0.0f; + } + else if (analogValue > 1.0f) + { + analogValue = 1.0f; + } + + _analogValue = analogValue; + Repaint(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +float AnalogBar::GetAnalogValue() +{ + return _analogValue; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void AnalogBar::ApplySchemeSettings(IScheme *pScheme) +{ + Panel::ApplySchemeSettings(pScheme); + + SetBgColor( Color( 255 - GetFgColor().r(), 255 - GetFgColor().g(), 255 - GetFgColor().b(), GetFgColor().a() ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: utility function for calculating a time remaining string +//----------------------------------------------------------------------------- +bool AnalogBar::ConstructTimeRemainingString(wchar_t *output, int outputBufferSizeInBytes, float startTime, float currentTime, float currentAnalogValue, float lastAnalogValueUpdateTime, bool addRemainingSuffix) +{ + Assert( outputBufferSizeInBytes >= sizeof(output[0]) ); + Assert(lastAnalogValueUpdateTime <= currentTime); + output[0] = 0; + + // calculate pre-extrapolation values + float timeElapsed = lastAnalogValueUpdateTime - startTime; + float totalTime = timeElapsed / currentAnalogValue; + + // calculate seconds + int secondsRemaining = (int)(totalTime - timeElapsed); + if (lastAnalogValueUpdateTime < currentTime) + { + // old update, extrapolate + float analogValueRate = currentAnalogValue / timeElapsed; + float extrapolatedAnalogValue = analogValueRate * (currentTime - startTime); + float extrapolatedTotalTime = (currentTime - startTime) / extrapolatedAnalogValue; + secondsRemaining = (int)(extrapolatedTotalTime - timeElapsed); + } + // if there's some time, make sure it's at least one second left + if ( secondsRemaining == 0 && ( ( totalTime - timeElapsed ) > 0 ) ) + { + secondsRemaining = 1; + } + + // calculate minutes + int minutesRemaining = 0; + while (secondsRemaining >= 60) + { + minutesRemaining++; + secondsRemaining -= 60; + } + + char minutesBuf[16]; + Q_snprintf(minutesBuf, sizeof( minutesBuf ), "%d", minutesRemaining); + char secondsBuf[16]; + Q_snprintf(secondsBuf, sizeof( secondsBuf ), "%d", secondsRemaining); + + if (minutesRemaining > 0) + { + wchar_t unicodeMinutes[16]; + g_pVGuiLocalize->ConvertANSIToUnicode(minutesBuf, unicodeMinutes, sizeof( unicodeMinutes )); + wchar_t unicodeSeconds[16]; + g_pVGuiLocalize->ConvertANSIToUnicode(secondsBuf, unicodeSeconds, sizeof( unicodeSeconds )); + + const char *unlocalizedString = "#vgui_TimeLeftMinutesSeconds"; + if (minutesRemaining == 1 && secondsRemaining == 1) + { + unlocalizedString = "#vgui_TimeLeftMinuteSecond"; + } + else if (minutesRemaining == 1) + { + unlocalizedString = "#vgui_TimeLeftMinuteSeconds"; + } + else if (secondsRemaining == 1) + { + unlocalizedString = "#vgui_TimeLeftMinutesSecond"; + } + + char unlocString[64]; + Q_strncpy(unlocString, unlocalizedString,sizeof( unlocString )); + if (addRemainingSuffix) + { + Q_strncat(unlocString, "Remaining", sizeof(unlocString ), COPY_ALL_CHARACTERS); + } + g_pVGuiLocalize->ConstructString(output, outputBufferSizeInBytes, g_pVGuiLocalize->Find(unlocString), 2, unicodeMinutes, unicodeSeconds); + + } + else if (secondsRemaining > 0) + { + wchar_t unicodeSeconds[16]; + g_pVGuiLocalize->ConvertANSIToUnicode(secondsBuf, unicodeSeconds, sizeof( unicodeSeconds )); + + const char *unlocalizedString = "#vgui_TimeLeftSeconds"; + if (secondsRemaining == 1) + { + unlocalizedString = "#vgui_TimeLeftSecond"; + } + char unlocString[64]; + Q_strncpy(unlocString, unlocalizedString,sizeof(unlocString)); + if (addRemainingSuffix) + { + Q_strncat(unlocString, "Remaining",sizeof(unlocString), COPY_ALL_CHARACTERS); + } + g_pVGuiLocalize->ConstructString(output, outputBufferSizeInBytes, g_pVGuiLocalize->Find(unlocString), 1, unicodeSeconds); + } + else + { + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void AnalogBar::SetBarInset( int pixels ) +{ + m_iBarInset = pixels; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +int AnalogBar::GetBarInset( void ) +{ + return m_iBarInset; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void AnalogBar::ApplySettings(KeyValues *inResourceData) +{ + _analogValue = inResourceData->GetFloat("analogValue", 0.0f); + + const char *dialogVar = inResourceData->GetString("variable", ""); + if (dialogVar && *dialogVar) + { + m_pszDialogVar = new char[strlen(dialogVar) + 1]; + strcpy(m_pszDialogVar, dialogVar); + } + + BaseClass::ApplySettings(inResourceData); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void AnalogBar::GetSettings(KeyValues *outResourceData) +{ + BaseClass::GetSettings(outResourceData); + outResourceData->SetFloat("analogValue", _analogValue ); + + if (m_pszDialogVar) + { + outResourceData->SetString("variable", m_pszDialogVar); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a string description of the panel fields for use in the UI +//----------------------------------------------------------------------------- +const char *AnalogBar::GetDescription( void ) +{ + static char buf[1024]; + _snprintf(buf, sizeof(buf), "%s, string analogValue, string variable", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: updates analogValue bar bases on values +//----------------------------------------------------------------------------- +void AnalogBar::OnDialogVariablesChanged(KeyValues *dialogVariables) +{ + if (m_pszDialogVar) + { + int val = dialogVariables->GetInt(m_pszDialogVar, -1); + if (val >= 0.0f) + { + SetAnalogValue(val / 100.0f); + } + } +} + + +DECLARE_BUILD_FACTORY( ContinuousAnalogBar ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ContinuousAnalogBar::ContinuousAnalogBar(Panel *parent, const char *panelName) : AnalogBar(parent, panelName) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ContinuousAnalogBar::Paint() +{ + int x = 0, y = 0; + int wide, tall; + GetSize(wide, tall); + + surface()->DrawSetColor(GetFgColor()); + + switch( m_iAnalogValueDirection ) + { + case PROGRESS_EAST: + surface()->DrawFilledRect( x, y, x + (int)( wide * _analogValue ), y + tall ); + break; + + case PROGRESS_WEST: + surface()->DrawFilledRect( x + (int)( wide * ( 1.0f - _analogValue ) ), y, x + wide, y + tall ); + break; + + case PROGRESS_NORTH: + surface()->DrawFilledRect( x, y + (int)( tall * ( 1.0f - _analogValue ) ), x + wide, y + tall ); + break; + + case PROGRESS_SOUTH: + surface()->DrawFilledRect( x, y, x + wide, y + (int)( tall * _analogValue ) ); + break; + } } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/AnimatingImagePanel.cpp b/mp/src/vgui2/vgui_controls/AnimatingImagePanel.cpp index 564219a5..65c4798b 100644 --- a/mp/src/vgui2/vgui_controls/AnimatingImagePanel.cpp +++ b/mp/src/vgui2/vgui_controls/AnimatingImagePanel.cpp @@ -1,216 +1,216 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#define PROTECTED_THINGS_DISABLE - -#include -#include -#include -#include -#include -#include - -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY( AnimatingImagePanel ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -AnimatingImagePanel::AnimatingImagePanel(Panel *parent, const char *name) : Panel(parent, name) -{ - m_iCurrentImage = 0; - m_iFrameTimeMillis = 100; // 10Hz frame rate - m_iNextFrameTime = 0; - m_pImageName = NULL; - m_bFiltered = false; - m_bScaleImage = false; - m_bAnimating = false; - ivgui()->AddTickSignal(GetVPanel()); -} - -//----------------------------------------------------------------------------- -// Purpose: Layout the panel for drawing. -//----------------------------------------------------------------------------- -void AnimatingImagePanel::PerformLayout() -{ - Panel::PerformLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Add an image to the end of the list of animations -//----------------------------------------------------------------------------- -void AnimatingImagePanel::AddImage(IImage *image) -{ - m_Frames.AddToTail(image); - - if ( !m_bScaleImage && image != NULL ) - { - int wide,tall; - image->GetSize(wide,tall); - SetSize(wide,tall); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Load a set of animations by name. -// Input: -// baseName: is the name of the animations without their frame number or -// file extension, (e.g. c1.tga becomes just c.) -// framecount: number of frames in the animation -//----------------------------------------------------------------------------- -void AnimatingImagePanel::LoadAnimation(const char *baseName, int frameCount) -{ - m_Frames.RemoveAll(); - for (int i = 1; i <= frameCount; i++) - { - char imageName[512]; - Q_snprintf(imageName, sizeof( imageName ), "%s%d", baseName, i); - AddImage(scheme()->GetImage(imageName, m_bFiltered)); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Draw the current image -//----------------------------------------------------------------------------- -void AnimatingImagePanel::PaintBackground() -{ - if ( m_Frames.IsValidIndex( m_iCurrentImage ) && m_Frames[m_iCurrentImage] != NULL ) - { - IImage *pImage = m_Frames[m_iCurrentImage]; - - surface()->DrawSetColor( 255, 255, 255, 255 ); - pImage->SetPos(0, 0); - - if ( m_bScaleImage ) - { - // Image size is stored in the bitmap, so temporarily set its size - // to our panel size and then restore after we draw it. - - int imageWide, imageTall; - pImage->GetSize( imageWide, imageTall ); - - int wide, tall; - GetSize( wide, tall ); - pImage->SetSize( wide, tall ); - - pImage->SetColor( Color( 255,255,255,255 ) ); - pImage->Paint(); - - pImage->SetSize( imageWide, imageTall ); - } - else - { - pImage->Paint(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Called every frame the panel is visible -//----------------------------------------------------------------------------- -void AnimatingImagePanel::OnTick() -{ - if (m_bAnimating && system()->GetTimeMillis() >= m_iNextFrameTime) - { - m_iNextFrameTime = system()->GetTimeMillis() + m_iFrameTimeMillis; - m_iCurrentImage++; - if (!m_Frames.IsValidIndex(m_iCurrentImage)) - { - m_iCurrentImage = 0; - } - Repaint(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Get control settings for editing -// Output: outResourceData- a set of keyvalues of imagenames. -//----------------------------------------------------------------------------- -void AnimatingImagePanel::GetSettings(KeyValues *outResourceData) -{ - BaseClass::GetSettings(outResourceData); - if (m_pImageName) - { - outResourceData->SetString("image", m_pImageName); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Applies resource settings -//----------------------------------------------------------------------------- -void AnimatingImagePanel::ApplySettings(KeyValues *inResourceData) -{ - BaseClass::ApplySettings(inResourceData); - - const char *imageName = inResourceData->GetString("image", NULL); - if (imageName) - { - m_bScaleImage = ( inResourceData->GetInt( "scaleImage", 0 ) == 1 ); - - delete [] m_pImageName; - int len = Q_strlen(imageName) + 1; - m_pImageName = new char[len]; - Q_strncpy(m_pImageName, imageName, len); - - // add in the command - LoadAnimation(m_pImageName, inResourceData->GetInt("frames")); - } - - m_iFrameTimeMillis = inResourceData->GetInt( "anim_framerate", 100 ); -} - -//----------------------------------------------------------------------------- -// Purpose: Get editing details -//----------------------------------------------------------------------------- -const char *AnimatingImagePanel::GetDescription() -{ - static char buf[1024]; - Q_snprintf(buf, sizeof(buf), "%s, string image", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: Starts the image doing its animation -//----------------------------------------------------------------------------- -void AnimatingImagePanel::StartAnimation() -{ - m_bAnimating = true; -// ivgui()->AddTickSignal(GetVPanel()); -} - -//----------------------------------------------------------------------------- -// Purpose: Stops the images animation -//----------------------------------------------------------------------------- -void AnimatingImagePanel::StopAnimation() -{ - m_bAnimating = false; -// ivgui()->RemoveTickSignal(GetVPanel()); -} - -//----------------------------------------------------------------------------- -// Purpose: Resets the animation to the start of the sequence. -//----------------------------------------------------------------------------- -void AnimatingImagePanel::ResetAnimation(int frame) -{ - if(m_Frames.IsValidIndex(frame)) - { - m_iCurrentImage = frame; - } - else - { - m_iCurrentImage = 0; - } - Repaint(); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#define PROTECTED_THINGS_DISABLE + +#include +#include +#include +#include +#include +#include + +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY( AnimatingImagePanel ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +AnimatingImagePanel::AnimatingImagePanel(Panel *parent, const char *name) : Panel(parent, name) +{ + m_iCurrentImage = 0; + m_iFrameTimeMillis = 100; // 10Hz frame rate + m_iNextFrameTime = 0; + m_pImageName = NULL; + m_bFiltered = false; + m_bScaleImage = false; + m_bAnimating = false; + ivgui()->AddTickSignal(GetVPanel()); +} + +//----------------------------------------------------------------------------- +// Purpose: Layout the panel for drawing. +//----------------------------------------------------------------------------- +void AnimatingImagePanel::PerformLayout() +{ + Panel::PerformLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Add an image to the end of the list of animations +//----------------------------------------------------------------------------- +void AnimatingImagePanel::AddImage(IImage *image) +{ + m_Frames.AddToTail(image); + + if ( !m_bScaleImage && image != NULL ) + { + int wide,tall; + image->GetSize(wide,tall); + SetSize(wide,tall); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Load a set of animations by name. +// Input: +// baseName: is the name of the animations without their frame number or +// file extension, (e.g. c1.tga becomes just c.) +// framecount: number of frames in the animation +//----------------------------------------------------------------------------- +void AnimatingImagePanel::LoadAnimation(const char *baseName, int frameCount) +{ + m_Frames.RemoveAll(); + for (int i = 1; i <= frameCount; i++) + { + char imageName[512]; + Q_snprintf(imageName, sizeof( imageName ), "%s%d", baseName, i); + AddImage(scheme()->GetImage(imageName, m_bFiltered)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draw the current image +//----------------------------------------------------------------------------- +void AnimatingImagePanel::PaintBackground() +{ + if ( m_Frames.IsValidIndex( m_iCurrentImage ) && m_Frames[m_iCurrentImage] != NULL ) + { + IImage *pImage = m_Frames[m_iCurrentImage]; + + surface()->DrawSetColor( 255, 255, 255, 255 ); + pImage->SetPos(0, 0); + + if ( m_bScaleImage ) + { + // Image size is stored in the bitmap, so temporarily set its size + // to our panel size and then restore after we draw it. + + int imageWide, imageTall; + pImage->GetSize( imageWide, imageTall ); + + int wide, tall; + GetSize( wide, tall ); + pImage->SetSize( wide, tall ); + + pImage->SetColor( Color( 255,255,255,255 ) ); + pImage->Paint(); + + pImage->SetSize( imageWide, imageTall ); + } + else + { + pImage->Paint(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called every frame the panel is visible +//----------------------------------------------------------------------------- +void AnimatingImagePanel::OnTick() +{ + if (m_bAnimating && system()->GetTimeMillis() >= m_iNextFrameTime) + { + m_iNextFrameTime = system()->GetTimeMillis() + m_iFrameTimeMillis; + m_iCurrentImage++; + if (!m_Frames.IsValidIndex(m_iCurrentImage)) + { + m_iCurrentImage = 0; + } + Repaint(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get control settings for editing +// Output: outResourceData- a set of keyvalues of imagenames. +//----------------------------------------------------------------------------- +void AnimatingImagePanel::GetSettings(KeyValues *outResourceData) +{ + BaseClass::GetSettings(outResourceData); + if (m_pImageName) + { + outResourceData->SetString("image", m_pImageName); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Applies resource settings +//----------------------------------------------------------------------------- +void AnimatingImagePanel::ApplySettings(KeyValues *inResourceData) +{ + BaseClass::ApplySettings(inResourceData); + + const char *imageName = inResourceData->GetString("image", NULL); + if (imageName) + { + m_bScaleImage = ( inResourceData->GetInt( "scaleImage", 0 ) == 1 ); + + delete [] m_pImageName; + int len = Q_strlen(imageName) + 1; + m_pImageName = new char[len]; + Q_strncpy(m_pImageName, imageName, len); + + // add in the command + LoadAnimation(m_pImageName, inResourceData->GetInt("frames")); + } + + m_iFrameTimeMillis = inResourceData->GetInt( "anim_framerate", 100 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Get editing details +//----------------------------------------------------------------------------- +const char *AnimatingImagePanel::GetDescription() +{ + static char buf[1024]; + Q_snprintf(buf, sizeof(buf), "%s, string image", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: Starts the image doing its animation +//----------------------------------------------------------------------------- +void AnimatingImagePanel::StartAnimation() +{ + m_bAnimating = true; +// ivgui()->AddTickSignal(GetVPanel()); +} + +//----------------------------------------------------------------------------- +// Purpose: Stops the images animation +//----------------------------------------------------------------------------- +void AnimatingImagePanel::StopAnimation() +{ + m_bAnimating = false; +// ivgui()->RemoveTickSignal(GetVPanel()); +} + +//----------------------------------------------------------------------------- +// Purpose: Resets the animation to the start of the sequence. +//----------------------------------------------------------------------------- +void AnimatingImagePanel::ResetAnimation(int frame) +{ + if(m_Frames.IsValidIndex(frame)) + { + m_iCurrentImage = frame; + } + else + { + m_iCurrentImage = 0; + } + Repaint(); +} diff --git a/mp/src/vgui2/vgui_controls/AnimationController.cpp b/mp/src/vgui2/vgui_controls/AnimationController.cpp index a618231c..76a35250 100644 --- a/mp/src/vgui2/vgui_controls/AnimationController.cpp +++ b/mp/src/vgui2/vgui_controls/AnimationController.cpp @@ -1,1651 +1,1651 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// -#pragma warning( disable : 4244 ) // conversion from 'double' to 'float', possible loss of data - -#include -#include -#include -#include -#include -#include -#include "filesystem.h" -#include "filesystem_helpers.h" - -#include -#include -#include "mempool.h" -#include "utldict.h" -#include "mathlib/mathlib.h" -#include "characterset.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include -// for SRC -#include -#include - -using namespace vgui; - -static CUtlSymbolTable g_ScriptSymbols(0, 128, true); - -// singleton accessor for animation controller for use by the vgui controls -namespace vgui -{ -AnimationController *GetAnimationController() -{ - static AnimationController *s_pAnimationController = new AnimationController(NULL); - return s_pAnimationController; -} -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -AnimationController::AnimationController(Panel *parent) : BaseClass(parent, NULL) -{ - m_hSizePanel = 0; - m_nScreenBounds[ 0 ] = m_nScreenBounds[ 1 ] = -1; - m_nScreenBounds[ 2 ] = m_nScreenBounds[ 3 ] = -1; - - m_bAutoReloadScript = false; - - // always invisible - SetVisible(false); - - SetProportional(true); - - // get the names of common types - m_sPosition = g_ScriptSymbols.AddString("position"); - m_sSize = g_ScriptSymbols.AddString("size"); - m_sFgColor = g_ScriptSymbols.AddString("fgcolor"); - m_sBgColor = g_ScriptSymbols.AddString("bgcolor"); - - m_sXPos = g_ScriptSymbols.AddString("xpos"); - m_sYPos = g_ScriptSymbols.AddString("ypos"); - m_sWide = g_ScriptSymbols.AddString("wide"); - m_sTall = g_ScriptSymbols.AddString("tall"); - - m_flCurrentTime = 0.0f; -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -AnimationController::~AnimationController() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Sets which script file to use -//----------------------------------------------------------------------------- -bool AnimationController::SetScriptFile( VPANEL sizingPanel, const char *fileName, bool wipeAll /*=false*/ ) -{ - m_hSizePanel = sizingPanel; - - if ( wipeAll ) - { - // clear the current script - m_Sequences.RemoveAll(); - m_ScriptFileNames.RemoveAll(); - - CancelAllAnimations(); - } - - // Store off this filename for reloading later on (if we don't have it already) - UtlSymId_t sFilename = g_ScriptSymbols.AddString( fileName ); - if ( m_ScriptFileNames.Find( sFilename ) == m_ScriptFileNames.InvalidIndex() ) - { - m_ScriptFileNames.AddToTail( sFilename ); - } - - UpdateScreenSize(); - - // load the new script file - return LoadScriptFile( fileName ); -} - -//----------------------------------------------------------------------------- -// Purpose: reloads the currently set script file -//----------------------------------------------------------------------------- -void AnimationController::ReloadScriptFile() -{ - // Clear all current sequences - m_Sequences.RemoveAll(); - - UpdateScreenSize(); - - // Reload each file we've loaded - for ( int i = 0; i < m_ScriptFileNames.Count(); i++ ) - { - const char *lpszFilename = g_ScriptSymbols.String( m_ScriptFileNames[i] ); - if ( strlen( lpszFilename ) > 0) - { - if ( LoadScriptFile( lpszFilename ) == false ) - { - Assert( 0 ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: loads a script file from disk -//----------------------------------------------------------------------------- -bool AnimationController::LoadScriptFile(const char *fileName) -{ - FileHandle_t f = g_pFullFileSystem->Open(fileName, "rt"); - if (!f) - { - Warning("Couldn't find script file %s\n", fileName); - return false; - } - - // read the whole thing into memory - int size = g_pFullFileSystem->Size(f); - // read into temporary memory block - int nBufSize = size+1; - if ( IsXbox() ) - { - nBufSize = AlignValue( nBufSize, 512 ); - } - char *pMem = (char *)malloc(nBufSize); - int bytesRead = g_pFullFileSystem->ReadEx(pMem, nBufSize, size, f); - Assert(bytesRead <= size); - pMem[bytesRead] = 0; - g_pFullFileSystem->Close(f); - // parse - bool success = ParseScriptFile(pMem, bytesRead); - free(pMem); - return success; -} - -AnimationController::RelativeAlignmentLookup AnimationController::g_AlignmentLookup[] = -{ - { AnimationController::a_northwest , "northwest" }, - { AnimationController::a_north , "north" }, - { AnimationController::a_northeast , "northeast" }, - { AnimationController::a_west , "west" }, - { AnimationController::a_center , "center" }, - { AnimationController::a_east , "east" }, - { AnimationController::a_southwest , "southwest" }, - { AnimationController::a_south , "south" }, - { AnimationController::a_southeast , "southeast" }, - - { AnimationController::a_northwest , "nw" }, - { AnimationController::a_north , "n" }, - { AnimationController::a_northeast , "ne" }, - { AnimationController::a_west , "w" }, - { AnimationController::a_center , "c" }, - { AnimationController::a_east , "e" }, - { AnimationController::a_southwest , "sw" }, - { AnimationController::a_south , "s" }, - { AnimationController::a_southeast , "se" }, -}; - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -AnimationController::RelativeAlignment AnimationController::LookupAlignment( char const *token ) -{ - int c = ARRAYSIZE( g_AlignmentLookup ); - - for ( int i = 0; i < c; i++ ) - { - if ( !Q_stricmp( token, g_AlignmentLookup[ i ].name ) ) - { - return g_AlignmentLookup[ i ].align; - } - } - - return AnimationController::a_northwest; -} - -//----------------------------------------------------------------------------- -// Purpose: Parse position including right edge and center adjustment out of a -// token. This is relative to the screen -//----------------------------------------------------------------------------- -void AnimationController::SetupPosition( AnimCmdAnimate_t& cmd, float *output, char const *psz, int screendimension ) -{ - bool r = false, c = false; - int pos; - if ( psz[0] == '(' ) - { - psz++; - - if ( Q_strstr( psz, ")" ) ) - { - char sz[ 256 ]; - Q_strncpy( sz, psz, sizeof( sz ) ); - - char *colon = Q_strstr( sz, ":" ); - if ( colon ) - { - *colon = 0; - - RelativeAlignment ra = LookupAlignment( sz ); - - colon++; - - char *panelName = colon; - char *panelEnd = Q_strstr( panelName, ")" ); - if ( panelEnd ) - { - *panelEnd = 0; - - if ( Q_strlen( panelName ) > 0 ) - { - // - cmd.align.relativePosition = true; - cmd.align.alignPanel = g_ScriptSymbols.AddString(panelName); - cmd.align.alignment = ra; - } - } - } - - psz = Q_strstr( psz, ")" ) + 1; - } - } - else if (psz[0] == 'r' || psz[0] == 'R') - { - r = true; - psz++; - } - else if (psz[0] == 'c' || psz[0] == 'C') - { - c = true; - psz++; - } - - // get the number - pos = atoi(psz); - - // scale the values - if (IsProportional()) - { - pos = vgui::scheme()->GetProportionalScaledValueEx( GetScheme(), pos ); - } - - // adjust the positions - if (r) - { - pos = screendimension - pos; - } - if (c) - { - pos = (screendimension / 2) + pos; - } - - // set the value - *output = static_cast( pos ); -} - - -//----------------------------------------------------------------------------- -// Purpose: parses a script into sequences -//----------------------------------------------------------------------------- -bool AnimationController::ParseScriptFile(char *pMem, int length) -{ - // get the scheme (for looking up color names) - IScheme *scheme = vgui::scheme()->GetIScheme(GetScheme()); - - // get our screen size (for left/right/center alignment) - int screenWide = m_nScreenBounds[ 2 ]; - int screenTall = m_nScreenBounds[ 3 ]; - - // start by getting the first token - char token[512]; - pMem = ParseFile(pMem, token, NULL); - while (token[0]) - { - bool bAccepted = true; - - // should be 'event' - if (stricmp(token, "event")) - { - Warning("Couldn't parse script file: expected 'event', found '%s'\n", token); - return false; - } - - // get the event name - pMem = ParseFile(pMem, token, NULL); - if (strlen(token) < 1) - { - Warning("Couldn't parse script file: expected , found nothing\n"); - return false; - } - - int seqIndex; - UtlSymId_t nameIndex = g_ScriptSymbols.AddString(token); - - // Create a new sequence - seqIndex = m_Sequences.AddToTail(); - AnimSequence_t &seq = m_Sequences[seqIndex]; - seq.name = nameIndex; - seq.duration = 0.0f; - - // get the open brace or a conditional - pMem = ParseFile(pMem, token, NULL); - if ( Q_stristr( token, "[$" ) ) - { - bAccepted = EvaluateConditional( token ); - - // now get the open brace - pMem = ParseFile(pMem, token, NULL); - } - - if (stricmp(token, "{")) - { - Warning("Couldn't parse script sequence '%s': expected '{', found '%s'\n", g_ScriptSymbols.String(seq.name), token); - return false; - } - - // walk the commands - while (token && token[0]) - { - // get the command type - pMem = ParseFile(pMem, token, NULL); - - // skip out when we hit the end of the sequence - if (token[0] == '}') - break; - - // create a new command - int cmdIndex = seq.cmdList.AddToTail(); - AnimCommand_t &animCmd = seq.cmdList[cmdIndex]; - memset(&animCmd, 0, sizeof(animCmd)); - if (!stricmp(token, "animate")) - { - animCmd.commandType = CMD_ANIMATE; - // parse out the animation commands - AnimCmdAnimate_t &cmdAnimate = animCmd.cmdData.animate; - // panel to manipulate - pMem = ParseFile(pMem, token, NULL); - cmdAnimate.panel = g_ScriptSymbols.AddString(token); - // variable to change - pMem = ParseFile(pMem, token, NULL); - cmdAnimate.variable = g_ScriptSymbols.AddString(token); - // target value - pMem = ParseFile(pMem, token, NULL); - if (cmdAnimate.variable == m_sPosition) - { - // Get first token - SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenWide ); - - // Get second token from "token" - char token2[32]; - char *psz = ParseFile(token, token2, NULL); - psz = ParseFile(psz, token2, NULL); - psz = token2; - - // Position Y goes into ".b" - SetupPosition( cmdAnimate, &cmdAnimate.target.b, psz, screenTall ); - } - else if ( cmdAnimate.variable == m_sXPos ) - { - // XPos and YPos both use target ".a" - SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenWide ); - } - else if ( cmdAnimate.variable == m_sYPos ) - { - // XPos and YPos both use target ".a" - SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenTall ); - } - else - { - // parse the floating point values right out - if (0 == sscanf(token, "%f %f %f %f", &cmdAnimate.target.a, &cmdAnimate.target.b, &cmdAnimate.target.c, &cmdAnimate.target.d)) - { - //============================================================================= - // HPE_BEGIN: - // [pfreese] Improved handling colors not defined in scheme - //============================================================================= - - // could be referencing a value in the scheme file, lookup - Color default_invisible_black(0, 0, 0, 0); - Color col = scheme->GetColor(token, default_invisible_black); - - // we don't have a way of seeing if the color is not declared in the scheme, so we use this - // silly method of trying again with a different default to see if we get the fallback again - if (col == default_invisible_black) - { - Color error_pink(255, 0, 255, 255); // make it extremely obvious if a scheme lookup fails - col = scheme->GetColor(token, error_pink); - - // commented out for Soldier/Demo release...(getting spammed in console) - // we'll try to figure this out after the update is out -// if (col == error_pink) -// { -// Warning("Missing color in scheme: %s\n", token); -// } - } - - //============================================================================= - // HPE_END - //============================================================================= - - cmdAnimate.target.a = col[0]; - cmdAnimate.target.b = col[1]; - cmdAnimate.target.c = col[2]; - cmdAnimate.target.d = col[3]; - } - } - - // fix up scale - if (cmdAnimate.variable == m_sSize) - { - if (IsProportional()) - { - cmdAnimate.target.a = static_cast( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.a) ); - cmdAnimate.target.b = static_cast( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.b) ); - } - } - else if (cmdAnimate.variable == m_sWide || - cmdAnimate.variable == m_sTall ) - { - if (IsProportional()) - { - // Wide and tall both use.a - cmdAnimate.target.a = static_cast( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.a) ); - } - } - - // interpolation function - pMem = ParseFile(pMem, token, NULL); - if (!stricmp(token, "Accel")) - { - cmdAnimate.interpolationFunction = INTERPOLATOR_ACCEL; - } - else if (!stricmp(token, "Deaccel")) - { - cmdAnimate.interpolationFunction = INTERPOLATOR_DEACCEL; - } - else if ( !stricmp(token, "Spline")) - { - cmdAnimate.interpolationFunction = INTERPOLATOR_SIMPLESPLINE; - } - else if (!stricmp(token,"Pulse")) - { - cmdAnimate.interpolationFunction = INTERPOLATOR_PULSE; - // frequencey - pMem = ParseFile(pMem, token, NULL); - cmdAnimate.interpolationParameter = (float)atof(token); - } - else if ( !stricmp( token, "Flicker")) - { - cmdAnimate.interpolationFunction = INTERPOLATOR_FLICKER; - // noiseamount - pMem = ParseFile(pMem, token, NULL); - cmdAnimate.interpolationParameter = (float)atof(token); - } - else if (!stricmp(token, "Bounce")) - { - cmdAnimate.interpolationFunction = INTERPOLATOR_BOUNCE; - } - else - { - cmdAnimate.interpolationFunction = INTERPOLATOR_LINEAR; - } - // start time - pMem = ParseFile(pMem, token, NULL); - cmdAnimate.startTime = (float)atof(token); - // duration - pMem = ParseFile(pMem, token, NULL); - cmdAnimate.duration = (float)atof(token); - // check max duration - if (cmdAnimate.startTime + cmdAnimate.duration > seq.duration) - { - seq.duration = cmdAnimate.startTime + cmdAnimate.duration; - } - } - else if (!stricmp(token, "runevent")) - { - animCmd.commandType = CMD_RUNEVENT; - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.timeDelay = (float)atof(token); - } - else if (!stricmp(token, "stopevent")) - { - animCmd.commandType = CMD_STOPEVENT; - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.timeDelay = (float)atof(token); - } - else if (!stricmp(token, "StopPanelAnimations")) - { - animCmd.commandType = CMD_STOPPANELANIMATIONS; - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.timeDelay = (float)atof(token); - } - else if (!stricmp(token, "stopanimation")) - { - animCmd.commandType = CMD_STOPANIMATION; - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token); - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.timeDelay = (float)atof(token); - } - else if ( !stricmp( token, "SetFont" )) - { - animCmd.commandType = CMD_SETFONT; - // Panel name - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); - // Font parameter - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token); - // Font name from scheme - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.variable2 = g_ScriptSymbols.AddString(token); - - // Set time - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.timeDelay = (float)atof(token); - } - else if ( !stricmp( token, "SetTexture" )) - { - animCmd.commandType = CMD_SETTEXTURE; - // Panel name - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); - // Texture Id - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token); - // material name - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.variable2 = g_ScriptSymbols.AddString(token); - - // Set time - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.timeDelay = (float)atof(token); - } - else if ( !stricmp( token, "SetString" )) - { - animCmd.commandType = CMD_SETSTRING; - // Panel name - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); - // String variable name - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token); - // String value to set - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.variable2 = g_ScriptSymbols.AddString(token); - - // Set time - pMem = ParseFile(pMem, token, NULL); - animCmd.cmdData.runEvent.timeDelay = (float)atof(token); - } - else - { - Warning("Couldn't parse script sequence '%s': expected , found '%s'\n", g_ScriptSymbols.String(seq.name), token); - return false; - } - - // Look ahead one token for a conditional - char *peek = ParseFile(pMem, token, NULL); - if ( Q_stristr( token, "[$" ) ) - { - if ( !EvaluateConditional( token ) ) - { - seq.cmdList.Remove( cmdIndex ); - } - pMem = peek; - } - } - - if ( bAccepted ) - { - // Attempt to find a collision in the sequences, replacing the old one if found - int seqIterator; - for ( seqIterator = 0; seqIterator < m_Sequences.Count()-1; seqIterator++ ) - { - if ( m_Sequences[seqIterator].name == nameIndex ) - { - // Get rid of it, we're overriding it - m_Sequences.Remove( seqIndex ); - break; - } - } - } - else - { - // Dump the entire sequence - m_Sequences.Remove( seqIndex ); - } - - // get the next token, if any - pMem = ParseFile(pMem, token, NULL); - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: checks all posted animation events, firing if time -//----------------------------------------------------------------------------- -void AnimationController::UpdatePostedMessages(bool bRunToCompletion) -{ - CUtlVector eventsRanThisFrame; - - // check all posted messages - for (int i = 0; i < m_PostedMessages.Count(); i++) - { - PostedMessage_t &msgRef = m_PostedMessages[i]; - if (m_flCurrentTime < msgRef.startTime && !bRunToCompletion) - continue; - - // take a copy of th message - PostedMessage_t msg = msgRef; - - // remove the event - // do this before handling the message because the message queue may be messed with - m_PostedMessages.Remove(i); - // reset the count, start the whole queue again - i = -1; - - // handle the event - switch (msg.commandType) - { - case CMD_RUNEVENT: - { - RanEvent_t curEvent; - curEvent.event = msg.event; - curEvent.pParent = msg.parent.Get(); - - // run the event, but only if we haven't already run it this frame, for this parent - if (!eventsRanThisFrame.HasElement(curEvent)) - { - eventsRanThisFrame.AddToTail(curEvent); - RunCmd_RunEvent(msg); - } - } - break; - case CMD_STOPEVENT: - RunCmd_StopEvent(msg); - break; - case CMD_STOPPANELANIMATIONS: - RunCmd_StopPanelAnimations(msg); - break; - case CMD_STOPANIMATION: - RunCmd_StopAnimation(msg); - break; - case CMD_SETFONT: - RunCmd_SetFont(msg); - break; - case CMD_SETTEXTURE: - RunCmd_SetTexture(msg); - break; - case CMD_SETSTRING: - RunCmd_SetString( msg ); - break; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: runs the current animations -//----------------------------------------------------------------------------- -void AnimationController::UpdateActiveAnimations(bool bRunToCompletion) -{ - // iterate all the currently active animations - for (int i = 0; i < m_ActiveAnimations.Count(); i++) - { - ActiveAnimation_t &anim = m_ActiveAnimations[i]; - - // see if the anim is ready to start - if (m_flCurrentTime < anim.startTime && !bRunToCompletion) - continue; - - if (!anim.panel.Get()) - { - // panel is gone, remove the animation - m_ActiveAnimations.Remove(i); - --i; - continue; - } - - if (!anim.started && !bRunToCompletion) - { - // start the animation from the current value - anim.startValue = GetValue(anim, anim.panel, anim.variable); - anim.started = true; - - // Msg( "Starting animation of %s => %.2f (seq: %s) (%s)\n", g_ScriptSymbols.String(anim.variable), anim.endValue.a, g_ScriptSymbols.String(anim.seqName), anim.panel->GetName()); - } - - // get the interpolated value - Value_t val; - if (m_flCurrentTime >= anim.endTime || bRunToCompletion) - { - // animation is done, use the last value - val = anim.endValue; - } - else - { - // get the interpolated value - val = GetInterpolatedValue(anim.interpolator, anim.interpolatorParam, m_flCurrentTime, anim.startTime, anim.endTime, anim.startValue, anim.endValue); - } - - // apply the new value to the panel - SetValue(anim, anim.panel, anim.variable, val); - - // Msg( "Animate value: %s => %.2f for panel '%s'\n", g_ScriptSymbols.String(anim.variable), val.a, anim.panel->GetName()); - - // see if we can remove the animation - if (m_flCurrentTime >= anim.endTime || bRunToCompletion) - { - m_ActiveAnimations.Remove(i); - --i; - } - } -} - -bool AnimationController::UpdateScreenSize() -{ - // get our screen size (for left/right/center alignment) - int screenWide, screenTall; - int sx = 0, sy = 0; - if ( m_hSizePanel != 0 ) - { - ipanel()->GetSize( m_hSizePanel, screenWide, screenTall ); - ipanel()->GetPos( m_hSizePanel, sx, sy ); - } - else - { - surface()->GetScreenSize(screenWide, screenTall); - } - - bool changed = m_nScreenBounds[ 0 ] != sx || - m_nScreenBounds[ 1 ] != sy || - m_nScreenBounds[ 2 ] != screenWide || - m_nScreenBounds[ 3 ] != screenTall; - - m_nScreenBounds[ 0 ] = sx; - m_nScreenBounds[ 1 ] = sy; - m_nScreenBounds[ 2 ] = screenWide; - m_nScreenBounds[ 3 ] = screenTall; - - return changed; -} -//----------------------------------------------------------------------------- -// Purpose: runs a frame of animation -//----------------------------------------------------------------------------- -void AnimationController::UpdateAnimations( float currentTime ) -{ - m_flCurrentTime = currentTime; - - if ( UpdateScreenSize() && m_ScriptFileNames.Count() ) - { - RunAllAnimationsToCompletion(); - ReloadScriptFile(); - } - - UpdatePostedMessages(false); - UpdateActiveAnimations(false); -} - -//----------------------------------------------------------------------------- -// Purpose: plays all animations to completion instantly -//----------------------------------------------------------------------------- -void AnimationController::RunAllAnimationsToCompletion() -{ - // Msg( "AnimationController::RunAllAnimationsToCompletion()\n" ); - UpdatePostedMessages(true); - UpdateActiveAnimations(true); -} - -//----------------------------------------------------------------------------- -// Purpose: Stops all current animations -//----------------------------------------------------------------------------- -void AnimationController::CancelAllAnimations() -{ - // Msg( "AnimationController::CancelAllAnimations()\n" ); - - m_ActiveAnimations.RemoveAll(); - m_PostedMessages.RemoveAll(); -} - -//----------------------------------------------------------------------------- -// Purpose: produces an interpolated value -//----------------------------------------------------------------------------- -AnimationController::Value_t AnimationController::GetInterpolatedValue(int interpolator, float interpolatorParam, float currentTime, float startTime, float endTime, Value_t &startValue, Value_t &endValue) -{ - // calculate how far we are into the animation - float pos = (currentTime - startTime) / (endTime - startTime); - - // adjust the percentage through by the interpolation function - switch (interpolator) - { - case INTERPOLATOR_ACCEL: - pos *= pos; - break; - case INTERPOLATOR_DEACCEL: - pos = sqrtf(pos); - break; - case INTERPOLATOR_SIMPLESPLINE: - pos = SimpleSpline( pos ); - break; - case INTERPOLATOR_PULSE: - // Make sure we end at 1.0, so use cosine - pos = 0.5f + 0.5f * ( cos( pos * 2.0f * M_PI * interpolatorParam ) ); - break; - case INTERPOLATOR_FLICKER: - if ( RandomFloat( 0.0f, 1.0f ) < interpolatorParam ) - { - pos = 1.0f; - } - else - { - pos = 0.0f; - } - break; - case INTERPOLATOR_BOUNCE: - { - // fall from startValue to endValue, bouncing a few times and settling out at endValue - const float hit1 = 0.33f; - const float hit2 = 0.67f; - const float hit3 = 1.0f; - - if ( pos < hit1 ) - { - pos = 1.0f - sin( M_PI * pos / hit1 ); - } - else if ( pos < hit2 ) - { - pos = 0.5f + 0.5f * ( 1.0f - sin( M_PI * ( pos - hit1 ) / ( hit2 - hit1 ) ) ); - } - else - { - pos = 0.8f + 0.2f * ( 1.0f - sin( M_PI * ( pos - hit2 ) / ( hit3 - hit2 ) ) ); - } - break; - } - case INTERPOLATOR_LINEAR: - default: - break; - } - - // calculate the value - Value_t val; - val.a = ((endValue.a - startValue.a) * pos) + startValue.a; - val.b = ((endValue.b - startValue.b) * pos) + startValue.b; - val.c = ((endValue.c - startValue.c) * pos) + startValue.c; - val.d = ((endValue.d - startValue.d) * pos) + startValue.d; - return val; -} - -//----------------------------------------------------------------------------- -// Purpose: sets that the script file should be reloaded each time a script is ran -// used for development -//----------------------------------------------------------------------------- -void AnimationController::SetAutoReloadScript(bool state) -{ - m_bAutoReloadScript = state; -} - -//----------------------------------------------------------------------------- -// Purpose: starts an animation sequence script -//----------------------------------------------------------------------------- -bool AnimationController::StartAnimationSequence(const char *sequenceName) -{ - // We support calling an animation on elements that are not the calling - // panel's children. Use the base parent to start the search. - - return StartAnimationSequence( GetParent(), sequenceName ); -} - -//----------------------------------------------------------------------------- -// Purpose: starts an animation sequence script -//----------------------------------------------------------------------------- -bool AnimationController::StartAnimationSequence(Panel *pWithinParent, const char *sequenceName) -{ - Assert( pWithinParent ); - - if (m_bAutoReloadScript) - { - // Reload the script files - ReloadScriptFile(); - } - - // lookup the symbol for the name - UtlSymId_t seqName = g_ScriptSymbols.Find(sequenceName); - if (seqName == UTL_INVAL_SYMBOL) - return false; - - // Msg("Starting animation sequence %s\n", sequenceName); - - // remove the existing command from the queue - RemoveQueuedAnimationCommands(seqName, pWithinParent); - - // look through for the sequence - int i; - for (i = 0; i < m_Sequences.Count(); i++) - { - if (m_Sequences[i].name == seqName) - break; - } - if (i >= m_Sequences.Count()) - return false; - - // execute the sequence - for (int cmdIndex = 0; cmdIndex < m_Sequences[i].cmdList.Count(); cmdIndex++) - { - ExecAnimationCommand(seqName, m_Sequences[i].cmdList[cmdIndex], pWithinParent); - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Runs a custom command from code, not from a script file -//----------------------------------------------------------------------------- -void AnimationController::RunAnimationCommand(vgui::Panel *panel, const char *variable, float targetValue, float startDelaySeconds, float duration, Interpolators_e interpolator, float animParameter /* = 0 */ ) -{ - // clear any previous animations of this variable - UtlSymId_t var = g_ScriptSymbols.AddString(variable); - RemoveQueuedAnimationByType(panel, var, UTL_INVAL_SYMBOL); - - // build a new animation - AnimCmdAnimate_t animateCmd; - memset(&animateCmd, 0, sizeof(animateCmd)); - animateCmd.panel = 0; - animateCmd.variable = var; - animateCmd.target.a = targetValue; - animateCmd.interpolationFunction = interpolator; - animateCmd.interpolationParameter = animParameter; - animateCmd.startTime = startDelaySeconds; - animateCmd.duration = duration; - - // start immediately - StartCmd_Animate(panel, 0, animateCmd); -} - -//----------------------------------------------------------------------------- -// Purpose: Runs a custom command from code, not from a script file -//----------------------------------------------------------------------------- -void AnimationController::RunAnimationCommand(vgui::Panel *panel, const char *variable, Color targetValue, float startDelaySeconds, float duration, Interpolators_e interpolator, float animParameter /* = 0 */ ) -{ - // clear any previous animations of this variable - UtlSymId_t var = g_ScriptSymbols.AddString(variable); - RemoveQueuedAnimationByType(panel, var, UTL_INVAL_SYMBOL); - - // build a new animation - AnimCmdAnimate_t animateCmd; - memset(&animateCmd, 0, sizeof(animateCmd)); - animateCmd.panel = 0; - animateCmd.variable = var; - animateCmd.target.a = targetValue[0]; - animateCmd.target.b = targetValue[1]; - animateCmd.target.c = targetValue[2]; - animateCmd.target.d = targetValue[3]; - animateCmd.interpolationFunction = interpolator; - animateCmd.interpolationParameter = animParameter; - animateCmd.startTime = startDelaySeconds; - animateCmd.duration = duration; - - // start immediately - StartCmd_Animate(panel, 0, animateCmd); -} - -//----------------------------------------------------------------------------- -// Purpose: gets the length of an animation sequence, in seconds -//----------------------------------------------------------------------------- -float AnimationController::GetAnimationSequenceLength(const char *sequenceName) -{ - // lookup the symbol for the name - UtlSymId_t seqName = g_ScriptSymbols.Find(sequenceName); - if (seqName == UTL_INVAL_SYMBOL) - return 0.0f; - - // look through for the sequence - int i; - for (i = 0; i < m_Sequences.Count(); i++) - { - if (m_Sequences[i].name == seqName) - break; - } - if (i >= m_Sequences.Count()) - return 0.0f; - - // sequence found - return m_Sequences[i].duration; -} - -//----------------------------------------------------------------------------- -// Purpose: removes an existing set of commands from the queue -//----------------------------------------------------------------------------- -void AnimationController::RemoveQueuedAnimationCommands(UtlSymId_t seqName, Panel *pWithinParent) -{ - // Msg("Removing queued anims for sequence %s\n", g_ScriptSymbols.String(seqName)); - - // remove messages posted by this sequence - // if pWithinParent is specified, remove only messages under that parent - {for (int i = 0; i < m_PostedMessages.Count(); i++) - { - if ( ( m_PostedMessages[i].seqName == seqName ) && - ( !pWithinParent || ( m_PostedMessages[i].parent == pWithinParent ) ) ) - { - m_PostedMessages.Remove(i); - --i; - } - }} - - // remove all animations - // if pWithinParent is specified, remove only animations under that parent - for (int i = 0; i < m_ActiveAnimations.Count(); i++) - { - if ( m_ActiveAnimations[i].seqName != seqName ) - continue; - - // panel this anim is on, m_ActiveAnimations[i].panel - if ( pWithinParent ) - { - Panel *animPanel = m_ActiveAnimations[i].panel; - - if ( !animPanel ) - continue; - - Panel *foundPanel = pWithinParent->FindChildByName(animPanel->GetName(),true); - - if ( foundPanel != animPanel ) - continue; - } - - m_ActiveAnimations.Remove(i); - --i; - } -} - -//----------------------------------------------------------------------------- -// Purpose: removes the specified queued animation -//----------------------------------------------------------------------------- -void AnimationController::RemoveQueuedAnimationByType(vgui::Panel *panel, UtlSymId_t variable, UtlSymId_t sequenceToIgnore) -{ - for (int i = 0; i < m_ActiveAnimations.Count(); i++) - { - if (m_ActiveAnimations[i].panel == panel && m_ActiveAnimations[i].variable == variable && m_ActiveAnimations[i].seqName != sequenceToIgnore) - { - // Msg("Removing queued anim %s::%s::%s\n", g_ScriptSymbols.String(m_ActiveAnimations[i].seqName), panel->GetName(), g_ScriptSymbols.String(variable)); - m_ActiveAnimations.Remove(i); - break; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: runs a single line of the script -//----------------------------------------------------------------------------- -void AnimationController::ExecAnimationCommand(UtlSymId_t seqName, AnimCommand_t &animCommand, Panel *pWithinParent) -{ - if (animCommand.commandType == CMD_ANIMATE) - { - StartCmd_Animate(seqName, animCommand.cmdData.animate, pWithinParent); - } - else - { - // post the command to happen at the specified time - PostedMessage_t &msg = m_PostedMessages[m_PostedMessages.AddToTail()]; - msg.seqName = seqName; - msg.commandType = animCommand.commandType; - msg.event = animCommand.cmdData.runEvent.event; - msg.variable = animCommand.cmdData.runEvent.variable; - msg.variable2 = animCommand.cmdData.runEvent.variable2; - msg.startTime = m_flCurrentTime + animCommand.cmdData.runEvent.timeDelay; - msg.parent = pWithinParent; - } -} - -//----------------------------------------------------------------------------- -// Purpose: starts a variable animation -//----------------------------------------------------------------------------- -void AnimationController::StartCmd_Animate(UtlSymId_t seqName, AnimCmdAnimate_t &cmd, Panel *pWithinParent) -{ - Assert( pWithinParent ); - if ( !pWithinParent ) - return; - - // make sure the child exists - Panel *panel = pWithinParent->FindChildByName(g_ScriptSymbols.String(cmd.panel),true); - if ( !panel ) - { - // Check the parent - Panel *parent = GetParent(); - if ( !Q_stricmp( parent->GetName(), g_ScriptSymbols.String(cmd.panel) ) ) - { - panel = parent; - } - } - if (!panel) - return; - - StartCmd_Animate(panel, seqName, cmd); -} - -//----------------------------------------------------------------------------- -// Purpose: Starts an animation command for the specified panel -//----------------------------------------------------------------------------- -void AnimationController::StartCmd_Animate(Panel *panel, UtlSymId_t seqName, AnimCmdAnimate_t &cmd) -{ - // build a command to add to the animation queue - ActiveAnimation_t &anim = m_ActiveAnimations[m_ActiveAnimations.AddToTail()]; - anim.panel = panel; - anim.seqName = seqName; - anim.variable = cmd.variable; - anim.interpolator = cmd.interpolationFunction; - anim.interpolatorParam = cmd.interpolationParameter; - // timings - anim.startTime = m_flCurrentTime + cmd.startTime; - anim.endTime = anim.startTime + cmd.duration; - // values - anim.started = false; - anim.endValue = cmd.target; - - anim.align = cmd.align; -} - -//----------------------------------------------------------------------------- -// Purpose: a posted message to run another event -//----------------------------------------------------------------------------- -void AnimationController::RunCmd_RunEvent(PostedMessage_t &msg) -{ - StartAnimationSequence(msg.parent.Get(), g_ScriptSymbols.String(msg.event)); -} - -//----------------------------------------------------------------------------- -// Purpose: a posted message to stop another event -//----------------------------------------------------------------------------- -void AnimationController::RunCmd_StopEvent(PostedMessage_t &msg) -{ - RemoveQueuedAnimationCommands(msg.event, msg.parent); -} - -//----------------------------------------------------------------------------- -// Purpose: a posted message to stop all animations relevant to a specified panel -//----------------------------------------------------------------------------- -void AnimationController::RunCmd_StopPanelAnimations(PostedMessage_t &msg) -{ - Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event)); - Assert(panel != NULL); - if (!panel) - return; - - // loop through all the active animations cancelling any that - // are operating on said panel, except for the event specified - for (int i = 0; i < m_ActiveAnimations.Count(); i++) - { - if (m_ActiveAnimations[i].panel == panel && m_ActiveAnimations[i].seqName != msg.seqName) - { - m_ActiveAnimations.Remove(i); - --i; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: a posted message to stop animations of a specific type -//----------------------------------------------------------------------------- -void AnimationController::RunCmd_StopAnimation(PostedMessage_t &msg) -{ - Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event)); - Assert(panel != NULL); - if (!panel) - return; - - RemoveQueuedAnimationByType(panel, msg.variable, msg.seqName); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void AnimationController::RunCmd_SetFont( PostedMessage_t &msg ) -{ - Panel *parent = msg.parent.Get(); - - if ( !parent ) - { - parent = GetParent(); - } - - Panel *panel = parent->FindChildByName(g_ScriptSymbols.String(msg.event), true); - Assert(panel != NULL); - if (!panel) - return; - - KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(msg.variable)); - inputData->SetString(g_ScriptSymbols.String(msg.variable), g_ScriptSymbols.String(msg.variable2)); - if (!panel->SetInfo(inputData)) - { - // Assert(!("Unhandlable var in AnimationController::SetValue())")); - } - inputData->deleteThis(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void AnimationController::RunCmd_SetTexture( PostedMessage_t &msg ) -{ - Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event)); - Assert(panel != NULL); - if (!panel) - return; - - KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(msg.variable)); - inputData->SetString(g_ScriptSymbols.String(msg.variable), g_ScriptSymbols.String(msg.variable2)); - if (!panel->SetInfo(inputData)) - { - // Assert(!("Unhandlable var in AnimationController::SetValue())")); - } - inputData->deleteThis(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void AnimationController::RunCmd_SetString( PostedMessage_t &msg ) -{ - Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event)); - Assert(panel != NULL); - if (!panel) - return; - - KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(msg.variable)); - inputData->SetString(g_ScriptSymbols.String(msg.variable), g_ScriptSymbols.String(msg.variable2)); - if (!panel->SetInfo(inputData)) - { - // Assert(!("Unhandlable var in AnimationController::SetValue())")); - } - inputData->deleteThis(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int AnimationController::GetRelativeOffset( AnimAlign_t& align, bool xcoord ) -{ - if ( !align.relativePosition ) - return 0; - - Panel *panel = GetParent()->FindChildByName(g_ScriptSymbols.String(align.alignPanel), true); - if ( !panel ) - return 0; - - int x, y, w, h; - panel->GetBounds( x, y, w, h ); - - int offset =0; - switch ( align.alignment ) - { - default: - case a_northwest: - offset = xcoord ? x : y; - break; - case a_north: - offset = xcoord ? ( x + w ) / 2 : y; - break; - case a_northeast: - offset = xcoord ? ( x + w ) : y; - break; - case a_west: - offset = xcoord ? x : ( y + h ) / 2; - break; - case a_center: - offset = xcoord ? ( x + w ) / 2 : ( y + h ) / 2; - break; - case a_east: - offset = xcoord ? ( x + w ) : ( y + h ) / 2; - break; - case a_southwest: - offset = xcoord ? x : ( y + h ); - break; - case a_south: - offset = xcoord ? ( x + w ) / 2 : ( y + h ); - break; - case a_southeast: - offset = xcoord ? ( x + w ) : ( y + h ); - break; - } - - return offset; -} - -//----------------------------------------------------------------------------- -// Purpose: Gets the specified value from a panel -//----------------------------------------------------------------------------- -AnimationController::Value_t AnimationController::GetValue(ActiveAnimation_t& anim, Panel *panel, UtlSymId_t var) -{ - Value_t val = { 0, 0, 0, 0 }; - if (var == m_sPosition) - { - int x, y; - panel->GetPos(x, y); - val.a = (float)(x - GetRelativeOffset( anim.align, true ) ); - val.b = (float)(y - GetRelativeOffset( anim.align, false ) ); - } - else if (var == m_sSize) - { - int w, t; - panel->GetSize(w, t); - val.a = (float)w; - val.b = (float)t; - } - else if (var == m_sFgColor) - { - Color col = panel->GetFgColor(); - val.a = col[0]; - val.b = col[1]; - val.c = col[2]; - val.d = col[3]; - } - else if (var == m_sBgColor) - { - Color col = panel->GetBgColor(); - val.a = col[0]; - val.b = col[1]; - val.c = col[2]; - val.d = col[3]; - } - else if ( var == m_sXPos ) - { - int x, y; - panel->GetPos(x, y); - val.a = (float)( x - GetRelativeOffset( anim.align, true ) ); - } - else if ( var == m_sYPos ) - { - int x, y; - panel->GetPos(x, y); - val.a = (float)( y - GetRelativeOffset( anim.align, false ) ); - } - else if ( var == m_sWide ) - { - int w, h; - panel->GetSize(w, h); - val.a = (float)w; - } - else if ( var == m_sTall ) - { - int w, h; - panel->GetSize(w, h); - val.a = (float)h; - } - else - { - KeyValues *outputData = new KeyValues(g_ScriptSymbols.String(var)); - if (panel->RequestInfo(outputData)) - { - // find the var and lookup it's type - KeyValues *kv = outputData->FindKey(g_ScriptSymbols.String(var)); - if (kv && kv->GetDataType() == KeyValues::TYPE_FLOAT) - { - val.a = kv->GetFloat(); - val.b = 0.0f; - val.c = 0.0f; - val.d = 0.0f; - } - else if (kv && kv->GetDataType() == KeyValues::TYPE_COLOR) - { - Color col = kv->GetColor(); - val.a = col[0]; - val.b = col[1]; - val.c = col[2]; - val.d = col[3]; - } - } - else - { - // Assert(!("Unhandlable var in AnimationController::GetValue())")); - } - outputData->deleteThis(); - } - return val; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets a value in a panel -//----------------------------------------------------------------------------- -void AnimationController::SetValue(ActiveAnimation_t& anim, Panel *panel, UtlSymId_t var, Value_t &value) -{ - if (var == m_sPosition) - { - int x = (int)value.a + GetRelativeOffset( anim.align, true ); - int y = (int)value.b + GetRelativeOffset( anim.align, false ); - panel->SetPos(x, y); - } - else if (var == m_sSize) - { - panel->SetSize((int)value.a, (int)value.b); - } - else if (var == m_sFgColor) - { - Color col = panel->GetFgColor(); - col[0] = (unsigned char)value.a; - col[1] = (unsigned char)value.b; - col[2] = (unsigned char)value.c; - col[3] = (unsigned char)value.d; - panel->SetFgColor(col); - } - else if (var == m_sBgColor) - { - Color col = panel->GetBgColor(); - col[0] = (unsigned char)value.a; - col[1] = (unsigned char)value.b; - col[2] = (unsigned char)value.c; - col[3] = (unsigned char)value.d; - panel->SetBgColor(col); - } - else if (var == m_sXPos) - { - int newx = (int)value.a + GetRelativeOffset( anim.align, true ); - int x, y; - panel->GetPos( x, y ); - x = newx; - panel->SetPos(x, y); - } - else if (var == m_sYPos) - { - int newy = (int)value.a + GetRelativeOffset( anim.align, false ); - int x, y; - panel->GetPos( x, y ); - y = newy; - panel->SetPos(x, y); - } - else if (var == m_sWide) - { - int neww = (int)value.a; - int w, h; - panel->GetSize( w, h ); - w = neww; - panel->SetSize(w, h); - } - else if (var == m_sTall) - { - int newh = (int)value.a; - int w, h; - panel->GetSize( w, h ); - h = newh; - panel->SetSize(w, h); - } - else - { - KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(var)); - // set the custom value - if (value.b == 0.0f && value.c == 0.0f && value.d == 0.0f) - { - // only the first value is non-zero, so probably just a float value - inputData->SetFloat(g_ScriptSymbols.String(var), value.a); - } - else - { - // multivalue, set the color - Color col((unsigned char)value.a, (unsigned char)value.b, (unsigned char)value.c, (unsigned char)value.d); - inputData->SetColor(g_ScriptSymbols.String(var), col); - } - if (!panel->SetInfo(inputData)) - { - // Assert(!("Unhandlable var in AnimationController::SetValue())")); - } - inputData->deleteThis(); - } -} -// Hooks between panels and animation controller system - -class CPanelAnimationDictionary -{ -public: - CPanelAnimationDictionary() : m_PanelAnimationMapPool( 32 ) - { - } - - ~CPanelAnimationDictionary() - { - m_PanelAnimationMapPool.Clear(); - } - - PanelAnimationMap *FindOrAddPanelAnimationMap( char const *className ); - PanelAnimationMap *FindPanelAnimationMap( char const *className ); - void PanelAnimationDumpVars( char const *className ); -private: - - struct PanelAnimationMapDictionaryEntry - { - PanelAnimationMap *map; - }; - - char const *StripNamespace( char const *className ); - void PanelAnimationDumpMap( PanelAnimationMap *map, bool recursive ); - - CClassMemoryPool< PanelAnimationMap > m_PanelAnimationMapPool; - CUtlDict< PanelAnimationMapDictionaryEntry, int > m_AnimationMaps; -}; - - -char const *CPanelAnimationDictionary::StripNamespace( char const *className ) -{ - if ( !Q_strnicmp( className, "vgui::", 6 ) ) - { - return className + 6; - } - return className; -} - -//----------------------------------------------------------------------------- -// Purpose: Find but don't add mapping -//----------------------------------------------------------------------------- -PanelAnimationMap *CPanelAnimationDictionary::FindPanelAnimationMap( char const *className ) -{ - int lookup = m_AnimationMaps.Find( StripNamespace( className ) ); - if ( lookup != m_AnimationMaps.InvalidIndex() ) - { - return m_AnimationMaps[ lookup ].map; - } - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -PanelAnimationMap *CPanelAnimationDictionary::FindOrAddPanelAnimationMap( char const *className ) -{ - PanelAnimationMap *map = FindPanelAnimationMap( className ); - if ( map ) - return map; - - Panel::InitPropertyConverters(); - - PanelAnimationMapDictionaryEntry entry; - entry.map = (PanelAnimationMap *)m_PanelAnimationMapPool.Alloc(); - m_AnimationMaps.Insert( StripNamespace( className ), entry ); - return entry.map; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPanelAnimationDictionary::PanelAnimationDumpMap( PanelAnimationMap *map, bool recursive ) -{ - if ( map->pfnClassName ) - { - Msg( "%s\n", (*map->pfnClassName)() ); - } - int c = map->entries.Count(); - for ( int i = 0; i < c; i++ ) - { - PanelAnimationMapEntry *e = &map->entries[ i ]; - Msg( " %s %s\n", e->type(), e->name() ); - } - - if ( recursive && map->baseMap ) - { - PanelAnimationDumpMap( map->baseMap, recursive ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPanelAnimationDictionary::PanelAnimationDumpVars( char const *className ) -{ - if ( className == NULL ) - { - for ( int i = 0; i < (int)m_AnimationMaps.Count(); i++ ) - { - PanelAnimationDumpMap( m_AnimationMaps[ i ].map, false ); - } - } - else - { - PanelAnimationMap *map = FindPanelAnimationMap( className ); - if ( map ) - { - PanelAnimationDumpMap( map, true ); - } - else - { - Msg( "No such Panel Animation class %s\n", className ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: singleton accessor -//----------------------------------------------------------------------------- -CPanelAnimationDictionary& GetPanelAnimationDictionary() -{ - static CPanelAnimationDictionary dictionary; - return dictionary; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -PanelAnimationMap *FindOrAddPanelAnimationMap( char const *className ) -{ - return GetPanelAnimationDictionary().FindOrAddPanelAnimationMap( className ); -} - -//----------------------------------------------------------------------------- -// Purpose: Find but don't add mapping -//----------------------------------------------------------------------------- -PanelAnimationMap *FindPanelAnimationMap( char const *className ) -{ - return GetPanelAnimationDictionary().FindPanelAnimationMap( className ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PanelAnimationDumpVars( char const *className ) -{ - GetPanelAnimationDictionary().PanelAnimationDumpVars( className ); +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#pragma warning( disable : 4244 ) // conversion from 'double' to 'float', possible loss of data + +#include +#include +#include +#include +#include +#include +#include "filesystem.h" +#include "filesystem_helpers.h" + +#include +#include +#include "mempool.h" +#include "utldict.h" +#include "mathlib/mathlib.h" +#include "characterset.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include +// for SRC +#include +#include + +using namespace vgui; + +static CUtlSymbolTable g_ScriptSymbols(0, 128, true); + +// singleton accessor for animation controller for use by the vgui controls +namespace vgui +{ +AnimationController *GetAnimationController() +{ + static AnimationController *s_pAnimationController = new AnimationController(NULL); + return s_pAnimationController; +} +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +AnimationController::AnimationController(Panel *parent) : BaseClass(parent, NULL) +{ + m_hSizePanel = 0; + m_nScreenBounds[ 0 ] = m_nScreenBounds[ 1 ] = -1; + m_nScreenBounds[ 2 ] = m_nScreenBounds[ 3 ] = -1; + + m_bAutoReloadScript = false; + + // always invisible + SetVisible(false); + + SetProportional(true); + + // get the names of common types + m_sPosition = g_ScriptSymbols.AddString("position"); + m_sSize = g_ScriptSymbols.AddString("size"); + m_sFgColor = g_ScriptSymbols.AddString("fgcolor"); + m_sBgColor = g_ScriptSymbols.AddString("bgcolor"); + + m_sXPos = g_ScriptSymbols.AddString("xpos"); + m_sYPos = g_ScriptSymbols.AddString("ypos"); + m_sWide = g_ScriptSymbols.AddString("wide"); + m_sTall = g_ScriptSymbols.AddString("tall"); + + m_flCurrentTime = 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +AnimationController::~AnimationController() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Sets which script file to use +//----------------------------------------------------------------------------- +bool AnimationController::SetScriptFile( VPANEL sizingPanel, const char *fileName, bool wipeAll /*=false*/ ) +{ + m_hSizePanel = sizingPanel; + + if ( wipeAll ) + { + // clear the current script + m_Sequences.RemoveAll(); + m_ScriptFileNames.RemoveAll(); + + CancelAllAnimations(); + } + + // Store off this filename for reloading later on (if we don't have it already) + UtlSymId_t sFilename = g_ScriptSymbols.AddString( fileName ); + if ( m_ScriptFileNames.Find( sFilename ) == m_ScriptFileNames.InvalidIndex() ) + { + m_ScriptFileNames.AddToTail( sFilename ); + } + + UpdateScreenSize(); + + // load the new script file + return LoadScriptFile( fileName ); +} + +//----------------------------------------------------------------------------- +// Purpose: reloads the currently set script file +//----------------------------------------------------------------------------- +void AnimationController::ReloadScriptFile() +{ + // Clear all current sequences + m_Sequences.RemoveAll(); + + UpdateScreenSize(); + + // Reload each file we've loaded + for ( int i = 0; i < m_ScriptFileNames.Count(); i++ ) + { + const char *lpszFilename = g_ScriptSymbols.String( m_ScriptFileNames[i] ); + if ( strlen( lpszFilename ) > 0) + { + if ( LoadScriptFile( lpszFilename ) == false ) + { + Assert( 0 ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: loads a script file from disk +//----------------------------------------------------------------------------- +bool AnimationController::LoadScriptFile(const char *fileName) +{ + FileHandle_t f = g_pFullFileSystem->Open(fileName, "rt"); + if (!f) + { + Warning("Couldn't find script file %s\n", fileName); + return false; + } + + // read the whole thing into memory + int size = g_pFullFileSystem->Size(f); + // read into temporary memory block + int nBufSize = size+1; + if ( IsXbox() ) + { + nBufSize = AlignValue( nBufSize, 512 ); + } + char *pMem = (char *)malloc(nBufSize); + int bytesRead = g_pFullFileSystem->ReadEx(pMem, nBufSize, size, f); + Assert(bytesRead <= size); + pMem[bytesRead] = 0; + g_pFullFileSystem->Close(f); + // parse + bool success = ParseScriptFile(pMem, bytesRead); + free(pMem); + return success; +} + +AnimationController::RelativeAlignmentLookup AnimationController::g_AlignmentLookup[] = +{ + { AnimationController::a_northwest , "northwest" }, + { AnimationController::a_north , "north" }, + { AnimationController::a_northeast , "northeast" }, + { AnimationController::a_west , "west" }, + { AnimationController::a_center , "center" }, + { AnimationController::a_east , "east" }, + { AnimationController::a_southwest , "southwest" }, + { AnimationController::a_south , "south" }, + { AnimationController::a_southeast , "southeast" }, + + { AnimationController::a_northwest , "nw" }, + { AnimationController::a_north , "n" }, + { AnimationController::a_northeast , "ne" }, + { AnimationController::a_west , "w" }, + { AnimationController::a_center , "c" }, + { AnimationController::a_east , "e" }, + { AnimationController::a_southwest , "sw" }, + { AnimationController::a_south , "s" }, + { AnimationController::a_southeast , "se" }, +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +AnimationController::RelativeAlignment AnimationController::LookupAlignment( char const *token ) +{ + int c = ARRAYSIZE( g_AlignmentLookup ); + + for ( int i = 0; i < c; i++ ) + { + if ( !Q_stricmp( token, g_AlignmentLookup[ i ].name ) ) + { + return g_AlignmentLookup[ i ].align; + } + } + + return AnimationController::a_northwest; +} + +//----------------------------------------------------------------------------- +// Purpose: Parse position including right edge and center adjustment out of a +// token. This is relative to the screen +//----------------------------------------------------------------------------- +void AnimationController::SetupPosition( AnimCmdAnimate_t& cmd, float *output, char const *psz, int screendimension ) +{ + bool r = false, c = false; + int pos; + if ( psz[0] == '(' ) + { + psz++; + + if ( Q_strstr( psz, ")" ) ) + { + char sz[ 256 ]; + Q_strncpy( sz, psz, sizeof( sz ) ); + + char *colon = Q_strstr( sz, ":" ); + if ( colon ) + { + *colon = 0; + + RelativeAlignment ra = LookupAlignment( sz ); + + colon++; + + char *panelName = colon; + char *panelEnd = Q_strstr( panelName, ")" ); + if ( panelEnd ) + { + *panelEnd = 0; + + if ( Q_strlen( panelName ) > 0 ) + { + // + cmd.align.relativePosition = true; + cmd.align.alignPanel = g_ScriptSymbols.AddString(panelName); + cmd.align.alignment = ra; + } + } + } + + psz = Q_strstr( psz, ")" ) + 1; + } + } + else if (psz[0] == 'r' || psz[0] == 'R') + { + r = true; + psz++; + } + else if (psz[0] == 'c' || psz[0] == 'C') + { + c = true; + psz++; + } + + // get the number + pos = atoi(psz); + + // scale the values + if (IsProportional()) + { + pos = vgui::scheme()->GetProportionalScaledValueEx( GetScheme(), pos ); + } + + // adjust the positions + if (r) + { + pos = screendimension - pos; + } + if (c) + { + pos = (screendimension / 2) + pos; + } + + // set the value + *output = static_cast( pos ); +} + + +//----------------------------------------------------------------------------- +// Purpose: parses a script into sequences +//----------------------------------------------------------------------------- +bool AnimationController::ParseScriptFile(char *pMem, int length) +{ + // get the scheme (for looking up color names) + IScheme *scheme = vgui::scheme()->GetIScheme(GetScheme()); + + // get our screen size (for left/right/center alignment) + int screenWide = m_nScreenBounds[ 2 ]; + int screenTall = m_nScreenBounds[ 3 ]; + + // start by getting the first token + char token[512]; + pMem = ParseFile(pMem, token, NULL); + while (token[0]) + { + bool bAccepted = true; + + // should be 'event' + if (stricmp(token, "event")) + { + Warning("Couldn't parse script file: expected 'event', found '%s'\n", token); + return false; + } + + // get the event name + pMem = ParseFile(pMem, token, NULL); + if (strlen(token) < 1) + { + Warning("Couldn't parse script file: expected , found nothing\n"); + return false; + } + + int seqIndex; + UtlSymId_t nameIndex = g_ScriptSymbols.AddString(token); + + // Create a new sequence + seqIndex = m_Sequences.AddToTail(); + AnimSequence_t &seq = m_Sequences[seqIndex]; + seq.name = nameIndex; + seq.duration = 0.0f; + + // get the open brace or a conditional + pMem = ParseFile(pMem, token, NULL); + if ( Q_stristr( token, "[$" ) ) + { + bAccepted = EvaluateConditional( token ); + + // now get the open brace + pMem = ParseFile(pMem, token, NULL); + } + + if (stricmp(token, "{")) + { + Warning("Couldn't parse script sequence '%s': expected '{', found '%s'\n", g_ScriptSymbols.String(seq.name), token); + return false; + } + + // walk the commands + while (token && token[0]) + { + // get the command type + pMem = ParseFile(pMem, token, NULL); + + // skip out when we hit the end of the sequence + if (token[0] == '}') + break; + + // create a new command + int cmdIndex = seq.cmdList.AddToTail(); + AnimCommand_t &animCmd = seq.cmdList[cmdIndex]; + memset(&animCmd, 0, sizeof(animCmd)); + if (!stricmp(token, "animate")) + { + animCmd.commandType = CMD_ANIMATE; + // parse out the animation commands + AnimCmdAnimate_t &cmdAnimate = animCmd.cmdData.animate; + // panel to manipulate + pMem = ParseFile(pMem, token, NULL); + cmdAnimate.panel = g_ScriptSymbols.AddString(token); + // variable to change + pMem = ParseFile(pMem, token, NULL); + cmdAnimate.variable = g_ScriptSymbols.AddString(token); + // target value + pMem = ParseFile(pMem, token, NULL); + if (cmdAnimate.variable == m_sPosition) + { + // Get first token + SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenWide ); + + // Get second token from "token" + char token2[32]; + char *psz = ParseFile(token, token2, NULL); + psz = ParseFile(psz, token2, NULL); + psz = token2; + + // Position Y goes into ".b" + SetupPosition( cmdAnimate, &cmdAnimate.target.b, psz, screenTall ); + } + else if ( cmdAnimate.variable == m_sXPos ) + { + // XPos and YPos both use target ".a" + SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenWide ); + } + else if ( cmdAnimate.variable == m_sYPos ) + { + // XPos and YPos both use target ".a" + SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenTall ); + } + else + { + // parse the floating point values right out + if (0 == sscanf(token, "%f %f %f %f", &cmdAnimate.target.a, &cmdAnimate.target.b, &cmdAnimate.target.c, &cmdAnimate.target.d)) + { + //============================================================================= + // HPE_BEGIN: + // [pfreese] Improved handling colors not defined in scheme + //============================================================================= + + // could be referencing a value in the scheme file, lookup + Color default_invisible_black(0, 0, 0, 0); + Color col = scheme->GetColor(token, default_invisible_black); + + // we don't have a way of seeing if the color is not declared in the scheme, so we use this + // silly method of trying again with a different default to see if we get the fallback again + if (col == default_invisible_black) + { + Color error_pink(255, 0, 255, 255); // make it extremely obvious if a scheme lookup fails + col = scheme->GetColor(token, error_pink); + + // commented out for Soldier/Demo release...(getting spammed in console) + // we'll try to figure this out after the update is out +// if (col == error_pink) +// { +// Warning("Missing color in scheme: %s\n", token); +// } + } + + //============================================================================= + // HPE_END + //============================================================================= + + cmdAnimate.target.a = col[0]; + cmdAnimate.target.b = col[1]; + cmdAnimate.target.c = col[2]; + cmdAnimate.target.d = col[3]; + } + } + + // fix up scale + if (cmdAnimate.variable == m_sSize) + { + if (IsProportional()) + { + cmdAnimate.target.a = static_cast( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.a) ); + cmdAnimate.target.b = static_cast( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.b) ); + } + } + else if (cmdAnimate.variable == m_sWide || + cmdAnimate.variable == m_sTall ) + { + if (IsProportional()) + { + // Wide and tall both use.a + cmdAnimate.target.a = static_cast( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.a) ); + } + } + + // interpolation function + pMem = ParseFile(pMem, token, NULL); + if (!stricmp(token, "Accel")) + { + cmdAnimate.interpolationFunction = INTERPOLATOR_ACCEL; + } + else if (!stricmp(token, "Deaccel")) + { + cmdAnimate.interpolationFunction = INTERPOLATOR_DEACCEL; + } + else if ( !stricmp(token, "Spline")) + { + cmdAnimate.interpolationFunction = INTERPOLATOR_SIMPLESPLINE; + } + else if (!stricmp(token,"Pulse")) + { + cmdAnimate.interpolationFunction = INTERPOLATOR_PULSE; + // frequencey + pMem = ParseFile(pMem, token, NULL); + cmdAnimate.interpolationParameter = (float)atof(token); + } + else if ( !stricmp( token, "Flicker")) + { + cmdAnimate.interpolationFunction = INTERPOLATOR_FLICKER; + // noiseamount + pMem = ParseFile(pMem, token, NULL); + cmdAnimate.interpolationParameter = (float)atof(token); + } + else if (!stricmp(token, "Bounce")) + { + cmdAnimate.interpolationFunction = INTERPOLATOR_BOUNCE; + } + else + { + cmdAnimate.interpolationFunction = INTERPOLATOR_LINEAR; + } + // start time + pMem = ParseFile(pMem, token, NULL); + cmdAnimate.startTime = (float)atof(token); + // duration + pMem = ParseFile(pMem, token, NULL); + cmdAnimate.duration = (float)atof(token); + // check max duration + if (cmdAnimate.startTime + cmdAnimate.duration > seq.duration) + { + seq.duration = cmdAnimate.startTime + cmdAnimate.duration; + } + } + else if (!stricmp(token, "runevent")) + { + animCmd.commandType = CMD_RUNEVENT; + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.timeDelay = (float)atof(token); + } + else if (!stricmp(token, "stopevent")) + { + animCmd.commandType = CMD_STOPEVENT; + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.timeDelay = (float)atof(token); + } + else if (!stricmp(token, "StopPanelAnimations")) + { + animCmd.commandType = CMD_STOPPANELANIMATIONS; + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.timeDelay = (float)atof(token); + } + else if (!stricmp(token, "stopanimation")) + { + animCmd.commandType = CMD_STOPANIMATION; + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token); + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.timeDelay = (float)atof(token); + } + else if ( !stricmp( token, "SetFont" )) + { + animCmd.commandType = CMD_SETFONT; + // Panel name + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); + // Font parameter + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token); + // Font name from scheme + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.variable2 = g_ScriptSymbols.AddString(token); + + // Set time + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.timeDelay = (float)atof(token); + } + else if ( !stricmp( token, "SetTexture" )) + { + animCmd.commandType = CMD_SETTEXTURE; + // Panel name + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); + // Texture Id + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token); + // material name + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.variable2 = g_ScriptSymbols.AddString(token); + + // Set time + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.timeDelay = (float)atof(token); + } + else if ( !stricmp( token, "SetString" )) + { + animCmd.commandType = CMD_SETSTRING; + // Panel name + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token); + // String variable name + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token); + // String value to set + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.variable2 = g_ScriptSymbols.AddString(token); + + // Set time + pMem = ParseFile(pMem, token, NULL); + animCmd.cmdData.runEvent.timeDelay = (float)atof(token); + } + else + { + Warning("Couldn't parse script sequence '%s': expected , found '%s'\n", g_ScriptSymbols.String(seq.name), token); + return false; + } + + // Look ahead one token for a conditional + char *peek = ParseFile(pMem, token, NULL); + if ( Q_stristr( token, "[$" ) ) + { + if ( !EvaluateConditional( token ) ) + { + seq.cmdList.Remove( cmdIndex ); + } + pMem = peek; + } + } + + if ( bAccepted ) + { + // Attempt to find a collision in the sequences, replacing the old one if found + int seqIterator; + for ( seqIterator = 0; seqIterator < m_Sequences.Count()-1; seqIterator++ ) + { + if ( m_Sequences[seqIterator].name == nameIndex ) + { + // Get rid of it, we're overriding it + m_Sequences.Remove( seqIndex ); + break; + } + } + } + else + { + // Dump the entire sequence + m_Sequences.Remove( seqIndex ); + } + + // get the next token, if any + pMem = ParseFile(pMem, token, NULL); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: checks all posted animation events, firing if time +//----------------------------------------------------------------------------- +void AnimationController::UpdatePostedMessages(bool bRunToCompletion) +{ + CUtlVector eventsRanThisFrame; + + // check all posted messages + for (int i = 0; i < m_PostedMessages.Count(); i++) + { + PostedMessage_t &msgRef = m_PostedMessages[i]; + if (m_flCurrentTime < msgRef.startTime && !bRunToCompletion) + continue; + + // take a copy of th message + PostedMessage_t msg = msgRef; + + // remove the event + // do this before handling the message because the message queue may be messed with + m_PostedMessages.Remove(i); + // reset the count, start the whole queue again + i = -1; + + // handle the event + switch (msg.commandType) + { + case CMD_RUNEVENT: + { + RanEvent_t curEvent; + curEvent.event = msg.event; + curEvent.pParent = msg.parent.Get(); + + // run the event, but only if we haven't already run it this frame, for this parent + if (!eventsRanThisFrame.HasElement(curEvent)) + { + eventsRanThisFrame.AddToTail(curEvent); + RunCmd_RunEvent(msg); + } + } + break; + case CMD_STOPEVENT: + RunCmd_StopEvent(msg); + break; + case CMD_STOPPANELANIMATIONS: + RunCmd_StopPanelAnimations(msg); + break; + case CMD_STOPANIMATION: + RunCmd_StopAnimation(msg); + break; + case CMD_SETFONT: + RunCmd_SetFont(msg); + break; + case CMD_SETTEXTURE: + RunCmd_SetTexture(msg); + break; + case CMD_SETSTRING: + RunCmd_SetString( msg ); + break; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: runs the current animations +//----------------------------------------------------------------------------- +void AnimationController::UpdateActiveAnimations(bool bRunToCompletion) +{ + // iterate all the currently active animations + for (int i = 0; i < m_ActiveAnimations.Count(); i++) + { + ActiveAnimation_t &anim = m_ActiveAnimations[i]; + + // see if the anim is ready to start + if (m_flCurrentTime < anim.startTime && !bRunToCompletion) + continue; + + if (!anim.panel.Get()) + { + // panel is gone, remove the animation + m_ActiveAnimations.Remove(i); + --i; + continue; + } + + if (!anim.started && !bRunToCompletion) + { + // start the animation from the current value + anim.startValue = GetValue(anim, anim.panel, anim.variable); + anim.started = true; + + // Msg( "Starting animation of %s => %.2f (seq: %s) (%s)\n", g_ScriptSymbols.String(anim.variable), anim.endValue.a, g_ScriptSymbols.String(anim.seqName), anim.panel->GetName()); + } + + // get the interpolated value + Value_t val; + if (m_flCurrentTime >= anim.endTime || bRunToCompletion) + { + // animation is done, use the last value + val = anim.endValue; + } + else + { + // get the interpolated value + val = GetInterpolatedValue(anim.interpolator, anim.interpolatorParam, m_flCurrentTime, anim.startTime, anim.endTime, anim.startValue, anim.endValue); + } + + // apply the new value to the panel + SetValue(anim, anim.panel, anim.variable, val); + + // Msg( "Animate value: %s => %.2f for panel '%s'\n", g_ScriptSymbols.String(anim.variable), val.a, anim.panel->GetName()); + + // see if we can remove the animation + if (m_flCurrentTime >= anim.endTime || bRunToCompletion) + { + m_ActiveAnimations.Remove(i); + --i; + } + } +} + +bool AnimationController::UpdateScreenSize() +{ + // get our screen size (for left/right/center alignment) + int screenWide, screenTall; + int sx = 0, sy = 0; + if ( m_hSizePanel != 0 ) + { + ipanel()->GetSize( m_hSizePanel, screenWide, screenTall ); + ipanel()->GetPos( m_hSizePanel, sx, sy ); + } + else + { + surface()->GetScreenSize(screenWide, screenTall); + } + + bool changed = m_nScreenBounds[ 0 ] != sx || + m_nScreenBounds[ 1 ] != sy || + m_nScreenBounds[ 2 ] != screenWide || + m_nScreenBounds[ 3 ] != screenTall; + + m_nScreenBounds[ 0 ] = sx; + m_nScreenBounds[ 1 ] = sy; + m_nScreenBounds[ 2 ] = screenWide; + m_nScreenBounds[ 3 ] = screenTall; + + return changed; +} +//----------------------------------------------------------------------------- +// Purpose: runs a frame of animation +//----------------------------------------------------------------------------- +void AnimationController::UpdateAnimations( float currentTime ) +{ + m_flCurrentTime = currentTime; + + if ( UpdateScreenSize() && m_ScriptFileNames.Count() ) + { + RunAllAnimationsToCompletion(); + ReloadScriptFile(); + } + + UpdatePostedMessages(false); + UpdateActiveAnimations(false); +} + +//----------------------------------------------------------------------------- +// Purpose: plays all animations to completion instantly +//----------------------------------------------------------------------------- +void AnimationController::RunAllAnimationsToCompletion() +{ + // Msg( "AnimationController::RunAllAnimationsToCompletion()\n" ); + UpdatePostedMessages(true); + UpdateActiveAnimations(true); +} + +//----------------------------------------------------------------------------- +// Purpose: Stops all current animations +//----------------------------------------------------------------------------- +void AnimationController::CancelAllAnimations() +{ + // Msg( "AnimationController::CancelAllAnimations()\n" ); + + m_ActiveAnimations.RemoveAll(); + m_PostedMessages.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: produces an interpolated value +//----------------------------------------------------------------------------- +AnimationController::Value_t AnimationController::GetInterpolatedValue(int interpolator, float interpolatorParam, float currentTime, float startTime, float endTime, Value_t &startValue, Value_t &endValue) +{ + // calculate how far we are into the animation + float pos = (currentTime - startTime) / (endTime - startTime); + + // adjust the percentage through by the interpolation function + switch (interpolator) + { + case INTERPOLATOR_ACCEL: + pos *= pos; + break; + case INTERPOLATOR_DEACCEL: + pos = sqrtf(pos); + break; + case INTERPOLATOR_SIMPLESPLINE: + pos = SimpleSpline( pos ); + break; + case INTERPOLATOR_PULSE: + // Make sure we end at 1.0, so use cosine + pos = 0.5f + 0.5f * ( cos( pos * 2.0f * M_PI * interpolatorParam ) ); + break; + case INTERPOLATOR_FLICKER: + if ( RandomFloat( 0.0f, 1.0f ) < interpolatorParam ) + { + pos = 1.0f; + } + else + { + pos = 0.0f; + } + break; + case INTERPOLATOR_BOUNCE: + { + // fall from startValue to endValue, bouncing a few times and settling out at endValue + const float hit1 = 0.33f; + const float hit2 = 0.67f; + const float hit3 = 1.0f; + + if ( pos < hit1 ) + { + pos = 1.0f - sin( M_PI * pos / hit1 ); + } + else if ( pos < hit2 ) + { + pos = 0.5f + 0.5f * ( 1.0f - sin( M_PI * ( pos - hit1 ) / ( hit2 - hit1 ) ) ); + } + else + { + pos = 0.8f + 0.2f * ( 1.0f - sin( M_PI * ( pos - hit2 ) / ( hit3 - hit2 ) ) ); + } + break; + } + case INTERPOLATOR_LINEAR: + default: + break; + } + + // calculate the value + Value_t val; + val.a = ((endValue.a - startValue.a) * pos) + startValue.a; + val.b = ((endValue.b - startValue.b) * pos) + startValue.b; + val.c = ((endValue.c - startValue.c) * pos) + startValue.c; + val.d = ((endValue.d - startValue.d) * pos) + startValue.d; + return val; +} + +//----------------------------------------------------------------------------- +// Purpose: sets that the script file should be reloaded each time a script is ran +// used for development +//----------------------------------------------------------------------------- +void AnimationController::SetAutoReloadScript(bool state) +{ + m_bAutoReloadScript = state; +} + +//----------------------------------------------------------------------------- +// Purpose: starts an animation sequence script +//----------------------------------------------------------------------------- +bool AnimationController::StartAnimationSequence(const char *sequenceName) +{ + // We support calling an animation on elements that are not the calling + // panel's children. Use the base parent to start the search. + + return StartAnimationSequence( GetParent(), sequenceName ); +} + +//----------------------------------------------------------------------------- +// Purpose: starts an animation sequence script +//----------------------------------------------------------------------------- +bool AnimationController::StartAnimationSequence(Panel *pWithinParent, const char *sequenceName) +{ + Assert( pWithinParent ); + + if (m_bAutoReloadScript) + { + // Reload the script files + ReloadScriptFile(); + } + + // lookup the symbol for the name + UtlSymId_t seqName = g_ScriptSymbols.Find(sequenceName); + if (seqName == UTL_INVAL_SYMBOL) + return false; + + // Msg("Starting animation sequence %s\n", sequenceName); + + // remove the existing command from the queue + RemoveQueuedAnimationCommands(seqName, pWithinParent); + + // look through for the sequence + int i; + for (i = 0; i < m_Sequences.Count(); i++) + { + if (m_Sequences[i].name == seqName) + break; + } + if (i >= m_Sequences.Count()) + return false; + + // execute the sequence + for (int cmdIndex = 0; cmdIndex < m_Sequences[i].cmdList.Count(); cmdIndex++) + { + ExecAnimationCommand(seqName, m_Sequences[i].cmdList[cmdIndex], pWithinParent); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Runs a custom command from code, not from a script file +//----------------------------------------------------------------------------- +void AnimationController::RunAnimationCommand(vgui::Panel *panel, const char *variable, float targetValue, float startDelaySeconds, float duration, Interpolators_e interpolator, float animParameter /* = 0 */ ) +{ + // clear any previous animations of this variable + UtlSymId_t var = g_ScriptSymbols.AddString(variable); + RemoveQueuedAnimationByType(panel, var, UTL_INVAL_SYMBOL); + + // build a new animation + AnimCmdAnimate_t animateCmd; + memset(&animateCmd, 0, sizeof(animateCmd)); + animateCmd.panel = 0; + animateCmd.variable = var; + animateCmd.target.a = targetValue; + animateCmd.interpolationFunction = interpolator; + animateCmd.interpolationParameter = animParameter; + animateCmd.startTime = startDelaySeconds; + animateCmd.duration = duration; + + // start immediately + StartCmd_Animate(panel, 0, animateCmd); +} + +//----------------------------------------------------------------------------- +// Purpose: Runs a custom command from code, not from a script file +//----------------------------------------------------------------------------- +void AnimationController::RunAnimationCommand(vgui::Panel *panel, const char *variable, Color targetValue, float startDelaySeconds, float duration, Interpolators_e interpolator, float animParameter /* = 0 */ ) +{ + // clear any previous animations of this variable + UtlSymId_t var = g_ScriptSymbols.AddString(variable); + RemoveQueuedAnimationByType(panel, var, UTL_INVAL_SYMBOL); + + // build a new animation + AnimCmdAnimate_t animateCmd; + memset(&animateCmd, 0, sizeof(animateCmd)); + animateCmd.panel = 0; + animateCmd.variable = var; + animateCmd.target.a = targetValue[0]; + animateCmd.target.b = targetValue[1]; + animateCmd.target.c = targetValue[2]; + animateCmd.target.d = targetValue[3]; + animateCmd.interpolationFunction = interpolator; + animateCmd.interpolationParameter = animParameter; + animateCmd.startTime = startDelaySeconds; + animateCmd.duration = duration; + + // start immediately + StartCmd_Animate(panel, 0, animateCmd); +} + +//----------------------------------------------------------------------------- +// Purpose: gets the length of an animation sequence, in seconds +//----------------------------------------------------------------------------- +float AnimationController::GetAnimationSequenceLength(const char *sequenceName) +{ + // lookup the symbol for the name + UtlSymId_t seqName = g_ScriptSymbols.Find(sequenceName); + if (seqName == UTL_INVAL_SYMBOL) + return 0.0f; + + // look through for the sequence + int i; + for (i = 0; i < m_Sequences.Count(); i++) + { + if (m_Sequences[i].name == seqName) + break; + } + if (i >= m_Sequences.Count()) + return 0.0f; + + // sequence found + return m_Sequences[i].duration; +} + +//----------------------------------------------------------------------------- +// Purpose: removes an existing set of commands from the queue +//----------------------------------------------------------------------------- +void AnimationController::RemoveQueuedAnimationCommands(UtlSymId_t seqName, Panel *pWithinParent) +{ + // Msg("Removing queued anims for sequence %s\n", g_ScriptSymbols.String(seqName)); + + // remove messages posted by this sequence + // if pWithinParent is specified, remove only messages under that parent + {for (int i = 0; i < m_PostedMessages.Count(); i++) + { + if ( ( m_PostedMessages[i].seqName == seqName ) && + ( !pWithinParent || ( m_PostedMessages[i].parent == pWithinParent ) ) ) + { + m_PostedMessages.Remove(i); + --i; + } + }} + + // remove all animations + // if pWithinParent is specified, remove only animations under that parent + for (int i = 0; i < m_ActiveAnimations.Count(); i++) + { + if ( m_ActiveAnimations[i].seqName != seqName ) + continue; + + // panel this anim is on, m_ActiveAnimations[i].panel + if ( pWithinParent ) + { + Panel *animPanel = m_ActiveAnimations[i].panel; + + if ( !animPanel ) + continue; + + Panel *foundPanel = pWithinParent->FindChildByName(animPanel->GetName(),true); + + if ( foundPanel != animPanel ) + continue; + } + + m_ActiveAnimations.Remove(i); + --i; + } +} + +//----------------------------------------------------------------------------- +// Purpose: removes the specified queued animation +//----------------------------------------------------------------------------- +void AnimationController::RemoveQueuedAnimationByType(vgui::Panel *panel, UtlSymId_t variable, UtlSymId_t sequenceToIgnore) +{ + for (int i = 0; i < m_ActiveAnimations.Count(); i++) + { + if (m_ActiveAnimations[i].panel == panel && m_ActiveAnimations[i].variable == variable && m_ActiveAnimations[i].seqName != sequenceToIgnore) + { + // Msg("Removing queued anim %s::%s::%s\n", g_ScriptSymbols.String(m_ActiveAnimations[i].seqName), panel->GetName(), g_ScriptSymbols.String(variable)); + m_ActiveAnimations.Remove(i); + break; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: runs a single line of the script +//----------------------------------------------------------------------------- +void AnimationController::ExecAnimationCommand(UtlSymId_t seqName, AnimCommand_t &animCommand, Panel *pWithinParent) +{ + if (animCommand.commandType == CMD_ANIMATE) + { + StartCmd_Animate(seqName, animCommand.cmdData.animate, pWithinParent); + } + else + { + // post the command to happen at the specified time + PostedMessage_t &msg = m_PostedMessages[m_PostedMessages.AddToTail()]; + msg.seqName = seqName; + msg.commandType = animCommand.commandType; + msg.event = animCommand.cmdData.runEvent.event; + msg.variable = animCommand.cmdData.runEvent.variable; + msg.variable2 = animCommand.cmdData.runEvent.variable2; + msg.startTime = m_flCurrentTime + animCommand.cmdData.runEvent.timeDelay; + msg.parent = pWithinParent; + } +} + +//----------------------------------------------------------------------------- +// Purpose: starts a variable animation +//----------------------------------------------------------------------------- +void AnimationController::StartCmd_Animate(UtlSymId_t seqName, AnimCmdAnimate_t &cmd, Panel *pWithinParent) +{ + Assert( pWithinParent ); + if ( !pWithinParent ) + return; + + // make sure the child exists + Panel *panel = pWithinParent->FindChildByName(g_ScriptSymbols.String(cmd.panel),true); + if ( !panel ) + { + // Check the parent + Panel *parent = GetParent(); + if ( !Q_stricmp( parent->GetName(), g_ScriptSymbols.String(cmd.panel) ) ) + { + panel = parent; + } + } + if (!panel) + return; + + StartCmd_Animate(panel, seqName, cmd); +} + +//----------------------------------------------------------------------------- +// Purpose: Starts an animation command for the specified panel +//----------------------------------------------------------------------------- +void AnimationController::StartCmd_Animate(Panel *panel, UtlSymId_t seqName, AnimCmdAnimate_t &cmd) +{ + // build a command to add to the animation queue + ActiveAnimation_t &anim = m_ActiveAnimations[m_ActiveAnimations.AddToTail()]; + anim.panel = panel; + anim.seqName = seqName; + anim.variable = cmd.variable; + anim.interpolator = cmd.interpolationFunction; + anim.interpolatorParam = cmd.interpolationParameter; + // timings + anim.startTime = m_flCurrentTime + cmd.startTime; + anim.endTime = anim.startTime + cmd.duration; + // values + anim.started = false; + anim.endValue = cmd.target; + + anim.align = cmd.align; +} + +//----------------------------------------------------------------------------- +// Purpose: a posted message to run another event +//----------------------------------------------------------------------------- +void AnimationController::RunCmd_RunEvent(PostedMessage_t &msg) +{ + StartAnimationSequence(msg.parent.Get(), g_ScriptSymbols.String(msg.event)); +} + +//----------------------------------------------------------------------------- +// Purpose: a posted message to stop another event +//----------------------------------------------------------------------------- +void AnimationController::RunCmd_StopEvent(PostedMessage_t &msg) +{ + RemoveQueuedAnimationCommands(msg.event, msg.parent); +} + +//----------------------------------------------------------------------------- +// Purpose: a posted message to stop all animations relevant to a specified panel +//----------------------------------------------------------------------------- +void AnimationController::RunCmd_StopPanelAnimations(PostedMessage_t &msg) +{ + Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event)); + Assert(panel != NULL); + if (!panel) + return; + + // loop through all the active animations cancelling any that + // are operating on said panel, except for the event specified + for (int i = 0; i < m_ActiveAnimations.Count(); i++) + { + if (m_ActiveAnimations[i].panel == panel && m_ActiveAnimations[i].seqName != msg.seqName) + { + m_ActiveAnimations.Remove(i); + --i; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: a posted message to stop animations of a specific type +//----------------------------------------------------------------------------- +void AnimationController::RunCmd_StopAnimation(PostedMessage_t &msg) +{ + Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event)); + Assert(panel != NULL); + if (!panel) + return; + + RemoveQueuedAnimationByType(panel, msg.variable, msg.seqName); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void AnimationController::RunCmd_SetFont( PostedMessage_t &msg ) +{ + Panel *parent = msg.parent.Get(); + + if ( !parent ) + { + parent = GetParent(); + } + + Panel *panel = parent->FindChildByName(g_ScriptSymbols.String(msg.event), true); + Assert(panel != NULL); + if (!panel) + return; + + KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(msg.variable)); + inputData->SetString(g_ScriptSymbols.String(msg.variable), g_ScriptSymbols.String(msg.variable2)); + if (!panel->SetInfo(inputData)) + { + // Assert(!("Unhandlable var in AnimationController::SetValue())")); + } + inputData->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void AnimationController::RunCmd_SetTexture( PostedMessage_t &msg ) +{ + Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event)); + Assert(panel != NULL); + if (!panel) + return; + + KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(msg.variable)); + inputData->SetString(g_ScriptSymbols.String(msg.variable), g_ScriptSymbols.String(msg.variable2)); + if (!panel->SetInfo(inputData)) + { + // Assert(!("Unhandlable var in AnimationController::SetValue())")); + } + inputData->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void AnimationController::RunCmd_SetString( PostedMessage_t &msg ) +{ + Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event)); + Assert(panel != NULL); + if (!panel) + return; + + KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(msg.variable)); + inputData->SetString(g_ScriptSymbols.String(msg.variable), g_ScriptSymbols.String(msg.variable2)); + if (!panel->SetInfo(inputData)) + { + // Assert(!("Unhandlable var in AnimationController::SetValue())")); + } + inputData->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int AnimationController::GetRelativeOffset( AnimAlign_t& align, bool xcoord ) +{ + if ( !align.relativePosition ) + return 0; + + Panel *panel = GetParent()->FindChildByName(g_ScriptSymbols.String(align.alignPanel), true); + if ( !panel ) + return 0; + + int x, y, w, h; + panel->GetBounds( x, y, w, h ); + + int offset =0; + switch ( align.alignment ) + { + default: + case a_northwest: + offset = xcoord ? x : y; + break; + case a_north: + offset = xcoord ? ( x + w ) / 2 : y; + break; + case a_northeast: + offset = xcoord ? ( x + w ) : y; + break; + case a_west: + offset = xcoord ? x : ( y + h ) / 2; + break; + case a_center: + offset = xcoord ? ( x + w ) / 2 : ( y + h ) / 2; + break; + case a_east: + offset = xcoord ? ( x + w ) : ( y + h ) / 2; + break; + case a_southwest: + offset = xcoord ? x : ( y + h ); + break; + case a_south: + offset = xcoord ? ( x + w ) / 2 : ( y + h ); + break; + case a_southeast: + offset = xcoord ? ( x + w ) : ( y + h ); + break; + } + + return offset; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the specified value from a panel +//----------------------------------------------------------------------------- +AnimationController::Value_t AnimationController::GetValue(ActiveAnimation_t& anim, Panel *panel, UtlSymId_t var) +{ + Value_t val = { 0, 0, 0, 0 }; + if (var == m_sPosition) + { + int x, y; + panel->GetPos(x, y); + val.a = (float)(x - GetRelativeOffset( anim.align, true ) ); + val.b = (float)(y - GetRelativeOffset( anim.align, false ) ); + } + else if (var == m_sSize) + { + int w, t; + panel->GetSize(w, t); + val.a = (float)w; + val.b = (float)t; + } + else if (var == m_sFgColor) + { + Color col = panel->GetFgColor(); + val.a = col[0]; + val.b = col[1]; + val.c = col[2]; + val.d = col[3]; + } + else if (var == m_sBgColor) + { + Color col = panel->GetBgColor(); + val.a = col[0]; + val.b = col[1]; + val.c = col[2]; + val.d = col[3]; + } + else if ( var == m_sXPos ) + { + int x, y; + panel->GetPos(x, y); + val.a = (float)( x - GetRelativeOffset( anim.align, true ) ); + } + else if ( var == m_sYPos ) + { + int x, y; + panel->GetPos(x, y); + val.a = (float)( y - GetRelativeOffset( anim.align, false ) ); + } + else if ( var == m_sWide ) + { + int w, h; + panel->GetSize(w, h); + val.a = (float)w; + } + else if ( var == m_sTall ) + { + int w, h; + panel->GetSize(w, h); + val.a = (float)h; + } + else + { + KeyValues *outputData = new KeyValues(g_ScriptSymbols.String(var)); + if (panel->RequestInfo(outputData)) + { + // find the var and lookup it's type + KeyValues *kv = outputData->FindKey(g_ScriptSymbols.String(var)); + if (kv && kv->GetDataType() == KeyValues::TYPE_FLOAT) + { + val.a = kv->GetFloat(); + val.b = 0.0f; + val.c = 0.0f; + val.d = 0.0f; + } + else if (kv && kv->GetDataType() == KeyValues::TYPE_COLOR) + { + Color col = kv->GetColor(); + val.a = col[0]; + val.b = col[1]; + val.c = col[2]; + val.d = col[3]; + } + } + else + { + // Assert(!("Unhandlable var in AnimationController::GetValue())")); + } + outputData->deleteThis(); + } + return val; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets a value in a panel +//----------------------------------------------------------------------------- +void AnimationController::SetValue(ActiveAnimation_t& anim, Panel *panel, UtlSymId_t var, Value_t &value) +{ + if (var == m_sPosition) + { + int x = (int)value.a + GetRelativeOffset( anim.align, true ); + int y = (int)value.b + GetRelativeOffset( anim.align, false ); + panel->SetPos(x, y); + } + else if (var == m_sSize) + { + panel->SetSize((int)value.a, (int)value.b); + } + else if (var == m_sFgColor) + { + Color col = panel->GetFgColor(); + col[0] = (unsigned char)value.a; + col[1] = (unsigned char)value.b; + col[2] = (unsigned char)value.c; + col[3] = (unsigned char)value.d; + panel->SetFgColor(col); + } + else if (var == m_sBgColor) + { + Color col = panel->GetBgColor(); + col[0] = (unsigned char)value.a; + col[1] = (unsigned char)value.b; + col[2] = (unsigned char)value.c; + col[3] = (unsigned char)value.d; + panel->SetBgColor(col); + } + else if (var == m_sXPos) + { + int newx = (int)value.a + GetRelativeOffset( anim.align, true ); + int x, y; + panel->GetPos( x, y ); + x = newx; + panel->SetPos(x, y); + } + else if (var == m_sYPos) + { + int newy = (int)value.a + GetRelativeOffset( anim.align, false ); + int x, y; + panel->GetPos( x, y ); + y = newy; + panel->SetPos(x, y); + } + else if (var == m_sWide) + { + int neww = (int)value.a; + int w, h; + panel->GetSize( w, h ); + w = neww; + panel->SetSize(w, h); + } + else if (var == m_sTall) + { + int newh = (int)value.a; + int w, h; + panel->GetSize( w, h ); + h = newh; + panel->SetSize(w, h); + } + else + { + KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(var)); + // set the custom value + if (value.b == 0.0f && value.c == 0.0f && value.d == 0.0f) + { + // only the first value is non-zero, so probably just a float value + inputData->SetFloat(g_ScriptSymbols.String(var), value.a); + } + else + { + // multivalue, set the color + Color col((unsigned char)value.a, (unsigned char)value.b, (unsigned char)value.c, (unsigned char)value.d); + inputData->SetColor(g_ScriptSymbols.String(var), col); + } + if (!panel->SetInfo(inputData)) + { + // Assert(!("Unhandlable var in AnimationController::SetValue())")); + } + inputData->deleteThis(); + } +} +// Hooks between panels and animation controller system + +class CPanelAnimationDictionary +{ +public: + CPanelAnimationDictionary() : m_PanelAnimationMapPool( 32 ) + { + } + + ~CPanelAnimationDictionary() + { + m_PanelAnimationMapPool.Clear(); + } + + PanelAnimationMap *FindOrAddPanelAnimationMap( char const *className ); + PanelAnimationMap *FindPanelAnimationMap( char const *className ); + void PanelAnimationDumpVars( char const *className ); +private: + + struct PanelAnimationMapDictionaryEntry + { + PanelAnimationMap *map; + }; + + char const *StripNamespace( char const *className ); + void PanelAnimationDumpMap( PanelAnimationMap *map, bool recursive ); + + CClassMemoryPool< PanelAnimationMap > m_PanelAnimationMapPool; + CUtlDict< PanelAnimationMapDictionaryEntry, int > m_AnimationMaps; +}; + + +char const *CPanelAnimationDictionary::StripNamespace( char const *className ) +{ + if ( !Q_strnicmp( className, "vgui::", 6 ) ) + { + return className + 6; + } + return className; +} + +//----------------------------------------------------------------------------- +// Purpose: Find but don't add mapping +//----------------------------------------------------------------------------- +PanelAnimationMap *CPanelAnimationDictionary::FindPanelAnimationMap( char const *className ) +{ + int lookup = m_AnimationMaps.Find( StripNamespace( className ) ); + if ( lookup != m_AnimationMaps.InvalidIndex() ) + { + return m_AnimationMaps[ lookup ].map; + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +PanelAnimationMap *CPanelAnimationDictionary::FindOrAddPanelAnimationMap( char const *className ) +{ + PanelAnimationMap *map = FindPanelAnimationMap( className ); + if ( map ) + return map; + + Panel::InitPropertyConverters(); + + PanelAnimationMapDictionaryEntry entry; + entry.map = (PanelAnimationMap *)m_PanelAnimationMapPool.Alloc(); + m_AnimationMaps.Insert( StripNamespace( className ), entry ); + return entry.map; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPanelAnimationDictionary::PanelAnimationDumpMap( PanelAnimationMap *map, bool recursive ) +{ + if ( map->pfnClassName ) + { + Msg( "%s\n", (*map->pfnClassName)() ); + } + int c = map->entries.Count(); + for ( int i = 0; i < c; i++ ) + { + PanelAnimationMapEntry *e = &map->entries[ i ]; + Msg( " %s %s\n", e->type(), e->name() ); + } + + if ( recursive && map->baseMap ) + { + PanelAnimationDumpMap( map->baseMap, recursive ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPanelAnimationDictionary::PanelAnimationDumpVars( char const *className ) +{ + if ( className == NULL ) + { + for ( int i = 0; i < (int)m_AnimationMaps.Count(); i++ ) + { + PanelAnimationDumpMap( m_AnimationMaps[ i ].map, false ); + } + } + else + { + PanelAnimationMap *map = FindPanelAnimationMap( className ); + if ( map ) + { + PanelAnimationDumpMap( map, true ); + } + else + { + Msg( "No such Panel Animation class %s\n", className ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: singleton accessor +//----------------------------------------------------------------------------- +CPanelAnimationDictionary& GetPanelAnimationDictionary() +{ + static CPanelAnimationDictionary dictionary; + return dictionary; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +PanelAnimationMap *FindOrAddPanelAnimationMap( char const *className ) +{ + return GetPanelAnimationDictionary().FindOrAddPanelAnimationMap( className ); +} + +//----------------------------------------------------------------------------- +// Purpose: Find but don't add mapping +//----------------------------------------------------------------------------- +PanelAnimationMap *FindPanelAnimationMap( char const *className ) +{ + return GetPanelAnimationDictionary().FindPanelAnimationMap( className ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PanelAnimationDumpVars( char const *className ) +{ + GetPanelAnimationDictionary().PanelAnimationDumpVars( className ); } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/BitmapImagePanel.cpp b/mp/src/vgui2/vgui_controls/BitmapImagePanel.cpp index e6f40128..7e76a3cc 100644 --- a/mp/src/vgui2/vgui_controls/BitmapImagePanel.cpp +++ b/mp/src/vgui2/vgui_controls/BitmapImagePanel.cpp @@ -1,364 +1,364 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#include "vgui_controls/BitmapImagePanel.h" -#include "vgui/ISurface.h" -#include "vgui/IScheme.h" -#include "vgui/IBorder.h" -#include "KeyValues.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#ifndef min -#define min(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -using namespace vgui; - -//----------------------------------------------------------------------------- -/** - * Simple utility function to allocate memory and duplicate a string - */ -static inline char *CloneString( const char *str ) -{ - char *cloneStr = new char [ strlen(str)+1 ]; - strcpy( cloneStr, str ); - return cloneStr; -} - -DECLARE_BUILD_FACTORY_DEFAULT_TEXT( CBitmapImagePanel, BitmapImagePanel ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -CBitmapImagePanel::CBitmapImagePanel( Panel *parent, char const *panelName, - char const *filename /*= NULL*/ ) : Panel( parent, panelName ) -{ - m_pImage = NULL; - - SetBounds( 0, 0, 100, 100 ); - - m_pszImageName = NULL; - m_pszColorName = NULL; - - m_hardwareFiltered = false; - m_preserveAspectRatio = false; - m_contentAlignment = Label::a_center; - - if ( filename && filename[ 0 ] ) - { - m_pImage = scheme()->GetImage( filename, NULL ); - m_pszImageName = CloneString( filename ); - } - - m_bgColor = Color(255, 255, 255, 255); -} -CBitmapImagePanel::~CBitmapImagePanel() -{ - delete [] m_pszImageName; - delete [] m_pszColorName; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBitmapImagePanel::ComputeImagePosition(int &x, int &y, int &w, int &h) -{ - if (!m_pImage) - { - x = y = w = h = 0; - return; - } - - if ( !m_preserveAspectRatio ) - { - x = y = 0; - GetSize( w, h ); - return; - } - - int panelWide, panelTall; - GetSize( panelWide, panelTall ); - - int imageWide, imageTall; - m_pImage->GetSize( imageWide, imageTall ); - - if ( panelWide > 0 && panelTall > 0 && imageWide > 0 && imageTall > 0 ) - { - float xScale = (float)panelWide / (float)imageWide; - float yScale = (float)panelTall / (float)imageTall; - float scale = min( xScale, yScale ); - - w = (int) (imageWide * scale); - h = (int) (imageTall * scale); - - switch (m_contentAlignment) - { - case Label::a_northwest: - x = y = 0; - break; - case Label::a_north: - x = (panelWide - w) / 2; - y = 0; - break; - case Label::a_northeast: - x = (panelWide - w); - y = 0; - break; - case Label::a_west: - x = 0; - y = (panelTall - h) / 2; - break; - case Label::a_center: - x = (panelWide - w) / 2; - y = (panelTall - h) / 2; - break; - case Label::a_east: - x = (panelWide - w); - y = (panelTall - h) / 2; - break; - case Label::a_southwest: - x = (panelWide - w); - y = 0; - break; - case Label::a_south: - x = (panelWide - w); - y = (panelTall - h) / 2; - break; - case Label::a_southeast: - x = (panelWide - w); - y = (panelTall - h); - break; - default: - x = y = 0; - break; - } - } - else - { - x = y = 0; - w = panelWide; - h = panelTall; - return; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBitmapImagePanel::PaintBorder() -{ - int x, y, w, h; - ComputeImagePosition(x, y, w, h); - - IBorder *pBorder = GetBorder(); - if ( pBorder ) - pBorder->Paint( x, y, x+w, y+h, -1, 0, 0 ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBitmapImagePanel::PaintBackground() -{ - if (!m_pImage) - return; - - int x, y, w, h; - ComputeImagePosition(x, y, w, h); - - m_pImage->SetPos(x, y); - m_pImage->SetSize( w, h ); - m_pImage->SetColor( m_bgColor ); - surface()->DrawSetColor( m_bgColor ); - m_pImage->Paint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBitmapImagePanel::setTexture( char const *filename, bool hardwareFiltered ) -{ - m_hardwareFiltered = hardwareFiltered; - - if ( m_pszImageName ) - { - delete[] m_pszImageName; - m_pszImageName = NULL; - } - if ( filename && filename[ 0 ] ) - { - m_pImage = scheme()->GetImage( filename, m_hardwareFiltered ); - m_pszImageName = CloneString( filename ); - } - else - { - m_pImage = NULL; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBitmapImagePanel::SetContentAlignment(Label::Alignment alignment) -{ - m_contentAlignment=alignment; - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Gets control settings for editing -//----------------------------------------------------------------------------- -void CBitmapImagePanel::GetSettings(KeyValues *outResourceData) -{ - BaseClass::GetSettings(outResourceData); - if (m_pszImageName) - { - outResourceData->SetString("image", m_pszImageName); - } - if (m_pszColorName) - { - outResourceData->SetString("imagecolor", m_pszColorName); - } - const char *alignmentString = ""; - switch ( m_contentAlignment ) - { - case Label::a_northwest: alignmentString = "north-west"; break; - case Label::a_north: alignmentString = "north"; break; - case Label::a_northeast: alignmentString = "north-east"; break; - case Label::a_center: alignmentString = "center"; break; - case Label::a_east: alignmentString = "east"; break; - case Label::a_southwest: alignmentString = "south-west"; break; - case Label::a_south: alignmentString = "south"; break; - case Label::a_southeast: alignmentString = "south-east"; break; - case Label::a_west: - default: alignmentString = "center"; break; - } - outResourceData->SetString( "imageAlignment", alignmentString ); - outResourceData->SetInt("preserveAspectRatio", m_preserveAspectRatio); - outResourceData->SetInt("filtered", m_hardwareFiltered); -} - -//----------------------------------------------------------------------------- -// Purpose: Applies designer settings from res file -//----------------------------------------------------------------------------- -void CBitmapImagePanel::ApplySettings(KeyValues *inResourceData) -{ - if ( m_pszImageName ) - { - delete[] m_pszImageName; - m_pszImageName = NULL; - } - - if ( m_pszColorName ) - { - delete[] m_pszColorName; - m_pszColorName = NULL; - } - - const char *imageName = inResourceData->GetString("image", ""); - if (*imageName) - { - setTexture( imageName ); - } - - const char *colorName = inResourceData->GetString("imagecolor", ""); - if (*colorName) - { - m_pszColorName = CloneString( colorName ); - InvalidateLayout(false,true); // force ApplySchemeSettings to run - } - - const char *keyString = inResourceData->GetString("imageAlignment", ""); - if (keyString && *keyString) - { - int align = -1; - - if ( !stricmp(keyString, "north-west") ) - { - align = Label::a_northwest; - } - else if ( !stricmp(keyString, "north") ) - { - align = Label::a_north; - } - else if ( !stricmp(keyString, "north-east") ) - { - align = Label::a_northeast; - } - else if ( !stricmp(keyString, "west") ) - { - align = Label::a_west; - } - else if ( !stricmp(keyString, "center") ) - { - align = Label::a_center; - } - else if ( !stricmp(keyString, "east") ) - { - align = Label::a_east; - } - else if ( !stricmp(keyString, "south-west") ) - { - align = Label::a_southwest; - } - else if ( !stricmp(keyString, "south") ) - { - align = Label::a_south; - } - else if ( !stricmp(keyString, "south-east") ) - { - align = Label::a_southeast; - } - - if ( align != -1 ) - { - SetContentAlignment( (Label::Alignment)align ); - } - } - - keyString = inResourceData->GetString("preserveAspectRatio", ""); - if (keyString && *keyString) - { - m_preserveAspectRatio = atoi( keyString ) != 0; - } - - keyString = inResourceData->GetString("filtered", ""); - if (keyString && *keyString) - { - m_hardwareFiltered = atoi( keyString ) != 0; - } - - BaseClass::ApplySettings(inResourceData); -} - -//----------------------------------------------------------------------------- -// Purpose: load the image, this is done just before this control is displayed -//----------------------------------------------------------------------------- -void CBitmapImagePanel::ApplySchemeSettings( IScheme *pScheme ) -{ - BaseClass::ApplySchemeSettings(pScheme); - - if ( m_pszColorName ) - { - setImageColor( pScheme->GetColor( m_pszColorName, m_bgColor ) ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Describes editing details -//----------------------------------------------------------------------------- -const char *CBitmapImagePanel::GetDescription() -{ - static char buf[1024]; - _snprintf(buf, sizeof(buf), "%s, string image, string imagecolor, alignment imageAlignment, int preserveAspectRatio, int filtered", BaseClass::GetDescription()); - return buf; -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include "vgui_controls/BitmapImagePanel.h" +#include "vgui/ISurface.h" +#include "vgui/IScheme.h" +#include "vgui/IBorder.h" +#include "KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +using namespace vgui; + +//----------------------------------------------------------------------------- +/** + * Simple utility function to allocate memory and duplicate a string + */ +static inline char *CloneString( const char *str ) +{ + char *cloneStr = new char [ strlen(str)+1 ]; + strcpy( cloneStr, str ); + return cloneStr; +} + +DECLARE_BUILD_FACTORY_DEFAULT_TEXT( CBitmapImagePanel, BitmapImagePanel ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CBitmapImagePanel::CBitmapImagePanel( Panel *parent, char const *panelName, + char const *filename /*= NULL*/ ) : Panel( parent, panelName ) +{ + m_pImage = NULL; + + SetBounds( 0, 0, 100, 100 ); + + m_pszImageName = NULL; + m_pszColorName = NULL; + + m_hardwareFiltered = false; + m_preserveAspectRatio = false; + m_contentAlignment = Label::a_center; + + if ( filename && filename[ 0 ] ) + { + m_pImage = scheme()->GetImage( filename, NULL ); + m_pszImageName = CloneString( filename ); + } + + m_bgColor = Color(255, 255, 255, 255); +} +CBitmapImagePanel::~CBitmapImagePanel() +{ + delete [] m_pszImageName; + delete [] m_pszColorName; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBitmapImagePanel::ComputeImagePosition(int &x, int &y, int &w, int &h) +{ + if (!m_pImage) + { + x = y = w = h = 0; + return; + } + + if ( !m_preserveAspectRatio ) + { + x = y = 0; + GetSize( w, h ); + return; + } + + int panelWide, panelTall; + GetSize( panelWide, panelTall ); + + int imageWide, imageTall; + m_pImage->GetSize( imageWide, imageTall ); + + if ( panelWide > 0 && panelTall > 0 && imageWide > 0 && imageTall > 0 ) + { + float xScale = (float)panelWide / (float)imageWide; + float yScale = (float)panelTall / (float)imageTall; + float scale = min( xScale, yScale ); + + w = (int) (imageWide * scale); + h = (int) (imageTall * scale); + + switch (m_contentAlignment) + { + case Label::a_northwest: + x = y = 0; + break; + case Label::a_north: + x = (panelWide - w) / 2; + y = 0; + break; + case Label::a_northeast: + x = (panelWide - w); + y = 0; + break; + case Label::a_west: + x = 0; + y = (panelTall - h) / 2; + break; + case Label::a_center: + x = (panelWide - w) / 2; + y = (panelTall - h) / 2; + break; + case Label::a_east: + x = (panelWide - w); + y = (panelTall - h) / 2; + break; + case Label::a_southwest: + x = (panelWide - w); + y = 0; + break; + case Label::a_south: + x = (panelWide - w); + y = (panelTall - h) / 2; + break; + case Label::a_southeast: + x = (panelWide - w); + y = (panelTall - h); + break; + default: + x = y = 0; + break; + } + } + else + { + x = y = 0; + w = panelWide; + h = panelTall; + return; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBitmapImagePanel::PaintBorder() +{ + int x, y, w, h; + ComputeImagePosition(x, y, w, h); + + IBorder *pBorder = GetBorder(); + if ( pBorder ) + pBorder->Paint( x, y, x+w, y+h, -1, 0, 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBitmapImagePanel::PaintBackground() +{ + if (!m_pImage) + return; + + int x, y, w, h; + ComputeImagePosition(x, y, w, h); + + m_pImage->SetPos(x, y); + m_pImage->SetSize( w, h ); + m_pImage->SetColor( m_bgColor ); + surface()->DrawSetColor( m_bgColor ); + m_pImage->Paint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBitmapImagePanel::setTexture( char const *filename, bool hardwareFiltered ) +{ + m_hardwareFiltered = hardwareFiltered; + + if ( m_pszImageName ) + { + delete[] m_pszImageName; + m_pszImageName = NULL; + } + if ( filename && filename[ 0 ] ) + { + m_pImage = scheme()->GetImage( filename, m_hardwareFiltered ); + m_pszImageName = CloneString( filename ); + } + else + { + m_pImage = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBitmapImagePanel::SetContentAlignment(Label::Alignment alignment) +{ + m_contentAlignment=alignment; + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets control settings for editing +//----------------------------------------------------------------------------- +void CBitmapImagePanel::GetSettings(KeyValues *outResourceData) +{ + BaseClass::GetSettings(outResourceData); + if (m_pszImageName) + { + outResourceData->SetString("image", m_pszImageName); + } + if (m_pszColorName) + { + outResourceData->SetString("imagecolor", m_pszColorName); + } + const char *alignmentString = ""; + switch ( m_contentAlignment ) + { + case Label::a_northwest: alignmentString = "north-west"; break; + case Label::a_north: alignmentString = "north"; break; + case Label::a_northeast: alignmentString = "north-east"; break; + case Label::a_center: alignmentString = "center"; break; + case Label::a_east: alignmentString = "east"; break; + case Label::a_southwest: alignmentString = "south-west"; break; + case Label::a_south: alignmentString = "south"; break; + case Label::a_southeast: alignmentString = "south-east"; break; + case Label::a_west: + default: alignmentString = "center"; break; + } + outResourceData->SetString( "imageAlignment", alignmentString ); + outResourceData->SetInt("preserveAspectRatio", m_preserveAspectRatio); + outResourceData->SetInt("filtered", m_hardwareFiltered); +} + +//----------------------------------------------------------------------------- +// Purpose: Applies designer settings from res file +//----------------------------------------------------------------------------- +void CBitmapImagePanel::ApplySettings(KeyValues *inResourceData) +{ + if ( m_pszImageName ) + { + delete[] m_pszImageName; + m_pszImageName = NULL; + } + + if ( m_pszColorName ) + { + delete[] m_pszColorName; + m_pszColorName = NULL; + } + + const char *imageName = inResourceData->GetString("image", ""); + if (*imageName) + { + setTexture( imageName ); + } + + const char *colorName = inResourceData->GetString("imagecolor", ""); + if (*colorName) + { + m_pszColorName = CloneString( colorName ); + InvalidateLayout(false,true); // force ApplySchemeSettings to run + } + + const char *keyString = inResourceData->GetString("imageAlignment", ""); + if (keyString && *keyString) + { + int align = -1; + + if ( !stricmp(keyString, "north-west") ) + { + align = Label::a_northwest; + } + else if ( !stricmp(keyString, "north") ) + { + align = Label::a_north; + } + else if ( !stricmp(keyString, "north-east") ) + { + align = Label::a_northeast; + } + else if ( !stricmp(keyString, "west") ) + { + align = Label::a_west; + } + else if ( !stricmp(keyString, "center") ) + { + align = Label::a_center; + } + else if ( !stricmp(keyString, "east") ) + { + align = Label::a_east; + } + else if ( !stricmp(keyString, "south-west") ) + { + align = Label::a_southwest; + } + else if ( !stricmp(keyString, "south") ) + { + align = Label::a_south; + } + else if ( !stricmp(keyString, "south-east") ) + { + align = Label::a_southeast; + } + + if ( align != -1 ) + { + SetContentAlignment( (Label::Alignment)align ); + } + } + + keyString = inResourceData->GetString("preserveAspectRatio", ""); + if (keyString && *keyString) + { + m_preserveAspectRatio = atoi( keyString ) != 0; + } + + keyString = inResourceData->GetString("filtered", ""); + if (keyString && *keyString) + { + m_hardwareFiltered = atoi( keyString ) != 0; + } + + BaseClass::ApplySettings(inResourceData); +} + +//----------------------------------------------------------------------------- +// Purpose: load the image, this is done just before this control is displayed +//----------------------------------------------------------------------------- +void CBitmapImagePanel::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings(pScheme); + + if ( m_pszColorName ) + { + setImageColor( pScheme->GetColor( m_pszColorName, m_bgColor ) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Describes editing details +//----------------------------------------------------------------------------- +const char *CBitmapImagePanel::GetDescription() +{ + static char buf[1024]; + _snprintf(buf, sizeof(buf), "%s, string image, string imagecolor, alignment imageAlignment, int preserveAspectRatio, int filtered", BaseClass::GetDescription()); + return buf; +} diff --git a/mp/src/vgui2/vgui_controls/BuildFactoryHelper.cpp b/mp/src/vgui2/vgui_controls/BuildFactoryHelper.cpp index 6d3743d3..46f38cd1 100644 --- a/mp/src/vgui2/vgui_controls/BuildFactoryHelper.cpp +++ b/mp/src/vgui2/vgui_controls/BuildFactoryHelper.cpp @@ -1,104 +1,104 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: Helper for the CHudElement class to add themselves to the list of hud elements -// -// $NoKeywords: $ -//=============================================================================// -#include "vgui/IVGui.h" -#include "vgui_controls/MessageMap.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -using namespace vgui; - -// Start with empty list -CBuildFactoryHelper *CBuildFactoryHelper::m_sHelpers = NULL; - -//----------------------------------------------------------------------------- -// Purpose: Constructs a panel factory -// Input : pfnCreate - fn Ptr to a function which generates a panel -//----------------------------------------------------------------------------- -CBuildFactoryHelper::CBuildFactoryHelper( char const *className, PANELCREATEFUNC func ) -{ - // Make this fatal - if ( HasFactory( className ) ) - { - Error( "CBuildFactoryHelper: Factory for '%s' already exists!!!!\n", className ); - } - - //List is empty, or element belongs at front, insert here - m_pNext = m_sHelpers; - m_sHelpers = this; - - Assert( func ); - m_CreateFunc = func; - Assert( className ); - m_pClassName = className; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns next object in list -// Output : CBuildFactoryHelper -//----------------------------------------------------------------------------- -CBuildFactoryHelper *CBuildFactoryHelper::GetNext( void ) -{ - return m_pNext; -} - -char const *CBuildFactoryHelper::GetClassName() const -{ - return m_pClassName; -} - -vgui::Panel *CBuildFactoryHelper::CreatePanel() -{ - if ( !m_CreateFunc ) - return NULL; - - return ( *m_CreateFunc )(); -} - -// private static meethod -bool CBuildFactoryHelper::HasFactory( char const *className ) -{ - CBuildFactoryHelper *p = m_sHelpers; - while ( p ) - { - if ( !Q_stricmp( className, p->GetClassName() ) ) - return true; - - p = p->GetNext(); - } - return false; -} - -// static method -vgui::Panel *CBuildFactoryHelper::InstancePanel( char const *className ) -{ - CBuildFactoryHelper *p = m_sHelpers; - while ( p ) - { - if ( !Q_stricmp( className, p->GetClassName() ) ) - return p->CreatePanel(); - - p = p->GetNext(); - } - return NULL; -} - -// static method -void CBuildFactoryHelper::GetFactoryNames( CUtlVector< char const * >& list ) -{ - list.RemoveAll(); - - CBuildFactoryHelper *p = m_sHelpers; - while ( p ) - { - list.AddToTail( p->GetClassName() ); - p = p->GetNext(); - } -} - - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Helper for the CHudElement class to add themselves to the list of hud elements +// +// $NoKeywords: $ +//=============================================================================// +#include "vgui/IVGui.h" +#include "vgui_controls/MessageMap.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +// Start with empty list +CBuildFactoryHelper *CBuildFactoryHelper::m_sHelpers = NULL; + +//----------------------------------------------------------------------------- +// Purpose: Constructs a panel factory +// Input : pfnCreate - fn Ptr to a function which generates a panel +//----------------------------------------------------------------------------- +CBuildFactoryHelper::CBuildFactoryHelper( char const *className, PANELCREATEFUNC func ) +{ + // Make this fatal + if ( HasFactory( className ) ) + { + Error( "CBuildFactoryHelper: Factory for '%s' already exists!!!!\n", className ); + } + + //List is empty, or element belongs at front, insert here + m_pNext = m_sHelpers; + m_sHelpers = this; + + Assert( func ); + m_CreateFunc = func; + Assert( className ); + m_pClassName = className; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns next object in list +// Output : CBuildFactoryHelper +//----------------------------------------------------------------------------- +CBuildFactoryHelper *CBuildFactoryHelper::GetNext( void ) +{ + return m_pNext; +} + +char const *CBuildFactoryHelper::GetClassName() const +{ + return m_pClassName; +} + +vgui::Panel *CBuildFactoryHelper::CreatePanel() +{ + if ( !m_CreateFunc ) + return NULL; + + return ( *m_CreateFunc )(); +} + +// private static meethod +bool CBuildFactoryHelper::HasFactory( char const *className ) +{ + CBuildFactoryHelper *p = m_sHelpers; + while ( p ) + { + if ( !Q_stricmp( className, p->GetClassName() ) ) + return true; + + p = p->GetNext(); + } + return false; +} + +// static method +vgui::Panel *CBuildFactoryHelper::InstancePanel( char const *className ) +{ + CBuildFactoryHelper *p = m_sHelpers; + while ( p ) + { + if ( !Q_stricmp( className, p->GetClassName() ) ) + return p->CreatePanel(); + + p = p->GetNext(); + } + return NULL; +} + +// static method +void CBuildFactoryHelper::GetFactoryNames( CUtlVector< char const * >& list ) +{ + list.RemoveAll(); + + CBuildFactoryHelper *p = m_sHelpers; + while ( p ) + { + list.AddToTail( p->GetClassName() ); + p = p->GetNext(); + } +} + + + diff --git a/mp/src/vgui2/vgui_controls/BuildGroup.cpp b/mp/src/vgui2/vgui_controls/BuildGroup.cpp index cf9cae8e..749a20c9 100644 --- a/mp/src/vgui2/vgui_controls/BuildGroup.cpp +++ b/mp/src/vgui2/vgui_controls/BuildGroup.cpp @@ -1,1448 +1,1448 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - //========= Copyright © 1996-2003, Valve LLC, All rights reserved. ============ -// -// The copyright to the contents herein is the property of Valve, L.L.C. -// The contents may be used and/or copied only with the written permission of -// Valve, L.L.C., or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $NoKeywords: $ -//============================================================================= - - -#include -#define PROTECTED_THINGS_DISABLE - -#include "utldict.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include "filesystem.h" - -#if defined( _X360 ) -#include "xbox/xbox_win32stubs.h" -#endif - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Handle table -//----------------------------------------------------------------------------- -IMPLEMENT_HANDLES( BuildGroup, 20 ) - - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -BuildGroup::BuildGroup(Panel *parentPanel, Panel *contextPanel) -{ - CONSTRUCT_HANDLE( ); - - _enabled=false; - _snapX=1; - _snapY=1; - _cursor_sizenwse = dc_sizenwse; - _cursor_sizenesw = dc_sizenesw; - _cursor_sizewe = dc_sizewe; - _cursor_sizens = dc_sizens; - _cursor_sizeall = dc_sizeall; - _currentPanel=null; - _dragging=false; - m_pResourceName=NULL; - m_pResourcePathID = NULL; - m_hBuildDialog=NULL; - m_pParentPanel=parentPanel; - for (int i=0; i<4; ++i) - _rulerNumber[i] = NULL; - SetContextPanel(contextPanel); - _showRulers = false; - -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -BuildGroup::~BuildGroup() -{ - if (m_hBuildDialog) - delete m_hBuildDialog.Get(); - m_hBuildDialog = NULL; - - delete [] m_pResourceName; - delete [] m_pResourcePathID; - - for (int i=0; i <4; ++i) - { - if (_rulerNumber[i]) - { - delete _rulerNumber[i]; - _rulerNumber[i]= NULL; - } - } - - DESTRUCT_HANDLE(); -} - -//----------------------------------------------------------------------------- -// Purpose: Toggles build mode on/off -// Input : state - new state -//----------------------------------------------------------------------------- -void BuildGroup::SetEnabled(bool state) -{ - if(_enabled != state) - { - _enabled = state; - _currentPanel = NULL; - - if ( state ) - { - ActivateBuildDialog(); - } - else - { - // hide the build dialog - if ( m_hBuildDialog ) - { - m_hBuildDialog->OnCommand("Close"); - } - - // request focus for our main panel - m_pParentPanel->RequestFocus(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Check if buildgroup is enabled -//----------------------------------------------------------------------------- -bool BuildGroup::IsEnabled() -{ - return _enabled; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the list of panels that are currently selected -//----------------------------------------------------------------------------- -CUtlVector *BuildGroup::GetControlGroup() -{ - return &_controlGroup; -} - -//----------------------------------------------------------------------------- -// Purpose: Check if ruler display is activated -//----------------------------------------------------------------------------- -bool BuildGroup::HasRulersOn() -{ - return _showRulers; -} - -//----------------------------------------------------------------------------- -// Purpose: Toggle ruler display -//----------------------------------------------------------------------------- -void BuildGroup::ToggleRulerDisplay() -{ - _showRulers = !_showRulers; - - if (_rulerNumber[0] == NULL) // rulers haven't been initialized - { - _rulerNumber[0] = new Label(m_pBuildContext, NULL, ""); - _rulerNumber[1] = new Label(m_pBuildContext, NULL, ""); - _rulerNumber[2] = new Label(m_pBuildContext, NULL, ""); - _rulerNumber[3] = new Label(m_pBuildContext, NULL, ""); - } - SetRulerLabelsVisible(_showRulers); - - m_pBuildContext->Repaint(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Tobble visibility of ruler number labels -//----------------------------------------------------------------------------- -void BuildGroup::SetRulerLabelsVisible(bool state) -{ - _rulerNumber[0]->SetVisible(state); - _rulerNumber[1]->SetVisible(state); - _rulerNumber[2]->SetVisible(state); - _rulerNumber[3]->SetVisible(state); -} - -void BuildGroup::ApplySchemeSettings( IScheme *pScheme ) -{ - DrawRulers(); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw Rulers on screen if conditions are right -//----------------------------------------------------------------------------- -void BuildGroup::DrawRulers() -{ - // don't draw if visibility is off - if (!_showRulers) - { - return; - } - - // no drawing if we selected the context panel - if (m_pBuildContext == _currentPanel) - { - SetRulerLabelsVisible(false); - return; - } - else - SetRulerLabelsVisible(true); - - int x, y, wide, tall; - // get base panel's postition - m_pBuildContext->GetBounds(x, y, wide, tall); - m_pBuildContext->ScreenToLocal(x,y); - - int cx, cy, cwide, ctall; - _currentPanel->GetBounds (cx, cy, cwide, ctall); - - surface()->PushMakeCurrent(m_pBuildContext->GetVPanel(), false); - - // draw rulers - surface()->DrawSetColor(255, 255, 255, 255); // white color - - surface()->DrawFilledRect(0, cy, cx, cy+1); //top horiz left - surface()->DrawFilledRect(cx+cwide, cy, wide, cy+1); //top horiz right - - surface()->DrawFilledRect(0, cy+ctall-1, cx, cy+ctall); //bottom horiz left - surface()->DrawFilledRect(cx+cwide, cy+ctall-1, wide, cy+ctall); //bottom horiz right - - surface()->DrawFilledRect(cx,0,cx+1,cy); //top vert left - surface()->DrawFilledRect(cx+cwide-1,0, cx+cwide, cy); //top vert right - - surface()->DrawFilledRect(cx,cy+ctall, cx+1, tall); //bottom vert left - surface()->DrawFilledRect(cx+cwide-1, cy+ctall, cx+cwide, tall); //bottom vert right - - surface()->PopMakeCurrent(m_pBuildContext->GetVPanel()); - - // now let's put numbers with the rulers - char textstring[20]; - Q_snprintf (textstring, sizeof( textstring ), "%d", cx); - _rulerNumber[0]->SetText(textstring); - int twide, ttall; - _rulerNumber[0]->GetContentSize(twide,ttall); - _rulerNumber[0]->SetSize(twide,ttall); - _rulerNumber[0]->SetPos(cx/2-twide/2, cy-ttall+3); - - Q_snprintf (textstring, sizeof( textstring ), "%d", cy); - _rulerNumber[1]->SetText(textstring); - _rulerNumber[1]->GetContentSize(twide,ttall); - _rulerNumber[1]->SetSize(twide,ttall); - _rulerNumber[1]->GetSize(twide,ttall); - _rulerNumber[1]->SetPos(cx-twide + 3, cy/2-ttall/2); - - Q_snprintf (textstring, sizeof( textstring ), "%d", cy); - _rulerNumber[2]->SetText(textstring); - _rulerNumber[2]->GetContentSize(twide,ttall); - _rulerNumber[2]->SetSize(twide,ttall); - _rulerNumber[2]->SetPos(cx+cwide+(wide-cx-cwide)/2 - twide/2, cy+ctall-3); - - Q_snprintf (textstring, sizeof( textstring ), "%d", cy); - _rulerNumber[3]->SetText(textstring); - _rulerNumber[3]->GetContentSize(twide,ttall); - _rulerNumber[3]->SetSize(twide,ttall); - _rulerNumber[3]->SetPos(cx+cwide, cy+ctall+(tall-cy-ctall)/2 - ttall/2); - -} - -//----------------------------------------------------------------------------- -// Purpose: respond to cursor movments -//----------------------------------------------------------------------------- -bool BuildGroup::CursorMoved(int x, int y, Panel *panel) -{ - Assert(panel); - - if ( !m_hBuildDialog.Get() ) - { - if ( panel->GetParent() ) - { - EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() ); - if ( ep ) - { - BuildGroup *bg = ep->GetBuildGroup(); - if ( bg && bg != this ) - { - bg->CursorMoved( x, y, panel ); - } - } - } - return false; - } - - // no moving uneditable panels - // commented out because this has issues with panels moving - // to front and obscuring other panels - //if (!panel->IsBuildModeEditable()) - // return; - - if (_dragging) - { - input()->GetCursorPos(x, y); - - if (_dragMouseCode == MOUSE_RIGHT) - { - int newW = max( 1, _dragStartPanelSize[ 0 ] + x - _dragStartCursorPos[0] ); - int newH = max( 1, _dragStartPanelSize[ 1 ] + y - _dragStartCursorPos[1] ); - - bool shift = ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ); - bool ctrl = ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ); - - if ( shift ) - { - newW = _dragStartPanelSize[ 0 ]; - } - if ( ctrl ) - { - newH = _dragStartPanelSize[ 1 ]; - } - - panel->SetSize( newW, newH ); - ApplySnap(panel); - } - else - { - for (int i=0; i < _controlGroup.Count(); ++i) - { - // now fix offset of member panels with respect to the one we are dragging - Panel *groupMember = _controlGroup[i].Get(); - groupMember->SetPos(_dragStartPanelPos[0] + _groupDeltaX[i] +(x-_dragStartCursorPos[0]), _dragStartPanelPos[1] + _groupDeltaY[i] +(y-_dragStartCursorPos[1])); - ApplySnap(groupMember); - } - } - - // update the build dialog - if (m_hBuildDialog) - { - KeyValues *keyval = new KeyValues("UpdateControlData"); - keyval->SetPtr("panel", GetCurrentPanel()); - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL); - - keyval = new KeyValues("EnableSaveButton"); - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL); - } - - panel->Repaint(); - panel->CallParentFunction(new KeyValues("Repaint")); - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool BuildGroup::MousePressed(MouseCode code, Panel *panel) -{ - Assert(panel); - - if ( !m_hBuildDialog.Get() ) - { - if ( panel->GetParent() ) - { - EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() ); - if ( ep ) - { - BuildGroup *bg = ep->GetBuildGroup(); - if ( bg && bg != this ) - { - bg->MousePressed( code, panel ); - } - } - } - return false; - } - - // if people click on the base build dialog panel. - if (panel == m_hBuildDialog) - { - // hide the click menu if its up - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("HideNewControlMenu"), NULL); - return true; - } - - // don't select unnamed items - if (strlen(panel->GetName()) < 1) - return true; - - bool shift = ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ); - if (!shift) - { - _controlGroup.RemoveAll(); - } - - // Show new ctrl menu if they click on the bg (not on a subcontrol) - if ( code == MOUSE_RIGHT && panel == GetContextPanel()) - { - // trigger a drop down menu to create new controls - ivgui()->PostMessage (m_hBuildDialog->GetVPanel(), new KeyValues("ShowNewControlMenu"), NULL); - } - else - { - // don't respond if we click on ruler numbers - if (_showRulers) // rulers are visible - { - for ( int i=0; i < 4; i++) - { - if ( panel == _rulerNumber[i]) - return true; - } - } - - _dragging = true; - _dragMouseCode = code; - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("HideNewControlMenu"), NULL); - - int x, y; - input()->GetCursorPos(x, y); - - _dragStartCursorPos[0] = x; - _dragStartCursorPos[1] = y; - - - input()->SetMouseCapture(panel->GetVPanel()); - - _groupDeltaX.RemoveAll(); - _groupDeltaY.RemoveAll(); - - // basepanel is the panel that all the deltas will be calculated from. - // it is the last panel we clicked in because if we move the panels as a group - // it would be from that one - Panel *basePanel = NULL; - // find the panel we clicked in, that is the base panel - // it might already be in the group - for (int i=0; i< _controlGroup.Count(); ++i) - { - if (panel == _controlGroup[i].Get()) - { - basePanel = panel; - break; - } - } - - // if its not in the group we just added this panel. get it in the group - if (basePanel == NULL) - { - PHandle temp; - temp = panel; - _controlGroup.AddToTail(temp); - basePanel = panel; - } - - basePanel->GetPos(x,y); - _dragStartPanelPos[0]=x; - _dragStartPanelPos[1]=y; - - basePanel->GetSize( _dragStartPanelSize[ 0 ], _dragStartPanelSize[ 1 ] ); - - // figure out the deltas of the other panels from the base panel - for (int i=0; i<_controlGroup.Count(); ++i) - { - int cx, cy; - _controlGroup[i].Get()->GetPos(cx, cy); - _groupDeltaX.AddToTail(cx - x); - _groupDeltaY.AddToTail(cy - y); - } - - // if this panel wasn't already selected update the buildmode dialog controls to show its info - if(_currentPanel != panel) - { - _currentPanel = panel; - - if ( m_hBuildDialog ) - { - // think this is taken care of by SetActiveControl. - //ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("ApplyDataToControls"), NULL); - - KeyValues *keyval = new KeyValues("SetActiveControl"); - keyval->SetPtr("PanelPtr", GetCurrentPanel()); - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL); - } - } - - // store undo information upon panel selection. - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("StoreUndo"), NULL); - - panel->RequestFocus(); - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool BuildGroup::MouseReleased(MouseCode code, Panel *panel) -{ - if ( !m_hBuildDialog.Get() ) - { - if ( panel->GetParent() ) - { - EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() ); - if ( ep ) - { - BuildGroup *bg = ep->GetBuildGroup(); - if ( bg && bg != this ) - { - bg->MouseReleased( code, panel ); - } - } - } - return false; - } - - Assert(panel); - - _dragging=false; - input()->SetMouseCapture(null); - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool BuildGroup::MouseDoublePressed(MouseCode code, Panel *panel) -{ - Assert(panel); - return MousePressed( code, panel ); -} - -bool BuildGroup::KeyTyped( wchar_t unichar, Panel *panel ) -{ - if ( !m_hBuildDialog.Get() ) - { - if ( panel->GetParent() ) - { - EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() ); - if ( ep ) - { - BuildGroup *bg = ep->GetBuildGroup(); - if ( bg && bg != this ) - { - bg->KeyTyped( unichar, panel ); - } - } - } - return false; - } - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool BuildGroup::KeyCodeTyped(KeyCode code, Panel *panel) -{ - if ( !m_hBuildDialog.Get() ) - { - if ( panel->GetParent() ) - { - EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() ); - if ( ep ) - { - BuildGroup *bg = ep->GetBuildGroup(); - if ( bg && bg != this ) - { - bg->KeyCodeTyped( code, panel ); - } - } - } - return false; - } - - Assert(panel); - - int dx=0; - int dy=0; - - bool shift = ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ); - bool ctrl = ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ); - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - - - if ( ctrl && shift && alt && code == KEY_B) - { - // enable build mode - EditablePanel *ep = dynamic_cast< EditablePanel * >( panel ); - if ( ep ) - { - ep->ActivateBuildMode(); - } - return true; - } - - switch (code) - { - case KEY_LEFT: - { - dx-=_snapX; - break; - } - case KEY_RIGHT: - { - dx+=_snapX; - break; - } - case KEY_UP: - { - dy-=_snapY; - break; - } - case KEY_DOWN: - { - dy+=_snapY; - break; - } - case KEY_DELETE: - { - // delete the panel we have selected - ivgui()->PostMessage (m_hBuildDialog->GetVPanel(), new KeyValues ("DeletePanel"), NULL); - break; - } - - } - - if (ctrl) - { - switch (code) - { - case KEY_Z: - { - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("Undo"), NULL); - break; - } - - case KEY_C: - { - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("Copy"), NULL); - break; - } - case KEY_V: - { - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("Paste"), NULL); - break; - } - } - } - - if(dx||dy) - { - //TODO: make this stuff actually snap - - int x,y,wide,tall; - - panel->GetBounds(x,y,wide,tall); - - if(shift) - { - panel->SetSize(wide+dx,tall+dy); - } - else - { - panel->SetPos(x+dx,y+dy); - } - - ApplySnap(panel); - - panel->Repaint(); - if (panel->GetVParent() != 0) - { - panel->PostMessage(panel->GetVParent(), new KeyValues("Repaint")); - } - - - // update the build dialog - if (m_hBuildDialog) - { - // post that it's active - KeyValues *keyval = new KeyValues("SetActiveControl"); - keyval->SetPtr("PanelPtr", GetCurrentPanel()); - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL); - - // post that it's been changed - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("PanelMoved"), NULL); - } - } - - // If holding key while dragging, simulate moving cursor so shift/ctrl key changes take effect - if ( _dragging && panel != GetContextPanel() ) - { - int x, y; - input()->GetCursorPos( x, y ); - CursorMoved( x, y, panel ); - } - - return true; -} - -bool BuildGroup::KeyCodeReleased(KeyCode code, Panel *panel ) -{ - if ( !m_hBuildDialog.Get() ) - { - if ( panel->GetParent() ) - { - EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() ); - if ( ep ) - { - BuildGroup *bg = ep->GetBuildGroup(); - if ( bg && bg != this ) - { - bg->KeyCodeTyped( code, panel ); - } - } - } - return false; - } - - // If holding key while dragging, simulate moving cursor so shift/ctrl key changes take effect - if ( _dragging && panel != GetContextPanel() ) - { - int x, y; - input()->GetCursorPos( x, y ); - CursorMoved( x, y, panel ); - } - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Searches for a BuildModeDialog in the hierarchy -//----------------------------------------------------------------------------- -Panel *BuildGroup::CreateBuildDialog( void ) -{ - // request the panel - Panel *buildDialog = NULL; - KeyValues *data = new KeyValues("BuildDialog"); - data->SetPtr("BuildGroupPtr", this); - if (m_pBuildContext->RequestInfo(data)) - { - buildDialog = (Panel *)data->GetPtr("PanelPtr"); - } - - // initialize the build dialog if found - if ( buildDialog ) - { - input()->ReleaseAppModalSurface(); - } - - return buildDialog; -} - -//----------------------------------------------------------------------------- -// Purpose: Activates the build mode settings dialog -//----------------------------------------------------------------------------- -void BuildGroup::ActivateBuildDialog( void ) -{ - // create the build mode dialog first time through - if (!m_hBuildDialog.Get()) - { - m_hBuildDialog = CreateBuildDialog(); - - if (!m_hBuildDialog.Get()) - return; - } - - m_hBuildDialog->SetVisible( true ); - - // send a message to set the initial dialog controls info - _currentPanel = m_pParentPanel; - KeyValues *keyval = new KeyValues("SetActiveControl"); - keyval->SetPtr("PanelPtr", GetCurrentPanel()); - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -HCursor BuildGroup::GetCursor(Panel *panel) -{ - Assert(panel); - - int x,y,wide,tall; - input()->GetCursorPos(x,y); - panel->ScreenToLocal(x,y); - panel->GetSize(wide,tall); - - if(x < 2) - { - if(y < 4) - { - return _cursor_sizenwse; - } - else - if(y<(tall-4)) - { - return _cursor_sizewe; - } - else - { - return _cursor_sizenesw; - } - } - - return _cursor_sizeall; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void BuildGroup::ApplySnap(Panel *panel) -{ - Assert(panel); - - int x,y,wide,tall; - panel->GetBounds(x,y,wide,tall); - - x=(x/_snapX)*_snapX; - y=(y/_snapY)*_snapY; - panel->SetPos(x,y); - - int xx,yy; - xx=x+wide; - yy=y+tall; - - xx=(xx/_snapX)*_snapX; - yy=(yy/_snapY)*_snapY; - panel->SetSize(xx-x,yy-y); -} - -//----------------------------------------------------------------------------- -// Purpose: Return the currently selected panel -//----------------------------------------------------------------------------- -Panel *BuildGroup::GetCurrentPanel() -{ - return _currentPanel; -} - -//----------------------------------------------------------------------------- -// Purpose: Add panel the list of panels that are in the build group -//----------------------------------------------------------------------------- -void BuildGroup::PanelAdded(Panel *panel) -{ - Assert(panel); - - PHandle temp; - temp = panel; - int c = _panelDar.Count(); - for ( int i = 0; i < c; ++i ) - { - if ( _panelDar[ i ] == temp ) - { - return; - } - } - _panelDar.AddToTail(temp); -} - -//----------------------------------------------------------------------------- -// Purpose: loads the control settings from file -//----------------------------------------------------------------------------- -void BuildGroup::LoadControlSettings(const char *controlResourceName, const char *pathID, KeyValues *pPreloadedKeyValues, KeyValues *pConditions) -{ - // make sure the file is registered - RegisterControlSettingsFile(controlResourceName, pathID); - - // Use the keyvalues they passed in or load them. - KeyValues *rDat = pPreloadedKeyValues; - if ( !rDat ) - { - // load the resource data from the file - rDat = new KeyValues(controlResourceName); - - // check the skins directory first, if an explicit pathID hasn't been set - bool bSuccess = false; - if (!pathID) - { - bSuccess = rDat->LoadFromFile(g_pFullFileSystem, controlResourceName, "SKIN"); - } - if (!bSuccess) - { - bSuccess = rDat->LoadFromFile(g_pFullFileSystem, controlResourceName, pathID); - } - - if ( bSuccess ) - { - if ( IsX360() ) - { - rDat->ProcessResolutionKeys( surface()->GetResolutionKey() ); - } - if ( IsPC() ) - { - ConVarRef cl_hud_minmode( "cl_hud_minmode", true ); - if ( cl_hud_minmode.IsValid() && cl_hud_minmode.GetBool() ) - { - rDat->ProcessResolutionKeys( "_minmode" ); - } - } - - if ( pConditions && pConditions->GetFirstSubKey() ) - { - ProcessConditionalKeys( rDat, pConditions ); - } - } - } - - // save off the resource name - delete [] m_pResourceName; - m_pResourceName = new char[strlen(controlResourceName) + 1]; - strcpy(m_pResourceName, controlResourceName); - - if (pathID) - { - delete [] m_pResourcePathID; - m_pResourcePathID = new char[strlen(pathID) + 1]; - strcpy(m_pResourcePathID, pathID); - } - - // delete any controls not in both files - DeleteAllControlsCreatedByControlSettingsFile(); - - // loop through the resource data sticking info into controls - ApplySettings(rDat); - - if (m_pParentPanel) - { - m_pParentPanel->InvalidateLayout(); - m_pParentPanel->Repaint(); - } - - if ( rDat != pPreloadedKeyValues ) - { - rDat->deleteThis(); - } -} - -void BuildGroup::ProcessConditionalKeys( KeyValues *pData, KeyValues *pConditions ) -{ - // for each condition, look for it in keys - // if its a positive condition, promote all of its children, replacing values - - if ( pData ) - { - KeyValues *pSubKey = pData->GetFirstSubKey(); - if ( !pSubKey ) - { - // not a block - return; - } - - for ( ; pSubKey != NULL; pSubKey = pSubKey->GetNextKey() ) - { - // recursively descend each sub block - ProcessConditionalKeys( pSubKey, pConditions ); - - KeyValues *pCondition = pConditions->GetFirstSubKey(); - for ( ; pCondition != NULL; pCondition = pCondition->GetNextKey() ) - { - // if we match any conditions in this sub block, copy up - KeyValues *pConditionBlock = pSubKey->FindKey( pCondition->GetName() ); - if ( pConditionBlock ) - { - KeyValues *pOverridingKey; - for ( pOverridingKey = pConditionBlock->GetFirstSubKey(); pOverridingKey != NULL; pOverridingKey = pOverridingKey->GetNextKey() ) - { - KeyValues *pExistingKey = pSubKey->FindKey( pOverridingKey->GetName() ); - if ( pExistingKey ) - { - pExistingKey->SetStringValue( pOverridingKey->GetString() ); - } - else - { - KeyValues *copy = pOverridingKey->MakeCopy(); - pSubKey->AddSubKey( copy ); - } - } - } - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: registers that a control settings file may be loaded -// use when the dialog may have multiple states and the editor will need to be able to switch between them -//----------------------------------------------------------------------------- -void BuildGroup::RegisterControlSettingsFile(const char *controlResourceName, const char *pathID) -{ - // add the file into a list for build mode - CUtlSymbol sym(controlResourceName); - if (!m_RegisteredControlSettingsFiles.IsValidIndex(m_RegisteredControlSettingsFiles.Find(sym))) - { - m_RegisteredControlSettingsFiles.AddToTail(sym); - } -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor / iterator -//----------------------------------------------------------------------------- -int BuildGroup::GetRegisteredControlSettingsFileCount() -{ - return m_RegisteredControlSettingsFiles.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -const char *BuildGroup::GetRegisteredControlSettingsFileByIndex(int index) -{ - return m_RegisteredControlSettingsFiles[index].String(); -} - -//----------------------------------------------------------------------------- -// Purpose: reloads the control settings from file -//----------------------------------------------------------------------------- -void BuildGroup::ReloadControlSettings() -{ - delete m_hBuildDialog.Get(); - m_hBuildDialog = NULL; - - // loop though objects in the current control group and remove them all - // the 0th panel is always the contextPanel which is not deletable - for( int i = 1; i < _panelDar.Count(); i++ ) - { - if (!_panelDar[i].Get()) // this can happen if we had two of the same handle in the list - { - _panelDar.Remove(i); - --i; - continue; - } - - // only delete deletable panels, as the only deletable panels - // are the ones created using the resource file - if ( _panelDar[i].Get()->IsBuildModeDeletable()) - { - delete _panelDar[i].Get(); - _panelDar.Remove(i); - --i; - } - } - - if (m_pResourceName) - { - EditablePanel *edit = dynamic_cast(m_pParentPanel); - if (edit) - { - edit->LoadControlSettings(m_pResourceName, m_pResourcePathID); - } - else - { - LoadControlSettings(m_pResourceName, m_pResourcePathID); - } - } - - _controlGroup.RemoveAll(); - - ActivateBuildDialog(); -} - -//----------------------------------------------------------------------------- -// Purpose: changes which control settings are currently loaded -//----------------------------------------------------------------------------- -void BuildGroup::ChangeControlSettingsFile(const char *controlResourceName) -{ - // clear any current state - _controlGroup.RemoveAll(); - _currentPanel = m_pParentPanel; - - // load the new state, via the dialog if possible - EditablePanel *edit = dynamic_cast(m_pParentPanel); - if (edit) - { - edit->LoadControlSettings(controlResourceName, m_pResourcePathID); - } - else - { - LoadControlSettings(controlResourceName, m_pResourcePathID); - } - - // force it to update - KeyValues *keyval = new KeyValues("SetActiveControl"); - keyval->SetPtr("PanelPtr", GetCurrentPanel()); - ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL); -} - -//----------------------------------------------------------------------------- -// Purpose: saves control settings to file -//----------------------------------------------------------------------------- -bool BuildGroup::SaveControlSettings( void ) -{ - bool bSuccess = false; - if ( m_pResourceName ) - { - KeyValues *rDat = new KeyValues( m_pResourceName ); - - // get the data from our controls - GetSettings( rDat ); - - char fullpath[ 512 ]; - g_pFullFileSystem->RelativePathToFullPath( m_pResourceName, m_pResourcePathID, fullpath, sizeof( fullpath ) ); - - // save the data out to a file - bSuccess = rDat->SaveToFile( g_pFullFileSystem, fullpath, NULL ); - if (!bSuccess) - { - MessageBox *dlg = new MessageBox("BuildMode - Error saving file", "Error: Could not save changes. File is most likely read only."); - dlg->DoModal(); - } - - rDat->deleteThis(); - } - - return bSuccess; -} - -//----------------------------------------------------------------------------- -// Purpose: Deletes all the controls not created by the code -//----------------------------------------------------------------------------- -void BuildGroup::DeleteAllControlsCreatedByControlSettingsFile() -{ - // loop though objects in the current control group and remove them all - // the 0th panel is always the contextPanel which is not deletable - for ( int i = 1; i < _panelDar.Count(); i++ ) - { - if (!_panelDar[i].Get()) // this can happen if we had two of the same handle in the list - { - _panelDar.Remove(i); - --i; - continue; - } - - // only delete deletable panels, as the only deletable panels - // are the ones created using the resource file - if ( _panelDar[i].Get()->IsBuildModeDeletable()) - { - delete _panelDar[i].Get(); - _panelDar.Remove(i); - --i; - } - } - - _currentPanel = m_pBuildContext; - _currentPanel->InvalidateLayout(); - m_pBuildContext->Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: serializes settings from a resource data container -//----------------------------------------------------------------------------- -void BuildGroup::ApplySettings( KeyValues *resourceData ) -{ - // loop through all the keys, applying them wherever - for (KeyValues *controlKeys = resourceData->GetFirstSubKey(); controlKeys != NULL; controlKeys = controlKeys->GetNextKey()) - { - bool bFound = false; - - // Skip keys that are atomic.. - if (controlKeys->GetDataType() != KeyValues::TYPE_NONE) - continue; - - char const *keyName = controlKeys->GetName(); - - // check to see if any buildgroup panels have this name - for ( int i = 0; i < _panelDar.Count(); i++ ) - { - Panel *panel = _panelDar[i].Get(); - - if (!panel) // this can happen if we had two of the same handle in the list - { - _panelDar.Remove(i); - --i; - continue; - } - - - Assert (panel); - - // make the control name match CASE INSENSITIVE! - char const *panelName = panel->GetName(); - - if (!Q_stricmp(panelName, keyName)) - { - // apply the settings - panel->ApplySettings(controlKeys); - bFound = true; - break; - } - } - - if ( !bFound ) - { - // the key was not found in the registered list, check to see if we should create it - if ( keyName /*controlKeys->GetInt("AlwaysCreate", false)*/ ) - { - // create the control even though it wasn't registered - NewControl( controlKeys ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Create a new control in the context panel -// Input: name: class name of control to create -// controlKeys: keyvalues of settings for the panel. -// name OR controlKeys should be set, not both. -// x,y position relative to base panel -// Output: Panel *newPanel, NULL if failed to create new control. -//----------------------------------------------------------------------------- -Panel *BuildGroup::NewControl( const char *name, int x, int y) -{ - Assert (name); - - Panel *newPanel = NULL; - // returns NULL on failure - newPanel = static_cast(m_pParentPanel)->CreateControlByName(name); - - if (newPanel) - { - // panel successfully created - newPanel->SetParent(m_pParentPanel); - newPanel->SetBuildGroup(this); - newPanel->SetPos(x, y); - - char newFieldName[255]; - GetNewFieldName(newFieldName, sizeof(newFieldName), newPanel); - newPanel->SetName(newFieldName); - - newPanel->AddActionSignalTarget(m_pParentPanel); - newPanel->SetBuildModeEditable(true); - newPanel->SetBuildModeDeletable(true); - - // make sure it gets freed - newPanel->SetAutoDelete(true); - } - - return newPanel; -} - -//----------------------------------------------------------------------------- -// Purpose: Create a new control in the context panel -// Input: controlKeys: keyvalues of settings for the panel only works when applying initial settings. -// Output: Panel *newPanel, NULL if failed to create new control. -//----------------------------------------------------------------------------- -Panel *BuildGroup::NewControl( KeyValues *controlKeys, int x, int y) -{ - Assert (controlKeys); - - Panel *newPanel = NULL; - if (controlKeys) - { -// Warning( "Creating new control \"%s\" of type \"%s\"\n", controlKeys->GetString( "fieldName" ), controlKeys->GetString( "ControlName" ) ); - KeyValues *keyVal = new KeyValues("ControlFactory", "ControlName", controlKeys->GetString("ControlName")); - m_pBuildContext->RequestInfo(keyVal); - // returns NULL on failure - newPanel = (Panel *)keyVal->GetPtr("PanelPtr"); - keyVal->deleteThis(); - } - else - { - return NULL; - } - - if (newPanel) - { - // panel successfully created - newPanel->SetParent(m_pParentPanel); - newPanel->SetBuildGroup(this); - newPanel->SetPos(x, y); - - newPanel->SetName(controlKeys->GetName()); // name before applysettings :) - newPanel->ApplySettings(controlKeys); - - newPanel->AddActionSignalTarget(m_pParentPanel); - newPanel->SetBuildModeEditable(true); - newPanel->SetBuildModeDeletable(true); - - // make sure it gets freed - newPanel->SetAutoDelete(true); - } - - return newPanel; -} - -//----------------------------------------------------------------------------- -// Purpose: Get a new unique fieldname for a new control -//----------------------------------------------------------------------------- -void BuildGroup::GetNewFieldName(char *newFieldName, int newFieldNameSize, Panel *newPanel) -{ - int fieldNameNumber=1; - char defaultName[25]; - - Q_strncpy( defaultName, newPanel->GetClassName(), sizeof( defaultName ) ); - - while (1) - { - Q_snprintf (newFieldName, newFieldNameSize, "%s%d", defaultName, fieldNameNumber); - if ( FieldNameTaken(newFieldName) == NULL) - break; - ++fieldNameNumber; - } -} - -//----------------------------------------------------------------------------- -// Purpose: check to see if any buildgroup panels have this fieldname -// Input : fieldName, name to check -// Output : ptr to a panel that has the name if it is taken -//----------------------------------------------------------------------------- -Panel *BuildGroup::FieldNameTaken(const char *fieldName) -{ - for ( int i = 0; i < _panelDar.Count(); i++ ) - { - Panel *panel = _panelDar[i].Get(); - if ( !panel ) - continue; - - if (!stricmp(panel->GetName(), fieldName) ) - { - return panel; - } - } - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: serializes settings to a resource data container -//----------------------------------------------------------------------------- -void BuildGroup::GetSettings( KeyValues *resourceData ) -{ - // loop through all the objects getting their settings - for( int i = 0; i < _panelDar.Count(); i++ ) - { - Panel *panel = _panelDar[i].Get(); - if (!panel) - continue; - - bool isRuler = false; - // do not get setting for ruler labels. - if (_showRulers) // rulers are visible - { - for (int i = 0; i < 4; i++) - { - if (panel == _rulerNumber[i]) - { - isRuler = true; - break; - } - } - if (isRuler) - { - isRuler = false; - continue; - } - } - - // Don't save the setting of the buildmodedialog - if (!stricmp(panel->GetName(), "BuildDialog")) - continue; - - // get the keys section from the data file - if (panel->GetName() && *panel->GetName()) - { - KeyValues *datKey = resourceData->FindKey(panel->GetName(), true); - - // get the settings - panel->GetSettings(datKey); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: loop though objects in the current control group and remove them all -//----------------------------------------------------------------------------- -void BuildGroup::RemoveSettings() -{ - // loop though objects in the current control group and remove them all - int i; - for( i = 0; i < _controlGroup.Count(); i++ ) - { - // only delete delatable panels - if ( _controlGroup[i].Get()->IsBuildModeDeletable()) - { - delete _controlGroup[i].Get(); - _controlGroup.Remove(i); - --i; - } - } - - // remove deleted panels from the handle list - for( i = 0; i < _panelDar.Count(); i++ ) - { - if ( !_panelDar[i].Get() ) - { - _panelDar.Remove(i); - --i; - } - } - - _currentPanel = m_pBuildContext; - _currentPanel->InvalidateLayout(); - m_pBuildContext->Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: sets the panel from which the build group gets all it's object creation info -//----------------------------------------------------------------------------- -void BuildGroup::SetContextPanel(Panel *contextPanel) -{ - m_pBuildContext = contextPanel; -} - -//----------------------------------------------------------------------------- -// Purpose: gets the panel from which the build group gets all it's object creation info -//----------------------------------------------------------------------------- -Panel *BuildGroup::GetContextPanel() -{ - return m_pBuildContext; -} - -//----------------------------------------------------------------------------- -// Purpose: get the list of panels in the buildgroup -//----------------------------------------------------------------------------- -CUtlVector *BuildGroup::GetPanelList() -{ - return &_panelDar; -} - -//----------------------------------------------------------------------------- -// Purpose: dialog variables -//----------------------------------------------------------------------------- -KeyValues *BuildGroup::GetDialogVariables() -{ - EditablePanel *edit = dynamic_cast(m_pParentPanel); - if (edit) - { - return edit->GetDialogVariables(); - } - - return NULL; -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + //========= Copyright © 1996-2003, Valve LLC, All rights reserved. ============ +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + + +#include +#define PROTECTED_THINGS_DISABLE + +#include "utldict.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "filesystem.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Handle table +//----------------------------------------------------------------------------- +IMPLEMENT_HANDLES( BuildGroup, 20 ) + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +BuildGroup::BuildGroup(Panel *parentPanel, Panel *contextPanel) +{ + CONSTRUCT_HANDLE( ); + + _enabled=false; + _snapX=1; + _snapY=1; + _cursor_sizenwse = dc_sizenwse; + _cursor_sizenesw = dc_sizenesw; + _cursor_sizewe = dc_sizewe; + _cursor_sizens = dc_sizens; + _cursor_sizeall = dc_sizeall; + _currentPanel=null; + _dragging=false; + m_pResourceName=NULL; + m_pResourcePathID = NULL; + m_hBuildDialog=NULL; + m_pParentPanel=parentPanel; + for (int i=0; i<4; ++i) + _rulerNumber[i] = NULL; + SetContextPanel(contextPanel); + _showRulers = false; + +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +BuildGroup::~BuildGroup() +{ + if (m_hBuildDialog) + delete m_hBuildDialog.Get(); + m_hBuildDialog = NULL; + + delete [] m_pResourceName; + delete [] m_pResourcePathID; + + for (int i=0; i <4; ++i) + { + if (_rulerNumber[i]) + { + delete _rulerNumber[i]; + _rulerNumber[i]= NULL; + } + } + + DESTRUCT_HANDLE(); +} + +//----------------------------------------------------------------------------- +// Purpose: Toggles build mode on/off +// Input : state - new state +//----------------------------------------------------------------------------- +void BuildGroup::SetEnabled(bool state) +{ + if(_enabled != state) + { + _enabled = state; + _currentPanel = NULL; + + if ( state ) + { + ActivateBuildDialog(); + } + else + { + // hide the build dialog + if ( m_hBuildDialog ) + { + m_hBuildDialog->OnCommand("Close"); + } + + // request focus for our main panel + m_pParentPanel->RequestFocus(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Check if buildgroup is enabled +//----------------------------------------------------------------------------- +bool BuildGroup::IsEnabled() +{ + return _enabled; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the list of panels that are currently selected +//----------------------------------------------------------------------------- +CUtlVector *BuildGroup::GetControlGroup() +{ + return &_controlGroup; +} + +//----------------------------------------------------------------------------- +// Purpose: Check if ruler display is activated +//----------------------------------------------------------------------------- +bool BuildGroup::HasRulersOn() +{ + return _showRulers; +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle ruler display +//----------------------------------------------------------------------------- +void BuildGroup::ToggleRulerDisplay() +{ + _showRulers = !_showRulers; + + if (_rulerNumber[0] == NULL) // rulers haven't been initialized + { + _rulerNumber[0] = new Label(m_pBuildContext, NULL, ""); + _rulerNumber[1] = new Label(m_pBuildContext, NULL, ""); + _rulerNumber[2] = new Label(m_pBuildContext, NULL, ""); + _rulerNumber[3] = new Label(m_pBuildContext, NULL, ""); + } + SetRulerLabelsVisible(_showRulers); + + m_pBuildContext->Repaint(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Tobble visibility of ruler number labels +//----------------------------------------------------------------------------- +void BuildGroup::SetRulerLabelsVisible(bool state) +{ + _rulerNumber[0]->SetVisible(state); + _rulerNumber[1]->SetVisible(state); + _rulerNumber[2]->SetVisible(state); + _rulerNumber[3]->SetVisible(state); +} + +void BuildGroup::ApplySchemeSettings( IScheme *pScheme ) +{ + DrawRulers(); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw Rulers on screen if conditions are right +//----------------------------------------------------------------------------- +void BuildGroup::DrawRulers() +{ + // don't draw if visibility is off + if (!_showRulers) + { + return; + } + + // no drawing if we selected the context panel + if (m_pBuildContext == _currentPanel) + { + SetRulerLabelsVisible(false); + return; + } + else + SetRulerLabelsVisible(true); + + int x, y, wide, tall; + // get base panel's postition + m_pBuildContext->GetBounds(x, y, wide, tall); + m_pBuildContext->ScreenToLocal(x,y); + + int cx, cy, cwide, ctall; + _currentPanel->GetBounds (cx, cy, cwide, ctall); + + surface()->PushMakeCurrent(m_pBuildContext->GetVPanel(), false); + + // draw rulers + surface()->DrawSetColor(255, 255, 255, 255); // white color + + surface()->DrawFilledRect(0, cy, cx, cy+1); //top horiz left + surface()->DrawFilledRect(cx+cwide, cy, wide, cy+1); //top horiz right + + surface()->DrawFilledRect(0, cy+ctall-1, cx, cy+ctall); //bottom horiz left + surface()->DrawFilledRect(cx+cwide, cy+ctall-1, wide, cy+ctall); //bottom horiz right + + surface()->DrawFilledRect(cx,0,cx+1,cy); //top vert left + surface()->DrawFilledRect(cx+cwide-1,0, cx+cwide, cy); //top vert right + + surface()->DrawFilledRect(cx,cy+ctall, cx+1, tall); //bottom vert left + surface()->DrawFilledRect(cx+cwide-1, cy+ctall, cx+cwide, tall); //bottom vert right + + surface()->PopMakeCurrent(m_pBuildContext->GetVPanel()); + + // now let's put numbers with the rulers + char textstring[20]; + Q_snprintf (textstring, sizeof( textstring ), "%d", cx); + _rulerNumber[0]->SetText(textstring); + int twide, ttall; + _rulerNumber[0]->GetContentSize(twide,ttall); + _rulerNumber[0]->SetSize(twide,ttall); + _rulerNumber[0]->SetPos(cx/2-twide/2, cy-ttall+3); + + Q_snprintf (textstring, sizeof( textstring ), "%d", cy); + _rulerNumber[1]->SetText(textstring); + _rulerNumber[1]->GetContentSize(twide,ttall); + _rulerNumber[1]->SetSize(twide,ttall); + _rulerNumber[1]->GetSize(twide,ttall); + _rulerNumber[1]->SetPos(cx-twide + 3, cy/2-ttall/2); + + Q_snprintf (textstring, sizeof( textstring ), "%d", cy); + _rulerNumber[2]->SetText(textstring); + _rulerNumber[2]->GetContentSize(twide,ttall); + _rulerNumber[2]->SetSize(twide,ttall); + _rulerNumber[2]->SetPos(cx+cwide+(wide-cx-cwide)/2 - twide/2, cy+ctall-3); + + Q_snprintf (textstring, sizeof( textstring ), "%d", cy); + _rulerNumber[3]->SetText(textstring); + _rulerNumber[3]->GetContentSize(twide,ttall); + _rulerNumber[3]->SetSize(twide,ttall); + _rulerNumber[3]->SetPos(cx+cwide, cy+ctall+(tall-cy-ctall)/2 - ttall/2); + +} + +//----------------------------------------------------------------------------- +// Purpose: respond to cursor movments +//----------------------------------------------------------------------------- +bool BuildGroup::CursorMoved(int x, int y, Panel *panel) +{ + Assert(panel); + + if ( !m_hBuildDialog.Get() ) + { + if ( panel->GetParent() ) + { + EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() ); + if ( ep ) + { + BuildGroup *bg = ep->GetBuildGroup(); + if ( bg && bg != this ) + { + bg->CursorMoved( x, y, panel ); + } + } + } + return false; + } + + // no moving uneditable panels + // commented out because this has issues with panels moving + // to front and obscuring other panels + //if (!panel->IsBuildModeEditable()) + // return; + + if (_dragging) + { + input()->GetCursorPos(x, y); + + if (_dragMouseCode == MOUSE_RIGHT) + { + int newW = max( 1, _dragStartPanelSize[ 0 ] + x - _dragStartCursorPos[0] ); + int newH = max( 1, _dragStartPanelSize[ 1 ] + y - _dragStartCursorPos[1] ); + + bool shift = ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ); + bool ctrl = ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ); + + if ( shift ) + { + newW = _dragStartPanelSize[ 0 ]; + } + if ( ctrl ) + { + newH = _dragStartPanelSize[ 1 ]; + } + + panel->SetSize( newW, newH ); + ApplySnap(panel); + } + else + { + for (int i=0; i < _controlGroup.Count(); ++i) + { + // now fix offset of member panels with respect to the one we are dragging + Panel *groupMember = _controlGroup[i].Get(); + groupMember->SetPos(_dragStartPanelPos[0] + _groupDeltaX[i] +(x-_dragStartCursorPos[0]), _dragStartPanelPos[1] + _groupDeltaY[i] +(y-_dragStartCursorPos[1])); + ApplySnap(groupMember); + } + } + + // update the build dialog + if (m_hBuildDialog) + { + KeyValues *keyval = new KeyValues("UpdateControlData"); + keyval->SetPtr("panel", GetCurrentPanel()); + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL); + + keyval = new KeyValues("EnableSaveButton"); + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL); + } + + panel->Repaint(); + panel->CallParentFunction(new KeyValues("Repaint")); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool BuildGroup::MousePressed(MouseCode code, Panel *panel) +{ + Assert(panel); + + if ( !m_hBuildDialog.Get() ) + { + if ( panel->GetParent() ) + { + EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() ); + if ( ep ) + { + BuildGroup *bg = ep->GetBuildGroup(); + if ( bg && bg != this ) + { + bg->MousePressed( code, panel ); + } + } + } + return false; + } + + // if people click on the base build dialog panel. + if (panel == m_hBuildDialog) + { + // hide the click menu if its up + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("HideNewControlMenu"), NULL); + return true; + } + + // don't select unnamed items + if (strlen(panel->GetName()) < 1) + return true; + + bool shift = ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ); + if (!shift) + { + _controlGroup.RemoveAll(); + } + + // Show new ctrl menu if they click on the bg (not on a subcontrol) + if ( code == MOUSE_RIGHT && panel == GetContextPanel()) + { + // trigger a drop down menu to create new controls + ivgui()->PostMessage (m_hBuildDialog->GetVPanel(), new KeyValues("ShowNewControlMenu"), NULL); + } + else + { + // don't respond if we click on ruler numbers + if (_showRulers) // rulers are visible + { + for ( int i=0; i < 4; i++) + { + if ( panel == _rulerNumber[i]) + return true; + } + } + + _dragging = true; + _dragMouseCode = code; + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("HideNewControlMenu"), NULL); + + int x, y; + input()->GetCursorPos(x, y); + + _dragStartCursorPos[0] = x; + _dragStartCursorPos[1] = y; + + + input()->SetMouseCapture(panel->GetVPanel()); + + _groupDeltaX.RemoveAll(); + _groupDeltaY.RemoveAll(); + + // basepanel is the panel that all the deltas will be calculated from. + // it is the last panel we clicked in because if we move the panels as a group + // it would be from that one + Panel *basePanel = NULL; + // find the panel we clicked in, that is the base panel + // it might already be in the group + for (int i=0; i< _controlGroup.Count(); ++i) + { + if (panel == _controlGroup[i].Get()) + { + basePanel = panel; + break; + } + } + + // if its not in the group we just added this panel. get it in the group + if (basePanel == NULL) + { + PHandle temp; + temp = panel; + _controlGroup.AddToTail(temp); + basePanel = panel; + } + + basePanel->GetPos(x,y); + _dragStartPanelPos[0]=x; + _dragStartPanelPos[1]=y; + + basePanel->GetSize( _dragStartPanelSize[ 0 ], _dragStartPanelSize[ 1 ] ); + + // figure out the deltas of the other panels from the base panel + for (int i=0; i<_controlGroup.Count(); ++i) + { + int cx, cy; + _controlGroup[i].Get()->GetPos(cx, cy); + _groupDeltaX.AddToTail(cx - x); + _groupDeltaY.AddToTail(cy - y); + } + + // if this panel wasn't already selected update the buildmode dialog controls to show its info + if(_currentPanel != panel) + { + _currentPanel = panel; + + if ( m_hBuildDialog ) + { + // think this is taken care of by SetActiveControl. + //ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("ApplyDataToControls"), NULL); + + KeyValues *keyval = new KeyValues("SetActiveControl"); + keyval->SetPtr("PanelPtr", GetCurrentPanel()); + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL); + } + } + + // store undo information upon panel selection. + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("StoreUndo"), NULL); + + panel->RequestFocus(); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool BuildGroup::MouseReleased(MouseCode code, Panel *panel) +{ + if ( !m_hBuildDialog.Get() ) + { + if ( panel->GetParent() ) + { + EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() ); + if ( ep ) + { + BuildGroup *bg = ep->GetBuildGroup(); + if ( bg && bg != this ) + { + bg->MouseReleased( code, panel ); + } + } + } + return false; + } + + Assert(panel); + + _dragging=false; + input()->SetMouseCapture(null); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool BuildGroup::MouseDoublePressed(MouseCode code, Panel *panel) +{ + Assert(panel); + return MousePressed( code, panel ); +} + +bool BuildGroup::KeyTyped( wchar_t unichar, Panel *panel ) +{ + if ( !m_hBuildDialog.Get() ) + { + if ( panel->GetParent() ) + { + EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() ); + if ( ep ) + { + BuildGroup *bg = ep->GetBuildGroup(); + if ( bg && bg != this ) + { + bg->KeyTyped( unichar, panel ); + } + } + } + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool BuildGroup::KeyCodeTyped(KeyCode code, Panel *panel) +{ + if ( !m_hBuildDialog.Get() ) + { + if ( panel->GetParent() ) + { + EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() ); + if ( ep ) + { + BuildGroup *bg = ep->GetBuildGroup(); + if ( bg && bg != this ) + { + bg->KeyCodeTyped( code, panel ); + } + } + } + return false; + } + + Assert(panel); + + int dx=0; + int dy=0; + + bool shift = ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ); + bool ctrl = ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ); + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + + + if ( ctrl && shift && alt && code == KEY_B) + { + // enable build mode + EditablePanel *ep = dynamic_cast< EditablePanel * >( panel ); + if ( ep ) + { + ep->ActivateBuildMode(); + } + return true; + } + + switch (code) + { + case KEY_LEFT: + { + dx-=_snapX; + break; + } + case KEY_RIGHT: + { + dx+=_snapX; + break; + } + case KEY_UP: + { + dy-=_snapY; + break; + } + case KEY_DOWN: + { + dy+=_snapY; + break; + } + case KEY_DELETE: + { + // delete the panel we have selected + ivgui()->PostMessage (m_hBuildDialog->GetVPanel(), new KeyValues ("DeletePanel"), NULL); + break; + } + + } + + if (ctrl) + { + switch (code) + { + case KEY_Z: + { + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("Undo"), NULL); + break; + } + + case KEY_C: + { + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("Copy"), NULL); + break; + } + case KEY_V: + { + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("Paste"), NULL); + break; + } + } + } + + if(dx||dy) + { + //TODO: make this stuff actually snap + + int x,y,wide,tall; + + panel->GetBounds(x,y,wide,tall); + + if(shift) + { + panel->SetSize(wide+dx,tall+dy); + } + else + { + panel->SetPos(x+dx,y+dy); + } + + ApplySnap(panel); + + panel->Repaint(); + if (panel->GetVParent() != 0) + { + panel->PostMessage(panel->GetVParent(), new KeyValues("Repaint")); + } + + + // update the build dialog + if (m_hBuildDialog) + { + // post that it's active + KeyValues *keyval = new KeyValues("SetActiveControl"); + keyval->SetPtr("PanelPtr", GetCurrentPanel()); + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL); + + // post that it's been changed + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("PanelMoved"), NULL); + } + } + + // If holding key while dragging, simulate moving cursor so shift/ctrl key changes take effect + if ( _dragging && panel != GetContextPanel() ) + { + int x, y; + input()->GetCursorPos( x, y ); + CursorMoved( x, y, panel ); + } + + return true; +} + +bool BuildGroup::KeyCodeReleased(KeyCode code, Panel *panel ) +{ + if ( !m_hBuildDialog.Get() ) + { + if ( panel->GetParent() ) + { + EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() ); + if ( ep ) + { + BuildGroup *bg = ep->GetBuildGroup(); + if ( bg && bg != this ) + { + bg->KeyCodeTyped( code, panel ); + } + } + } + return false; + } + + // If holding key while dragging, simulate moving cursor so shift/ctrl key changes take effect + if ( _dragging && panel != GetContextPanel() ) + { + int x, y; + input()->GetCursorPos( x, y ); + CursorMoved( x, y, panel ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Searches for a BuildModeDialog in the hierarchy +//----------------------------------------------------------------------------- +Panel *BuildGroup::CreateBuildDialog( void ) +{ + // request the panel + Panel *buildDialog = NULL; + KeyValues *data = new KeyValues("BuildDialog"); + data->SetPtr("BuildGroupPtr", this); + if (m_pBuildContext->RequestInfo(data)) + { + buildDialog = (Panel *)data->GetPtr("PanelPtr"); + } + + // initialize the build dialog if found + if ( buildDialog ) + { + input()->ReleaseAppModalSurface(); + } + + return buildDialog; +} + +//----------------------------------------------------------------------------- +// Purpose: Activates the build mode settings dialog +//----------------------------------------------------------------------------- +void BuildGroup::ActivateBuildDialog( void ) +{ + // create the build mode dialog first time through + if (!m_hBuildDialog.Get()) + { + m_hBuildDialog = CreateBuildDialog(); + + if (!m_hBuildDialog.Get()) + return; + } + + m_hBuildDialog->SetVisible( true ); + + // send a message to set the initial dialog controls info + _currentPanel = m_pParentPanel; + KeyValues *keyval = new KeyValues("SetActiveControl"); + keyval->SetPtr("PanelPtr", GetCurrentPanel()); + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +HCursor BuildGroup::GetCursor(Panel *panel) +{ + Assert(panel); + + int x,y,wide,tall; + input()->GetCursorPos(x,y); + panel->ScreenToLocal(x,y); + panel->GetSize(wide,tall); + + if(x < 2) + { + if(y < 4) + { + return _cursor_sizenwse; + } + else + if(y<(tall-4)) + { + return _cursor_sizewe; + } + else + { + return _cursor_sizenesw; + } + } + + return _cursor_sizeall; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void BuildGroup::ApplySnap(Panel *panel) +{ + Assert(panel); + + int x,y,wide,tall; + panel->GetBounds(x,y,wide,tall); + + x=(x/_snapX)*_snapX; + y=(y/_snapY)*_snapY; + panel->SetPos(x,y); + + int xx,yy; + xx=x+wide; + yy=y+tall; + + xx=(xx/_snapX)*_snapX; + yy=(yy/_snapY)*_snapY; + panel->SetSize(xx-x,yy-y); +} + +//----------------------------------------------------------------------------- +// Purpose: Return the currently selected panel +//----------------------------------------------------------------------------- +Panel *BuildGroup::GetCurrentPanel() +{ + return _currentPanel; +} + +//----------------------------------------------------------------------------- +// Purpose: Add panel the list of panels that are in the build group +//----------------------------------------------------------------------------- +void BuildGroup::PanelAdded(Panel *panel) +{ + Assert(panel); + + PHandle temp; + temp = panel; + int c = _panelDar.Count(); + for ( int i = 0; i < c; ++i ) + { + if ( _panelDar[ i ] == temp ) + { + return; + } + } + _panelDar.AddToTail(temp); +} + +//----------------------------------------------------------------------------- +// Purpose: loads the control settings from file +//----------------------------------------------------------------------------- +void BuildGroup::LoadControlSettings(const char *controlResourceName, const char *pathID, KeyValues *pPreloadedKeyValues, KeyValues *pConditions) +{ + // make sure the file is registered + RegisterControlSettingsFile(controlResourceName, pathID); + + // Use the keyvalues they passed in or load them. + KeyValues *rDat = pPreloadedKeyValues; + if ( !rDat ) + { + // load the resource data from the file + rDat = new KeyValues(controlResourceName); + + // check the skins directory first, if an explicit pathID hasn't been set + bool bSuccess = false; + if (!pathID) + { + bSuccess = rDat->LoadFromFile(g_pFullFileSystem, controlResourceName, "SKIN"); + } + if (!bSuccess) + { + bSuccess = rDat->LoadFromFile(g_pFullFileSystem, controlResourceName, pathID); + } + + if ( bSuccess ) + { + if ( IsX360() ) + { + rDat->ProcessResolutionKeys( surface()->GetResolutionKey() ); + } + if ( IsPC() ) + { + ConVarRef cl_hud_minmode( "cl_hud_minmode", true ); + if ( cl_hud_minmode.IsValid() && cl_hud_minmode.GetBool() ) + { + rDat->ProcessResolutionKeys( "_minmode" ); + } + } + + if ( pConditions && pConditions->GetFirstSubKey() ) + { + ProcessConditionalKeys( rDat, pConditions ); + } + } + } + + // save off the resource name + delete [] m_pResourceName; + m_pResourceName = new char[strlen(controlResourceName) + 1]; + strcpy(m_pResourceName, controlResourceName); + + if (pathID) + { + delete [] m_pResourcePathID; + m_pResourcePathID = new char[strlen(pathID) + 1]; + strcpy(m_pResourcePathID, pathID); + } + + // delete any controls not in both files + DeleteAllControlsCreatedByControlSettingsFile(); + + // loop through the resource data sticking info into controls + ApplySettings(rDat); + + if (m_pParentPanel) + { + m_pParentPanel->InvalidateLayout(); + m_pParentPanel->Repaint(); + } + + if ( rDat != pPreloadedKeyValues ) + { + rDat->deleteThis(); + } +} + +void BuildGroup::ProcessConditionalKeys( KeyValues *pData, KeyValues *pConditions ) +{ + // for each condition, look for it in keys + // if its a positive condition, promote all of its children, replacing values + + if ( pData ) + { + KeyValues *pSubKey = pData->GetFirstSubKey(); + if ( !pSubKey ) + { + // not a block + return; + } + + for ( ; pSubKey != NULL; pSubKey = pSubKey->GetNextKey() ) + { + // recursively descend each sub block + ProcessConditionalKeys( pSubKey, pConditions ); + + KeyValues *pCondition = pConditions->GetFirstSubKey(); + for ( ; pCondition != NULL; pCondition = pCondition->GetNextKey() ) + { + // if we match any conditions in this sub block, copy up + KeyValues *pConditionBlock = pSubKey->FindKey( pCondition->GetName() ); + if ( pConditionBlock ) + { + KeyValues *pOverridingKey; + for ( pOverridingKey = pConditionBlock->GetFirstSubKey(); pOverridingKey != NULL; pOverridingKey = pOverridingKey->GetNextKey() ) + { + KeyValues *pExistingKey = pSubKey->FindKey( pOverridingKey->GetName() ); + if ( pExistingKey ) + { + pExistingKey->SetStringValue( pOverridingKey->GetString() ); + } + else + { + KeyValues *copy = pOverridingKey->MakeCopy(); + pSubKey->AddSubKey( copy ); + } + } + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: registers that a control settings file may be loaded +// use when the dialog may have multiple states and the editor will need to be able to switch between them +//----------------------------------------------------------------------------- +void BuildGroup::RegisterControlSettingsFile(const char *controlResourceName, const char *pathID) +{ + // add the file into a list for build mode + CUtlSymbol sym(controlResourceName); + if (!m_RegisteredControlSettingsFiles.IsValidIndex(m_RegisteredControlSettingsFiles.Find(sym))) + { + m_RegisteredControlSettingsFiles.AddToTail(sym); + } +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor / iterator +//----------------------------------------------------------------------------- +int BuildGroup::GetRegisteredControlSettingsFileCount() +{ + return m_RegisteredControlSettingsFiles.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +const char *BuildGroup::GetRegisteredControlSettingsFileByIndex(int index) +{ + return m_RegisteredControlSettingsFiles[index].String(); +} + +//----------------------------------------------------------------------------- +// Purpose: reloads the control settings from file +//----------------------------------------------------------------------------- +void BuildGroup::ReloadControlSettings() +{ + delete m_hBuildDialog.Get(); + m_hBuildDialog = NULL; + + // loop though objects in the current control group and remove them all + // the 0th panel is always the contextPanel which is not deletable + for( int i = 1; i < _panelDar.Count(); i++ ) + { + if (!_panelDar[i].Get()) // this can happen if we had two of the same handle in the list + { + _panelDar.Remove(i); + --i; + continue; + } + + // only delete deletable panels, as the only deletable panels + // are the ones created using the resource file + if ( _panelDar[i].Get()->IsBuildModeDeletable()) + { + delete _panelDar[i].Get(); + _panelDar.Remove(i); + --i; + } + } + + if (m_pResourceName) + { + EditablePanel *edit = dynamic_cast(m_pParentPanel); + if (edit) + { + edit->LoadControlSettings(m_pResourceName, m_pResourcePathID); + } + else + { + LoadControlSettings(m_pResourceName, m_pResourcePathID); + } + } + + _controlGroup.RemoveAll(); + + ActivateBuildDialog(); +} + +//----------------------------------------------------------------------------- +// Purpose: changes which control settings are currently loaded +//----------------------------------------------------------------------------- +void BuildGroup::ChangeControlSettingsFile(const char *controlResourceName) +{ + // clear any current state + _controlGroup.RemoveAll(); + _currentPanel = m_pParentPanel; + + // load the new state, via the dialog if possible + EditablePanel *edit = dynamic_cast(m_pParentPanel); + if (edit) + { + edit->LoadControlSettings(controlResourceName, m_pResourcePathID); + } + else + { + LoadControlSettings(controlResourceName, m_pResourcePathID); + } + + // force it to update + KeyValues *keyval = new KeyValues("SetActiveControl"); + keyval->SetPtr("PanelPtr", GetCurrentPanel()); + ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: saves control settings to file +//----------------------------------------------------------------------------- +bool BuildGroup::SaveControlSettings( void ) +{ + bool bSuccess = false; + if ( m_pResourceName ) + { + KeyValues *rDat = new KeyValues( m_pResourceName ); + + // get the data from our controls + GetSettings( rDat ); + + char fullpath[ 512 ]; + g_pFullFileSystem->RelativePathToFullPath( m_pResourceName, m_pResourcePathID, fullpath, sizeof( fullpath ) ); + + // save the data out to a file + bSuccess = rDat->SaveToFile( g_pFullFileSystem, fullpath, NULL ); + if (!bSuccess) + { + MessageBox *dlg = new MessageBox("BuildMode - Error saving file", "Error: Could not save changes. File is most likely read only."); + dlg->DoModal(); + } + + rDat->deleteThis(); + } + + return bSuccess; +} + +//----------------------------------------------------------------------------- +// Purpose: Deletes all the controls not created by the code +//----------------------------------------------------------------------------- +void BuildGroup::DeleteAllControlsCreatedByControlSettingsFile() +{ + // loop though objects in the current control group and remove them all + // the 0th panel is always the contextPanel which is not deletable + for ( int i = 1; i < _panelDar.Count(); i++ ) + { + if (!_panelDar[i].Get()) // this can happen if we had two of the same handle in the list + { + _panelDar.Remove(i); + --i; + continue; + } + + // only delete deletable panels, as the only deletable panels + // are the ones created using the resource file + if ( _panelDar[i].Get()->IsBuildModeDeletable()) + { + delete _panelDar[i].Get(); + _panelDar.Remove(i); + --i; + } + } + + _currentPanel = m_pBuildContext; + _currentPanel->InvalidateLayout(); + m_pBuildContext->Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: serializes settings from a resource data container +//----------------------------------------------------------------------------- +void BuildGroup::ApplySettings( KeyValues *resourceData ) +{ + // loop through all the keys, applying them wherever + for (KeyValues *controlKeys = resourceData->GetFirstSubKey(); controlKeys != NULL; controlKeys = controlKeys->GetNextKey()) + { + bool bFound = false; + + // Skip keys that are atomic.. + if (controlKeys->GetDataType() != KeyValues::TYPE_NONE) + continue; + + char const *keyName = controlKeys->GetName(); + + // check to see if any buildgroup panels have this name + for ( int i = 0; i < _panelDar.Count(); i++ ) + { + Panel *panel = _panelDar[i].Get(); + + if (!panel) // this can happen if we had two of the same handle in the list + { + _panelDar.Remove(i); + --i; + continue; + } + + + Assert (panel); + + // make the control name match CASE INSENSITIVE! + char const *panelName = panel->GetName(); + + if (!Q_stricmp(panelName, keyName)) + { + // apply the settings + panel->ApplySettings(controlKeys); + bFound = true; + break; + } + } + + if ( !bFound ) + { + // the key was not found in the registered list, check to see if we should create it + if ( keyName /*controlKeys->GetInt("AlwaysCreate", false)*/ ) + { + // create the control even though it wasn't registered + NewControl( controlKeys ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Create a new control in the context panel +// Input: name: class name of control to create +// controlKeys: keyvalues of settings for the panel. +// name OR controlKeys should be set, not both. +// x,y position relative to base panel +// Output: Panel *newPanel, NULL if failed to create new control. +//----------------------------------------------------------------------------- +Panel *BuildGroup::NewControl( const char *name, int x, int y) +{ + Assert (name); + + Panel *newPanel = NULL; + // returns NULL on failure + newPanel = static_cast(m_pParentPanel)->CreateControlByName(name); + + if (newPanel) + { + // panel successfully created + newPanel->SetParent(m_pParentPanel); + newPanel->SetBuildGroup(this); + newPanel->SetPos(x, y); + + char newFieldName[255]; + GetNewFieldName(newFieldName, sizeof(newFieldName), newPanel); + newPanel->SetName(newFieldName); + + newPanel->AddActionSignalTarget(m_pParentPanel); + newPanel->SetBuildModeEditable(true); + newPanel->SetBuildModeDeletable(true); + + // make sure it gets freed + newPanel->SetAutoDelete(true); + } + + return newPanel; +} + +//----------------------------------------------------------------------------- +// Purpose: Create a new control in the context panel +// Input: controlKeys: keyvalues of settings for the panel only works when applying initial settings. +// Output: Panel *newPanel, NULL if failed to create new control. +//----------------------------------------------------------------------------- +Panel *BuildGroup::NewControl( KeyValues *controlKeys, int x, int y) +{ + Assert (controlKeys); + + Panel *newPanel = NULL; + if (controlKeys) + { +// Warning( "Creating new control \"%s\" of type \"%s\"\n", controlKeys->GetString( "fieldName" ), controlKeys->GetString( "ControlName" ) ); + KeyValues *keyVal = new KeyValues("ControlFactory", "ControlName", controlKeys->GetString("ControlName")); + m_pBuildContext->RequestInfo(keyVal); + // returns NULL on failure + newPanel = (Panel *)keyVal->GetPtr("PanelPtr"); + keyVal->deleteThis(); + } + else + { + return NULL; + } + + if (newPanel) + { + // panel successfully created + newPanel->SetParent(m_pParentPanel); + newPanel->SetBuildGroup(this); + newPanel->SetPos(x, y); + + newPanel->SetName(controlKeys->GetName()); // name before applysettings :) + newPanel->ApplySettings(controlKeys); + + newPanel->AddActionSignalTarget(m_pParentPanel); + newPanel->SetBuildModeEditable(true); + newPanel->SetBuildModeDeletable(true); + + // make sure it gets freed + newPanel->SetAutoDelete(true); + } + + return newPanel; +} + +//----------------------------------------------------------------------------- +// Purpose: Get a new unique fieldname for a new control +//----------------------------------------------------------------------------- +void BuildGroup::GetNewFieldName(char *newFieldName, int newFieldNameSize, Panel *newPanel) +{ + int fieldNameNumber=1; + char defaultName[25]; + + Q_strncpy( defaultName, newPanel->GetClassName(), sizeof( defaultName ) ); + + while (1) + { + Q_snprintf (newFieldName, newFieldNameSize, "%s%d", defaultName, fieldNameNumber); + if ( FieldNameTaken(newFieldName) == NULL) + break; + ++fieldNameNumber; + } +} + +//----------------------------------------------------------------------------- +// Purpose: check to see if any buildgroup panels have this fieldname +// Input : fieldName, name to check +// Output : ptr to a panel that has the name if it is taken +//----------------------------------------------------------------------------- +Panel *BuildGroup::FieldNameTaken(const char *fieldName) +{ + for ( int i = 0; i < _panelDar.Count(); i++ ) + { + Panel *panel = _panelDar[i].Get(); + if ( !panel ) + continue; + + if (!stricmp(panel->GetName(), fieldName) ) + { + return panel; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: serializes settings to a resource data container +//----------------------------------------------------------------------------- +void BuildGroup::GetSettings( KeyValues *resourceData ) +{ + // loop through all the objects getting their settings + for( int i = 0; i < _panelDar.Count(); i++ ) + { + Panel *panel = _panelDar[i].Get(); + if (!panel) + continue; + + bool isRuler = false; + // do not get setting for ruler labels. + if (_showRulers) // rulers are visible + { + for (int i = 0; i < 4; i++) + { + if (panel == _rulerNumber[i]) + { + isRuler = true; + break; + } + } + if (isRuler) + { + isRuler = false; + continue; + } + } + + // Don't save the setting of the buildmodedialog + if (!stricmp(panel->GetName(), "BuildDialog")) + continue; + + // get the keys section from the data file + if (panel->GetName() && *panel->GetName()) + { + KeyValues *datKey = resourceData->FindKey(panel->GetName(), true); + + // get the settings + panel->GetSettings(datKey); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: loop though objects in the current control group and remove them all +//----------------------------------------------------------------------------- +void BuildGroup::RemoveSettings() +{ + // loop though objects in the current control group and remove them all + int i; + for( i = 0; i < _controlGroup.Count(); i++ ) + { + // only delete delatable panels + if ( _controlGroup[i].Get()->IsBuildModeDeletable()) + { + delete _controlGroup[i].Get(); + _controlGroup.Remove(i); + --i; + } + } + + // remove deleted panels from the handle list + for( i = 0; i < _panelDar.Count(); i++ ) + { + if ( !_panelDar[i].Get() ) + { + _panelDar.Remove(i); + --i; + } + } + + _currentPanel = m_pBuildContext; + _currentPanel->InvalidateLayout(); + m_pBuildContext->Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the panel from which the build group gets all it's object creation info +//----------------------------------------------------------------------------- +void BuildGroup::SetContextPanel(Panel *contextPanel) +{ + m_pBuildContext = contextPanel; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the panel from which the build group gets all it's object creation info +//----------------------------------------------------------------------------- +Panel *BuildGroup::GetContextPanel() +{ + return m_pBuildContext; +} + +//----------------------------------------------------------------------------- +// Purpose: get the list of panels in the buildgroup +//----------------------------------------------------------------------------- +CUtlVector *BuildGroup::GetPanelList() +{ + return &_panelDar; +} + +//----------------------------------------------------------------------------- +// Purpose: dialog variables +//----------------------------------------------------------------------------- +KeyValues *BuildGroup::GetDialogVariables() +{ + EditablePanel *edit = dynamic_cast(m_pParentPanel); + if (edit) + { + return edit->GetDialogVariables(); + } + + return NULL; +} diff --git a/mp/src/vgui2/vgui_controls/BuildModeDialog.cpp b/mp/src/vgui2/vgui_controls/BuildModeDialog.cpp index 9ae8c8c5..c743c202 100644 --- a/mp/src/vgui2/vgui_controls/BuildModeDialog.cpp +++ b/mp/src/vgui2/vgui_controls/BuildModeDialog.cpp @@ -1,1441 +1,1441 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -struct PanelItem_t -{ - PanelItem_t() : m_EditLabel(NULL) {} - - Panel *m_EditLabel; - TextEntry *m_EditPanel; - ComboBox *m_pCombo; - Button *m_EditButton; - char m_szName[64]; - int m_iType; -}; - -class CSmallTextEntry : public TextEntry -{ - DECLARE_CLASS_SIMPLE( CSmallTextEntry, TextEntry ); -public: - - CSmallTextEntry( Panel *parent, char const *panelName ) : - BaseClass( parent, panelName ) - { - } - - virtual void ApplySchemeSettings( IScheme *scheme ) - { - BaseClass::ApplySchemeSettings( scheme ); - - SetFont( scheme->GetFont( "DefaultVerySmall" ) ); - } -}; - -//----------------------------------------------------------------------------- -// Purpose: Holds a list of all the edit fields for the currently selected panel -//----------------------------------------------------------------------------- -class BuildModeDialog::PanelList -{ -public: - - CUtlVector m_PanelList; - - void AddItem( Panel *label, TextEntry *edit, ComboBox *combo, Button *button, const char *name, int type ) - { - PanelItem_t item; - item.m_EditLabel = label; - item.m_EditPanel = edit; - Q_strncpy(item.m_szName, name, sizeof(item.m_szName)); - item.m_iType = type; - item.m_pCombo = combo; - item.m_EditButton = button; - - m_PanelList.AddToTail( item ); - } - - void RemoveAll( void ) - { - for ( int i = 0; i < m_PanelList.Size(); i++ ) - { - PanelItem_t *item = &m_PanelList[i]; - delete item->m_EditLabel; - delete item->m_EditPanel; - delete item->m_EditButton; - } - - m_PanelList.RemoveAll(); - m_pControls->RemoveAll(); - } - - KeyValues *m_pResourceData; - PanelListPanel *m_pControls; -}; - -//----------------------------------------------------------------------------- -// Purpose: Dialog for adding localized strings -//----------------------------------------------------------------------------- -class BuildModeLocalizedStringEditDialog : public Frame -{ - DECLARE_CLASS_SIMPLE(BuildModeLocalizedStringEditDialog, Frame); - -public: - -#pragma warning( disable : 4355 ) - BuildModeLocalizedStringEditDialog() : Frame(this, NULL) - { - m_pTokenEntry = new TextEntry(this, NULL); - m_pValueEntry = new TextEntry(this, NULL); - m_pFileCombo = new ComboBox(this, NULL, 12, false); - m_pOKButton = new Button(this, NULL, "OK"); - m_pCancelButton = new Button(this, NULL, "Cancel"); - - m_pCancelButton->SetCommand("Close"); - m_pOKButton->SetCommand("OK"); - - // add the files to the combo - for (int i = 0; i < g_pVGuiLocalize->GetLocalizationFileCount(); i++) - { - m_pFileCombo->AddItem(g_pVGuiLocalize->GetLocalizationFileName(i), NULL); - } - } -#pragma warning( default : 4355 ) - - virtual void DoModal(const char *token) - { - input()->SetAppModalSurface(GetVPanel()); - - // setup data - m_pTokenEntry->SetText(token); - - // lookup the value - StringIndex_t val = g_pVGuiLocalize->FindIndex(token); - if (val != INVALID_LOCALIZE_STRING_INDEX) - { - m_pValueEntry->SetText(g_pVGuiLocalize->GetValueByIndex(val)); - - // set the place in the file combo - m_pFileCombo->SetText(g_pVGuiLocalize->GetFileNameByIndex(val)); - } - else - { - m_pValueEntry->SetText(""); - } - } - -private: - virtual void PerformLayout() - { - } - - virtual void OnClose() - { - input()->SetAppModalSurface(NULL); - BaseClass::OnClose(); - //PostActionSignal(new KeyValues("Command" - } - - virtual void OnCommand(const char *command) - { - if (!stricmp(command, "OK")) - { - //!! apply changes - } - else - { - BaseClass::OnCommand(command); - } - } - - vgui::TextEntry *m_pTokenEntry; - vgui::TextEntry *m_pValueEntry; - vgui::ComboBox *m_pFileCombo; - vgui::Button *m_pOKButton; - vgui::Button *m_pCancelButton; -}; - -class CBuildModeDialogMgr -{ -public: - - void Add( BuildModeDialog *pDlg ); - void Remove( BuildModeDialog *pDlg ); - - int Count() const; - -private: - CUtlVector< BuildModeDialog * > m_vecBuildDialogs; -}; - -static CBuildModeDialogMgr g_BuildModeDialogMgr; - -void CBuildModeDialogMgr::Add( BuildModeDialog *pDlg ) -{ - if ( m_vecBuildDialogs.Find( pDlg ) == m_vecBuildDialogs.InvalidIndex() ) - { - m_vecBuildDialogs.AddToTail( pDlg ); - } -} - -void CBuildModeDialogMgr::Remove( BuildModeDialog *pDlg ) -{ - m_vecBuildDialogs.FindAndRemove( pDlg ); -} - -int CBuildModeDialogMgr::Count() const -{ - return m_vecBuildDialogs.Count(); -} - -int GetBuildModeDialogCount() -{ - return g_BuildModeDialogMgr.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -BuildModeDialog::BuildModeDialog(BuildGroup *buildGroup) : Frame(buildGroup->GetContextPanel(), "BuildModeDialog") -{ - SetMinimumSize(300, 256); - SetSize(300, 420); - m_pCurrentPanel = NULL; - m_pEditableParents = NULL; - m_pEditableChildren = NULL; - m_pNextChild = NULL; - m_pPrevChild = NULL; - m_pBuildGroup = buildGroup; - _undoSettings = NULL; - _copySettings = NULL; - _autoUpdate = false; - MakePopup(); - SetTitle("VGUI Build Mode Editor", true); - - CreateControls(); - LoadUserConfig("BuildModeDialog"); - - g_BuildModeDialogMgr.Add( this ); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -BuildModeDialog::~BuildModeDialog() -{ - g_BuildModeDialogMgr.Remove( this ); - - m_pPanelList->m_pResourceData->deleteThis(); - m_pPanelList->m_pControls->DeleteAllItems(); - if (_undoSettings) - _undoSettings->deleteThis(); - if (_copySettings) - _copySettings->deleteThis(); -} - -//----------------------------------------------------------------------------- -// Purpose: makes sure build mode has been shut down properly -//----------------------------------------------------------------------------- -void BuildModeDialog::OnClose() -{ - if (m_pBuildGroup->IsEnabled()) - { - m_pBuildGroup->SetEnabled(false); - } - else - { - BaseClass::OnClose(); - MarkForDeletion(); - } -} - -class CBuildModeNavCombo : public ComboBox -{ - DECLARE_CLASS_SIMPLE( CBuildModeNavCombo, ComboBox ); -public: - - CBuildModeNavCombo(Panel *parent, const char *panelName, int numLines, bool allowEdit, bool getParents, Panel *context ) : - BaseClass( parent, panelName, numLines, allowEdit ), - m_bParents( getParents ) - { - m_hContext = context; - } - - virtual void OnShowMenu(Menu *menu) - { - menu->DeleteAllItems(); - if ( !m_hContext.Get() ) - return; - - if ( m_bParents ) - { - Panel *p = m_hContext->GetParent(); - while ( p ) - { - EditablePanel *ep = dynamic_cast < EditablePanel * >( p ); - if ( ep && ep->GetBuildGroup() ) - { - KeyValues *kv = new KeyValues( "Panel" ); - kv->SetPtr( "ptr", p ); - char const *text = ep->GetName() ? ep->GetName() : "unnamed"; - menu->AddMenuItem( text, new KeyValues("SetText", "text", text), GetParent(), kv ); - } - p = p->GetParent(); - } - } - else - { - int i; - int c = m_hContext->GetChildCount(); - for ( i = 0; i < c; ++i ) - { - EditablePanel *ep = dynamic_cast < EditablePanel * >( m_hContext->GetChild( i ) ); - if ( ep && ep->IsVisible() && ep->GetBuildGroup() ) - { - KeyValues *kv = new KeyValues( "Panel" ); - kv->SetPtr( "ptr", ep ); - char const *text = ep->GetName() ? ep->GetName() : "unnamed"; - menu->AddMenuItem( text, new KeyValues("SetText", "text", text), GetParent(), kv ); - } - } - } - } -private: - bool m_bParents; - vgui::PHandle m_hContext; -}; - -//----------------------------------------------------------------------------- -// Purpose: Creates the build mode editing controls -//----------------------------------------------------------------------------- -void BuildModeDialog::CreateControls() -{ - int i; - m_pPanelList = new PanelList; - m_pPanelList->m_pResourceData = new KeyValues( "BuildDialog" ); - m_pPanelList->m_pControls = new PanelListPanel(this, "BuildModeControls"); - - // file to edit combo box is first - m_pFileSelectionCombo = new ComboBox(this, "FileSelectionCombo", 10, false); - for ( i = 0; i < m_pBuildGroup->GetRegisteredControlSettingsFileCount(); i++) - { - m_pFileSelectionCombo->AddItem(m_pBuildGroup->GetRegisteredControlSettingsFileByIndex(i), NULL); - } - if (m_pFileSelectionCombo->GetItemCount() < 2) - { - m_pFileSelectionCombo->SetEnabled(false); - } - - int buttonH = 18; - - // status info at top of dialog - m_pStatusLabel = new Label(this, "StatusLabel", "[nothing currently selected]"); - m_pStatusLabel->SetTextColorState(Label::CS_DULL); - m_pStatusLabel->SetTall( buttonH ); - m_pDivider = new Divider(this, "Divider"); - // drop-down combo box for adding new controls - m_pAddNewControlCombo = new ComboBox(this, NULL, 30, false); - m_pAddNewControlCombo->SetSize(116, buttonH); - m_pAddNewControlCombo->SetOpenDirection(Menu::DOWN); - - m_pEditableParents = new CBuildModeNavCombo( this, NULL, 15, false, true, m_pBuildGroup->GetContextPanel() ); - m_pEditableParents->SetSize(116, buttonH); - m_pEditableParents->SetOpenDirection(Menu::DOWN); - - m_pEditableChildren = new CBuildModeNavCombo( this, NULL, 15, false, false, m_pBuildGroup->GetContextPanel() ); - m_pEditableChildren->SetSize(116, buttonH); - m_pEditableChildren->SetOpenDirection(Menu::DOWN); - - m_pNextChild = new Button( this, "NextChild", "Next", this ); - m_pNextChild->SetCommand( new KeyValues( "OnChangeChild", "direction", 1 ) ); - - m_pPrevChild = new Button( this, "PrevChild", "Prev", this ); - m_pPrevChild->SetCommand( new KeyValues( "OnChangeChild", "direction", -1 ) ); - - // controls that can be added - // this list comes from controls EditablePanel can create by name. - int defaultItem = m_pAddNewControlCombo->AddItem("None", NULL); - - CUtlVector< char const * > names; - CBuildFactoryHelper::GetFactoryNames( names ); - // Sort the names - CUtlRBTree< char const *, int > sorted( 0, 0, StringLessThan ); - - for ( i = 0; i < names.Count(); ++i ) - { - sorted.Insert( names[ i ] ); - } - - for ( i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) ) - { - m_pAddNewControlCombo->AddItem( sorted[ i ], NULL ); - } - - m_pAddNewControlCombo->ActivateItem(defaultItem); - - m_pExitButton = new Button(this, "ExitButton", "&Exit"); - m_pExitButton->SetSize(64, buttonH); - - m_pSaveButton = new Button(this, "SaveButton", "&Save"); - m_pSaveButton->SetSize(64, buttonH); - - m_pApplyButton = new Button(this, "ApplyButton", "&Apply"); - m_pApplyButton->SetSize(64, buttonH); - - m_pReloadLocalization = new Button( this, "Localization", "&Reload Localization" ); - m_pReloadLocalization->SetSize( 100, buttonH ); - - m_pExitButton->SetCommand("Exit"); - m_pSaveButton->SetCommand("Save"); - m_pApplyButton->SetCommand("Apply"); - m_pReloadLocalization->SetCommand( new KeyValues( "ReloadLocalization" ) ); - - m_pDeleteButton = new Button(this, "DeletePanelButton", "Delete"); - m_pDeleteButton->SetSize(64, buttonH); - m_pDeleteButton->SetCommand("DeletePanel"); - - m_pVarsButton = new MenuButton(this, "VarsButton", "Variables"); - m_pVarsButton->SetSize(72, buttonH); - m_pVarsButton->SetOpenDirection(Menu::UP); - - // iterate the vars - KeyValues *vars = m_pBuildGroup->GetDialogVariables(); - if (vars && vars->GetFirstSubKey()) - { - // create the menu - m_pVarsButton->SetEnabled(true); - Menu *menu = new Menu(m_pVarsButton, "VarsMenu"); - - // set all the variables to be copied to the clipboard when selected - for (KeyValues *kv = vars->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey()) - { - char buf[32]; - _snprintf(buf, sizeof(buf), "%%%s%%", kv->GetName()); - menu->AddMenuItem(kv->GetName(), new KeyValues("SetClipboardText", "text", buf), this); - } - - m_pVarsButton->SetMenu(menu); - } - else - { - // no variables - m_pVarsButton->SetEnabled(false); - } - - m_pApplyButton->SetTabPosition(1); - m_pPanelList->m_pControls->SetTabPosition(2); - m_pVarsButton->SetTabPosition(3); - m_pDeleteButton->SetTabPosition(4); - m_pAddNewControlCombo->SetTabPosition(5); - m_pSaveButton->SetTabPosition(6); - m_pExitButton->SetTabPosition(7); - - m_pEditableParents->SetTabPosition( 8 ); - m_pEditableChildren->SetTabPosition( 9 ); - - m_pPrevChild->SetTabPosition( 10 ); - m_pNextChild->SetTabPosition( 11 ); - - m_pReloadLocalization->SetTabPosition( 12 ); -} - -void BuildModeDialog::ApplySchemeSettings( IScheme *pScheme ) -{ - BaseClass::ApplySchemeSettings( pScheme ); - - HFont font = pScheme->GetFont( "DefaultVerySmall" ); - m_pStatusLabel->SetFont( font ); - m_pReloadLocalization->SetFont( font ); - m_pExitButton->SetFont( font ); - m_pSaveButton->SetFont( font ); - m_pApplyButton->SetFont( font ); - m_pAddNewControlCombo->SetFont( font ); - m_pEditableParents->SetFont( font ); - m_pEditableChildren->SetFont( font ); - m_pDeleteButton->SetFont( font ); - m_pVarsButton->SetFont( font ); - m_pPrevChild->SetFont( font ); - m_pNextChild->SetFont( font ); -} - -//----------------------------------------------------------------------------- -// Purpose: lays out controls -//----------------------------------------------------------------------------- -void BuildModeDialog::PerformLayout() -{ - BaseClass::PerformLayout(); - - // layout parameters - const int BORDER_GAP = 16, YGAP_SMALL = 4, YGAP_LARGE = 8, TITLE_HEIGHT = 24, BOTTOM_CONTROLS_HEIGHT = 145, XGAP = 6; - - int wide, tall; - GetSize(wide, tall); - - int xpos = BORDER_GAP; - int ypos = BORDER_GAP + TITLE_HEIGHT; - - // controls from top down - // selection combo - m_pFileSelectionCombo->SetBounds(xpos, ypos, wide - (BORDER_GAP * 2), m_pStatusLabel->GetTall()); - ypos += (m_pStatusLabel->GetTall() + YGAP_SMALL); - - // status - m_pStatusLabel->SetBounds(xpos, ypos, wide - (BORDER_GAP * 2), m_pStatusLabel->GetTall()); - ypos += (m_pStatusLabel->GetTall() + YGAP_SMALL); - - // center control - m_pPanelList->m_pControls->SetPos(xpos, ypos); - m_pPanelList->m_pControls->SetSize(wide - (BORDER_GAP * 2), tall - (ypos + BOTTOM_CONTROLS_HEIGHT)); - - // controls from bottom-right - ypos = tall - BORDER_GAP; - xpos = BORDER_GAP + m_pVarsButton->GetWide() + m_pDeleteButton->GetWide() + m_pAddNewControlCombo->GetWide() + (XGAP * 2); - - // bottom row of buttons - ypos -= m_pApplyButton->GetTall(); - xpos -= m_pApplyButton->GetWide(); - m_pApplyButton->SetPos(xpos, ypos); - - xpos -= m_pExitButton->GetWide(); - xpos -= XGAP; - m_pExitButton->SetPos(xpos, ypos); - - xpos -= m_pSaveButton->GetWide(); - xpos -= XGAP; - m_pSaveButton->SetPos(xpos, ypos); - - // divider - xpos = BORDER_GAP; - ypos -= (YGAP_LARGE + m_pDivider->GetTall()); - m_pDivider->SetBounds(xpos, ypos, wide - (xpos + BORDER_GAP), 2); - - ypos -= (YGAP_LARGE + m_pVarsButton->GetTall()); - - xpos = BORDER_GAP; - m_pEditableParents->SetPos( xpos, ypos ); - m_pEditableChildren->SetPos( xpos + 150, ypos ); - - ypos -= (YGAP_LARGE + 18 ); - xpos = BORDER_GAP; - m_pReloadLocalization->SetPos( xpos, ypos ); - - xpos += ( XGAP ) + m_pReloadLocalization->GetWide(); - - m_pPrevChild->SetPos( xpos, ypos ); - m_pPrevChild->SetSize( 64, m_pReloadLocalization->GetTall() ); - xpos += ( XGAP ) + m_pPrevChild->GetWide(); - - m_pNextChild->SetPos( xpos, ypos ); - m_pNextChild->SetSize( 64, m_pReloadLocalization->GetTall() ); - - ypos -= (YGAP_LARGE + m_pVarsButton->GetTall()); - xpos = BORDER_GAP; - - // edit buttons - m_pVarsButton->SetPos(xpos, ypos); - xpos += (XGAP + m_pVarsButton->GetWide()); - m_pDeleteButton->SetPos(xpos, ypos); - xpos += (XGAP + m_pDeleteButton->GetWide()); - m_pAddNewControlCombo->SetPos(xpos, ypos); -} - - -//----------------------------------------------------------------------------- -// Purpose: Deletes all the controls from the panel -//----------------------------------------------------------------------------- -void BuildModeDialog::RemoveAllControls( void ) -{ - // free the array - m_pPanelList->RemoveAll(); -} - -//----------------------------------------------------------------------------- -// Purpose: simple helper function to get a token from a string -// Input : char **string - pointer to the string pointer, which will be incremented -// Output : const char * - pointer to the token -//----------------------------------------------------------------------------- -const char *ParseTokenFromString( const char **string ) -{ - static char buf[128]; - buf[0] = 0; - - // find the first alnum character - const char *tok = *string; - while ( !V_isalnum(*tok) && *tok != 0 ) - { - tok++; - } - - // read in all the alnum characters - int pos = 0; - while ( V_isalnum(tok[pos]) ) - { - buf[pos] = tok[pos]; - pos++; - } - - // null terminate the token - buf[pos] = 0; - - // update the main string pointer - *string = &(tok[pos]); - - // return a pointer to the static buffer - return buf; -} - -void BuildModeDialog::OnTextKillFocus() -{ - if ( !m_pCurrentPanel ) - return; - - ApplyDataToControls(); -} - - -//----------------------------------------------------------------------------- -// Purpose: sets up the current control to edit -//----------------------------------------------------------------------------- -void BuildModeDialog::SetActiveControl(Panel *controlToEdit) -{ - if (m_pCurrentPanel == controlToEdit) - { - // it's already set, so just update the property data and quit - if (m_pCurrentPanel) - { - UpdateControlData(m_pCurrentPanel); - } - return; - } - - // reset the data - m_pCurrentPanel = controlToEdit; - RemoveAllControls(); - m_pPanelList->m_pControls->MoveScrollBarToTop(); - - if (!m_pCurrentPanel) - { - m_pStatusLabel->SetText("[nothing currently selected]"); - m_pStatusLabel->SetTextColorState(Label::CS_DULL); - RemoveAllControls(); - return; - } - - // get the control description string - const char *controlDesc = m_pCurrentPanel->GetDescription(); - - // parse out the control description - int tabPosition = 1; - while (1) - { - const char *dataType = ParseTokenFromString(&controlDesc); - - // finish when we have no more tokens - if (*dataType == 0) - break; - - // default the data type to a string - int datat = TYPE_STRING; - - if (!stricmp(dataType, "int")) - { - datat = TYPE_STRING; //!! just for now - } - else if (!stricmp(dataType, "alignment")) - { - datat = TYPE_ALIGNMENT; - } - else if (!stricmp(dataType, "autoresize")) - { - datat = TYPE_AUTORESIZE; - } - else if (!stricmp(dataType, "corner")) - { - datat = TYPE_CORNER; - } - else if (!stricmp(dataType, "localize")) - { - datat = TYPE_LOCALIZEDSTRING; - } - - // get the field name - const char *fieldName = ParseTokenFromString(&controlDesc); - - int itemHeight = 18; - - // build a control & label - Label *label = new Label(this, NULL, fieldName); - label->SetSize(96, itemHeight); - label->SetContentAlignment(Label::a_east); - - TextEntry *edit = NULL; - ComboBox *editCombo = NULL; - Button *editButton = NULL; - if (datat == TYPE_ALIGNMENT) - { - // drop-down combo box - editCombo = new ComboBox(this, NULL, 9, false); - editCombo->AddItem("north-west", NULL); - editCombo->AddItem("north", NULL); - editCombo->AddItem("north-east", NULL); - editCombo->AddItem("west", NULL); - editCombo->AddItem("center", NULL); - editCombo->AddItem("east", NULL); - editCombo->AddItem("south-west", NULL); - editCombo->AddItem("south", NULL); - editCombo->AddItem("south-east", NULL); - - edit = editCombo; - } - else if (datat == TYPE_AUTORESIZE) - { - // drop-down combo box - editCombo = new ComboBox(this, NULL, 4, false); - editCombo->AddItem( "0 - no auto-resize", NULL); - editCombo->AddItem( "1 - resize right", NULL); - editCombo->AddItem( "2 - resize down", NULL); - editCombo->AddItem( "3 - down & right", NULL); - - edit = editCombo; - } - else if (datat == TYPE_CORNER) - { - // drop-down combo box - editCombo = new ComboBox(this, NULL, 4, false); - editCombo->AddItem("0 - top-left", NULL); - editCombo->AddItem("1 - top-right", NULL); - editCombo->AddItem("2 - bottom-left", NULL); - editCombo->AddItem("3 - bottom-right", NULL); - - edit = editCombo; - } - else if (datat == TYPE_LOCALIZEDSTRING) - { - editButton = new Button(this, NULL, "..."); - editButton->SetParent(this); - editButton->AddActionSignalTarget(this); - editButton->SetTabPosition(tabPosition++); - editButton->SetTall( itemHeight ); - label->SetAssociatedControl(editButton); - } - else - { - // normal string edit - edit = new CSmallTextEntry(this, NULL); - } - - if (edit) - { - edit->SetTall( itemHeight ); - edit->SetParent(this); - edit->AddActionSignalTarget(this); - edit->SetTabPosition(tabPosition++); - label->SetAssociatedControl(edit); - } - - HFont smallFont = scheme()->GetIScheme( GetScheme() )->GetFont( "DefaultVerySmall" ); - - if ( label ) - { - label->SetFont( smallFont ); - } - if ( edit ) - { - edit->SetFont( smallFont ); - } - if ( editCombo ) - { - editCombo->SetFont( smallFont ); - } - if ( editButton ) - { - editButton->SetFont( smallFont ); - } - - // add to our control list - m_pPanelList->AddItem(label, edit, editCombo, editButton, fieldName, datat); - - if ( edit ) - { - m_pPanelList->m_pControls->AddItem(label, edit); - } - else - { - m_pPanelList->m_pControls->AddItem(label, editButton); - } - } - - // check and see if the current panel is a Label - // iterate through the class hierarchy - if ( controlToEdit->IsBuildModeDeletable() ) - { - m_pDeleteButton->SetEnabled(true); - } - else - { - m_pDeleteButton->SetEnabled(false); - } - - // update the property data in the dialog - UpdateControlData(m_pCurrentPanel); - - // set our title - if ( m_pBuildGroup->GetResourceName() ) - { - m_pFileSelectionCombo->SetText(m_pBuildGroup->GetResourceName()); - } - else - { - m_pFileSelectionCombo->SetText("[ no resource file associated with dialog ]"); - } - - m_pApplyButton->SetEnabled(false); - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Updates the edit fields with information about the control -//----------------------------------------------------------------------------- -void BuildModeDialog::UpdateControlData(Panel *control) -{ - KeyValues *dat = m_pPanelList->m_pResourceData->FindKey( control->GetName(), true ); - control->GetSettings( dat ); - - // apply the settings to the edit panels - for ( int i = 0; i < m_pPanelList->m_PanelList.Size(); i++ ) - { - const char *name = m_pPanelList->m_PanelList[i].m_szName; - const char *datstring = dat->GetString( name, "" ); - - UpdateEditControl(m_pPanelList->m_PanelList[i], datstring); - } - - char statusText[512]; - Q_snprintf(statusText, sizeof(statusText), "%s: \'%s\'", control->GetClassName(), control->GetName()); - m_pStatusLabel->SetText(statusText); - m_pStatusLabel->SetTextColorState(Label::CS_NORMAL); -} - -//----------------------------------------------------------------------------- -// Purpose: Updates the data in a single edit control -//----------------------------------------------------------------------------- -void BuildModeDialog::UpdateEditControl(PanelItem_t &panelItem, const char *datstring) -{ - switch (panelItem.m_iType) - { - case TYPE_AUTORESIZE: - case TYPE_CORNER: - { - int dat = atoi(datstring); - panelItem.m_pCombo->ActivateItemByRow(dat); - } - break; - - case TYPE_LOCALIZEDSTRING: - { - panelItem.m_EditButton->SetText(datstring); - } - break; - - default: - { - wchar_t unicode[512]; - g_pVGuiLocalize->ConvertANSIToUnicode(datstring, unicode, sizeof(unicode)); - panelItem.m_EditPanel->SetText(unicode); - } - break; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Called when one of the buttons is pressed -//----------------------------------------------------------------------------- -void BuildModeDialog::OnCommand(const char *command) -{ - if (!stricmp(command, "Save")) - { - // apply the current data and save it to disk - ApplyDataToControls(); - if (m_pBuildGroup->SaveControlSettings()) - { - // disable save button until another change has been made - m_pSaveButton->SetEnabled(false); - } - } - else if (!stricmp(command, "Exit")) - { - // exit build mode - ExitBuildMode(); - } - else if (!stricmp(command, "Apply")) - { - // apply data to controls - ApplyDataToControls(); - } - else if (!stricmp(command, "DeletePanel")) - { - OnDeletePanel(); - } - else if (!stricmp(command, "RevertToSaved")) - { - RevertToSaved(); - } - else if (!stricmp(command, "ShowHelp")) - { - ShowHelp(); - } - else - { - BaseClass::OnCommand(command); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Deletes a panel from the buildgroup -//----------------------------------------------------------------------------- -void BuildModeDialog::OnDeletePanel() -{ - if (!m_pCurrentPanel->IsBuildModeEditable()) - { - return; - } - - m_pBuildGroup->RemoveSettings(); - SetActiveControl(m_pBuildGroup->GetCurrentPanel()); - - _undoSettings->deleteThis(); - _undoSettings = NULL; - m_pSaveButton->SetEnabled(true); -} - -//----------------------------------------------------------------------------- -// Purpose: Applies the current settings to the build controls -//----------------------------------------------------------------------------- -void BuildModeDialog::ApplyDataToControls() -{ - // don't apply if the panel is not editable - if ( !m_pCurrentPanel->IsBuildModeEditable()) - { - UpdateControlData( m_pCurrentPanel ); - return; // return success, since we are behaving as expected. - } - - char fieldName[512]; - if (m_pPanelList->m_PanelList[0].m_EditPanel) - { - m_pPanelList->m_PanelList[0].m_EditPanel->GetText(fieldName, sizeof(fieldName)); - } - else - { - m_pPanelList->m_PanelList[0].m_EditButton->GetText(fieldName, sizeof(fieldName)); - } - - // check to see if any buildgroup panels have this name - Panel *panel = m_pBuildGroup->FieldNameTaken(fieldName); - if (panel) - { - if (panel != m_pCurrentPanel)// make sure name is taken by some other panel not this one - { - char messageString[255]; - Q_snprintf(messageString, sizeof( messageString ), "Fieldname is not unique: %s\nRename it and try again.", fieldName); - MessageBox *errorBox = new MessageBox("Cannot Apply", messageString); - errorBox->DoModal(); - UpdateControlData(m_pCurrentPanel); - m_pApplyButton->SetEnabled(false); - return; - } - } - - // create a section to store settings - // m_pPanelList->m_pResourceData->getSection( m_pCurrentPanel->GetName(), true ); - KeyValues *dat = new KeyValues( m_pCurrentPanel->GetName() ); - - // loop through the textedit filling in settings - for ( int i = 0; i < m_pPanelList->m_PanelList.Size(); i++ ) - { - const char *name = m_pPanelList->m_PanelList[i].m_szName; - char buf[512]; - if (m_pPanelList->m_PanelList[i].m_EditPanel) - { - m_pPanelList->m_PanelList[i].m_EditPanel->GetText(buf, sizeof(buf)); - } - else - { - m_pPanelList->m_PanelList[i].m_EditButton->GetText(buf, sizeof(buf)); - } - - switch (m_pPanelList->m_PanelList[i].m_iType) - { - case TYPE_CORNER: - case TYPE_AUTORESIZE: - // the integer value is assumed to be the first part of the string for these items - dat->SetInt(name, atoi(buf)); - break; - - default: - dat->SetString(name, buf); - break; - } - } - - // dat is built, hand it back to the control - m_pCurrentPanel->ApplySettings( dat ); - - if ( m_pBuildGroup->GetContextPanel() ) - { - m_pBuildGroup->GetContextPanel()->Repaint(); - } - - m_pApplyButton->SetEnabled(false); - m_pSaveButton->SetEnabled(true); -} - -//----------------------------------------------------------------------------- -// Purpose: Store the settings of the current panel in a KeyValues -//----------------------------------------------------------------------------- -void BuildModeDialog::StoreUndoSettings() -{ - // don't save if the planel is not editable - if ( !m_pCurrentPanel->IsBuildModeEditable()) - { - if (_undoSettings) - _undoSettings->deleteThis(); - _undoSettings = NULL; - return; - } - - if (_undoSettings) - { - _undoSettings->deleteThis(); - _undoSettings = NULL; - } - - _undoSettings = StoreSettings(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Revert to the stored the settings of the current panel in a keyValues -//----------------------------------------------------------------------------- -void BuildModeDialog::DoUndo() -{ - if ( _undoSettings ) - { - m_pCurrentPanel->ApplySettings( _undoSettings ); - UpdateControlData(m_pCurrentPanel); - _undoSettings->deleteThis(); - _undoSettings = NULL; - } - - m_pSaveButton->SetEnabled(true); -} - -//----------------------------------------------------------------------------- -// Purpose: Copy the settings of the current panel into a keyValues -//----------------------------------------------------------------------------- -void BuildModeDialog::DoCopy() -{ - if (_copySettings) - { - _copySettings->deleteThis(); - _copySettings = NULL; - } - - _copySettings = StoreSettings(); - Q_strncpy (_copyClassName, m_pCurrentPanel->GetClassName(), sizeof( _copyClassName ) ); -} - -//----------------------------------------------------------------------------- -// Purpose: Create a new Panel with the _copySettings applied -//----------------------------------------------------------------------------- -void BuildModeDialog::DoPaste() -{ - // Make a new control located where you had the mouse - int x, y; - input()->GetCursorPos(x, y); - m_pBuildGroup->GetContextPanel()->ScreenToLocal(x,y); - - Panel *newPanel = OnNewControl(_copyClassName, x, y); - if (newPanel) - { - newPanel->ApplySettings(_copySettings); - newPanel->SetPos(x, y); - char name[255]; - m_pBuildGroup->GetNewFieldName(name, sizeof(name), newPanel); - newPanel->SetName(name); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Store the settings of the current panel in a keyValues -//----------------------------------------------------------------------------- -KeyValues *BuildModeDialog::StoreSettings() -{ - KeyValues *storedSettings; - storedSettings = new KeyValues( m_pCurrentPanel->GetName() ); - - // loop through the textedit filling in settings - for ( int i = 0; i < m_pPanelList->m_PanelList.Size(); i++ ) - { - const char *name = m_pPanelList->m_PanelList[i].m_szName; - char buf[512]; - if (m_pPanelList->m_PanelList[i].m_EditPanel) - { - m_pPanelList->m_PanelList[i].m_EditPanel->GetText(buf, sizeof(buf)); - } - else - { - m_pPanelList->m_PanelList[i].m_EditButton->GetText(buf, sizeof(buf)); - } - - switch (m_pPanelList->m_PanelList[i].m_iType) - { - case TYPE_CORNER: - case TYPE_AUTORESIZE: - // the integer value is assumed to be the first part of the string for these items - storedSettings->SetInt(name, atoi(buf)); - break; - - default: - storedSettings->SetString(name, buf); - break; - } - } - - return storedSettings; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void BuildModeDialog::OnKeyCodeTyped(KeyCode code) -{ - if (code == KEY_ENTER) // if someone hits return apply the changes - { - ApplyDataToControls(); - } - else - { - Frame::OnKeyCodeTyped(code); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Checks to see if any text has changed -//----------------------------------------------------------------------------- -void BuildModeDialog::OnTextChanged( Panel *panel ) -{ - if (panel == m_pFileSelectionCombo) - { - // reload file if it's changed - char newFile[512]; - m_pFileSelectionCombo->GetText(newFile, sizeof(newFile)); - - if (stricmp(newFile, m_pBuildGroup->GetResourceName()) != 0) - { - // file has changed, reload - SetActiveControl(NULL); - m_pBuildGroup->ChangeControlSettingsFile(newFile); - } - return; - } - - if (panel == m_pAddNewControlCombo) - { - char buf[40]; - m_pAddNewControlCombo->GetText(buf, 40); - if (stricmp(buf, "None") != 0) - { - OnNewControl(buf); - // reset box back to None - m_pAddNewControlCombo->ActivateItemByRow( 0 ); - } - } - - if ( panel == m_pEditableChildren ) - { - KeyValues *kv = m_pEditableChildren->GetActiveItemUserData(); - if ( kv ) - { - EditablePanel *ep = reinterpret_cast< EditablePanel * >( kv->GetPtr( "ptr" ) ); - if ( ep ) - { - ep->ActivateBuildMode(); - } - } - } - - if ( panel == m_pEditableParents ) - { - KeyValues *kv = m_pEditableParents->GetActiveItemUserData(); - if ( kv ) - { - EditablePanel *ep = reinterpret_cast< EditablePanel * >( kv->GetPtr( "ptr" ) ); - if ( ep ) - { - ep->ActivateBuildMode(); - } - } - } - - if (m_pCurrentPanel && m_pCurrentPanel->IsBuildModeEditable()) - { - m_pApplyButton->SetEnabled(true); - } - - if (_autoUpdate) - { - ApplyDataToControls(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void BuildModeDialog::ExitBuildMode( void ) -{ - // make sure rulers are off - if (m_pBuildGroup->HasRulersOn()) - { - m_pBuildGroup->ToggleRulerDisplay(); - } - m_pBuildGroup->SetEnabled(false); -} - -//----------------------------------------------------------------------------- -// Purpose: Create a new control in the context panel -//----------------------------------------------------------------------------- -Panel *BuildModeDialog::OnNewControl( const char *name, int x, int y) -{ - // returns NULL on failure - Panel *newPanel = m_pBuildGroup->NewControl(name, x, y); - if (newPanel) - { - // call mouse commands to simulate selecting the new - // panel. This will set everything up correctly in the buildGroup. - m_pBuildGroup->MousePressed(MOUSE_LEFT, newPanel); - m_pBuildGroup->MouseReleased(MOUSE_LEFT, newPanel); - } - - m_pSaveButton->SetEnabled(true); - - return newPanel; -} - -//----------------------------------------------------------------------------- -// Purpose: enable the save button, useful when buildgroup needs to Activate it. -//----------------------------------------------------------------------------- -void BuildModeDialog::EnableSaveButton() -{ - m_pSaveButton->SetEnabled(true); -} - -//----------------------------------------------------------------------------- -// Purpose: Revert to the saved settings in the .res file -//----------------------------------------------------------------------------- -void BuildModeDialog::RevertToSaved() -{ - // hide the dialog as reloading will destroy it - surface()->SetPanelVisible(this->GetVPanel(), false); - m_pBuildGroup->ReloadControlSettings(); -} - -//----------------------------------------------------------------------------- -// Purpose: Display some information about the editor -//----------------------------------------------------------------------------- -void BuildModeDialog::ShowHelp() -{ - char helpText[]= "In the Build Mode Dialog Window:\n" - "Delete button - deletes the currently selected panel if it is deletable.\n" - "Apply button - applies changes to the Context Panel.\n" - "Save button - saves all settings to file. \n" - "Revert to saved- reloads the last saved file.\n" - "Auto Update - any changes apply instantly.\n" - "Typing Enter in any text field applies changes.\n" - "New Control menu - creates a new panel in the upper left corner.\n\n" - "In the Context Panel:\n" - "After selecting and moving a panel Ctrl-z will undo the move.\n" - "Shift clicking panels allows multiple panels to be selected into a group.\n" - "Ctrl-c copies the settings of the last selected panel.\n" - "Ctrl-v creates a new panel with the copied settings at the location of the mouse pointer.\n" - "Arrow keys slowly move panels, holding shift + arrow will slowly resize it.\n" - "Holding right mouse button down opens a dropdown panel creation menu.\n" - " Panel will be created where the menu was opened.\n" - "Delete key deletes the currently selected panel if it is deletable.\n" - " Does nothing to multiple selections."; - - MessageBox *helpDlg = new MessageBox ("Build Mode Help", helpText, this); - helpDlg->AddActionSignalTarget(this); - helpDlg->DoModal(); -} - - -void BuildModeDialog::ShutdownBuildMode() -{ - m_pBuildGroup->SetEnabled(false); -} - -void BuildModeDialog::OnPanelMoved() -{ - m_pApplyButton->SetEnabled(true); -} - -//----------------------------------------------------------------------------- -// Purpose: message handles thats sets the text in the clipboard -//----------------------------------------------------------------------------- -void BuildModeDialog::OnSetClipboardText(const char *text) -{ - system()->SetClipboardText(text, strlen(text)); -} - -void BuildModeDialog::OnCreateNewControl( char const *text ) -{ - if ( !Q_stricmp( text, "None" ) ) - return; - - OnNewControl( text, m_nClick[ 0 ], m_nClick[ 1 ] ); -} - -void BuildModeDialog::OnShowNewControlMenu() -{ - if ( !m_pBuildGroup ) - return; - - int i; - - input()->GetCursorPos( m_nClick[ 0 ], m_nClick[ 1 ] ); - m_pBuildGroup->GetContextPanel()->ScreenToLocal( m_nClick[ 0 ], m_nClick[ 1 ] ); - - if ( m_hContextMenu ) - delete m_hContextMenu.Get(); - - m_hContextMenu = new Menu( this, "NewControls" ); - - // Show popup menu - m_hContextMenu->AddMenuItem( "None", "None", new KeyValues( "CreateNewControl", "text", "None" ), this ); - - CUtlVector< char const * > names; - CBuildFactoryHelper::GetFactoryNames( names ); - // Sort the names - CUtlRBTree< char const *, int > sorted( 0, 0, StringLessThan ); - - for ( i = 0; i < names.Count(); ++i ) - { - sorted.Insert( names[ i ] ); - } - - for ( i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) ) - { - m_hContextMenu->AddMenuItem( sorted[ i ], sorted[ i ], new KeyValues( "CreateNewControl", "text", sorted[ i ] ), this ); - } - - Menu::PlaceContextMenu( this, m_hContextMenu ); -} - -void BuildModeDialog::OnReloadLocalization() -{ - // reload localization files - g_pVGuiLocalize->ReloadLocalizationFiles( ); -} - -bool BuildModeDialog::IsBuildGroupEnabled() -{ - // Don't ever edit the actual build dialog!!! - return false; -} - -void BuildModeDialog::OnChangeChild( int direction ) -{ - Assert( direction == 1 || direction == -1 ); - if ( !m_pBuildGroup ) - return; - - Panel *current = m_pCurrentPanel; - Panel *context = m_pBuildGroup->GetContextPanel(); - - if ( !current || current == context ) - { - current = NULL; - if ( context->GetChildCount() > 0 ) - { - current = context->GetChild( 0 ); - } - } - else - { - int i; - // Move in direction requested - int children = context->GetChildCount(); - for ( i = 0; i < children; ++i ) - { - Panel *child = context->GetChild( i ); - if ( child == current ) - { - break; - } - } - - if ( i < children ) - { - for ( int offset = 1; offset < children; ++offset ) - { - int test = ( i + ( direction * offset ) ) % children; - if ( test < 0 ) - test += children; - if ( test == i ) - continue; - - Panel *check = context->GetChild( test ); - BuildModeDialog *bm = dynamic_cast< BuildModeDialog * >( check ); - if ( bm ) - continue; - - current = check; - break; - } - } - } - - if ( !current ) - { - return; - } - - SetActiveControl( current ); +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +struct PanelItem_t +{ + PanelItem_t() : m_EditLabel(NULL) {} + + Panel *m_EditLabel; + TextEntry *m_EditPanel; + ComboBox *m_pCombo; + Button *m_EditButton; + char m_szName[64]; + int m_iType; +}; + +class CSmallTextEntry : public TextEntry +{ + DECLARE_CLASS_SIMPLE( CSmallTextEntry, TextEntry ); +public: + + CSmallTextEntry( Panel *parent, char const *panelName ) : + BaseClass( parent, panelName ) + { + } + + virtual void ApplySchemeSettings( IScheme *scheme ) + { + BaseClass::ApplySchemeSettings( scheme ); + + SetFont( scheme->GetFont( "DefaultVerySmall" ) ); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: Holds a list of all the edit fields for the currently selected panel +//----------------------------------------------------------------------------- +class BuildModeDialog::PanelList +{ +public: + + CUtlVector m_PanelList; + + void AddItem( Panel *label, TextEntry *edit, ComboBox *combo, Button *button, const char *name, int type ) + { + PanelItem_t item; + item.m_EditLabel = label; + item.m_EditPanel = edit; + Q_strncpy(item.m_szName, name, sizeof(item.m_szName)); + item.m_iType = type; + item.m_pCombo = combo; + item.m_EditButton = button; + + m_PanelList.AddToTail( item ); + } + + void RemoveAll( void ) + { + for ( int i = 0; i < m_PanelList.Size(); i++ ) + { + PanelItem_t *item = &m_PanelList[i]; + delete item->m_EditLabel; + delete item->m_EditPanel; + delete item->m_EditButton; + } + + m_PanelList.RemoveAll(); + m_pControls->RemoveAll(); + } + + KeyValues *m_pResourceData; + PanelListPanel *m_pControls; +}; + +//----------------------------------------------------------------------------- +// Purpose: Dialog for adding localized strings +//----------------------------------------------------------------------------- +class BuildModeLocalizedStringEditDialog : public Frame +{ + DECLARE_CLASS_SIMPLE(BuildModeLocalizedStringEditDialog, Frame); + +public: + +#pragma warning( disable : 4355 ) + BuildModeLocalizedStringEditDialog() : Frame(this, NULL) + { + m_pTokenEntry = new TextEntry(this, NULL); + m_pValueEntry = new TextEntry(this, NULL); + m_pFileCombo = new ComboBox(this, NULL, 12, false); + m_pOKButton = new Button(this, NULL, "OK"); + m_pCancelButton = new Button(this, NULL, "Cancel"); + + m_pCancelButton->SetCommand("Close"); + m_pOKButton->SetCommand("OK"); + + // add the files to the combo + for (int i = 0; i < g_pVGuiLocalize->GetLocalizationFileCount(); i++) + { + m_pFileCombo->AddItem(g_pVGuiLocalize->GetLocalizationFileName(i), NULL); + } + } +#pragma warning( default : 4355 ) + + virtual void DoModal(const char *token) + { + input()->SetAppModalSurface(GetVPanel()); + + // setup data + m_pTokenEntry->SetText(token); + + // lookup the value + StringIndex_t val = g_pVGuiLocalize->FindIndex(token); + if (val != INVALID_LOCALIZE_STRING_INDEX) + { + m_pValueEntry->SetText(g_pVGuiLocalize->GetValueByIndex(val)); + + // set the place in the file combo + m_pFileCombo->SetText(g_pVGuiLocalize->GetFileNameByIndex(val)); + } + else + { + m_pValueEntry->SetText(""); + } + } + +private: + virtual void PerformLayout() + { + } + + virtual void OnClose() + { + input()->SetAppModalSurface(NULL); + BaseClass::OnClose(); + //PostActionSignal(new KeyValues("Command" + } + + virtual void OnCommand(const char *command) + { + if (!stricmp(command, "OK")) + { + //!! apply changes + } + else + { + BaseClass::OnCommand(command); + } + } + + vgui::TextEntry *m_pTokenEntry; + vgui::TextEntry *m_pValueEntry; + vgui::ComboBox *m_pFileCombo; + vgui::Button *m_pOKButton; + vgui::Button *m_pCancelButton; +}; + +class CBuildModeDialogMgr +{ +public: + + void Add( BuildModeDialog *pDlg ); + void Remove( BuildModeDialog *pDlg ); + + int Count() const; + +private: + CUtlVector< BuildModeDialog * > m_vecBuildDialogs; +}; + +static CBuildModeDialogMgr g_BuildModeDialogMgr; + +void CBuildModeDialogMgr::Add( BuildModeDialog *pDlg ) +{ + if ( m_vecBuildDialogs.Find( pDlg ) == m_vecBuildDialogs.InvalidIndex() ) + { + m_vecBuildDialogs.AddToTail( pDlg ); + } +} + +void CBuildModeDialogMgr::Remove( BuildModeDialog *pDlg ) +{ + m_vecBuildDialogs.FindAndRemove( pDlg ); +} + +int CBuildModeDialogMgr::Count() const +{ + return m_vecBuildDialogs.Count(); +} + +int GetBuildModeDialogCount() +{ + return g_BuildModeDialogMgr.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +BuildModeDialog::BuildModeDialog(BuildGroup *buildGroup) : Frame(buildGroup->GetContextPanel(), "BuildModeDialog") +{ + SetMinimumSize(300, 256); + SetSize(300, 420); + m_pCurrentPanel = NULL; + m_pEditableParents = NULL; + m_pEditableChildren = NULL; + m_pNextChild = NULL; + m_pPrevChild = NULL; + m_pBuildGroup = buildGroup; + _undoSettings = NULL; + _copySettings = NULL; + _autoUpdate = false; + MakePopup(); + SetTitle("VGUI Build Mode Editor", true); + + CreateControls(); + LoadUserConfig("BuildModeDialog"); + + g_BuildModeDialogMgr.Add( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +BuildModeDialog::~BuildModeDialog() +{ + g_BuildModeDialogMgr.Remove( this ); + + m_pPanelList->m_pResourceData->deleteThis(); + m_pPanelList->m_pControls->DeleteAllItems(); + if (_undoSettings) + _undoSettings->deleteThis(); + if (_copySettings) + _copySettings->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: makes sure build mode has been shut down properly +//----------------------------------------------------------------------------- +void BuildModeDialog::OnClose() +{ + if (m_pBuildGroup->IsEnabled()) + { + m_pBuildGroup->SetEnabled(false); + } + else + { + BaseClass::OnClose(); + MarkForDeletion(); + } +} + +class CBuildModeNavCombo : public ComboBox +{ + DECLARE_CLASS_SIMPLE( CBuildModeNavCombo, ComboBox ); +public: + + CBuildModeNavCombo(Panel *parent, const char *panelName, int numLines, bool allowEdit, bool getParents, Panel *context ) : + BaseClass( parent, panelName, numLines, allowEdit ), + m_bParents( getParents ) + { + m_hContext = context; + } + + virtual void OnShowMenu(Menu *menu) + { + menu->DeleteAllItems(); + if ( !m_hContext.Get() ) + return; + + if ( m_bParents ) + { + Panel *p = m_hContext->GetParent(); + while ( p ) + { + EditablePanel *ep = dynamic_cast < EditablePanel * >( p ); + if ( ep && ep->GetBuildGroup() ) + { + KeyValues *kv = new KeyValues( "Panel" ); + kv->SetPtr( "ptr", p ); + char const *text = ep->GetName() ? ep->GetName() : "unnamed"; + menu->AddMenuItem( text, new KeyValues("SetText", "text", text), GetParent(), kv ); + } + p = p->GetParent(); + } + } + else + { + int i; + int c = m_hContext->GetChildCount(); + for ( i = 0; i < c; ++i ) + { + EditablePanel *ep = dynamic_cast < EditablePanel * >( m_hContext->GetChild( i ) ); + if ( ep && ep->IsVisible() && ep->GetBuildGroup() ) + { + KeyValues *kv = new KeyValues( "Panel" ); + kv->SetPtr( "ptr", ep ); + char const *text = ep->GetName() ? ep->GetName() : "unnamed"; + menu->AddMenuItem( text, new KeyValues("SetText", "text", text), GetParent(), kv ); + } + } + } + } +private: + bool m_bParents; + vgui::PHandle m_hContext; +}; + +//----------------------------------------------------------------------------- +// Purpose: Creates the build mode editing controls +//----------------------------------------------------------------------------- +void BuildModeDialog::CreateControls() +{ + int i; + m_pPanelList = new PanelList; + m_pPanelList->m_pResourceData = new KeyValues( "BuildDialog" ); + m_pPanelList->m_pControls = new PanelListPanel(this, "BuildModeControls"); + + // file to edit combo box is first + m_pFileSelectionCombo = new ComboBox(this, "FileSelectionCombo", 10, false); + for ( i = 0; i < m_pBuildGroup->GetRegisteredControlSettingsFileCount(); i++) + { + m_pFileSelectionCombo->AddItem(m_pBuildGroup->GetRegisteredControlSettingsFileByIndex(i), NULL); + } + if (m_pFileSelectionCombo->GetItemCount() < 2) + { + m_pFileSelectionCombo->SetEnabled(false); + } + + int buttonH = 18; + + // status info at top of dialog + m_pStatusLabel = new Label(this, "StatusLabel", "[nothing currently selected]"); + m_pStatusLabel->SetTextColorState(Label::CS_DULL); + m_pStatusLabel->SetTall( buttonH ); + m_pDivider = new Divider(this, "Divider"); + // drop-down combo box for adding new controls + m_pAddNewControlCombo = new ComboBox(this, NULL, 30, false); + m_pAddNewControlCombo->SetSize(116, buttonH); + m_pAddNewControlCombo->SetOpenDirection(Menu::DOWN); + + m_pEditableParents = new CBuildModeNavCombo( this, NULL, 15, false, true, m_pBuildGroup->GetContextPanel() ); + m_pEditableParents->SetSize(116, buttonH); + m_pEditableParents->SetOpenDirection(Menu::DOWN); + + m_pEditableChildren = new CBuildModeNavCombo( this, NULL, 15, false, false, m_pBuildGroup->GetContextPanel() ); + m_pEditableChildren->SetSize(116, buttonH); + m_pEditableChildren->SetOpenDirection(Menu::DOWN); + + m_pNextChild = new Button( this, "NextChild", "Next", this ); + m_pNextChild->SetCommand( new KeyValues( "OnChangeChild", "direction", 1 ) ); + + m_pPrevChild = new Button( this, "PrevChild", "Prev", this ); + m_pPrevChild->SetCommand( new KeyValues( "OnChangeChild", "direction", -1 ) ); + + // controls that can be added + // this list comes from controls EditablePanel can create by name. + int defaultItem = m_pAddNewControlCombo->AddItem("None", NULL); + + CUtlVector< char const * > names; + CBuildFactoryHelper::GetFactoryNames( names ); + // Sort the names + CUtlRBTree< char const *, int > sorted( 0, 0, StringLessThan ); + + for ( i = 0; i < names.Count(); ++i ) + { + sorted.Insert( names[ i ] ); + } + + for ( i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) ) + { + m_pAddNewControlCombo->AddItem( sorted[ i ], NULL ); + } + + m_pAddNewControlCombo->ActivateItem(defaultItem); + + m_pExitButton = new Button(this, "ExitButton", "&Exit"); + m_pExitButton->SetSize(64, buttonH); + + m_pSaveButton = new Button(this, "SaveButton", "&Save"); + m_pSaveButton->SetSize(64, buttonH); + + m_pApplyButton = new Button(this, "ApplyButton", "&Apply"); + m_pApplyButton->SetSize(64, buttonH); + + m_pReloadLocalization = new Button( this, "Localization", "&Reload Localization" ); + m_pReloadLocalization->SetSize( 100, buttonH ); + + m_pExitButton->SetCommand("Exit"); + m_pSaveButton->SetCommand("Save"); + m_pApplyButton->SetCommand("Apply"); + m_pReloadLocalization->SetCommand( new KeyValues( "ReloadLocalization" ) ); + + m_pDeleteButton = new Button(this, "DeletePanelButton", "Delete"); + m_pDeleteButton->SetSize(64, buttonH); + m_pDeleteButton->SetCommand("DeletePanel"); + + m_pVarsButton = new MenuButton(this, "VarsButton", "Variables"); + m_pVarsButton->SetSize(72, buttonH); + m_pVarsButton->SetOpenDirection(Menu::UP); + + // iterate the vars + KeyValues *vars = m_pBuildGroup->GetDialogVariables(); + if (vars && vars->GetFirstSubKey()) + { + // create the menu + m_pVarsButton->SetEnabled(true); + Menu *menu = new Menu(m_pVarsButton, "VarsMenu"); + + // set all the variables to be copied to the clipboard when selected + for (KeyValues *kv = vars->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey()) + { + char buf[32]; + _snprintf(buf, sizeof(buf), "%%%s%%", kv->GetName()); + menu->AddMenuItem(kv->GetName(), new KeyValues("SetClipboardText", "text", buf), this); + } + + m_pVarsButton->SetMenu(menu); + } + else + { + // no variables + m_pVarsButton->SetEnabled(false); + } + + m_pApplyButton->SetTabPosition(1); + m_pPanelList->m_pControls->SetTabPosition(2); + m_pVarsButton->SetTabPosition(3); + m_pDeleteButton->SetTabPosition(4); + m_pAddNewControlCombo->SetTabPosition(5); + m_pSaveButton->SetTabPosition(6); + m_pExitButton->SetTabPosition(7); + + m_pEditableParents->SetTabPosition( 8 ); + m_pEditableChildren->SetTabPosition( 9 ); + + m_pPrevChild->SetTabPosition( 10 ); + m_pNextChild->SetTabPosition( 11 ); + + m_pReloadLocalization->SetTabPosition( 12 ); +} + +void BuildModeDialog::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + HFont font = pScheme->GetFont( "DefaultVerySmall" ); + m_pStatusLabel->SetFont( font ); + m_pReloadLocalization->SetFont( font ); + m_pExitButton->SetFont( font ); + m_pSaveButton->SetFont( font ); + m_pApplyButton->SetFont( font ); + m_pAddNewControlCombo->SetFont( font ); + m_pEditableParents->SetFont( font ); + m_pEditableChildren->SetFont( font ); + m_pDeleteButton->SetFont( font ); + m_pVarsButton->SetFont( font ); + m_pPrevChild->SetFont( font ); + m_pNextChild->SetFont( font ); +} + +//----------------------------------------------------------------------------- +// Purpose: lays out controls +//----------------------------------------------------------------------------- +void BuildModeDialog::PerformLayout() +{ + BaseClass::PerformLayout(); + + // layout parameters + const int BORDER_GAP = 16, YGAP_SMALL = 4, YGAP_LARGE = 8, TITLE_HEIGHT = 24, BOTTOM_CONTROLS_HEIGHT = 145, XGAP = 6; + + int wide, tall; + GetSize(wide, tall); + + int xpos = BORDER_GAP; + int ypos = BORDER_GAP + TITLE_HEIGHT; + + // controls from top down + // selection combo + m_pFileSelectionCombo->SetBounds(xpos, ypos, wide - (BORDER_GAP * 2), m_pStatusLabel->GetTall()); + ypos += (m_pStatusLabel->GetTall() + YGAP_SMALL); + + // status + m_pStatusLabel->SetBounds(xpos, ypos, wide - (BORDER_GAP * 2), m_pStatusLabel->GetTall()); + ypos += (m_pStatusLabel->GetTall() + YGAP_SMALL); + + // center control + m_pPanelList->m_pControls->SetPos(xpos, ypos); + m_pPanelList->m_pControls->SetSize(wide - (BORDER_GAP * 2), tall - (ypos + BOTTOM_CONTROLS_HEIGHT)); + + // controls from bottom-right + ypos = tall - BORDER_GAP; + xpos = BORDER_GAP + m_pVarsButton->GetWide() + m_pDeleteButton->GetWide() + m_pAddNewControlCombo->GetWide() + (XGAP * 2); + + // bottom row of buttons + ypos -= m_pApplyButton->GetTall(); + xpos -= m_pApplyButton->GetWide(); + m_pApplyButton->SetPos(xpos, ypos); + + xpos -= m_pExitButton->GetWide(); + xpos -= XGAP; + m_pExitButton->SetPos(xpos, ypos); + + xpos -= m_pSaveButton->GetWide(); + xpos -= XGAP; + m_pSaveButton->SetPos(xpos, ypos); + + // divider + xpos = BORDER_GAP; + ypos -= (YGAP_LARGE + m_pDivider->GetTall()); + m_pDivider->SetBounds(xpos, ypos, wide - (xpos + BORDER_GAP), 2); + + ypos -= (YGAP_LARGE + m_pVarsButton->GetTall()); + + xpos = BORDER_GAP; + m_pEditableParents->SetPos( xpos, ypos ); + m_pEditableChildren->SetPos( xpos + 150, ypos ); + + ypos -= (YGAP_LARGE + 18 ); + xpos = BORDER_GAP; + m_pReloadLocalization->SetPos( xpos, ypos ); + + xpos += ( XGAP ) + m_pReloadLocalization->GetWide(); + + m_pPrevChild->SetPos( xpos, ypos ); + m_pPrevChild->SetSize( 64, m_pReloadLocalization->GetTall() ); + xpos += ( XGAP ) + m_pPrevChild->GetWide(); + + m_pNextChild->SetPos( xpos, ypos ); + m_pNextChild->SetSize( 64, m_pReloadLocalization->GetTall() ); + + ypos -= (YGAP_LARGE + m_pVarsButton->GetTall()); + xpos = BORDER_GAP; + + // edit buttons + m_pVarsButton->SetPos(xpos, ypos); + xpos += (XGAP + m_pVarsButton->GetWide()); + m_pDeleteButton->SetPos(xpos, ypos); + xpos += (XGAP + m_pDeleteButton->GetWide()); + m_pAddNewControlCombo->SetPos(xpos, ypos); +} + + +//----------------------------------------------------------------------------- +// Purpose: Deletes all the controls from the panel +//----------------------------------------------------------------------------- +void BuildModeDialog::RemoveAllControls( void ) +{ + // free the array + m_pPanelList->RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: simple helper function to get a token from a string +// Input : char **string - pointer to the string pointer, which will be incremented +// Output : const char * - pointer to the token +//----------------------------------------------------------------------------- +const char *ParseTokenFromString( const char **string ) +{ + static char buf[128]; + buf[0] = 0; + + // find the first alnum character + const char *tok = *string; + while ( !V_isalnum(*tok) && *tok != 0 ) + { + tok++; + } + + // read in all the alnum characters + int pos = 0; + while ( V_isalnum(tok[pos]) ) + { + buf[pos] = tok[pos]; + pos++; + } + + // null terminate the token + buf[pos] = 0; + + // update the main string pointer + *string = &(tok[pos]); + + // return a pointer to the static buffer + return buf; +} + +void BuildModeDialog::OnTextKillFocus() +{ + if ( !m_pCurrentPanel ) + return; + + ApplyDataToControls(); +} + + +//----------------------------------------------------------------------------- +// Purpose: sets up the current control to edit +//----------------------------------------------------------------------------- +void BuildModeDialog::SetActiveControl(Panel *controlToEdit) +{ + if (m_pCurrentPanel == controlToEdit) + { + // it's already set, so just update the property data and quit + if (m_pCurrentPanel) + { + UpdateControlData(m_pCurrentPanel); + } + return; + } + + // reset the data + m_pCurrentPanel = controlToEdit; + RemoveAllControls(); + m_pPanelList->m_pControls->MoveScrollBarToTop(); + + if (!m_pCurrentPanel) + { + m_pStatusLabel->SetText("[nothing currently selected]"); + m_pStatusLabel->SetTextColorState(Label::CS_DULL); + RemoveAllControls(); + return; + } + + // get the control description string + const char *controlDesc = m_pCurrentPanel->GetDescription(); + + // parse out the control description + int tabPosition = 1; + while (1) + { + const char *dataType = ParseTokenFromString(&controlDesc); + + // finish when we have no more tokens + if (*dataType == 0) + break; + + // default the data type to a string + int datat = TYPE_STRING; + + if (!stricmp(dataType, "int")) + { + datat = TYPE_STRING; //!! just for now + } + else if (!stricmp(dataType, "alignment")) + { + datat = TYPE_ALIGNMENT; + } + else if (!stricmp(dataType, "autoresize")) + { + datat = TYPE_AUTORESIZE; + } + else if (!stricmp(dataType, "corner")) + { + datat = TYPE_CORNER; + } + else if (!stricmp(dataType, "localize")) + { + datat = TYPE_LOCALIZEDSTRING; + } + + // get the field name + const char *fieldName = ParseTokenFromString(&controlDesc); + + int itemHeight = 18; + + // build a control & label + Label *label = new Label(this, NULL, fieldName); + label->SetSize(96, itemHeight); + label->SetContentAlignment(Label::a_east); + + TextEntry *edit = NULL; + ComboBox *editCombo = NULL; + Button *editButton = NULL; + if (datat == TYPE_ALIGNMENT) + { + // drop-down combo box + editCombo = new ComboBox(this, NULL, 9, false); + editCombo->AddItem("north-west", NULL); + editCombo->AddItem("north", NULL); + editCombo->AddItem("north-east", NULL); + editCombo->AddItem("west", NULL); + editCombo->AddItem("center", NULL); + editCombo->AddItem("east", NULL); + editCombo->AddItem("south-west", NULL); + editCombo->AddItem("south", NULL); + editCombo->AddItem("south-east", NULL); + + edit = editCombo; + } + else if (datat == TYPE_AUTORESIZE) + { + // drop-down combo box + editCombo = new ComboBox(this, NULL, 4, false); + editCombo->AddItem( "0 - no auto-resize", NULL); + editCombo->AddItem( "1 - resize right", NULL); + editCombo->AddItem( "2 - resize down", NULL); + editCombo->AddItem( "3 - down & right", NULL); + + edit = editCombo; + } + else if (datat == TYPE_CORNER) + { + // drop-down combo box + editCombo = new ComboBox(this, NULL, 4, false); + editCombo->AddItem("0 - top-left", NULL); + editCombo->AddItem("1 - top-right", NULL); + editCombo->AddItem("2 - bottom-left", NULL); + editCombo->AddItem("3 - bottom-right", NULL); + + edit = editCombo; + } + else if (datat == TYPE_LOCALIZEDSTRING) + { + editButton = new Button(this, NULL, "..."); + editButton->SetParent(this); + editButton->AddActionSignalTarget(this); + editButton->SetTabPosition(tabPosition++); + editButton->SetTall( itemHeight ); + label->SetAssociatedControl(editButton); + } + else + { + // normal string edit + edit = new CSmallTextEntry(this, NULL); + } + + if (edit) + { + edit->SetTall( itemHeight ); + edit->SetParent(this); + edit->AddActionSignalTarget(this); + edit->SetTabPosition(tabPosition++); + label->SetAssociatedControl(edit); + } + + HFont smallFont = scheme()->GetIScheme( GetScheme() )->GetFont( "DefaultVerySmall" ); + + if ( label ) + { + label->SetFont( smallFont ); + } + if ( edit ) + { + edit->SetFont( smallFont ); + } + if ( editCombo ) + { + editCombo->SetFont( smallFont ); + } + if ( editButton ) + { + editButton->SetFont( smallFont ); + } + + // add to our control list + m_pPanelList->AddItem(label, edit, editCombo, editButton, fieldName, datat); + + if ( edit ) + { + m_pPanelList->m_pControls->AddItem(label, edit); + } + else + { + m_pPanelList->m_pControls->AddItem(label, editButton); + } + } + + // check and see if the current panel is a Label + // iterate through the class hierarchy + if ( controlToEdit->IsBuildModeDeletable() ) + { + m_pDeleteButton->SetEnabled(true); + } + else + { + m_pDeleteButton->SetEnabled(false); + } + + // update the property data in the dialog + UpdateControlData(m_pCurrentPanel); + + // set our title + if ( m_pBuildGroup->GetResourceName() ) + { + m_pFileSelectionCombo->SetText(m_pBuildGroup->GetResourceName()); + } + else + { + m_pFileSelectionCombo->SetText("[ no resource file associated with dialog ]"); + } + + m_pApplyButton->SetEnabled(false); + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Updates the edit fields with information about the control +//----------------------------------------------------------------------------- +void BuildModeDialog::UpdateControlData(Panel *control) +{ + KeyValues *dat = m_pPanelList->m_pResourceData->FindKey( control->GetName(), true ); + control->GetSettings( dat ); + + // apply the settings to the edit panels + for ( int i = 0; i < m_pPanelList->m_PanelList.Size(); i++ ) + { + const char *name = m_pPanelList->m_PanelList[i].m_szName; + const char *datstring = dat->GetString( name, "" ); + + UpdateEditControl(m_pPanelList->m_PanelList[i], datstring); + } + + char statusText[512]; + Q_snprintf(statusText, sizeof(statusText), "%s: \'%s\'", control->GetClassName(), control->GetName()); + m_pStatusLabel->SetText(statusText); + m_pStatusLabel->SetTextColorState(Label::CS_NORMAL); +} + +//----------------------------------------------------------------------------- +// Purpose: Updates the data in a single edit control +//----------------------------------------------------------------------------- +void BuildModeDialog::UpdateEditControl(PanelItem_t &panelItem, const char *datstring) +{ + switch (panelItem.m_iType) + { + case TYPE_AUTORESIZE: + case TYPE_CORNER: + { + int dat = atoi(datstring); + panelItem.m_pCombo->ActivateItemByRow(dat); + } + break; + + case TYPE_LOCALIZEDSTRING: + { + panelItem.m_EditButton->SetText(datstring); + } + break; + + default: + { + wchar_t unicode[512]; + g_pVGuiLocalize->ConvertANSIToUnicode(datstring, unicode, sizeof(unicode)); + panelItem.m_EditPanel->SetText(unicode); + } + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called when one of the buttons is pressed +//----------------------------------------------------------------------------- +void BuildModeDialog::OnCommand(const char *command) +{ + if (!stricmp(command, "Save")) + { + // apply the current data and save it to disk + ApplyDataToControls(); + if (m_pBuildGroup->SaveControlSettings()) + { + // disable save button until another change has been made + m_pSaveButton->SetEnabled(false); + } + } + else if (!stricmp(command, "Exit")) + { + // exit build mode + ExitBuildMode(); + } + else if (!stricmp(command, "Apply")) + { + // apply data to controls + ApplyDataToControls(); + } + else if (!stricmp(command, "DeletePanel")) + { + OnDeletePanel(); + } + else if (!stricmp(command, "RevertToSaved")) + { + RevertToSaved(); + } + else if (!stricmp(command, "ShowHelp")) + { + ShowHelp(); + } + else + { + BaseClass::OnCommand(command); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Deletes a panel from the buildgroup +//----------------------------------------------------------------------------- +void BuildModeDialog::OnDeletePanel() +{ + if (!m_pCurrentPanel->IsBuildModeEditable()) + { + return; + } + + m_pBuildGroup->RemoveSettings(); + SetActiveControl(m_pBuildGroup->GetCurrentPanel()); + + _undoSettings->deleteThis(); + _undoSettings = NULL; + m_pSaveButton->SetEnabled(true); +} + +//----------------------------------------------------------------------------- +// Purpose: Applies the current settings to the build controls +//----------------------------------------------------------------------------- +void BuildModeDialog::ApplyDataToControls() +{ + // don't apply if the panel is not editable + if ( !m_pCurrentPanel->IsBuildModeEditable()) + { + UpdateControlData( m_pCurrentPanel ); + return; // return success, since we are behaving as expected. + } + + char fieldName[512]; + if (m_pPanelList->m_PanelList[0].m_EditPanel) + { + m_pPanelList->m_PanelList[0].m_EditPanel->GetText(fieldName, sizeof(fieldName)); + } + else + { + m_pPanelList->m_PanelList[0].m_EditButton->GetText(fieldName, sizeof(fieldName)); + } + + // check to see if any buildgroup panels have this name + Panel *panel = m_pBuildGroup->FieldNameTaken(fieldName); + if (panel) + { + if (panel != m_pCurrentPanel)// make sure name is taken by some other panel not this one + { + char messageString[255]; + Q_snprintf(messageString, sizeof( messageString ), "Fieldname is not unique: %s\nRename it and try again.", fieldName); + MessageBox *errorBox = new MessageBox("Cannot Apply", messageString); + errorBox->DoModal(); + UpdateControlData(m_pCurrentPanel); + m_pApplyButton->SetEnabled(false); + return; + } + } + + // create a section to store settings + // m_pPanelList->m_pResourceData->getSection( m_pCurrentPanel->GetName(), true ); + KeyValues *dat = new KeyValues( m_pCurrentPanel->GetName() ); + + // loop through the textedit filling in settings + for ( int i = 0; i < m_pPanelList->m_PanelList.Size(); i++ ) + { + const char *name = m_pPanelList->m_PanelList[i].m_szName; + char buf[512]; + if (m_pPanelList->m_PanelList[i].m_EditPanel) + { + m_pPanelList->m_PanelList[i].m_EditPanel->GetText(buf, sizeof(buf)); + } + else + { + m_pPanelList->m_PanelList[i].m_EditButton->GetText(buf, sizeof(buf)); + } + + switch (m_pPanelList->m_PanelList[i].m_iType) + { + case TYPE_CORNER: + case TYPE_AUTORESIZE: + // the integer value is assumed to be the first part of the string for these items + dat->SetInt(name, atoi(buf)); + break; + + default: + dat->SetString(name, buf); + break; + } + } + + // dat is built, hand it back to the control + m_pCurrentPanel->ApplySettings( dat ); + + if ( m_pBuildGroup->GetContextPanel() ) + { + m_pBuildGroup->GetContextPanel()->Repaint(); + } + + m_pApplyButton->SetEnabled(false); + m_pSaveButton->SetEnabled(true); +} + +//----------------------------------------------------------------------------- +// Purpose: Store the settings of the current panel in a KeyValues +//----------------------------------------------------------------------------- +void BuildModeDialog::StoreUndoSettings() +{ + // don't save if the planel is not editable + if ( !m_pCurrentPanel->IsBuildModeEditable()) + { + if (_undoSettings) + _undoSettings->deleteThis(); + _undoSettings = NULL; + return; + } + + if (_undoSettings) + { + _undoSettings->deleteThis(); + _undoSettings = NULL; + } + + _undoSettings = StoreSettings(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Revert to the stored the settings of the current panel in a keyValues +//----------------------------------------------------------------------------- +void BuildModeDialog::DoUndo() +{ + if ( _undoSettings ) + { + m_pCurrentPanel->ApplySettings( _undoSettings ); + UpdateControlData(m_pCurrentPanel); + _undoSettings->deleteThis(); + _undoSettings = NULL; + } + + m_pSaveButton->SetEnabled(true); +} + +//----------------------------------------------------------------------------- +// Purpose: Copy the settings of the current panel into a keyValues +//----------------------------------------------------------------------------- +void BuildModeDialog::DoCopy() +{ + if (_copySettings) + { + _copySettings->deleteThis(); + _copySettings = NULL; + } + + _copySettings = StoreSettings(); + Q_strncpy (_copyClassName, m_pCurrentPanel->GetClassName(), sizeof( _copyClassName ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Create a new Panel with the _copySettings applied +//----------------------------------------------------------------------------- +void BuildModeDialog::DoPaste() +{ + // Make a new control located where you had the mouse + int x, y; + input()->GetCursorPos(x, y); + m_pBuildGroup->GetContextPanel()->ScreenToLocal(x,y); + + Panel *newPanel = OnNewControl(_copyClassName, x, y); + if (newPanel) + { + newPanel->ApplySettings(_copySettings); + newPanel->SetPos(x, y); + char name[255]; + m_pBuildGroup->GetNewFieldName(name, sizeof(name), newPanel); + newPanel->SetName(name); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Store the settings of the current panel in a keyValues +//----------------------------------------------------------------------------- +KeyValues *BuildModeDialog::StoreSettings() +{ + KeyValues *storedSettings; + storedSettings = new KeyValues( m_pCurrentPanel->GetName() ); + + // loop through the textedit filling in settings + for ( int i = 0; i < m_pPanelList->m_PanelList.Size(); i++ ) + { + const char *name = m_pPanelList->m_PanelList[i].m_szName; + char buf[512]; + if (m_pPanelList->m_PanelList[i].m_EditPanel) + { + m_pPanelList->m_PanelList[i].m_EditPanel->GetText(buf, sizeof(buf)); + } + else + { + m_pPanelList->m_PanelList[i].m_EditButton->GetText(buf, sizeof(buf)); + } + + switch (m_pPanelList->m_PanelList[i].m_iType) + { + case TYPE_CORNER: + case TYPE_AUTORESIZE: + // the integer value is assumed to be the first part of the string for these items + storedSettings->SetInt(name, atoi(buf)); + break; + + default: + storedSettings->SetString(name, buf); + break; + } + } + + return storedSettings; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void BuildModeDialog::OnKeyCodeTyped(KeyCode code) +{ + if (code == KEY_ENTER) // if someone hits return apply the changes + { + ApplyDataToControls(); + } + else + { + Frame::OnKeyCodeTyped(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Checks to see if any text has changed +//----------------------------------------------------------------------------- +void BuildModeDialog::OnTextChanged( Panel *panel ) +{ + if (panel == m_pFileSelectionCombo) + { + // reload file if it's changed + char newFile[512]; + m_pFileSelectionCombo->GetText(newFile, sizeof(newFile)); + + if (stricmp(newFile, m_pBuildGroup->GetResourceName()) != 0) + { + // file has changed, reload + SetActiveControl(NULL); + m_pBuildGroup->ChangeControlSettingsFile(newFile); + } + return; + } + + if (panel == m_pAddNewControlCombo) + { + char buf[40]; + m_pAddNewControlCombo->GetText(buf, 40); + if (stricmp(buf, "None") != 0) + { + OnNewControl(buf); + // reset box back to None + m_pAddNewControlCombo->ActivateItemByRow( 0 ); + } + } + + if ( panel == m_pEditableChildren ) + { + KeyValues *kv = m_pEditableChildren->GetActiveItemUserData(); + if ( kv ) + { + EditablePanel *ep = reinterpret_cast< EditablePanel * >( kv->GetPtr( "ptr" ) ); + if ( ep ) + { + ep->ActivateBuildMode(); + } + } + } + + if ( panel == m_pEditableParents ) + { + KeyValues *kv = m_pEditableParents->GetActiveItemUserData(); + if ( kv ) + { + EditablePanel *ep = reinterpret_cast< EditablePanel * >( kv->GetPtr( "ptr" ) ); + if ( ep ) + { + ep->ActivateBuildMode(); + } + } + } + + if (m_pCurrentPanel && m_pCurrentPanel->IsBuildModeEditable()) + { + m_pApplyButton->SetEnabled(true); + } + + if (_autoUpdate) + { + ApplyDataToControls(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void BuildModeDialog::ExitBuildMode( void ) +{ + // make sure rulers are off + if (m_pBuildGroup->HasRulersOn()) + { + m_pBuildGroup->ToggleRulerDisplay(); + } + m_pBuildGroup->SetEnabled(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Create a new control in the context panel +//----------------------------------------------------------------------------- +Panel *BuildModeDialog::OnNewControl( const char *name, int x, int y) +{ + // returns NULL on failure + Panel *newPanel = m_pBuildGroup->NewControl(name, x, y); + if (newPanel) + { + // call mouse commands to simulate selecting the new + // panel. This will set everything up correctly in the buildGroup. + m_pBuildGroup->MousePressed(MOUSE_LEFT, newPanel); + m_pBuildGroup->MouseReleased(MOUSE_LEFT, newPanel); + } + + m_pSaveButton->SetEnabled(true); + + return newPanel; +} + +//----------------------------------------------------------------------------- +// Purpose: enable the save button, useful when buildgroup needs to Activate it. +//----------------------------------------------------------------------------- +void BuildModeDialog::EnableSaveButton() +{ + m_pSaveButton->SetEnabled(true); +} + +//----------------------------------------------------------------------------- +// Purpose: Revert to the saved settings in the .res file +//----------------------------------------------------------------------------- +void BuildModeDialog::RevertToSaved() +{ + // hide the dialog as reloading will destroy it + surface()->SetPanelVisible(this->GetVPanel(), false); + m_pBuildGroup->ReloadControlSettings(); +} + +//----------------------------------------------------------------------------- +// Purpose: Display some information about the editor +//----------------------------------------------------------------------------- +void BuildModeDialog::ShowHelp() +{ + char helpText[]= "In the Build Mode Dialog Window:\n" + "Delete button - deletes the currently selected panel if it is deletable.\n" + "Apply button - applies changes to the Context Panel.\n" + "Save button - saves all settings to file. \n" + "Revert to saved- reloads the last saved file.\n" + "Auto Update - any changes apply instantly.\n" + "Typing Enter in any text field applies changes.\n" + "New Control menu - creates a new panel in the upper left corner.\n\n" + "In the Context Panel:\n" + "After selecting and moving a panel Ctrl-z will undo the move.\n" + "Shift clicking panels allows multiple panels to be selected into a group.\n" + "Ctrl-c copies the settings of the last selected panel.\n" + "Ctrl-v creates a new panel with the copied settings at the location of the mouse pointer.\n" + "Arrow keys slowly move panels, holding shift + arrow will slowly resize it.\n" + "Holding right mouse button down opens a dropdown panel creation menu.\n" + " Panel will be created where the menu was opened.\n" + "Delete key deletes the currently selected panel if it is deletable.\n" + " Does nothing to multiple selections."; + + MessageBox *helpDlg = new MessageBox ("Build Mode Help", helpText, this); + helpDlg->AddActionSignalTarget(this); + helpDlg->DoModal(); +} + + +void BuildModeDialog::ShutdownBuildMode() +{ + m_pBuildGroup->SetEnabled(false); +} + +void BuildModeDialog::OnPanelMoved() +{ + m_pApplyButton->SetEnabled(true); +} + +//----------------------------------------------------------------------------- +// Purpose: message handles thats sets the text in the clipboard +//----------------------------------------------------------------------------- +void BuildModeDialog::OnSetClipboardText(const char *text) +{ + system()->SetClipboardText(text, strlen(text)); +} + +void BuildModeDialog::OnCreateNewControl( char const *text ) +{ + if ( !Q_stricmp( text, "None" ) ) + return; + + OnNewControl( text, m_nClick[ 0 ], m_nClick[ 1 ] ); +} + +void BuildModeDialog::OnShowNewControlMenu() +{ + if ( !m_pBuildGroup ) + return; + + int i; + + input()->GetCursorPos( m_nClick[ 0 ], m_nClick[ 1 ] ); + m_pBuildGroup->GetContextPanel()->ScreenToLocal( m_nClick[ 0 ], m_nClick[ 1 ] ); + + if ( m_hContextMenu ) + delete m_hContextMenu.Get(); + + m_hContextMenu = new Menu( this, "NewControls" ); + + // Show popup menu + m_hContextMenu->AddMenuItem( "None", "None", new KeyValues( "CreateNewControl", "text", "None" ), this ); + + CUtlVector< char const * > names; + CBuildFactoryHelper::GetFactoryNames( names ); + // Sort the names + CUtlRBTree< char const *, int > sorted( 0, 0, StringLessThan ); + + for ( i = 0; i < names.Count(); ++i ) + { + sorted.Insert( names[ i ] ); + } + + for ( i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) ) + { + m_hContextMenu->AddMenuItem( sorted[ i ], sorted[ i ], new KeyValues( "CreateNewControl", "text", sorted[ i ] ), this ); + } + + Menu::PlaceContextMenu( this, m_hContextMenu ); +} + +void BuildModeDialog::OnReloadLocalization() +{ + // reload localization files + g_pVGuiLocalize->ReloadLocalizationFiles( ); +} + +bool BuildModeDialog::IsBuildGroupEnabled() +{ + // Don't ever edit the actual build dialog!!! + return false; +} + +void BuildModeDialog::OnChangeChild( int direction ) +{ + Assert( direction == 1 || direction == -1 ); + if ( !m_pBuildGroup ) + return; + + Panel *current = m_pCurrentPanel; + Panel *context = m_pBuildGroup->GetContextPanel(); + + if ( !current || current == context ) + { + current = NULL; + if ( context->GetChildCount() > 0 ) + { + current = context->GetChild( 0 ); + } + } + else + { + int i; + // Move in direction requested + int children = context->GetChildCount(); + for ( i = 0; i < children; ++i ) + { + Panel *child = context->GetChild( i ); + if ( child == current ) + { + break; + } + } + + if ( i < children ) + { + for ( int offset = 1; offset < children; ++offset ) + { + int test = ( i + ( direction * offset ) ) % children; + if ( test < 0 ) + test += children; + if ( test == i ) + continue; + + Panel *check = context->GetChild( test ); + BuildModeDialog *bm = dynamic_cast< BuildModeDialog * >( check ); + if ( bm ) + continue; + + current = check; + break; + } + } + } + + if ( !current ) + { + return; + } + + SetActiveControl( current ); } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/Button.cpp b/mp/src/vgui2/vgui_controls/Button.cpp index 2a70e8fa..cceb8043 100644 --- a/mp/src/vgui2/vgui_controls/Button.cpp +++ b/mp/src/vgui2/vgui_controls/Button.cpp @@ -1,1095 +1,1095 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: Basic button control -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -// global list of all the names of all the sounds played by buttons -CUtlSymbolTable g_ButtonSoundNames; - -DECLARE_BUILD_FACTORY_DEFAULT_TEXT( Button, Button ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -Button::Button(Panel *parent, const char *panelName, const char *text, Panel *pActionSignalTarget, const char *pCmd ) : Label(parent, panelName, text) -{ - Init(); - if ( pActionSignalTarget && pCmd ) - { - AddActionSignalTarget( pActionSignalTarget ); - SetCommand( pCmd ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -Button::Button(Panel *parent, const char *panelName, const wchar_t *wszText, Panel *pActionSignalTarget, const char *pCmd ) : Label(parent, panelName, wszText) -{ - Init(); - if ( pActionSignalTarget && pCmd ) - { - AddActionSignalTarget( pActionSignalTarget ); - SetCommand( pCmd ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::Init() -{ - _buttonFlags.SetFlag( USE_CAPTURE_MOUSE | BUTTON_BORDER_ENABLED ); - - _mouseClickMask = 0; - _actionMessage = NULL; - _defaultBorder = NULL; - _depressedBorder = NULL; - _keyFocusBorder = NULL; - m_bSelectionStateSaved = false; - m_bStaySelectedOnClick = false; - m_sArmedSoundName = UTL_INVAL_SYMBOL; - m_sDepressedSoundName = UTL_INVAL_SYMBOL; - m_sReleasedSoundName = UTL_INVAL_SYMBOL; - SetTextInset(6, 0); - SetMouseClickEnabled( MOUSE_LEFT, true ); - SetButtonActivationType(ACTIVATE_ONPRESSEDANDRELEASED); - - // labels have this off by default, but we need it on - SetPaintBackgroundEnabled( true ); - - _paint = true; - - REGISTER_COLOR_AS_OVERRIDABLE( _defaultFgColor, "defaultFgColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _defaultBgColor, "defaultBgColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _armedFgColor, "armedFgColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _armedBgColor, "armedBgColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _depressedFgColor, "depressedFgColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _depressedBgColor, "depressedBgColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _selectedFgColor, "selectedFgColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _selectedBgColor, "selectedBgColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _keyboardFocusColor, "keyboardFocusColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _blinkFgColor, "blinkFgColor_override" ); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -Button::~Button() -{ - if (_actionMessage) - { - _actionMessage->deleteThis(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::SetButtonActivationType(ActivationType_t activationType) -{ - _activationType = activationType; -} - -//----------------------------------------------------------------------------- -// Purpose: Set button border attribute enabled. -//----------------------------------------------------------------------------- -void Button::SetButtonBorderEnabled( bool state ) -{ - if ( state != _buttonFlags.IsFlagSet( BUTTON_BORDER_ENABLED ) ) - { - _buttonFlags.SetFlag( BUTTON_BORDER_ENABLED, state ); - InvalidateLayout(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set button selected state. -//----------------------------------------------------------------------------- -void Button::SetSelected( bool state ) -{ - if ( _buttonFlags.IsFlagSet( SELECTED ) != state ) - { - _buttonFlags.SetFlag( SELECTED, state ); - RecalculateDepressedState(); - InvalidateLayout(false); - } - - if ( state && _buttonFlags.IsFlagSet( ARMED ) ) - { - _buttonFlags.SetFlag( ARMED, false ); - InvalidateLayout(false); - } -} - -void Button::SetBlink( bool state ) -{ - if ( _buttonFlags.IsFlagSet( BLINK ) != state ) - { - _buttonFlags.SetFlag( BLINK, state ); - RecalculateDepressedState(); - InvalidateLayout(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set button force depressed state. -//----------------------------------------------------------------------------- -void Button::ForceDepressed(bool state) -{ - if ( _buttonFlags.IsFlagSet( FORCE_DEPRESSED ) != state ) - { - _buttonFlags.SetFlag( FORCE_DEPRESSED, state ); - RecalculateDepressedState(); - InvalidateLayout(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set button depressed state with respect to the force depressed state. -//----------------------------------------------------------------------------- -void Button::RecalculateDepressedState( void ) -{ - bool newState; - if (!IsEnabled()) - { - newState = false; - } - else - { - if ( m_bStaySelectedOnClick && _buttonFlags.IsFlagSet( SELECTED ) ) - { - newState = false; - } - else - { - newState = _buttonFlags.IsFlagSet( FORCE_DEPRESSED ) ? true : (_buttonFlags.IsFlagSet(ARMED) && _buttonFlags.IsFlagSet( SELECTED ) ); - } - } - - _buttonFlags.SetFlag( DEPRESSED, newState ); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets whether or not the button captures all mouse input when depressed -// Defaults to true -// Should be set to false for things like menu items where there is a higher-level mouse capture -//----------------------------------------------------------------------------- -void Button::SetUseCaptureMouse( bool state ) -{ - _buttonFlags.SetFlag( USE_CAPTURE_MOUSE, state ); -} - -//----------------------------------------------------------------------------- -// Purpose: Check if mouse capture is enabled. -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Button::IsUseCaptureMouseEnabled( void ) -{ - return _buttonFlags.IsFlagSet( USE_CAPTURE_MOUSE ); -} - -//----------------------------------------------------------------------------- -// Purpose: Set armed state. -//----------------------------------------------------------------------------- -void Button::SetArmed(bool state) -{ - if ( _buttonFlags.IsFlagSet( ARMED ) != state ) - { - _buttonFlags.SetFlag( ARMED, state ); - RecalculateDepressedState(); - InvalidateLayout(false); - - // play any sounds specified - if (state && m_sArmedSoundName != UTL_INVAL_SYMBOL) - { - surface()->PlaySound(g_ButtonSoundNames.String(m_sArmedSoundName)); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Check armed state -//----------------------------------------------------------------------------- -bool Button::IsArmed() -{ - return _buttonFlags.IsFlagSet( ARMED ); -} - - -KeyValues *Button::GetActionMessage() -{ - return _actionMessage->MakeCopy(); -} - -void Button::PlayButtonReleasedSound() -{ - // check for playing a transition sound - if ( m_sReleasedSoundName != UTL_INVAL_SYMBOL ) - { - surface()->PlaySound( g_ButtonSoundNames.String( m_sReleasedSoundName ) ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Activate a button click. -//----------------------------------------------------------------------------- -void Button::DoClick() -{ - SetSelected(true); - FireActionSignal(); - PlayButtonReleasedSound(); - - static ConVarRef vgui_nav_lock( "vgui_nav_lock" ); - if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateActivate() ) - { - vgui_nav_lock.SetValue( 1 ); - } - - if ( !m_bStaySelectedOnClick ) - { - SetSelected(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Check selected state -//----------------------------------------------------------------------------- -bool Button::IsSelected() -{ - return _buttonFlags.IsFlagSet( SELECTED ); -} - -//----------------------------------------------------------------------------- -// Purpose: Check depressed state -//----------------------------------------------------------------------------- -bool Button::IsDepressed() -{ - return _buttonFlags.IsFlagSet( DEPRESSED ); -} - -bool Button::IsBlinking( void ) -{ - return _buttonFlags.IsFlagSet( BLINK ); -} - - -//----------------------------------------------------------------------------- -// Drawing focus box? -//----------------------------------------------------------------------------- -bool Button::IsDrawingFocusBox() -{ - return _buttonFlags.IsFlagSet( DRAW_FOCUS_BOX ); -} - -void Button::DrawFocusBox( bool bEnable ) -{ - _buttonFlags.SetFlag( DRAW_FOCUS_BOX, bEnable ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::NavigateTo() -{ - BaseClass::NavigateTo(); - - SetArmed( true ); - - if ( IsPC() ) - { - RequestFocus( 0 ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::NavigateFrom() -{ - BaseClass::NavigateFrom(); - - SetArmed( false ); - - OnKeyCodeReleased( KEY_XBUTTON_A ); -} - -//----------------------------------------------------------------------------- -// Purpose: Paint button on screen -//----------------------------------------------------------------------------- -void Button::Paint(void) -{ - if ( !ShouldPaint() ) - return; - - BaseClass::Paint(); - - if ( HasFocus() && IsEnabled() && IsDrawingFocusBox() ) - { - int x0, y0, x1, y1; - int wide, tall; - GetSize(wide, tall); - x0 = 3, y0 = 3, x1 = wide - 4 , y1 = tall - 2; - DrawFocusBorder(x0, y0, x1, y1); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Perform graphical layout of button. -//----------------------------------------------------------------------------- -void Button::PerformLayout() -{ - // reset our border - SetBorder( GetBorder(_buttonFlags.IsFlagSet( DEPRESSED ), _buttonFlags.IsFlagSet( ARMED ), _buttonFlags.IsFlagSet( SELECTED ), HasFocus() ) ); - - // set our color - SetFgColor(GetButtonFgColor()); - SetBgColor(GetButtonBgColor()); - - BaseClass::PerformLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Get button foreground color -// Output : Color -//----------------------------------------------------------------------------- -Color Button::GetButtonFgColor() -{ - if ( !_buttonFlags.IsFlagSet( BLINK ) ) - { - if (_buttonFlags.IsFlagSet( DEPRESSED )) - return _depressedFgColor; - if (_buttonFlags.IsFlagSet( ARMED )) - return _armedFgColor; - if (_buttonFlags.IsFlagSet( SELECTED)) - return _selectedFgColor; - return _defaultFgColor; - } - - Color cBlendedColor; - - if (_buttonFlags.IsFlagSet( DEPRESSED )) - cBlendedColor = _depressedFgColor; - else if (_buttonFlags.IsFlagSet( ARMED )) - cBlendedColor = _armedFgColor; - else if (_buttonFlags.IsFlagSet( SELECTED )) - cBlendedColor = _selectedFgColor; - else - cBlendedColor = _defaultFgColor; - - float fBlink = ( sinf( system()->GetTimeMillis() * 0.01f ) + 1.0f ) * 0.5f; - - if ( _buttonFlags.IsFlagSet( BLINK ) ) - { - cBlendedColor[ 0 ] = (float)cBlendedColor[ 0 ] * fBlink + (float)_blinkFgColor[ 0 ] * ( 1.0f - fBlink ); - cBlendedColor[ 1 ] = (float)cBlendedColor[ 1 ] * fBlink + (float)_blinkFgColor[ 1 ] * ( 1.0f - fBlink ); - cBlendedColor[ 2 ] = (float)cBlendedColor[ 2 ] * fBlink + (float)_blinkFgColor[ 2 ] * ( 1.0f - fBlink ); - cBlendedColor[ 3 ] = (float)cBlendedColor[ 3 ] * fBlink + (float)_blinkFgColor[ 3 ] * ( 1.0f - fBlink ); - } - - return cBlendedColor; -} - -//----------------------------------------------------------------------------- -// Purpose: Get button background color -//----------------------------------------------------------------------------- -Color Button::GetButtonBgColor() -{ - if (_buttonFlags.IsFlagSet( DEPRESSED )) - return _depressedBgColor; - if (_buttonFlags.IsFlagSet( ARMED )) - return _armedBgColor; - if (_buttonFlags.IsFlagSet( SELECTED )) - return _selectedBgColor; - return _defaultBgColor; -} - -//----------------------------------------------------------------------------- -// Purpose: Called when key focus is received -//----------------------------------------------------------------------------- -void Button::OnSetFocus() -{ - InvalidateLayout(false); - BaseClass::OnSetFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: Respond when focus is killed -//----------------------------------------------------------------------------- -void Button::OnKillFocus() -{ - InvalidateLayout(false); - BaseClass::OnKillFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - // get the borders we need - _defaultBorder = pScheme->GetBorder("ButtonBorder"); - _depressedBorder = pScheme->GetBorder("ButtonDepressedBorder"); - _keyFocusBorder = pScheme->GetBorder("ButtonKeyFocusBorder"); - - _defaultFgColor = GetSchemeColor("Button.TextColor", Color(255, 255, 255, 255), pScheme); - _defaultBgColor = GetSchemeColor("Button.BgColor", Color(0, 0, 0, 255), pScheme); - - _armedFgColor = GetSchemeColor("Button.ArmedTextColor", _defaultFgColor, pScheme); - _armedBgColor = GetSchemeColor("Button.ArmedBgColor", _defaultBgColor, pScheme); - - _selectedFgColor = GetSchemeColor("Button.SelectedTextColor", _selectedFgColor, pScheme); - _selectedBgColor = GetSchemeColor("Button.SelectedBgColor", _selectedBgColor, pScheme); - - _depressedFgColor = GetSchemeColor("Button.DepressedTextColor", _defaultFgColor, pScheme); - _depressedBgColor = GetSchemeColor("Button.DepressedBgColor", _defaultBgColor, pScheme); - _keyboardFocusColor = GetSchemeColor("Button.FocusBorderColor", Color(0,0,0,255), pScheme); - - _blinkFgColor = GetSchemeColor("Button.BlinkColor", Color(255, 155, 0, 255), pScheme); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set default button colors. -//----------------------------------------------------------------------------- -void Button::SetDefaultColor(Color fgColor, Color bgColor) -{ - if (!(_defaultFgColor == fgColor && _defaultBgColor == bgColor)) - { - _defaultFgColor = fgColor; - _defaultBgColor = bgColor; - - InvalidateLayout(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set armed button colors -//----------------------------------------------------------------------------- -void Button::SetArmedColor(Color fgColor, Color bgColor) -{ - if (!(_armedFgColor == fgColor && _armedBgColor == bgColor)) - { - _armedFgColor = fgColor; - _armedBgColor = bgColor; - - InvalidateLayout(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set armed button colors -//----------------------------------------------------------------------------- -void Button::SetSelectedColor(Color fgColor, Color bgColor) -{ - if (!(_selectedFgColor == fgColor && _selectedBgColor == bgColor)) - { - _selectedFgColor = fgColor; - _selectedBgColor = bgColor; - - InvalidateLayout(false); - } -} -//----------------------------------------------------------------------------- -// Purpose: Set depressed button colors -//----------------------------------------------------------------------------- -void Button::SetDepressedColor(Color fgColor, Color bgColor) -{ - if (!(_depressedFgColor == fgColor && _depressedBgColor == bgColor)) - { - _depressedFgColor = fgColor; - _depressedBgColor = bgColor; - - InvalidateLayout(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set blink button color -//----------------------------------------------------------------------------- -void Button::SetBlinkColor(Color fgColor) -{ - if (!(_blinkFgColor == fgColor)) - { - _blinkFgColor = fgColor; - - InvalidateLayout(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set default button border attributes. -//----------------------------------------------------------------------------- -void Button::SetDefaultBorder(IBorder *border) -{ - _defaultBorder = border; - InvalidateLayout(false); -} - -//----------------------------------------------------------------------------- -// Purpose: Set depressed button border attributes. -//----------------------------------------------------------------------------- -void Button::SetDepressedBorder(IBorder *border) -{ - _depressedBorder = border; - InvalidateLayout(false); -} - -//----------------------------------------------------------------------------- -// Purpose: Set key focus button border attributes. -//----------------------------------------------------------------------------- -void Button::SetKeyFocusBorder(IBorder *border) -{ - _keyFocusBorder = border; - InvalidateLayout(false); -} - - -//----------------------------------------------------------------------------- -// Purpose: Get button border attributes. -//----------------------------------------------------------------------------- -IBorder *Button::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) -{ - if ( _buttonFlags.IsFlagSet( BUTTON_BORDER_ENABLED ) ) - { - // raised buttons with no armed state - if (depressed) - return _depressedBorder; - if (keyfocus) - return _keyFocusBorder; - if (IsEnabled() && _buttonFlags.IsFlagSet( DEFAULT_BUTTON )) - return _keyFocusBorder; - return _defaultBorder; - } - else - { - // flat buttons that raise - if (depressed) - return _depressedBorder; - if (armed) - return _defaultBorder; - } - - return _defaultBorder; -} - -//----------------------------------------------------------------------------- -// Purpose: sets this button to be the button that is accessed by default -// when the user hits ENTER or SPACE -//----------------------------------------------------------------------------- -void Button::SetAsCurrentDefaultButton(int state) -{ - if ( _buttonFlags.IsFlagSet( DEFAULT_BUTTON ) != (bool)state ) - { - _buttonFlags.SetFlag( DEFAULT_BUTTON, state ); - if (state) - { - // post a message up notifying our nav group that we're now the default button - KeyValues *msg = new KeyValues( "CurrentDefaultButtonSet" ); - msg->SetInt( "button", ToHandle() ); - CallParentFunction( msg ); - } - - InvalidateLayout(); - Repaint(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets this button to be the button that is accessed by default -// when the user hits ENTER or SPACE -//----------------------------------------------------------------------------- -void Button::SetAsDefaultButton(int state) -{ - if ( _buttonFlags.IsFlagSet( DEFAULT_BUTTON ) != (bool)state ) - { - _buttonFlags.SetFlag( DEFAULT_BUTTON, state ); - if (state) - { - // post a message up notifying our nav group that we're now the default button - KeyValues *msg = new KeyValues( "DefaultButtonSet" ); - msg->SetInt( "button", ToHandle() ); - CallParentFunction( msg ); - } - - InvalidateLayout(); - Repaint(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets rollover sound -//----------------------------------------------------------------------------- -void Button::SetArmedSound(const char *sound) -{ - if (sound) - { - m_sArmedSoundName = g_ButtonSoundNames.AddString(sound); - } - else - { - m_sArmedSoundName = UTL_INVAL_SYMBOL; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::SetDepressedSound(const char *sound) -{ - if (sound) - { - m_sDepressedSoundName = g_ButtonSoundNames.AddString(sound); - } - else - { - m_sDepressedSoundName = UTL_INVAL_SYMBOL; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::SetReleasedSound(const char *sound) -{ - if (sound) - { - m_sReleasedSoundName = g_ButtonSoundNames.AddString(sound); - } - else - { - m_sReleasedSoundName = UTL_INVAL_SYMBOL; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set button to be mouse clickable or not. -//----------------------------------------------------------------------------- -void Button::SetMouseClickEnabled(MouseCode code,bool state) -{ - if(state) - { - //set bit to 1 - _mouseClickMask|=1<<((int)(code+1)); - } - else - { - //set bit to 0 - _mouseClickMask&=~(1<<((int)(code+1))); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Check if button is mouse clickable -//----------------------------------------------------------------------------- -bool Button::IsMouseClickEnabled(MouseCode code) -{ - if(_mouseClickMask&(1<<((int)(code+1)))) - { - return true; - } - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: sets the command to send when the button is pressed -//----------------------------------------------------------------------------- -void Button::SetCommand( const char *command ) -{ - SetCommand(new KeyValues("Command", "command", command)); -} - -//----------------------------------------------------------------------------- -// Purpose: sets the message to send when the button is pressed -//----------------------------------------------------------------------------- -void Button::SetCommand( KeyValues *message ) -{ - // delete the old message - if (_actionMessage) - { - _actionMessage->deleteThis(); - } - - _actionMessage = message; -} - -//----------------------------------------------------------------------------- -// Purpose: Peeks at the message to send when button is pressed -// Input : - -// Output : KeyValues -//----------------------------------------------------------------------------- -KeyValues *Button::GetCommand() -{ - return _actionMessage; -} - -//----------------------------------------------------------------------------- -// Purpose: Message targets that the button has been pressed -//----------------------------------------------------------------------------- -void Button::FireActionSignal() -{ - // message-based action signal - if (_actionMessage) - { - // see if it's a url - if (!stricmp(_actionMessage->GetName(), "command") - && !strnicmp(_actionMessage->GetString("command", ""), "url ", strlen("url ")) - && strstr(_actionMessage->GetString("command", ""), "://")) - { - // it's a command to launch a url, run it - system()->ShellExecute("open", _actionMessage->GetString("command", " ") + 4); - } - PostActionSignal(_actionMessage->MakeCopy()); - } -} - -//----------------------------------------------------------------------------- -// Purpose: gets info about the button -//----------------------------------------------------------------------------- -bool Button::RequestInfo(KeyValues *outputData) -{ - if (!stricmp(outputData->GetName(), "CanBeDefaultButton")) - { - outputData->SetInt("result", CanBeDefaultButton() ? 1 : 0); - return true; - } - else if (!stricmp(outputData->GetName(), "GetState")) - { - outputData->SetInt("state", IsSelected()); - return true; - } - else if ( !stricmp( outputData->GetName(), "GetCommand" )) - { - if ( _actionMessage ) - { - outputData->SetString( "command", _actionMessage->GetString( "command", "" ) ); - } - else - { - outputData->SetString( "command", "" ); - } - return true; - } - - - return BaseClass::RequestInfo(outputData); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool Button::CanBeDefaultButton(void) -{ - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Get control settings for editing -//----------------------------------------------------------------------------- -void Button::GetSettings( KeyValues *outResourceData ) -{ - BaseClass::GetSettings(outResourceData); - - if (_actionMessage) - { - outResourceData->SetString("command", _actionMessage->GetString("command", "")); - } - outResourceData->SetInt("default", _buttonFlags.IsFlagSet( DEFAULT_BUTTON ) ); - if ( m_bSelectionStateSaved ) - { - outResourceData->SetInt( "selected", IsSelected() ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::ApplySettings( KeyValues *inResourceData ) -{ - BaseClass::ApplySettings(inResourceData); - - const char *cmd = inResourceData->GetString("command", ""); - if (*cmd) - { - // add in the command - SetCommand(cmd); - } - - // set default button state - int defaultButton = inResourceData->GetInt("default"); - if (defaultButton && CanBeDefaultButton()) - { - SetAsDefaultButton(true); - } - - // saved selection state - int iSelected = inResourceData->GetInt( "selected", -1 ); - if ( iSelected != -1 ) - { - SetSelected( iSelected != 0 ); - m_bSelectionStateSaved = true; - } - - m_bStaySelectedOnClick = inResourceData->GetBool( "stayselectedonclick", false ); - - const char *sound = inResourceData->GetString("sound_armed", ""); - if (*sound) - { - SetArmedSound(sound); - } - sound = inResourceData->GetString("sound_depressed", ""); - if (*sound) - { - SetDepressedSound(sound); - } - sound = inResourceData->GetString("sound_released", ""); - if (*sound) - { - SetReleasedSound(sound); - } - - _activationType = (ActivationType_t)inResourceData->GetInt( "button_activation_type", ACTIVATE_ONRELEASED ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Describes editing details -//----------------------------------------------------------------------------- -const char *Button::GetDescription( void ) -{ - static char buf[1024]; - Q_snprintf(buf, sizeof(buf), "%s, string command, int default", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::OnSetState(int state) -{ - SetSelected((bool)state); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::OnCursorEntered() -{ - if (IsEnabled() && !IsSelected() ) - { - SetArmed( true ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::OnCursorExited() -{ - if ( !_buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) && !IsSelected() ) - { - SetArmed( false ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::OnMousePressed(MouseCode code) -{ - if (!IsEnabled()) - return; - - if (!IsMouseClickEnabled(code)) - return; - - if (_activationType == ACTIVATE_ONPRESSED) - { - if ( IsKeyBoardInputEnabled() ) - { - RequestFocus(); - } - DoClick(); - return; - } - - // play activation sound - if (m_sDepressedSoundName != UTL_INVAL_SYMBOL) - { - surface()->PlaySound(g_ButtonSoundNames.String(m_sDepressedSoundName)); - } - - if (IsUseCaptureMouseEnabled() && _activationType == ACTIVATE_ONPRESSEDANDRELEASED) - { - { - if ( IsKeyBoardInputEnabled() ) - { - RequestFocus(); - } - SetSelected(true); - Repaint(); - } - - // lock mouse input to going to this button - input()->SetMouseCapture(GetVPanel()); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::OnMouseDoublePressed(MouseCode code) -{ - OnMousePressed(code); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::OnMouseReleased(MouseCode code) -{ - // ensure mouse capture gets released - if (IsUseCaptureMouseEnabled()) - { - input()->SetMouseCapture(NULL); - } - - if (_activationType == ACTIVATE_ONPRESSED) - return; - - if (!IsMouseClickEnabled(code)) - return; - - if (!IsSelected() && _activationType == ACTIVATE_ONPRESSEDANDRELEASED) - return; - - // it has to be both enabled and (mouse over the button or using a key) to fire - if ( IsEnabled() && ( GetVPanel() == input()->GetMouseOver() || _buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) ) ) - { - DoClick(); - } - else if ( !m_bStaySelectedOnClick ) - { - SetSelected(false); - } - - // make sure the button gets unselected - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::OnKeyCodePressed(KeyCode code) -{ - KeyCode localCode = GetBaseButtonCode( code ); - - if( ( localCode == KEY_XBUTTON_A ) && IsEnabled() ) - { - SetArmed( true ); - _buttonFlags.SetFlag( BUTTON_KEY_DOWN ); - if( _activationType != ACTIVATE_ONRELEASED ) - { - DoClick(); - } - } - else if (code == KEY_SPACE || code == KEY_ENTER) - { - SetArmed(true); - _buttonFlags.SetFlag( BUTTON_KEY_DOWN ); - OnMousePressed(MOUSE_LEFT); - if (IsUseCaptureMouseEnabled()) // undo the mouse capture since its a fake mouse click! - { - input()->SetMouseCapture(NULL); - } - } - else - { - _buttonFlags.ClearFlag( BUTTON_KEY_DOWN ); - BaseClass::OnKeyCodePressed( code ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Button::OnKeyCodeReleased( KeyCode keycode ) -{ - vgui::KeyCode code = GetBaseButtonCode( keycode ); - - if ( _buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) && ( code == KEY_XBUTTON_A || code == KEY_XBUTTON_START ) ) - { - SetArmed( true ); - if( _activationType != ACTIVATE_ONPRESSED ) - { - DoClick(); - } - } - else if (_buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) && (code == KEY_SPACE || code == KEY_ENTER)) - { - SetArmed(true); - OnMouseReleased(MOUSE_LEFT); - } - else - { - BaseClass::OnKeyCodeReleased( keycode ); - } - _buttonFlags.ClearFlag( BUTTON_KEY_DOWN ); - - if ( !( code == KEY_XSTICK1_UP || code == KEY_XSTICK1_DOWN || code == KEY_XSTICK1_LEFT || code == KEY_XSTICK1_RIGHT || - code == KEY_XSTICK2_UP || code == KEY_XSTICK2_DOWN || code == KEY_XSTICK2_LEFT || code == KEY_XSTICK2_RIGHT || - code == KEY_XBUTTON_UP || code == KEY_XBUTTON_DOWN || code == KEY_XBUTTON_LEFT || code == KEY_XBUTTON_RIGHT || - keycode == KEY_UP|| keycode == KEY_DOWN || keycode == KEY_LEFT || keycode == KEY_RIGHT ) ) - { - SetArmed( false ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Override this to draw different focus border -//----------------------------------------------------------------------------- -void Button::DrawFocusBorder(int tx0, int ty0, int tx1, int ty1) -{ - surface()->DrawSetColor(_keyboardFocusColor); - DrawDashedLine(tx0, ty0, tx1, ty0+1, 1, 1); // top - DrawDashedLine(tx0, ty0, tx0+1, ty1, 1, 1); // left - DrawDashedLine(tx0, ty1-1, tx1, ty1, 1, 1); // bottom - DrawDashedLine(tx1-1, ty0, tx1, ty1, 1, 1); // right -} - -//----------------------------------------------------------------------------- -// Purpose: Size the object to its button and text. - only works from in ApplySchemeSettings or PerformLayout() -//----------------------------------------------------------------------------- -void Button::SizeToContents() -{ - int wide, tall; - GetContentSize(wide, tall); - SetSize(wide + Label::Content, tall + Label::Content); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Basic button control +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +// global list of all the names of all the sounds played by buttons +CUtlSymbolTable g_ButtonSoundNames; + +DECLARE_BUILD_FACTORY_DEFAULT_TEXT( Button, Button ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Button::Button(Panel *parent, const char *panelName, const char *text, Panel *pActionSignalTarget, const char *pCmd ) : Label(parent, panelName, text) +{ + Init(); + if ( pActionSignalTarget && pCmd ) + { + AddActionSignalTarget( pActionSignalTarget ); + SetCommand( pCmd ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Button::Button(Panel *parent, const char *panelName, const wchar_t *wszText, Panel *pActionSignalTarget, const char *pCmd ) : Label(parent, panelName, wszText) +{ + Init(); + if ( pActionSignalTarget && pCmd ) + { + AddActionSignalTarget( pActionSignalTarget ); + SetCommand( pCmd ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::Init() +{ + _buttonFlags.SetFlag( USE_CAPTURE_MOUSE | BUTTON_BORDER_ENABLED ); + + _mouseClickMask = 0; + _actionMessage = NULL; + _defaultBorder = NULL; + _depressedBorder = NULL; + _keyFocusBorder = NULL; + m_bSelectionStateSaved = false; + m_bStaySelectedOnClick = false; + m_sArmedSoundName = UTL_INVAL_SYMBOL; + m_sDepressedSoundName = UTL_INVAL_SYMBOL; + m_sReleasedSoundName = UTL_INVAL_SYMBOL; + SetTextInset(6, 0); + SetMouseClickEnabled( MOUSE_LEFT, true ); + SetButtonActivationType(ACTIVATE_ONPRESSEDANDRELEASED); + + // labels have this off by default, but we need it on + SetPaintBackgroundEnabled( true ); + + _paint = true; + + REGISTER_COLOR_AS_OVERRIDABLE( _defaultFgColor, "defaultFgColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _defaultBgColor, "defaultBgColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _armedFgColor, "armedFgColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _armedBgColor, "armedBgColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _depressedFgColor, "depressedFgColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _depressedBgColor, "depressedBgColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _selectedFgColor, "selectedFgColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _selectedBgColor, "selectedBgColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _keyboardFocusColor, "keyboardFocusColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _blinkFgColor, "blinkFgColor_override" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +Button::~Button() +{ + if (_actionMessage) + { + _actionMessage->deleteThis(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::SetButtonActivationType(ActivationType_t activationType) +{ + _activationType = activationType; +} + +//----------------------------------------------------------------------------- +// Purpose: Set button border attribute enabled. +//----------------------------------------------------------------------------- +void Button::SetButtonBorderEnabled( bool state ) +{ + if ( state != _buttonFlags.IsFlagSet( BUTTON_BORDER_ENABLED ) ) + { + _buttonFlags.SetFlag( BUTTON_BORDER_ENABLED, state ); + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set button selected state. +//----------------------------------------------------------------------------- +void Button::SetSelected( bool state ) +{ + if ( _buttonFlags.IsFlagSet( SELECTED ) != state ) + { + _buttonFlags.SetFlag( SELECTED, state ); + RecalculateDepressedState(); + InvalidateLayout(false); + } + + if ( state && _buttonFlags.IsFlagSet( ARMED ) ) + { + _buttonFlags.SetFlag( ARMED, false ); + InvalidateLayout(false); + } +} + +void Button::SetBlink( bool state ) +{ + if ( _buttonFlags.IsFlagSet( BLINK ) != state ) + { + _buttonFlags.SetFlag( BLINK, state ); + RecalculateDepressedState(); + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set button force depressed state. +//----------------------------------------------------------------------------- +void Button::ForceDepressed(bool state) +{ + if ( _buttonFlags.IsFlagSet( FORCE_DEPRESSED ) != state ) + { + _buttonFlags.SetFlag( FORCE_DEPRESSED, state ); + RecalculateDepressedState(); + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set button depressed state with respect to the force depressed state. +//----------------------------------------------------------------------------- +void Button::RecalculateDepressedState( void ) +{ + bool newState; + if (!IsEnabled()) + { + newState = false; + } + else + { + if ( m_bStaySelectedOnClick && _buttonFlags.IsFlagSet( SELECTED ) ) + { + newState = false; + } + else + { + newState = _buttonFlags.IsFlagSet( FORCE_DEPRESSED ) ? true : (_buttonFlags.IsFlagSet(ARMED) && _buttonFlags.IsFlagSet( SELECTED ) ); + } + } + + _buttonFlags.SetFlag( DEPRESSED, newState ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets whether or not the button captures all mouse input when depressed +// Defaults to true +// Should be set to false for things like menu items where there is a higher-level mouse capture +//----------------------------------------------------------------------------- +void Button::SetUseCaptureMouse( bool state ) +{ + _buttonFlags.SetFlag( USE_CAPTURE_MOUSE, state ); +} + +//----------------------------------------------------------------------------- +// Purpose: Check if mouse capture is enabled. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Button::IsUseCaptureMouseEnabled( void ) +{ + return _buttonFlags.IsFlagSet( USE_CAPTURE_MOUSE ); +} + +//----------------------------------------------------------------------------- +// Purpose: Set armed state. +//----------------------------------------------------------------------------- +void Button::SetArmed(bool state) +{ + if ( _buttonFlags.IsFlagSet( ARMED ) != state ) + { + _buttonFlags.SetFlag( ARMED, state ); + RecalculateDepressedState(); + InvalidateLayout(false); + + // play any sounds specified + if (state && m_sArmedSoundName != UTL_INVAL_SYMBOL) + { + surface()->PlaySound(g_ButtonSoundNames.String(m_sArmedSoundName)); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Check armed state +//----------------------------------------------------------------------------- +bool Button::IsArmed() +{ + return _buttonFlags.IsFlagSet( ARMED ); +} + + +KeyValues *Button::GetActionMessage() +{ + return _actionMessage->MakeCopy(); +} + +void Button::PlayButtonReleasedSound() +{ + // check for playing a transition sound + if ( m_sReleasedSoundName != UTL_INVAL_SYMBOL ) + { + surface()->PlaySound( g_ButtonSoundNames.String( m_sReleasedSoundName ) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Activate a button click. +//----------------------------------------------------------------------------- +void Button::DoClick() +{ + SetSelected(true); + FireActionSignal(); + PlayButtonReleasedSound(); + + static ConVarRef vgui_nav_lock( "vgui_nav_lock" ); + if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateActivate() ) + { + vgui_nav_lock.SetValue( 1 ); + } + + if ( !m_bStaySelectedOnClick ) + { + SetSelected(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Check selected state +//----------------------------------------------------------------------------- +bool Button::IsSelected() +{ + return _buttonFlags.IsFlagSet( SELECTED ); +} + +//----------------------------------------------------------------------------- +// Purpose: Check depressed state +//----------------------------------------------------------------------------- +bool Button::IsDepressed() +{ + return _buttonFlags.IsFlagSet( DEPRESSED ); +} + +bool Button::IsBlinking( void ) +{ + return _buttonFlags.IsFlagSet( BLINK ); +} + + +//----------------------------------------------------------------------------- +// Drawing focus box? +//----------------------------------------------------------------------------- +bool Button::IsDrawingFocusBox() +{ + return _buttonFlags.IsFlagSet( DRAW_FOCUS_BOX ); +} + +void Button::DrawFocusBox( bool bEnable ) +{ + _buttonFlags.SetFlag( DRAW_FOCUS_BOX, bEnable ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::NavigateTo() +{ + BaseClass::NavigateTo(); + + SetArmed( true ); + + if ( IsPC() ) + { + RequestFocus( 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::NavigateFrom() +{ + BaseClass::NavigateFrom(); + + SetArmed( false ); + + OnKeyCodeReleased( KEY_XBUTTON_A ); +} + +//----------------------------------------------------------------------------- +// Purpose: Paint button on screen +//----------------------------------------------------------------------------- +void Button::Paint(void) +{ + if ( !ShouldPaint() ) + return; + + BaseClass::Paint(); + + if ( HasFocus() && IsEnabled() && IsDrawingFocusBox() ) + { + int x0, y0, x1, y1; + int wide, tall; + GetSize(wide, tall); + x0 = 3, y0 = 3, x1 = wide - 4 , y1 = tall - 2; + DrawFocusBorder(x0, y0, x1, y1); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Perform graphical layout of button. +//----------------------------------------------------------------------------- +void Button::PerformLayout() +{ + // reset our border + SetBorder( GetBorder(_buttonFlags.IsFlagSet( DEPRESSED ), _buttonFlags.IsFlagSet( ARMED ), _buttonFlags.IsFlagSet( SELECTED ), HasFocus() ) ); + + // set our color + SetFgColor(GetButtonFgColor()); + SetBgColor(GetButtonBgColor()); + + BaseClass::PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Get button foreground color +// Output : Color +//----------------------------------------------------------------------------- +Color Button::GetButtonFgColor() +{ + if ( !_buttonFlags.IsFlagSet( BLINK ) ) + { + if (_buttonFlags.IsFlagSet( DEPRESSED )) + return _depressedFgColor; + if (_buttonFlags.IsFlagSet( ARMED )) + return _armedFgColor; + if (_buttonFlags.IsFlagSet( SELECTED)) + return _selectedFgColor; + return _defaultFgColor; + } + + Color cBlendedColor; + + if (_buttonFlags.IsFlagSet( DEPRESSED )) + cBlendedColor = _depressedFgColor; + else if (_buttonFlags.IsFlagSet( ARMED )) + cBlendedColor = _armedFgColor; + else if (_buttonFlags.IsFlagSet( SELECTED )) + cBlendedColor = _selectedFgColor; + else + cBlendedColor = _defaultFgColor; + + float fBlink = ( sinf( system()->GetTimeMillis() * 0.01f ) + 1.0f ) * 0.5f; + + if ( _buttonFlags.IsFlagSet( BLINK ) ) + { + cBlendedColor[ 0 ] = (float)cBlendedColor[ 0 ] * fBlink + (float)_blinkFgColor[ 0 ] * ( 1.0f - fBlink ); + cBlendedColor[ 1 ] = (float)cBlendedColor[ 1 ] * fBlink + (float)_blinkFgColor[ 1 ] * ( 1.0f - fBlink ); + cBlendedColor[ 2 ] = (float)cBlendedColor[ 2 ] * fBlink + (float)_blinkFgColor[ 2 ] * ( 1.0f - fBlink ); + cBlendedColor[ 3 ] = (float)cBlendedColor[ 3 ] * fBlink + (float)_blinkFgColor[ 3 ] * ( 1.0f - fBlink ); + } + + return cBlendedColor; +} + +//----------------------------------------------------------------------------- +// Purpose: Get button background color +//----------------------------------------------------------------------------- +Color Button::GetButtonBgColor() +{ + if (_buttonFlags.IsFlagSet( DEPRESSED )) + return _depressedBgColor; + if (_buttonFlags.IsFlagSet( ARMED )) + return _armedBgColor; + if (_buttonFlags.IsFlagSet( SELECTED )) + return _selectedBgColor; + return _defaultBgColor; +} + +//----------------------------------------------------------------------------- +// Purpose: Called when key focus is received +//----------------------------------------------------------------------------- +void Button::OnSetFocus() +{ + InvalidateLayout(false); + BaseClass::OnSetFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Respond when focus is killed +//----------------------------------------------------------------------------- +void Button::OnKillFocus() +{ + InvalidateLayout(false); + BaseClass::OnKillFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + // get the borders we need + _defaultBorder = pScheme->GetBorder("ButtonBorder"); + _depressedBorder = pScheme->GetBorder("ButtonDepressedBorder"); + _keyFocusBorder = pScheme->GetBorder("ButtonKeyFocusBorder"); + + _defaultFgColor = GetSchemeColor("Button.TextColor", Color(255, 255, 255, 255), pScheme); + _defaultBgColor = GetSchemeColor("Button.BgColor", Color(0, 0, 0, 255), pScheme); + + _armedFgColor = GetSchemeColor("Button.ArmedTextColor", _defaultFgColor, pScheme); + _armedBgColor = GetSchemeColor("Button.ArmedBgColor", _defaultBgColor, pScheme); + + _selectedFgColor = GetSchemeColor("Button.SelectedTextColor", _selectedFgColor, pScheme); + _selectedBgColor = GetSchemeColor("Button.SelectedBgColor", _selectedBgColor, pScheme); + + _depressedFgColor = GetSchemeColor("Button.DepressedTextColor", _defaultFgColor, pScheme); + _depressedBgColor = GetSchemeColor("Button.DepressedBgColor", _defaultBgColor, pScheme); + _keyboardFocusColor = GetSchemeColor("Button.FocusBorderColor", Color(0,0,0,255), pScheme); + + _blinkFgColor = GetSchemeColor("Button.BlinkColor", Color(255, 155, 0, 255), pScheme); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set default button colors. +//----------------------------------------------------------------------------- +void Button::SetDefaultColor(Color fgColor, Color bgColor) +{ + if (!(_defaultFgColor == fgColor && _defaultBgColor == bgColor)) + { + _defaultFgColor = fgColor; + _defaultBgColor = bgColor; + + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set armed button colors +//----------------------------------------------------------------------------- +void Button::SetArmedColor(Color fgColor, Color bgColor) +{ + if (!(_armedFgColor == fgColor && _armedBgColor == bgColor)) + { + _armedFgColor = fgColor; + _armedBgColor = bgColor; + + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set armed button colors +//----------------------------------------------------------------------------- +void Button::SetSelectedColor(Color fgColor, Color bgColor) +{ + if (!(_selectedFgColor == fgColor && _selectedBgColor == bgColor)) + { + _selectedFgColor = fgColor; + _selectedBgColor = bgColor; + + InvalidateLayout(false); + } +} +//----------------------------------------------------------------------------- +// Purpose: Set depressed button colors +//----------------------------------------------------------------------------- +void Button::SetDepressedColor(Color fgColor, Color bgColor) +{ + if (!(_depressedFgColor == fgColor && _depressedBgColor == bgColor)) + { + _depressedFgColor = fgColor; + _depressedBgColor = bgColor; + + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set blink button color +//----------------------------------------------------------------------------- +void Button::SetBlinkColor(Color fgColor) +{ + if (!(_blinkFgColor == fgColor)) + { + _blinkFgColor = fgColor; + + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set default button border attributes. +//----------------------------------------------------------------------------- +void Button::SetDefaultBorder(IBorder *border) +{ + _defaultBorder = border; + InvalidateLayout(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Set depressed button border attributes. +//----------------------------------------------------------------------------- +void Button::SetDepressedBorder(IBorder *border) +{ + _depressedBorder = border; + InvalidateLayout(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Set key focus button border attributes. +//----------------------------------------------------------------------------- +void Button::SetKeyFocusBorder(IBorder *border) +{ + _keyFocusBorder = border; + InvalidateLayout(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Get button border attributes. +//----------------------------------------------------------------------------- +IBorder *Button::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) +{ + if ( _buttonFlags.IsFlagSet( BUTTON_BORDER_ENABLED ) ) + { + // raised buttons with no armed state + if (depressed) + return _depressedBorder; + if (keyfocus) + return _keyFocusBorder; + if (IsEnabled() && _buttonFlags.IsFlagSet( DEFAULT_BUTTON )) + return _keyFocusBorder; + return _defaultBorder; + } + else + { + // flat buttons that raise + if (depressed) + return _depressedBorder; + if (armed) + return _defaultBorder; + } + + return _defaultBorder; +} + +//----------------------------------------------------------------------------- +// Purpose: sets this button to be the button that is accessed by default +// when the user hits ENTER or SPACE +//----------------------------------------------------------------------------- +void Button::SetAsCurrentDefaultButton(int state) +{ + if ( _buttonFlags.IsFlagSet( DEFAULT_BUTTON ) != (bool)state ) + { + _buttonFlags.SetFlag( DEFAULT_BUTTON, state ); + if (state) + { + // post a message up notifying our nav group that we're now the default button + KeyValues *msg = new KeyValues( "CurrentDefaultButtonSet" ); + msg->SetInt( "button", ToHandle() ); + CallParentFunction( msg ); + } + + InvalidateLayout(); + Repaint(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets this button to be the button that is accessed by default +// when the user hits ENTER or SPACE +//----------------------------------------------------------------------------- +void Button::SetAsDefaultButton(int state) +{ + if ( _buttonFlags.IsFlagSet( DEFAULT_BUTTON ) != (bool)state ) + { + _buttonFlags.SetFlag( DEFAULT_BUTTON, state ); + if (state) + { + // post a message up notifying our nav group that we're now the default button + KeyValues *msg = new KeyValues( "DefaultButtonSet" ); + msg->SetInt( "button", ToHandle() ); + CallParentFunction( msg ); + } + + InvalidateLayout(); + Repaint(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets rollover sound +//----------------------------------------------------------------------------- +void Button::SetArmedSound(const char *sound) +{ + if (sound) + { + m_sArmedSoundName = g_ButtonSoundNames.AddString(sound); + } + else + { + m_sArmedSoundName = UTL_INVAL_SYMBOL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::SetDepressedSound(const char *sound) +{ + if (sound) + { + m_sDepressedSoundName = g_ButtonSoundNames.AddString(sound); + } + else + { + m_sDepressedSoundName = UTL_INVAL_SYMBOL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::SetReleasedSound(const char *sound) +{ + if (sound) + { + m_sReleasedSoundName = g_ButtonSoundNames.AddString(sound); + } + else + { + m_sReleasedSoundName = UTL_INVAL_SYMBOL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set button to be mouse clickable or not. +//----------------------------------------------------------------------------- +void Button::SetMouseClickEnabled(MouseCode code,bool state) +{ + if(state) + { + //set bit to 1 + _mouseClickMask|=1<<((int)(code+1)); + } + else + { + //set bit to 0 + _mouseClickMask&=~(1<<((int)(code+1))); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Check if button is mouse clickable +//----------------------------------------------------------------------------- +bool Button::IsMouseClickEnabled(MouseCode code) +{ + if(_mouseClickMask&(1<<((int)(code+1)))) + { + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: sets the command to send when the button is pressed +//----------------------------------------------------------------------------- +void Button::SetCommand( const char *command ) +{ + SetCommand(new KeyValues("Command", "command", command)); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the message to send when the button is pressed +//----------------------------------------------------------------------------- +void Button::SetCommand( KeyValues *message ) +{ + // delete the old message + if (_actionMessage) + { + _actionMessage->deleteThis(); + } + + _actionMessage = message; +} + +//----------------------------------------------------------------------------- +// Purpose: Peeks at the message to send when button is pressed +// Input : - +// Output : KeyValues +//----------------------------------------------------------------------------- +KeyValues *Button::GetCommand() +{ + return _actionMessage; +} + +//----------------------------------------------------------------------------- +// Purpose: Message targets that the button has been pressed +//----------------------------------------------------------------------------- +void Button::FireActionSignal() +{ + // message-based action signal + if (_actionMessage) + { + // see if it's a url + if (!stricmp(_actionMessage->GetName(), "command") + && !strnicmp(_actionMessage->GetString("command", ""), "url ", strlen("url ")) + && strstr(_actionMessage->GetString("command", ""), "://")) + { + // it's a command to launch a url, run it + system()->ShellExecute("open", _actionMessage->GetString("command", " ") + 4); + } + PostActionSignal(_actionMessage->MakeCopy()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: gets info about the button +//----------------------------------------------------------------------------- +bool Button::RequestInfo(KeyValues *outputData) +{ + if (!stricmp(outputData->GetName(), "CanBeDefaultButton")) + { + outputData->SetInt("result", CanBeDefaultButton() ? 1 : 0); + return true; + } + else if (!stricmp(outputData->GetName(), "GetState")) + { + outputData->SetInt("state", IsSelected()); + return true; + } + else if ( !stricmp( outputData->GetName(), "GetCommand" )) + { + if ( _actionMessage ) + { + outputData->SetString( "command", _actionMessage->GetString( "command", "" ) ); + } + else + { + outputData->SetString( "command", "" ); + } + return true; + } + + + return BaseClass::RequestInfo(outputData); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool Button::CanBeDefaultButton(void) +{ + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Get control settings for editing +//----------------------------------------------------------------------------- +void Button::GetSettings( KeyValues *outResourceData ) +{ + BaseClass::GetSettings(outResourceData); + + if (_actionMessage) + { + outResourceData->SetString("command", _actionMessage->GetString("command", "")); + } + outResourceData->SetInt("default", _buttonFlags.IsFlagSet( DEFAULT_BUTTON ) ); + if ( m_bSelectionStateSaved ) + { + outResourceData->SetInt( "selected", IsSelected() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings(inResourceData); + + const char *cmd = inResourceData->GetString("command", ""); + if (*cmd) + { + // add in the command + SetCommand(cmd); + } + + // set default button state + int defaultButton = inResourceData->GetInt("default"); + if (defaultButton && CanBeDefaultButton()) + { + SetAsDefaultButton(true); + } + + // saved selection state + int iSelected = inResourceData->GetInt( "selected", -1 ); + if ( iSelected != -1 ) + { + SetSelected( iSelected != 0 ); + m_bSelectionStateSaved = true; + } + + m_bStaySelectedOnClick = inResourceData->GetBool( "stayselectedonclick", false ); + + const char *sound = inResourceData->GetString("sound_armed", ""); + if (*sound) + { + SetArmedSound(sound); + } + sound = inResourceData->GetString("sound_depressed", ""); + if (*sound) + { + SetDepressedSound(sound); + } + sound = inResourceData->GetString("sound_released", ""); + if (*sound) + { + SetReleasedSound(sound); + } + + _activationType = (ActivationType_t)inResourceData->GetInt( "button_activation_type", ACTIVATE_ONRELEASED ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Describes editing details +//----------------------------------------------------------------------------- +const char *Button::GetDescription( void ) +{ + static char buf[1024]; + Q_snprintf(buf, sizeof(buf), "%s, string command, int default", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::OnSetState(int state) +{ + SetSelected((bool)state); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::OnCursorEntered() +{ + if (IsEnabled() && !IsSelected() ) + { + SetArmed( true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::OnCursorExited() +{ + if ( !_buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) && !IsSelected() ) + { + SetArmed( false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::OnMousePressed(MouseCode code) +{ + if (!IsEnabled()) + return; + + if (!IsMouseClickEnabled(code)) + return; + + if (_activationType == ACTIVATE_ONPRESSED) + { + if ( IsKeyBoardInputEnabled() ) + { + RequestFocus(); + } + DoClick(); + return; + } + + // play activation sound + if (m_sDepressedSoundName != UTL_INVAL_SYMBOL) + { + surface()->PlaySound(g_ButtonSoundNames.String(m_sDepressedSoundName)); + } + + if (IsUseCaptureMouseEnabled() && _activationType == ACTIVATE_ONPRESSEDANDRELEASED) + { + { + if ( IsKeyBoardInputEnabled() ) + { + RequestFocus(); + } + SetSelected(true); + Repaint(); + } + + // lock mouse input to going to this button + input()->SetMouseCapture(GetVPanel()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::OnMouseDoublePressed(MouseCode code) +{ + OnMousePressed(code); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::OnMouseReleased(MouseCode code) +{ + // ensure mouse capture gets released + if (IsUseCaptureMouseEnabled()) + { + input()->SetMouseCapture(NULL); + } + + if (_activationType == ACTIVATE_ONPRESSED) + return; + + if (!IsMouseClickEnabled(code)) + return; + + if (!IsSelected() && _activationType == ACTIVATE_ONPRESSEDANDRELEASED) + return; + + // it has to be both enabled and (mouse over the button or using a key) to fire + if ( IsEnabled() && ( GetVPanel() == input()->GetMouseOver() || _buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) ) ) + { + DoClick(); + } + else if ( !m_bStaySelectedOnClick ) + { + SetSelected(false); + } + + // make sure the button gets unselected + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::OnKeyCodePressed(KeyCode code) +{ + KeyCode localCode = GetBaseButtonCode( code ); + + if( ( localCode == KEY_XBUTTON_A ) && IsEnabled() ) + { + SetArmed( true ); + _buttonFlags.SetFlag( BUTTON_KEY_DOWN ); + if( _activationType != ACTIVATE_ONRELEASED ) + { + DoClick(); + } + } + else if (code == KEY_SPACE || code == KEY_ENTER) + { + SetArmed(true); + _buttonFlags.SetFlag( BUTTON_KEY_DOWN ); + OnMousePressed(MOUSE_LEFT); + if (IsUseCaptureMouseEnabled()) // undo the mouse capture since its a fake mouse click! + { + input()->SetMouseCapture(NULL); + } + } + else + { + _buttonFlags.ClearFlag( BUTTON_KEY_DOWN ); + BaseClass::OnKeyCodePressed( code ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Button::OnKeyCodeReleased( KeyCode keycode ) +{ + vgui::KeyCode code = GetBaseButtonCode( keycode ); + + if ( _buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) && ( code == KEY_XBUTTON_A || code == KEY_XBUTTON_START ) ) + { + SetArmed( true ); + if( _activationType != ACTIVATE_ONPRESSED ) + { + DoClick(); + } + } + else if (_buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) && (code == KEY_SPACE || code == KEY_ENTER)) + { + SetArmed(true); + OnMouseReleased(MOUSE_LEFT); + } + else + { + BaseClass::OnKeyCodeReleased( keycode ); + } + _buttonFlags.ClearFlag( BUTTON_KEY_DOWN ); + + if ( !( code == KEY_XSTICK1_UP || code == KEY_XSTICK1_DOWN || code == KEY_XSTICK1_LEFT || code == KEY_XSTICK1_RIGHT || + code == KEY_XSTICK2_UP || code == KEY_XSTICK2_DOWN || code == KEY_XSTICK2_LEFT || code == KEY_XSTICK2_RIGHT || + code == KEY_XBUTTON_UP || code == KEY_XBUTTON_DOWN || code == KEY_XBUTTON_LEFT || code == KEY_XBUTTON_RIGHT || + keycode == KEY_UP|| keycode == KEY_DOWN || keycode == KEY_LEFT || keycode == KEY_RIGHT ) ) + { + SetArmed( false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Override this to draw different focus border +//----------------------------------------------------------------------------- +void Button::DrawFocusBorder(int tx0, int ty0, int tx1, int ty1) +{ + surface()->DrawSetColor(_keyboardFocusColor); + DrawDashedLine(tx0, ty0, tx1, ty0+1, 1, 1); // top + DrawDashedLine(tx0, ty0, tx0+1, ty1, 1, 1); // left + DrawDashedLine(tx0, ty1-1, tx1, ty1, 1, 1); // bottom + DrawDashedLine(tx1-1, ty0, tx1, ty1, 1, 1); // right +} + +//----------------------------------------------------------------------------- +// Purpose: Size the object to its button and text. - only works from in ApplySchemeSettings or PerformLayout() +//----------------------------------------------------------------------------- +void Button::SizeToContents() +{ + int wide, tall; + GetContentSize(wide, tall); + SetSize(wide + Label::Content, tall + Label::Content); +} diff --git a/mp/src/vgui2/vgui_controls/CheckButton.cpp b/mp/src/vgui2/vgui_controls/CheckButton.cpp index ded20bd5..813f2d52 100644 --- a/mp/src/vgui2/vgui_controls/CheckButton.cpp +++ b/mp/src/vgui2/vgui_controls/CheckButton.cpp @@ -1,205 +1,205 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include - -#include -#include -#include - -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -void CheckImage::Paint() -{ - DrawSetTextFont(GetFont()); - - // draw background - if (_CheckButton->IsEnabled() && _CheckButton->IsCheckButtonCheckable() ) - { - DrawSetTextColor(_bgColor); - } - else - { - DrawSetTextColor(_CheckButton->GetDisabledBgColor()); - } - DrawPrintChar(0, 1, 'g'); - - // draw border box - DrawSetTextColor(_borderColor1); - DrawPrintChar(0, 1, 'e'); - DrawSetTextColor(_borderColor2); - DrawPrintChar(0, 1, 'f'); - - // draw selected check - if (_CheckButton->IsSelected()) - { - if ( !_CheckButton->IsEnabled() ) - { - DrawSetTextColor( _CheckButton->GetDisabledFgColor() ); - } - else - { - DrawSetTextColor(_checkColor); - } - - DrawPrintChar(0, 2, 'b'); - } -} - -DECLARE_BUILD_FACTORY_DEFAULT_TEXT( CheckButton, CheckButton ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -CheckButton::CheckButton(Panel *parent, const char *panelName, const char *text) : ToggleButton(parent, panelName, text) -{ - SetContentAlignment(a_west); - m_bCheckButtonCheckable = true; - - // create the image - _checkBoxImage = new CheckImage(this); - - SetTextImageIndex(1); - SetImageAtIndex(0, _checkBoxImage, CHECK_INSET); - - _selectedFgColor = Color( 196, 181, 80, 255 ); - _disabledFgColor = Color(130, 130, 130, 255); - _disabledBgColor = Color(62, 70, 55, 255); -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -CheckButton::~CheckButton() -{ - delete _checkBoxImage; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CheckButton::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetDefaultColor( GetSchemeColor("CheckButton.TextColor", pScheme), GetBgColor() ); - _checkBoxImage->_bgColor = GetSchemeColor("CheckButton.BgColor", Color(62, 70, 55, 255), pScheme); - _checkBoxImage->_borderColor1 = GetSchemeColor("CheckButton.Border1", Color(20, 20, 20, 255), pScheme); - _checkBoxImage->_borderColor2 = GetSchemeColor("CheckButton.Border2", Color(90, 90, 90, 255), pScheme); - _checkBoxImage->_checkColor = GetSchemeColor("CheckButton.Check", Color(20, 20, 20, 255), pScheme); - _selectedFgColor = GetSchemeColor("CheckButton.SelectedTextColor", GetSchemeColor("ControlText", pScheme), pScheme); - _disabledFgColor = GetSchemeColor("CheckButton.DisabledFgColor", Color(130, 130, 130, 255), pScheme); - _disabledBgColor = GetSchemeColor("CheckButton.DisabledBgColor", Color(62, 70, 55, 255), pScheme); - - Color bgArmedColor = GetSchemeColor( "CheckButton.ArmedBgColor", Color(62, 70, 55, 255), pScheme); - SetArmedColor( GetFgColor(), bgArmedColor ); - - Color bgDepressedColor = GetSchemeColor( "CheckButton.DepressedBgColor", Color(62, 70, 55, 255), pScheme); - SetDepressedColor( GetFgColor(), bgDepressedColor ); - - _highlightFgColor = GetSchemeColor( "CheckButton.HighlightFgColor", Color(62, 70, 55, 255), pScheme); - - SetContentAlignment(Label::a_west); - - _checkBoxImage->SetFont( pScheme->GetFont("Marlett", IsProportional()) ); - _checkBoxImage->ResizeImageToContent(); - SetImageAtIndex(0, _checkBoxImage, CHECK_INSET); - - // don't draw a background - SetPaintBackgroundEnabled(false); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -IBorder *CheckButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) -{ - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Check the button -//----------------------------------------------------------------------------- -void CheckButton::SetSelected(bool state ) -{ - if (m_bCheckButtonCheckable) - { - // send a message saying we've been checked - KeyValues *msg = new KeyValues("CheckButtonChecked", "state", (int)state); - PostActionSignal(msg); - - BaseClass::SetSelected(state); - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets whether or not the state of the check can be changed -//----------------------------------------------------------------------------- -void CheckButton::SetCheckButtonCheckable(bool state) -{ - m_bCheckButtonCheckable = state; - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Gets a different foreground text color if we are selected -//----------------------------------------------------------------------------- -#ifdef _X360 -Color CheckButton::GetButtonFgColor() -{ - if (HasFocus()) - { - return _selectedFgColor; - } - - return BaseClass::GetButtonFgColor(); -} -#else -Color CheckButton::GetButtonFgColor() -{ - if ( IsArmed() ) - { - return _highlightFgColor; - } - - if (IsSelected()) - { - return _selectedFgColor; - } - - return BaseClass::GetButtonFgColor(); -} -#endif - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CheckButton::OnCheckButtonChecked(Panel *panel) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CheckButton::SetHighlightColor(Color fgColor) -{ - if ( _highlightFgColor != fgColor ) - { - _highlightFgColor = fgColor; - - InvalidateLayout(false); - } -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include + +#include +#include +#include + +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +void CheckImage::Paint() +{ + DrawSetTextFont(GetFont()); + + // draw background + if (_CheckButton->IsEnabled() && _CheckButton->IsCheckButtonCheckable() ) + { + DrawSetTextColor(_bgColor); + } + else + { + DrawSetTextColor(_CheckButton->GetDisabledBgColor()); + } + DrawPrintChar(0, 1, 'g'); + + // draw border box + DrawSetTextColor(_borderColor1); + DrawPrintChar(0, 1, 'e'); + DrawSetTextColor(_borderColor2); + DrawPrintChar(0, 1, 'f'); + + // draw selected check + if (_CheckButton->IsSelected()) + { + if ( !_CheckButton->IsEnabled() ) + { + DrawSetTextColor( _CheckButton->GetDisabledFgColor() ); + } + else + { + DrawSetTextColor(_checkColor); + } + + DrawPrintChar(0, 2, 'b'); + } +} + +DECLARE_BUILD_FACTORY_DEFAULT_TEXT( CheckButton, CheckButton ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CheckButton::CheckButton(Panel *parent, const char *panelName, const char *text) : ToggleButton(parent, panelName, text) +{ + SetContentAlignment(a_west); + m_bCheckButtonCheckable = true; + + // create the image + _checkBoxImage = new CheckImage(this); + + SetTextImageIndex(1); + SetImageAtIndex(0, _checkBoxImage, CHECK_INSET); + + _selectedFgColor = Color( 196, 181, 80, 255 ); + _disabledFgColor = Color(130, 130, 130, 255); + _disabledBgColor = Color(62, 70, 55, 255); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CheckButton::~CheckButton() +{ + delete _checkBoxImage; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CheckButton::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetDefaultColor( GetSchemeColor("CheckButton.TextColor", pScheme), GetBgColor() ); + _checkBoxImage->_bgColor = GetSchemeColor("CheckButton.BgColor", Color(62, 70, 55, 255), pScheme); + _checkBoxImage->_borderColor1 = GetSchemeColor("CheckButton.Border1", Color(20, 20, 20, 255), pScheme); + _checkBoxImage->_borderColor2 = GetSchemeColor("CheckButton.Border2", Color(90, 90, 90, 255), pScheme); + _checkBoxImage->_checkColor = GetSchemeColor("CheckButton.Check", Color(20, 20, 20, 255), pScheme); + _selectedFgColor = GetSchemeColor("CheckButton.SelectedTextColor", GetSchemeColor("ControlText", pScheme), pScheme); + _disabledFgColor = GetSchemeColor("CheckButton.DisabledFgColor", Color(130, 130, 130, 255), pScheme); + _disabledBgColor = GetSchemeColor("CheckButton.DisabledBgColor", Color(62, 70, 55, 255), pScheme); + + Color bgArmedColor = GetSchemeColor( "CheckButton.ArmedBgColor", Color(62, 70, 55, 255), pScheme); + SetArmedColor( GetFgColor(), bgArmedColor ); + + Color bgDepressedColor = GetSchemeColor( "CheckButton.DepressedBgColor", Color(62, 70, 55, 255), pScheme); + SetDepressedColor( GetFgColor(), bgDepressedColor ); + + _highlightFgColor = GetSchemeColor( "CheckButton.HighlightFgColor", Color(62, 70, 55, 255), pScheme); + + SetContentAlignment(Label::a_west); + + _checkBoxImage->SetFont( pScheme->GetFont("Marlett", IsProportional()) ); + _checkBoxImage->ResizeImageToContent(); + SetImageAtIndex(0, _checkBoxImage, CHECK_INSET); + + // don't draw a background + SetPaintBackgroundEnabled(false); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IBorder *CheckButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) +{ + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Check the button +//----------------------------------------------------------------------------- +void CheckButton::SetSelected(bool state ) +{ + if (m_bCheckButtonCheckable) + { + // send a message saying we've been checked + KeyValues *msg = new KeyValues("CheckButtonChecked", "state", (int)state); + PostActionSignal(msg); + + BaseClass::SetSelected(state); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets whether or not the state of the check can be changed +//----------------------------------------------------------------------------- +void CheckButton::SetCheckButtonCheckable(bool state) +{ + m_bCheckButtonCheckable = state; + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets a different foreground text color if we are selected +//----------------------------------------------------------------------------- +#ifdef _X360 +Color CheckButton::GetButtonFgColor() +{ + if (HasFocus()) + { + return _selectedFgColor; + } + + return BaseClass::GetButtonFgColor(); +} +#else +Color CheckButton::GetButtonFgColor() +{ + if ( IsArmed() ) + { + return _highlightFgColor; + } + + if (IsSelected()) + { + return _selectedFgColor; + } + + return BaseClass::GetButtonFgColor(); +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CheckButton::OnCheckButtonChecked(Panel *panel) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CheckButton::SetHighlightColor(Color fgColor) +{ + if ( _highlightFgColor != fgColor ) + { + _highlightFgColor = fgColor; + + InvalidateLayout(false); + } +} + diff --git a/mp/src/vgui2/vgui_controls/CheckButtonList.cpp b/mp/src/vgui2/vgui_controls/CheckButtonList.cpp index de47fd21..f2fff2fb 100644 --- a/mp/src/vgui2/vgui_controls/CheckButtonList.cpp +++ b/mp/src/vgui2/vgui_controls/CheckButtonList.cpp @@ -1,212 +1,212 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -CheckButtonList::CheckButtonList(Panel *parent, const char *name) : BaseClass(parent, name) -{ - m_pScrollBar = new ScrollBar(this, NULL, true); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -CheckButtonList::~CheckButtonList() -{ - RemoveAll(); -} - -//----------------------------------------------------------------------------- -// Purpose: adds a check button to the list -//----------------------------------------------------------------------------- -int CheckButtonList::AddItem(const char *itemText, bool startsSelected, KeyValues *userData) -{ - CheckItem_t newItem; - newItem.checkButton = new vgui::CheckButton(this, NULL, itemText); - newItem.checkButton->SetSilentMode( true ); - newItem.checkButton->SetSelected(startsSelected); - newItem.checkButton->SetSilentMode( false ); - newItem.checkButton->AddActionSignalTarget(this); - newItem.userData = userData; - InvalidateLayout(); - return m_CheckItems.AddToTail(newItem); -} - -//----------------------------------------------------------------------------- -// Purpose: clears the list -//----------------------------------------------------------------------------- -void CheckButtonList::RemoveAll() -{ - for (int i = 0; i < m_CheckItems.Count(); i++) - { - m_CheckItems[i].checkButton->MarkForDeletion(); - if (m_CheckItems[i].userData) - { - m_CheckItems[i].userData->deleteThis(); - } - } - - m_CheckItems.RemoveAll(); -} - -//----------------------------------------------------------------------------- -// Purpose: returns the number of items in list that are checked -//----------------------------------------------------------------------------- -int CheckButtonList::GetCheckedItemCount() -{ - int count = 0; - for (int i = 0; i < m_CheckItems.Count(); i++) - { - if (m_CheckItems[i].checkButton->IsSelected()) - { - count++; - } - } - - return count; -} - -//----------------------------------------------------------------------------- -// Purpose: lays out buttons -//----------------------------------------------------------------------------- -void CheckButtonList::PerformLayout() -{ - BaseClass::PerformLayout(); - - // get sizes - int x = 4, y = 4, wide = GetWide() - ((x * 2) + m_pScrollBar->GetWide()), tall = 22; - - // set scrollbar - int totalHeight = y + (m_CheckItems.Count() * tall); - if (totalHeight > GetTall()) - { - m_pScrollBar->SetRange(0, totalHeight + 1); - m_pScrollBar->SetRangeWindow(GetTall()); - m_pScrollBar->SetVisible(true); - m_pScrollBar->SetBounds(GetWide() - 21, 0, 19, GetTall() - 2); - SetPaintBorderEnabled(true); - y -= m_pScrollBar->GetValue(); - } - else - { - m_pScrollBar->SetVisible(false); - SetPaintBorderEnabled(false); - } - - // position the items - for (int i = 0; i < m_CheckItems.Count(); i++) - { - CheckButton *btn = m_CheckItems[i].checkButton; - btn->SetBounds(x, y, wide, tall); - y += tall; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the border on the window -//----------------------------------------------------------------------------- -void CheckButtonList::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); -} - -//----------------------------------------------------------------------------- -// Purpose: iteration -//----------------------------------------------------------------------------- -bool CheckButtonList::IsItemIDValid(int itemID) -{ - return m_CheckItems.IsValidIndex(itemID); -} - -//----------------------------------------------------------------------------- -// Purpose: iteration -//----------------------------------------------------------------------------- -int CheckButtonList::GetHighestItemID() -{ - return m_CheckItems.Count() - 1; -} - -//----------------------------------------------------------------------------- -// Purpose: iteration -//----------------------------------------------------------------------------- -KeyValues *CheckButtonList::GetItemData(int itemID) -{ - return m_CheckItems[itemID].userData; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -int CheckButtonList::GetItemCount() -{ - return m_CheckItems.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -bool CheckButtonList::IsItemChecked(int itemID) -{ - return m_CheckItems[itemID].checkButton->IsSelected(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the state of the check button -//----------------------------------------------------------------------------- -void CheckButtonList::SetItemCheckable(int itemID, bool state) -{ - m_CheckItems[itemID].checkButton->SetCheckButtonCheckable(state); -} - -//----------------------------------------------------------------------------- -// Purpose: Forwards up check button selected message -//----------------------------------------------------------------------------- -void CheckButtonList::OnCheckButtonChecked( KeyValues *pParams ) -{ - vgui::Panel *pPanel = (vgui::Panel *)pParams->GetPtr( "panel" ); - int c = m_CheckItems.Count(); - for ( int i = 0; i < c; ++i ) - { - if ( pPanel == m_CheckItems[i].checkButton ) - { - KeyValues *kv = new KeyValues( "CheckButtonChecked", "itemid", i ); - kv->SetInt( "state", pParams->GetInt( "state" ) ); - PostActionSignal( kv ); - break; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: updates from scrollbar movement -//----------------------------------------------------------------------------- -void CheckButtonList::OnScrollBarSliderMoved() -{ - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Mouse wheeled -//----------------------------------------------------------------------------- -void CheckButtonList::OnMouseWheeled(int delta) -{ - int val = m_pScrollBar->GetValue(); - val -= (delta * 15); - m_pScrollBar->SetValue(val); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CheckButtonList::CheckButtonList(Panel *parent, const char *name) : BaseClass(parent, name) +{ + m_pScrollBar = new ScrollBar(this, NULL, true); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CheckButtonList::~CheckButtonList() +{ + RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: adds a check button to the list +//----------------------------------------------------------------------------- +int CheckButtonList::AddItem(const char *itemText, bool startsSelected, KeyValues *userData) +{ + CheckItem_t newItem; + newItem.checkButton = new vgui::CheckButton(this, NULL, itemText); + newItem.checkButton->SetSilentMode( true ); + newItem.checkButton->SetSelected(startsSelected); + newItem.checkButton->SetSilentMode( false ); + newItem.checkButton->AddActionSignalTarget(this); + newItem.userData = userData; + InvalidateLayout(); + return m_CheckItems.AddToTail(newItem); +} + +//----------------------------------------------------------------------------- +// Purpose: clears the list +//----------------------------------------------------------------------------- +void CheckButtonList::RemoveAll() +{ + for (int i = 0; i < m_CheckItems.Count(); i++) + { + m_CheckItems[i].checkButton->MarkForDeletion(); + if (m_CheckItems[i].userData) + { + m_CheckItems[i].userData->deleteThis(); + } + } + + m_CheckItems.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the number of items in list that are checked +//----------------------------------------------------------------------------- +int CheckButtonList::GetCheckedItemCount() +{ + int count = 0; + for (int i = 0; i < m_CheckItems.Count(); i++) + { + if (m_CheckItems[i].checkButton->IsSelected()) + { + count++; + } + } + + return count; +} + +//----------------------------------------------------------------------------- +// Purpose: lays out buttons +//----------------------------------------------------------------------------- +void CheckButtonList::PerformLayout() +{ + BaseClass::PerformLayout(); + + // get sizes + int x = 4, y = 4, wide = GetWide() - ((x * 2) + m_pScrollBar->GetWide()), tall = 22; + + // set scrollbar + int totalHeight = y + (m_CheckItems.Count() * tall); + if (totalHeight > GetTall()) + { + m_pScrollBar->SetRange(0, totalHeight + 1); + m_pScrollBar->SetRangeWindow(GetTall()); + m_pScrollBar->SetVisible(true); + m_pScrollBar->SetBounds(GetWide() - 21, 0, 19, GetTall() - 2); + SetPaintBorderEnabled(true); + y -= m_pScrollBar->GetValue(); + } + else + { + m_pScrollBar->SetVisible(false); + SetPaintBorderEnabled(false); + } + + // position the items + for (int i = 0; i < m_CheckItems.Count(); i++) + { + CheckButton *btn = m_CheckItems[i].checkButton; + btn->SetBounds(x, y, wide, tall); + y += tall; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the border on the window +//----------------------------------------------------------------------------- +void CheckButtonList::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); +} + +//----------------------------------------------------------------------------- +// Purpose: iteration +//----------------------------------------------------------------------------- +bool CheckButtonList::IsItemIDValid(int itemID) +{ + return m_CheckItems.IsValidIndex(itemID); +} + +//----------------------------------------------------------------------------- +// Purpose: iteration +//----------------------------------------------------------------------------- +int CheckButtonList::GetHighestItemID() +{ + return m_CheckItems.Count() - 1; +} + +//----------------------------------------------------------------------------- +// Purpose: iteration +//----------------------------------------------------------------------------- +KeyValues *CheckButtonList::GetItemData(int itemID) +{ + return m_CheckItems[itemID].userData; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +int CheckButtonList::GetItemCount() +{ + return m_CheckItems.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool CheckButtonList::IsItemChecked(int itemID) +{ + return m_CheckItems[itemID].checkButton->IsSelected(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the state of the check button +//----------------------------------------------------------------------------- +void CheckButtonList::SetItemCheckable(int itemID, bool state) +{ + m_CheckItems[itemID].checkButton->SetCheckButtonCheckable(state); +} + +//----------------------------------------------------------------------------- +// Purpose: Forwards up check button selected message +//----------------------------------------------------------------------------- +void CheckButtonList::OnCheckButtonChecked( KeyValues *pParams ) +{ + vgui::Panel *pPanel = (vgui::Panel *)pParams->GetPtr( "panel" ); + int c = m_CheckItems.Count(); + for ( int i = 0; i < c; ++i ) + { + if ( pPanel == m_CheckItems[i].checkButton ) + { + KeyValues *kv = new KeyValues( "CheckButtonChecked", "itemid", i ); + kv->SetInt( "state", pParams->GetInt( "state" ) ); + PostActionSignal( kv ); + break; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: updates from scrollbar movement +//----------------------------------------------------------------------------- +void CheckButtonList::OnScrollBarSliderMoved() +{ + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Mouse wheeled +//----------------------------------------------------------------------------- +void CheckButtonList::OnMouseWheeled(int delta) +{ + int val = m_pScrollBar->GetValue(); + val -= (delta * 15); + m_pScrollBar->SetValue(val); +} diff --git a/mp/src/vgui2/vgui_controls/CircularProgressBar.cpp b/mp/src/vgui2/vgui_controls/CircularProgressBar.cpp index 47ae6d20..545db716 100644 --- a/mp/src/vgui2/vgui_controls/CircularProgressBar.cpp +++ b/mp/src/vgui2/vgui_controls/CircularProgressBar.cpp @@ -1,292 +1,292 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include "mathlib/mathlib.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY( CircularProgressBar ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -CircularProgressBar::CircularProgressBar(Panel *parent, const char *panelName) : ProgressBar(parent, panelName) -{ - m_iProgressDirection = CircularProgressBar::PROGRESS_CCW; - - for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ ) - { - m_nTextureId[i] = -1; - m_pszImageName[i] = NULL; - m_lenImageName[i] = 0; - } - - m_iStartSegment = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -CircularProgressBar::~CircularProgressBar() -{ - for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ ) - { - if ( vgui::surface() && m_nTextureId[i] ) - { - vgui::surface()->DestroyTextureID( m_nTextureId[i] ); - m_nTextureId[i] = -1; - } - - delete [] m_pszImageName[i]; - m_lenImageName[i] = 0; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CircularProgressBar::ApplySettings(KeyValues *inResourceData) -{ - for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ ) - { - delete [] m_pszImageName[i]; - m_pszImageName[i] = NULL; - m_lenImageName[i] = 0; - } - - const char *imageName = inResourceData->GetString("fg_image", ""); - if (*imageName) - { - SetFgImage( imageName ); - } - imageName = inResourceData->GetString("bg_image", ""); - if (*imageName) - { - SetBgImage( imageName ); - } - - BaseClass::ApplySettings( inResourceData ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CircularProgressBar::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetFgColor(GetSchemeColor("CircularProgressBar.FgColor", pScheme)); - SetBgColor(GetSchemeColor("CircularProgressBar.BgColor", pScheme)); - SetBorder(NULL); - - for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ ) - { - if ( m_pszImageName[i] && strlen( m_pszImageName[i] ) > 0 ) - { - if ( m_nTextureId[i] == -1 ) - { - m_nTextureId[i] = surface()->CreateNewTextureID(); - } - - surface()->DrawSetTextureFile( m_nTextureId[i], m_pszImageName[i], true, false); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets an image by file name -//----------------------------------------------------------------------------- -void CircularProgressBar::SetImage(const char *imageName, progress_textures_t iPos) -{ - const char *pszDir = "vgui/"; - int len = Q_strlen(imageName) + 1; - len += strlen(pszDir); - - if ( m_pszImageName[iPos] && ( m_lenImageName[iPos] < len ) ) - { - // If we already have a buffer, but it is too short, then free the buffer - delete [] m_pszImageName[iPos]; - m_pszImageName[iPos] = NULL; - m_lenImageName[iPos] = 0; - } - - if ( !m_pszImageName[iPos] ) - { - m_pszImageName[iPos] = new char[ len ]; - m_lenImageName[iPos] = len; - } - - Q_snprintf( m_pszImageName[iPos], len, "%s%s", pszDir, imageName ); - InvalidateLayout(false, true); // force applyschemesettings to run -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CircularProgressBar::PaintBackground() -{ - // If we don't have a Bg image, use the foreground - int iTextureID = m_nTextureId[PROGRESS_TEXTURE_BG] != -1 ? m_nTextureId[PROGRESS_TEXTURE_BG] : m_nTextureId[PROGRESS_TEXTURE_FG]; - vgui::surface()->DrawSetTexture( iTextureID ); - vgui::surface()->DrawSetColor( GetBgColor() ); - - int wide, tall; - GetSize(wide, tall); - - vgui::surface()->DrawTexturedRect( 0, 0, wide, tall ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CircularProgressBar::Paint() -{ - float flProgress = GetProgress(); - float flEndAngle; - - if ( m_iProgressDirection == PROGRESS_CW ) - { - flEndAngle = flProgress; - } - else - { - flEndAngle = ( 1.0 - flProgress ); - } - - DrawCircleSegment( GetFgColor(), flEndAngle, ( m_iProgressDirection == PROGRESS_CW ) ); -} - -typedef struct -{ - float minProgressRadians; - - float vert1x; - float vert1y; - float vert2x; - float vert2y; - - int swipe_dir_x; - int swipe_dir_y; -} circular_progress_segment_t; - -namespace vgui -{ -// This defines the properties of the 8 circle segments -// in the circular progress bar. -circular_progress_segment_t Segments[8] = -{ - { 0.0, 0.5, 0.0, 1.0, 0.0, 1, 0 }, - { M_PI * 0.25, 1.0, 0.0, 1.0, 0.5, 0, 1 }, - { M_PI * 0.5, 1.0, 0.5, 1.0, 1.0, 0, 1 }, - { M_PI * 0.75, 1.0, 1.0, 0.5, 1.0, -1, 0 }, - { M_PI, 0.5, 1.0, 0.0, 1.0, -1, 0 }, - { M_PI * 1.25, 0.0, 1.0, 0.0, 0.5, 0, -1 }, - { M_PI * 1.5, 0.0, 0.5, 0.0, 0.0, 0, -1 }, - { M_PI * 1.75, 0.0, 0.0, 0.5, 0.0, 1, 0 }, -}; - -}; - -#define SEGMENT_ANGLE ( M_PI / 4 ) - -// function to draw from A to B degrees, with a direction -// we draw starting from the top ( 0 progress ) -void CircularProgressBar::DrawCircleSegment( Color c, float flEndProgress, bool bClockwise ) -{ - if ( m_nTextureId[PROGRESS_TEXTURE_FG] == -1 ) - return; - - int wide, tall; - GetSize(wide, tall); - - float flWide = (float)wide; - float flTall = (float)tall; - - float flHalfWide = (float)wide / 2; - float flHalfTall = (float)tall / 2; - - vgui::surface()->DrawSetTexture( m_nTextureId[PROGRESS_TEXTURE_FG] ); - vgui::surface()->DrawSetColor( c ); - - // TODO - if we want to progress CCW, reverse a few things - - float flEndProgressRadians = flEndProgress * M_PI * 2; - - int cur_wedge = m_iStartSegment; - for ( int i=0;i<8;i++ ) - { - if ( flEndProgressRadians > Segments[cur_wedge].minProgressRadians) - { - vgui::Vertex_t v[3]; - - // vert 0 is ( 0.5, 0.5 ) - v[0].m_Position.Init( flHalfWide, flHalfTall ); - v[0].m_TexCoord.Init( 0.5f, 0.5f ); - - float flInternalProgress = flEndProgressRadians - Segments[cur_wedge].minProgressRadians; - - if ( flInternalProgress < SEGMENT_ANGLE ) - { - // Calc how much of this slice we should be drawing - - if ( i % 2 == 1 ) - { - flInternalProgress = SEGMENT_ANGLE - flInternalProgress; - } - - float flTan = tan(flInternalProgress); - - float flDeltaX, flDeltaY; - - if ( i % 2 == 1 ) - { - flDeltaX = ( flHalfWide - flHalfTall * flTan ) * Segments[i].swipe_dir_x; - flDeltaY = ( flHalfTall - flHalfWide * flTan ) * Segments[i].swipe_dir_y; - } - else - { - flDeltaX = flHalfTall * flTan * Segments[i].swipe_dir_x; - flDeltaY = flHalfWide * flTan * Segments[i].swipe_dir_y; - } - - v[2].m_Position.Init( Segments[i].vert1x * flWide + flDeltaX, Segments[i].vert1y * flTall + flDeltaY ); - v[2].m_TexCoord.Init( Segments[i].vert1x + ( flDeltaX / flHalfWide ) * 0.5, Segments[i].vert1y + ( flDeltaY / flHalfTall ) * 0.5 ); - } - else - { - // full segment, easy calculation - v[2].m_Position.Init( flHalfWide + flWide * ( Segments[i].vert2x - 0.5 ), flHalfTall + flTall * ( Segments[i].vert2y - 0.5 ) ); - v[2].m_TexCoord.Init( Segments[i].vert2x, Segments[i].vert2y ); - } - - // vert 2 is ( Segments[i].vert1x, Segments[i].vert1y ) - v[1].m_Position.Init( flHalfWide + flWide * ( Segments[i].vert1x - 0.5 ), flHalfTall + flTall * ( Segments[i].vert1y - 0.5 ) ); - v[1].m_TexCoord.Init( Segments[i].vert1x, Segments[i].vert1y ); - - vgui::surface()->DrawTexturedPolygon( 3, v ); - } - - cur_wedge++; - if ( cur_wedge >= 8) - cur_wedge = 0; - } +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "mathlib/mathlib.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY( CircularProgressBar ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CircularProgressBar::CircularProgressBar(Panel *parent, const char *panelName) : ProgressBar(parent, panelName) +{ + m_iProgressDirection = CircularProgressBar::PROGRESS_CCW; + + for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ ) + { + m_nTextureId[i] = -1; + m_pszImageName[i] = NULL; + m_lenImageName[i] = 0; + } + + m_iStartSegment = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CircularProgressBar::~CircularProgressBar() +{ + for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ ) + { + if ( vgui::surface() && m_nTextureId[i] ) + { + vgui::surface()->DestroyTextureID( m_nTextureId[i] ); + m_nTextureId[i] = -1; + } + + delete [] m_pszImageName[i]; + m_lenImageName[i] = 0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CircularProgressBar::ApplySettings(KeyValues *inResourceData) +{ + for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ ) + { + delete [] m_pszImageName[i]; + m_pszImageName[i] = NULL; + m_lenImageName[i] = 0; + } + + const char *imageName = inResourceData->GetString("fg_image", ""); + if (*imageName) + { + SetFgImage( imageName ); + } + imageName = inResourceData->GetString("bg_image", ""); + if (*imageName) + { + SetBgImage( imageName ); + } + + BaseClass::ApplySettings( inResourceData ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CircularProgressBar::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetFgColor(GetSchemeColor("CircularProgressBar.FgColor", pScheme)); + SetBgColor(GetSchemeColor("CircularProgressBar.BgColor", pScheme)); + SetBorder(NULL); + + for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ ) + { + if ( m_pszImageName[i] && strlen( m_pszImageName[i] ) > 0 ) + { + if ( m_nTextureId[i] == -1 ) + { + m_nTextureId[i] = surface()->CreateNewTextureID(); + } + + surface()->DrawSetTextureFile( m_nTextureId[i], m_pszImageName[i], true, false); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets an image by file name +//----------------------------------------------------------------------------- +void CircularProgressBar::SetImage(const char *imageName, progress_textures_t iPos) +{ + const char *pszDir = "vgui/"; + int len = Q_strlen(imageName) + 1; + len += strlen(pszDir); + + if ( m_pszImageName[iPos] && ( m_lenImageName[iPos] < len ) ) + { + // If we already have a buffer, but it is too short, then free the buffer + delete [] m_pszImageName[iPos]; + m_pszImageName[iPos] = NULL; + m_lenImageName[iPos] = 0; + } + + if ( !m_pszImageName[iPos] ) + { + m_pszImageName[iPos] = new char[ len ]; + m_lenImageName[iPos] = len; + } + + Q_snprintf( m_pszImageName[iPos], len, "%s%s", pszDir, imageName ); + InvalidateLayout(false, true); // force applyschemesettings to run +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CircularProgressBar::PaintBackground() +{ + // If we don't have a Bg image, use the foreground + int iTextureID = m_nTextureId[PROGRESS_TEXTURE_BG] != -1 ? m_nTextureId[PROGRESS_TEXTURE_BG] : m_nTextureId[PROGRESS_TEXTURE_FG]; + vgui::surface()->DrawSetTexture( iTextureID ); + vgui::surface()->DrawSetColor( GetBgColor() ); + + int wide, tall; + GetSize(wide, tall); + + vgui::surface()->DrawTexturedRect( 0, 0, wide, tall ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CircularProgressBar::Paint() +{ + float flProgress = GetProgress(); + float flEndAngle; + + if ( m_iProgressDirection == PROGRESS_CW ) + { + flEndAngle = flProgress; + } + else + { + flEndAngle = ( 1.0 - flProgress ); + } + + DrawCircleSegment( GetFgColor(), flEndAngle, ( m_iProgressDirection == PROGRESS_CW ) ); +} + +typedef struct +{ + float minProgressRadians; + + float vert1x; + float vert1y; + float vert2x; + float vert2y; + + int swipe_dir_x; + int swipe_dir_y; +} circular_progress_segment_t; + +namespace vgui +{ +// This defines the properties of the 8 circle segments +// in the circular progress bar. +circular_progress_segment_t Segments[8] = +{ + { 0.0, 0.5, 0.0, 1.0, 0.0, 1, 0 }, + { M_PI * 0.25, 1.0, 0.0, 1.0, 0.5, 0, 1 }, + { M_PI * 0.5, 1.0, 0.5, 1.0, 1.0, 0, 1 }, + { M_PI * 0.75, 1.0, 1.0, 0.5, 1.0, -1, 0 }, + { M_PI, 0.5, 1.0, 0.0, 1.0, -1, 0 }, + { M_PI * 1.25, 0.0, 1.0, 0.0, 0.5, 0, -1 }, + { M_PI * 1.5, 0.0, 0.5, 0.0, 0.0, 0, -1 }, + { M_PI * 1.75, 0.0, 0.0, 0.5, 0.0, 1, 0 }, +}; + +}; + +#define SEGMENT_ANGLE ( M_PI / 4 ) + +// function to draw from A to B degrees, with a direction +// we draw starting from the top ( 0 progress ) +void CircularProgressBar::DrawCircleSegment( Color c, float flEndProgress, bool bClockwise ) +{ + if ( m_nTextureId[PROGRESS_TEXTURE_FG] == -1 ) + return; + + int wide, tall; + GetSize(wide, tall); + + float flWide = (float)wide; + float flTall = (float)tall; + + float flHalfWide = (float)wide / 2; + float flHalfTall = (float)tall / 2; + + vgui::surface()->DrawSetTexture( m_nTextureId[PROGRESS_TEXTURE_FG] ); + vgui::surface()->DrawSetColor( c ); + + // TODO - if we want to progress CCW, reverse a few things + + float flEndProgressRadians = flEndProgress * M_PI * 2; + + int cur_wedge = m_iStartSegment; + for ( int i=0;i<8;i++ ) + { + if ( flEndProgressRadians > Segments[cur_wedge].minProgressRadians) + { + vgui::Vertex_t v[3]; + + // vert 0 is ( 0.5, 0.5 ) + v[0].m_Position.Init( flHalfWide, flHalfTall ); + v[0].m_TexCoord.Init( 0.5f, 0.5f ); + + float flInternalProgress = flEndProgressRadians - Segments[cur_wedge].minProgressRadians; + + if ( flInternalProgress < SEGMENT_ANGLE ) + { + // Calc how much of this slice we should be drawing + + if ( i % 2 == 1 ) + { + flInternalProgress = SEGMENT_ANGLE - flInternalProgress; + } + + float flTan = tan(flInternalProgress); + + float flDeltaX, flDeltaY; + + if ( i % 2 == 1 ) + { + flDeltaX = ( flHalfWide - flHalfTall * flTan ) * Segments[i].swipe_dir_x; + flDeltaY = ( flHalfTall - flHalfWide * flTan ) * Segments[i].swipe_dir_y; + } + else + { + flDeltaX = flHalfTall * flTan * Segments[i].swipe_dir_x; + flDeltaY = flHalfWide * flTan * Segments[i].swipe_dir_y; + } + + v[2].m_Position.Init( Segments[i].vert1x * flWide + flDeltaX, Segments[i].vert1y * flTall + flDeltaY ); + v[2].m_TexCoord.Init( Segments[i].vert1x + ( flDeltaX / flHalfWide ) * 0.5, Segments[i].vert1y + ( flDeltaY / flHalfTall ) * 0.5 ); + } + else + { + // full segment, easy calculation + v[2].m_Position.Init( flHalfWide + flWide * ( Segments[i].vert2x - 0.5 ), flHalfTall + flTall * ( Segments[i].vert2y - 0.5 ) ); + v[2].m_TexCoord.Init( Segments[i].vert2x, Segments[i].vert2y ); + } + + // vert 2 is ( Segments[i].vert1x, Segments[i].vert1y ) + v[1].m_Position.Init( flHalfWide + flWide * ( Segments[i].vert1x - 0.5 ), flHalfTall + flTall * ( Segments[i].vert1y - 0.5 ) ); + v[1].m_TexCoord.Init( Segments[i].vert1x, Segments[i].vert1y ); + + vgui::surface()->DrawTexturedPolygon( 3, v ); + } + + cur_wedge++; + if ( cur_wedge >= 8) + cur_wedge = 0; + } } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/ComboBox.cpp b/mp/src/vgui2/vgui_controls/ComboBox.cpp index 4949bf19..f8be274b 100644 --- a/mp/src/vgui2/vgui_controls/ComboBox.cpp +++ b/mp/src/vgui2/vgui_controls/ComboBox.cpp @@ -1,1041 +1,1041 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#define PROTECTED_THINGS_DISABLE - -#include "vgui/Cursor.h" -#include "vgui/IInput.h" -#include "vgui/ILocalize.h" -#include "vgui/IScheme.h" -#include "vgui/ISurface.h" -#include "vgui/IPanel.h" -#include "KeyValues.h" - -#include "vgui_controls/ComboBox.h" -#include "vgui_controls/Menu.h" -#include "vgui_controls/MenuItem.h" -#include "vgui_controls/TextImage.h" - -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -using namespace vgui; - -namespace vgui -{ -ComboBoxButton::ComboBoxButton(ComboBox *parent, const char *panelName, const char *text) : Button(parent, panelName, text) -{ - SetButtonActivationType(ACTIVATE_ONPRESSED); -} - -void ComboBoxButton::ApplySchemeSettings(IScheme *pScheme) -{ - Button::ApplySchemeSettings(pScheme); - - SetFont(pScheme->GetFont("Marlett", IsProportional())); - SetContentAlignment(Label::a_west); -#ifdef OSX - SetTextInset(-3, 0); -#else - SetTextInset(3, 0); -#endif - SetDefaultBorder(pScheme->GetBorder("ScrollBarButtonBorder")); - - // arrow changes color but the background doesnt. - SetDefaultColor(GetSchemeColor("ComboBoxButton.ArrowColor", pScheme), GetSchemeColor("ComboBoxButton.BgColor", pScheme)); - SetArmedColor(GetSchemeColor("ComboBoxButton.ArmedArrowColor", pScheme), GetSchemeColor("ComboBoxButton.BgColor", pScheme)); - SetDepressedColor(GetSchemeColor("ComboBoxButton.ArmedArrowColor", pScheme), GetSchemeColor("ComboBoxButton.BgColor", pScheme)); - m_DisabledBgColor = GetSchemeColor("ComboBoxButton.DisabledBgColor", pScheme); -} - -IBorder * ComboBoxButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) -{ - return NULL; - // return Button::GetBorder(depressed, armed, selected, keyfocus); -} - -//----------------------------------------------------------------------------- -// Purpose: Dim the arrow on the button when exiting the box -// only if the menu is closed, so let the parent handle this. -//----------------------------------------------------------------------------- -void ComboBoxButton::OnCursorExited() -{ - // want the arrow to go grey when we exit the box if the menu is not open - CallParentFunction(new KeyValues("CursorExited")); -} - -} // namespace vgui - -vgui::Panel *ComboBox_Factory() -{ - return new ComboBox( NULL, NULL, 5, true ); -} -DECLARE_BUILD_FACTORY_CUSTOM( ComboBox, ComboBox_Factory ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -// Input : parent - parent class -// panelName -// numLines - number of lines in dropdown menu -// allowEdit - whether combobox is editable or not -//----------------------------------------------------------------------------- -ComboBox::ComboBox(Panel *parent, const char *panelName, int numLines, bool allowEdit ) : TextEntry(parent, panelName) -{ - SetEditable(allowEdit); - SetHorizontalScrolling(false); // do not scroll, always Start at the beginning of the text. - - // create the drop-down menu - m_pDropDown = new Menu(this, NULL); - m_pDropDown->AddActionSignalTarget(this); - m_pDropDown->SetTypeAheadMode( Menu::TYPE_AHEAD_MODE ); - - // button to Activate menu - m_pButton = new ComboBoxButton(this, "Button", "u"); - m_pButton->SetCommand("ButtonClicked"); - m_pButton->AddActionSignalTarget(this); - - SetNumberOfEditLines(numLines); - - m_bHighlight = false; - m_iDirection = Menu::DOWN; - m_iOpenOffsetY = 0; - m_bPreventTextChangeMessage = false; - m_szBorderOverride[0] = '\0'; -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -ComboBox::~ComboBox() -{ - m_pDropDown->DeletePanel(); - m_pButton->DeletePanel(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the number of items in the dropdown menu. -// Input : numLines - number of items in dropdown menu -//----------------------------------------------------------------------------- -void ComboBox::SetNumberOfEditLines( int numLines ) -{ - m_pDropDown->SetNumberOfVisibleItems( numLines ); -} - -//----------------------------------------------------------------------------- -// Purpose: Add an item to the drop down -// Input : char *itemText - name of dropdown menu item -//----------------------------------------------------------------------------- -int ComboBox::AddItem(const char *itemText, const KeyValues *userData) -{ - // when the menu item is selected it will send the custom message "SetText" - return m_pDropDown->AddMenuItem( itemText, new KeyValues("SetText", "text", itemText), this, userData ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Add an item to the drop down -// Input : char *itemText - name of dropdown menu item -//----------------------------------------------------------------------------- -int ComboBox::AddItem(const wchar_t *itemText, const KeyValues *userData) -{ - // add the element to the menu - // when the menu item is selected it will send the custom message "SetText" - KeyValues *kv = new KeyValues("SetText"); - kv->SetWString("text", itemText); - // get an ansi version for the menuitem name - char ansi[128]; - g_pVGuiLocalize->ConvertUnicodeToANSI(itemText, ansi, sizeof(ansi)); - return m_pDropDown->AddMenuItem(ansi, kv, this, userData); -} - - -//----------------------------------------------------------------------------- -// Removes a single item -//----------------------------------------------------------------------------- -void ComboBox::DeleteItem( int itemID ) -{ - if ( !m_pDropDown->IsValidMenuID(itemID)) - return; - - m_pDropDown->DeleteItem( itemID ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Updates a current item to the drop down -// Input : char *itemText - name of dropdown menu item -//----------------------------------------------------------------------------- -bool ComboBox::UpdateItem(int itemID, const char *itemText, const KeyValues *userData) -{ - if ( !m_pDropDown->IsValidMenuID(itemID)) - return false; - - // when the menu item is selected it will send the custom message "SetText" - m_pDropDown->UpdateMenuItem(itemID, itemText, new KeyValues("SetText", "text", itemText), userData); - InvalidateLayout(); - return true; -} -//----------------------------------------------------------------------------- -// Purpose: Updates a current item to the drop down -// Input : wchar_t *itemText - name of dropdown menu item -//----------------------------------------------------------------------------- -bool ComboBox::UpdateItem(int itemID, const wchar_t *itemText, const KeyValues *userData) -{ - if ( !m_pDropDown->IsValidMenuID(itemID)) - return false; - - // when the menu item is selected it will send the custom message "SetText" - KeyValues *kv = new KeyValues("SetText"); - kv->SetWString("text", itemText); - m_pDropDown->UpdateMenuItem(itemID, itemText, kv, userData); - InvalidateLayout(); - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Updates a current item to the drop down -// Input : wchar_t *itemText - name of dropdown menu item -//----------------------------------------------------------------------------- -bool ComboBox::IsItemIDValid( int itemID ) -{ - return m_pDropDown->IsValidMenuID(itemID); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ComboBox::SetItemEnabled(const char *itemText, bool state) -{ - m_pDropDown->SetItemEnabled(itemText, state); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ComboBox::SetItemEnabled(int itemID, bool state) -{ - m_pDropDown->SetItemEnabled(itemID, state); -} - -//----------------------------------------------------------------------------- -// Purpose: Remove all items from the drop down menu -//----------------------------------------------------------------------------- -void ComboBox::RemoveAll() -{ - m_pDropDown->DeleteAllItems(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ComboBox::GetItemCount() -{ - return m_pDropDown->GetItemCount(); -} - -int ComboBox::GetItemIDFromRow( int row ) -{ - // valid from [0, GetItemCount) - return m_pDropDown->GetMenuID( row ); -} - -//----------------------------------------------------------------------------- -// Purpose: Activate the item in the menu list, as if that menu item had been selected by the user -// Input : itemID - itemID from AddItem in list of dropdown items -//----------------------------------------------------------------------------- -void ComboBox::ActivateItem(int itemID) -{ - m_pDropDown->ActivateItem(itemID); -} - -//----------------------------------------------------------------------------- -// Purpose: Activate the item in the menu list, as if that menu item had been selected by the user -// Input : itemID - itemID from AddItem in list of dropdown items -//----------------------------------------------------------------------------- -void ComboBox::ActivateItemByRow(int row) -{ - m_pDropDown->ActivateItemByRow(row); -} - -//----------------------------------------------------------------------------- -// Purpose: Activate the item in the menu list, without sending a TextChanged message -// Input : row - row to activate -//----------------------------------------------------------------------------- -void ComboBox::SilentActivateItemByRow(int row) -{ - int itemID = GetItemIDFromRow( row ); - if ( itemID >= 0 ) - { - SilentActivateItem( itemID ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Activate the item in the menu list, without sending a TextChanged message -// Input : itemID - itemID from AddItem in list of dropdown items -//----------------------------------------------------------------------------- -void ComboBox::SilentActivateItem(int itemID) -{ - m_pDropDown->SilentActivateItem(itemID); - - // Now manually call our set text, with a wrapper to ensure we don't send the Text Changed message - wchar_t name[ 256 ]; - GetItemText( itemID, name, sizeof( name ) ); - - m_bPreventTextChangeMessage = true; - OnSetText( name ); - m_bPreventTextChangeMessage = false; -} - -//----------------------------------------------------------------------------- -// Purpose: Allows a custom menu to be used with the combo box -//----------------------------------------------------------------------------- -void ComboBox::SetMenu( Menu *menu ) -{ - if ( m_pDropDown ) - { - m_pDropDown->MarkForDeletion(); - } - - m_pDropDown = menu; - if ( m_pDropDown ) - { - m_pDropDown->SetParent( this ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Layout the format of the combo box for drawing on screen -//----------------------------------------------------------------------------- -void ComboBox::PerformLayout() -{ - int wide, tall; - GetPaintSize(wide, tall); - - BaseClass::PerformLayout(); - - HFont buttonFont = m_pButton->GetFont(); - int fontTall = surface()->GetFontTall( buttonFont ); - - int buttonSize = min( tall, fontTall ); - - int buttonY = ( ( tall - 1 ) - buttonSize ) / 2; - - // Some dropdown button icons in our games are wider than they are taller. We need to factor that in. - int button_wide, button_tall; - m_pButton->GetContentSize(button_wide, button_tall); - button_wide = max( buttonSize, button_wide ); - - m_pButton->SetBounds( wide - button_wide, buttonY, button_wide, buttonSize ); - if ( IsEditable() ) - { - SetCursor(dc_ibeam); - } - else - { - SetCursor(dc_arrow); - } - - m_pButton->SetEnabled(IsEnabled()); - - DoMenuLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ComboBox::DoMenuLayout() -{ - m_pDropDown->PositionRelativeToPanel( this, m_iDirection, m_iOpenOffsetY ); - - // reset the width of the drop down menu to be the width of the combo box - m_pDropDown->SetFixedWidth(GetWide()); - m_pDropDown->ForceCalculateWidth(); - -} - -//----------------------------------------------------------------------------- -// Purpose: Sorts the items in the list -//----------------------------------------------------------------------------- -void ComboBox::SortItems( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: return the index of the last selected item -//----------------------------------------------------------------------------- -int ComboBox::GetActiveItem() -{ - return m_pDropDown->GetActiveItem(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -KeyValues *ComboBox::GetActiveItemUserData() -{ - return m_pDropDown->GetItemUserData(GetActiveItem()); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -KeyValues *ComboBox::GetItemUserData(int itemID) -{ - return m_pDropDown->GetItemUserData(itemID); -} - - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void ComboBox::GetItemText( int itemID, wchar_t *text, int bufLenInBytes ) -{ - m_pDropDown->GetItemText( itemID, text, bufLenInBytes ); -} - -void ComboBox::GetItemText( int itemID, char *text, int bufLenInBytes ) -{ - m_pDropDown->GetItemText( itemID, text, bufLenInBytes ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool ComboBox::IsDropdownVisible() -{ - return m_pDropDown->IsVisible(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *inResourceData - -//----------------------------------------------------------------------------- -void ComboBox::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetBorder( pScheme->GetBorder( m_szBorderOverride[0] ? m_szBorderOverride : "ComboBoxBorder" ) ); -} - -void ComboBox::ApplySettings( KeyValues *pInResourceData ) -{ - BaseClass::ApplySettings( pInResourceData ); - - const char *pBorderOverride = pInResourceData->GetString( "border_override", NULL ); - if ( pBorderOverride ) - { - V_strncpy( m_szBorderOverride, pBorderOverride, sizeof( m_szBorderOverride ) ); - } - - KeyValues *pKVButton = pInResourceData->FindKey( "Button" ); - if ( pKVButton && m_pButton ) - { - m_pButton->ApplySettings( pKVButton ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set the visiblity of the drop down menu button. -//----------------------------------------------------------------------------- -void ComboBox::SetDropdownButtonVisible(bool state) -{ - m_pButton->SetVisible(state); -} - -//----------------------------------------------------------------------------- -// Purpose: overloads TextEntry MousePressed -//----------------------------------------------------------------------------- -void ComboBox::OnMousePressed(MouseCode code) -{ - if ( !m_pDropDown ) - return; - - if ( !IsEnabled() ) - return; - - // make sure it's getting pressed over us (it may not be due to mouse capture) - if ( !IsCursorOver() ) - { - HideMenu(); - return; - } - - if ( IsEditable() ) - { - BaseClass::OnMousePressed(code); - HideMenu(); - } - else - { - // clicking on a non-editable text box just activates the drop down menu - RequestFocus(); - DoClick(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Double-click acts the same as a single-click -//----------------------------------------------------------------------------- -void ComboBox::OnMouseDoublePressed(MouseCode code) -{ - if (IsEditable()) - { - BaseClass::OnMouseDoublePressed(code); - } - else - { - OnMousePressed(code); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Called when a command is received from the menu -// Changes the label text to be that of the command -// Input : char *command - -//----------------------------------------------------------------------------- -void ComboBox::OnCommand( const char *command ) -{ - if (!stricmp(command, "ButtonClicked")) - { - // hide / show the menu underneath - DoClick(); - } - - Panel::OnCommand(command); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ComboBox::OnSetText(const wchar_t *newtext) -{ - // see if the combobox text has changed, and if so, post a message detailing the new text - const wchar_t *text = newtext; - - // check if the new text is a localized string, if so undo it - if (*text == '#') - { - char cbuf[255]; - g_pVGuiLocalize->ConvertUnicodeToANSI(text, cbuf, 255); - - // try lookup in localization tables - StringIndex_t unlocalizedTextSymbol = g_pVGuiLocalize->FindIndex(cbuf + 1); - - if (unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX) - { - // we have a new text value - text = g_pVGuiLocalize->GetValueByIndex(unlocalizedTextSymbol); - } - } - - wchar_t wbuf[255]; - GetText(wbuf, 254); - - if ( wcscmp(wbuf, text) ) - { - // text has changed - SetText(text); - - // fire off that things have changed - if ( !m_bPreventTextChangeMessage ) - { - PostActionSignal(new KeyValues("TextChanged", "text", text)); - } - Repaint(); - } - - // close the box - HideMenu(); -} - -//----------------------------------------------------------------------------- -// Purpose: hides the menu -//----------------------------------------------------------------------------- -void ComboBox::HideMenu(void) -{ - if ( !m_pDropDown ) - return; - - // hide the menu - m_pDropDown->SetVisible(false); - Repaint(); - OnHideMenu(m_pDropDown); -} - -//----------------------------------------------------------------------------- -// Purpose: shows the menu -//----------------------------------------------------------------------------- -void ComboBox::ShowMenu(void) -{ - if ( !m_pDropDown ) - return; - - // hide the menu - m_pDropDown->SetVisible(false); - DoClick(); -} - -//----------------------------------------------------------------------------- -// Purpose: Called when the window loses focus; hides the menu -//----------------------------------------------------------------------------- -void ComboBox::OnKillFocus() -{ - SelectNoText(); -} - -//----------------------------------------------------------------------------- -// Purpose: Called when the menu is closed -//----------------------------------------------------------------------------- -void ComboBox::OnMenuClose() -{ - HideMenu(); - - if ( HasFocus() ) - { - SelectAllText(false); - } - else if ( m_bHighlight ) - { - m_bHighlight = false; - // we want the text to be highlighted when we request the focus -// SelectAllOnFirstFocus(true); - RequestFocus(); - } - // if cursor is in this box or the arrow box - else if ( IsCursorOver() )// make sure it's getting pressed over us (it may not be due to mouse capture) - { - SelectAllText(false); - OnCursorEntered(); - // Get focus so the box will unhighlight if we click somewhere else. - RequestFocus(); - } - else - { - m_pButton->SetArmed(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handles hotkey accesses -// FIXME: make this open different directions as necessary see menubutton. -//----------------------------------------------------------------------------- -void ComboBox::DoClick() -{ - // menu is already visible, hide the menu - if ( m_pDropDown->IsVisible() ) - { - HideMenu(); - return; - } - - // do nothing if menu is not enabled - if ( !m_pDropDown->IsEnabled() ) - { - return; - } - // force the menu to Think - m_pDropDown->PerformLayout(); - - // make sure we're at the top of the draw order (and therefore our children as well) - // RequestFocus(); - - // We want the item that is shown in the combo box to show as selected - int itemToSelect = -1; - int i; - wchar_t comboBoxContents[255]; - GetText(comboBoxContents, 255); - for ( i = 0 ; i < m_pDropDown->GetItemCount() ; i++ ) - { - wchar_t menuItemName[255]; - int menuID = m_pDropDown->GetMenuID(i); - m_pDropDown->GetMenuItem(menuID)->GetText(menuItemName, 255); - if (!wcscmp(menuItemName, comboBoxContents)) - { - itemToSelect = i; - break; - } - } - // if we found a match, highlight it on opening the menu - if ( itemToSelect >= 0 ) - { - m_pDropDown->SetCurrentlyHighlightedItem( m_pDropDown->GetMenuID(itemToSelect) ); - } - - // reset the dropdown's position - DoMenuLayout(); - - - // make sure we're at the top of the draw order (and therefore our children as well) - // this important to make sure the menu will be drawn in the foreground - MoveToFront(); - - // !KLUDGE! Force alpha to solid. Otherwise, - // we run into weird VGUI problems with pops - // and the stencil test - Color c = m_pDropDown->GetBgColor(); - c[3] = 255; - m_pDropDown->SetBgColor( c ); - - // notify - OnShowMenu(m_pDropDown); - - // show the menu - m_pDropDown->SetVisible(true); - - // bring to focus - m_pDropDown->RequestFocus(); - - // no text is highlighted when the menu is opened - SelectNoText(); - - // highlight the arrow while menu is open - m_pButton->SetArmed(true); - - Repaint(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Brighten the arrow on the button when entering the box -//----------------------------------------------------------------------------- -void ComboBox::OnCursorEntered() -{ - // want the arrow to go white when we enter the box - m_pButton->OnCursorEntered(); - TextEntry::OnCursorEntered(); -} - -//----------------------------------------------------------------------------- -// Purpose: Dim the arrow on the button when exiting the box -//----------------------------------------------------------------------------- -void ComboBox::OnCursorExited() -{ - // want the arrow to go grey when we exit the box if the menu is not open - if ( !m_pDropDown->IsVisible() ) - { - m_pButton->SetArmed(false); - TextEntry::OnCursorExited(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -#ifdef _X360 -void ComboBox::OnMenuItemSelected() -{ - m_bHighlight = true; - // For editable cbs, fill in the text field from whatever is chosen from the dropdown... - - //============================================================================= - // HPE_BEGIN: - // [pfreese] The text for the combo box should be updated regardless of its - // editable state, and in any case, the member variable below was never - // correctly initialized. - //============================================================================= - - // if ( m_bAllowEdit ) - - //============================================================================= - // HPE_END - //============================================================================= - { - int idx = GetActiveItem(); - if ( idx >= 0 ) - { - wchar_t name[ 256 ]; - GetItemText( idx, name, sizeof( name ) ); - - OnSetText( name ); - } - } - - Repaint(); - - // go to the next control - if(!NavigateDown()) - { - NavigateUp(); - } -} -#else -void ComboBox::OnMenuItemSelected() -{ - m_bHighlight = true; - // For editable cbs, fill in the text field from whatever is chosen from the dropdown... - //if ( m_bAllowEdit ) - { - int idx = GetActiveItem(); - if ( idx >= 0 ) - { - wchar_t name[ 256 ]; - GetItemText( idx, name, sizeof( name ) ); - - OnSetText( name ); - } - } - - Repaint(); -} -#endif - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ComboBox::OnSizeChanged(int wide, int tall) -{ - BaseClass::OnSizeChanged( wide, tall); - - // set the drawwidth. - int bwide, btall; - PerformLayout(); - m_pButton->GetSize( bwide, btall); - SetDrawWidth( wide - bwide ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -#ifdef _X360 -void ComboBox::OnSetFocus() -{ - BaseClass::OnSetFocus(); - - GotoTextEnd(); - SelectAllText(true); -} -#else -void ComboBox::OnSetFocus() -{ - BaseClass::OnSetFocus(); - - GotoTextEnd(); - SelectAllText(false); -} -#endif - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -#ifdef _X360 -void ComboBox::OnKeyCodePressed(KeyCode code) -{ - switch ( GetBaseButtonCode( code ) ) - { - case KEY_XBUTTON_A: - DoClick(); - break; - case KEY_XBUTTON_UP: - case KEY_XSTICK1_UP: - case KEY_XSTICK2_UP: - if(m_pDropDown->IsVisible()) - { - MoveAlongMenuItemList(-1); - } - else - { - BaseClass::OnKeyCodePressed(code); - } - break; - case KEY_XBUTTON_DOWN: - case KEY_XSTICK1_DOWN: - case KEY_XSTICK2_DOWN: - if(m_pDropDown->IsVisible()) - { - MoveAlongMenuItemList(1); - } - else - { - BaseClass::OnKeyCodePressed(code); - } - break; - default: - BaseClass::OnKeyCodePressed(code); - break; - } -} -#endif - -//----------------------------------------------------------------------------- -// Purpose: Handles up/down arrows -//----------------------------------------------------------------------------- -void ComboBox::OnKeyCodeTyped(KeyCode code) -{ - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - - if (alt) - { - switch (code) - { - case KEY_UP: - case KEY_DOWN: - { - DoClick(); - break; - } - default: - { - BaseClass::OnKeyCodeTyped(code); - break; - } - } - } - else - { - switch (code) - { - case KEY_HOME: - case KEY_END: - case KEY_PAGEUP: - case KEY_PAGEDOWN: - case KEY_UP: - case KEY_DOWN: - { - int itemSelected = m_pDropDown->GetCurrentlyHighlightedItem(); - m_pDropDown->OnKeyCodeTyped(code); - int itemToSelect = m_pDropDown->GetCurrentlyHighlightedItem(); - - if ( itemToSelect != itemSelected ) - { - SelectMenuItem(itemToSelect); - } - break; - } - - case KEY_ENTER: - { - int itemToSelect = m_pDropDown->GetCurrentlyHighlightedItem(); - m_pDropDown->ActivateItem(itemToSelect); - break; - } - - default: - { - BaseClass::OnKeyCodeTyped(code); - break; - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: handles key input -//----------------------------------------------------------------------------- -void ComboBox::OnKeyTyped(wchar_t unichar) -{ - if ( IsEditable() || unichar == '\t') // don't play with key presses in edit mode - { - BaseClass::OnKeyTyped( unichar ); - return; - } - - int itemSelected = m_pDropDown->GetCurrentlyHighlightedItem(); - m_pDropDown->OnKeyTyped(unichar); - int itemToSelect = m_pDropDown->GetCurrentlyHighlightedItem(); - - if ( itemToSelect != itemSelected ) - { - SelectMenuItem(itemToSelect); - } - else - { - BaseClass::OnKeyTyped( unichar ); - } -} - -void ComboBox::SelectMenuItem(int itemToSelect) -{ - // if we found this item, then we scroll up or down - if ( itemToSelect >= 0 && itemToSelect < m_pDropDown->GetItemCount() ) - { - wchar_t menuItemName[255]; - - int menuID = m_pDropDown->GetMenuID(itemToSelect); - m_pDropDown->GetMenuItem(menuID)->GetText(menuItemName, 254); - OnSetText(menuItemName); - SelectAllText(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ComboBox::MoveAlongMenuItemList(int direction) -{ - // We want the item that is shown in the combo box to show as selected - int itemToSelect = -1; - wchar_t menuItemName[255]; - int i; - - wchar_t comboBoxContents[255]; - GetText(comboBoxContents, 254); - for ( i = 0 ; i < m_pDropDown->GetItemCount() ; i++ ) - { - int menuID = m_pDropDown->GetMenuID(i); - m_pDropDown->GetMenuItem(menuID)->GetText(menuItemName, 254); - - if ( !wcscmp(menuItemName, comboBoxContents) ) - { - itemToSelect = i; - break; - } - } - - if ( itemToSelect >= 0 ) - { - int newItem = itemToSelect + direction; - if ( newItem < 0 ) - { - newItem = 0; - } - else if ( newItem >= m_pDropDown->GetItemCount() ) - { - newItem = m_pDropDown->GetItemCount() - 1; - } - SelectMenuItem(newItem); - } - -} - -void ComboBox::MoveToFirstMenuItem() -{ - SelectMenuItem(0); -} - -void ComboBox::MoveToLastMenuItem() -{ - SelectMenuItem(m_pDropDown->GetItemCount() - 1); -} - - -//----------------------------------------------------------------------------- -// Purpose: Sets the direction from the menu button the menu should open -//----------------------------------------------------------------------------- -void ComboBox::SetOpenDirection(Menu::MenuDirection_e direction) -{ - m_iDirection = direction; -} - -void ComboBox::SetFont( HFont font ) -{ - BaseClass::SetFont( font ); - - m_pDropDown->SetFont( font ); -} - - -void ComboBox::SetUseFallbackFont( bool bState, HFont hFallback ) -{ - BaseClass::SetUseFallbackFont( bState, hFallback ); - m_pDropDown->SetUseFallbackFont( bState, hFallback ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#define PROTECTED_THINGS_DISABLE + +#include "vgui/Cursor.h" +#include "vgui/IInput.h" +#include "vgui/ILocalize.h" +#include "vgui/IScheme.h" +#include "vgui/ISurface.h" +#include "vgui/IPanel.h" +#include "KeyValues.h" + +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/Menu.h" +#include "vgui_controls/MenuItem.h" +#include "vgui_controls/TextImage.h" + +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +namespace vgui +{ +ComboBoxButton::ComboBoxButton(ComboBox *parent, const char *panelName, const char *text) : Button(parent, panelName, text) +{ + SetButtonActivationType(ACTIVATE_ONPRESSED); +} + +void ComboBoxButton::ApplySchemeSettings(IScheme *pScheme) +{ + Button::ApplySchemeSettings(pScheme); + + SetFont(pScheme->GetFont("Marlett", IsProportional())); + SetContentAlignment(Label::a_west); +#ifdef OSX + SetTextInset(-3, 0); +#else + SetTextInset(3, 0); +#endif + SetDefaultBorder(pScheme->GetBorder("ScrollBarButtonBorder")); + + // arrow changes color but the background doesnt. + SetDefaultColor(GetSchemeColor("ComboBoxButton.ArrowColor", pScheme), GetSchemeColor("ComboBoxButton.BgColor", pScheme)); + SetArmedColor(GetSchemeColor("ComboBoxButton.ArmedArrowColor", pScheme), GetSchemeColor("ComboBoxButton.BgColor", pScheme)); + SetDepressedColor(GetSchemeColor("ComboBoxButton.ArmedArrowColor", pScheme), GetSchemeColor("ComboBoxButton.BgColor", pScheme)); + m_DisabledBgColor = GetSchemeColor("ComboBoxButton.DisabledBgColor", pScheme); +} + +IBorder * ComboBoxButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) +{ + return NULL; + // return Button::GetBorder(depressed, armed, selected, keyfocus); +} + +//----------------------------------------------------------------------------- +// Purpose: Dim the arrow on the button when exiting the box +// only if the menu is closed, so let the parent handle this. +//----------------------------------------------------------------------------- +void ComboBoxButton::OnCursorExited() +{ + // want the arrow to go grey when we exit the box if the menu is not open + CallParentFunction(new KeyValues("CursorExited")); +} + +} // namespace vgui + +vgui::Panel *ComboBox_Factory() +{ + return new ComboBox( NULL, NULL, 5, true ); +} +DECLARE_BUILD_FACTORY_CUSTOM( ComboBox, ComboBox_Factory ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +// Input : parent - parent class +// panelName +// numLines - number of lines in dropdown menu +// allowEdit - whether combobox is editable or not +//----------------------------------------------------------------------------- +ComboBox::ComboBox(Panel *parent, const char *panelName, int numLines, bool allowEdit ) : TextEntry(parent, panelName) +{ + SetEditable(allowEdit); + SetHorizontalScrolling(false); // do not scroll, always Start at the beginning of the text. + + // create the drop-down menu + m_pDropDown = new Menu(this, NULL); + m_pDropDown->AddActionSignalTarget(this); + m_pDropDown->SetTypeAheadMode( Menu::TYPE_AHEAD_MODE ); + + // button to Activate menu + m_pButton = new ComboBoxButton(this, "Button", "u"); + m_pButton->SetCommand("ButtonClicked"); + m_pButton->AddActionSignalTarget(this); + + SetNumberOfEditLines(numLines); + + m_bHighlight = false; + m_iDirection = Menu::DOWN; + m_iOpenOffsetY = 0; + m_bPreventTextChangeMessage = false; + m_szBorderOverride[0] = '\0'; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +ComboBox::~ComboBox() +{ + m_pDropDown->DeletePanel(); + m_pButton->DeletePanel(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the number of items in the dropdown menu. +// Input : numLines - number of items in dropdown menu +//----------------------------------------------------------------------------- +void ComboBox::SetNumberOfEditLines( int numLines ) +{ + m_pDropDown->SetNumberOfVisibleItems( numLines ); +} + +//----------------------------------------------------------------------------- +// Purpose: Add an item to the drop down +// Input : char *itemText - name of dropdown menu item +//----------------------------------------------------------------------------- +int ComboBox::AddItem(const char *itemText, const KeyValues *userData) +{ + // when the menu item is selected it will send the custom message "SetText" + return m_pDropDown->AddMenuItem( itemText, new KeyValues("SetText", "text", itemText), this, userData ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Add an item to the drop down +// Input : char *itemText - name of dropdown menu item +//----------------------------------------------------------------------------- +int ComboBox::AddItem(const wchar_t *itemText, const KeyValues *userData) +{ + // add the element to the menu + // when the menu item is selected it will send the custom message "SetText" + KeyValues *kv = new KeyValues("SetText"); + kv->SetWString("text", itemText); + // get an ansi version for the menuitem name + char ansi[128]; + g_pVGuiLocalize->ConvertUnicodeToANSI(itemText, ansi, sizeof(ansi)); + return m_pDropDown->AddMenuItem(ansi, kv, this, userData); +} + + +//----------------------------------------------------------------------------- +// Removes a single item +//----------------------------------------------------------------------------- +void ComboBox::DeleteItem( int itemID ) +{ + if ( !m_pDropDown->IsValidMenuID(itemID)) + return; + + m_pDropDown->DeleteItem( itemID ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Updates a current item to the drop down +// Input : char *itemText - name of dropdown menu item +//----------------------------------------------------------------------------- +bool ComboBox::UpdateItem(int itemID, const char *itemText, const KeyValues *userData) +{ + if ( !m_pDropDown->IsValidMenuID(itemID)) + return false; + + // when the menu item is selected it will send the custom message "SetText" + m_pDropDown->UpdateMenuItem(itemID, itemText, new KeyValues("SetText", "text", itemText), userData); + InvalidateLayout(); + return true; +} +//----------------------------------------------------------------------------- +// Purpose: Updates a current item to the drop down +// Input : wchar_t *itemText - name of dropdown menu item +//----------------------------------------------------------------------------- +bool ComboBox::UpdateItem(int itemID, const wchar_t *itemText, const KeyValues *userData) +{ + if ( !m_pDropDown->IsValidMenuID(itemID)) + return false; + + // when the menu item is selected it will send the custom message "SetText" + KeyValues *kv = new KeyValues("SetText"); + kv->SetWString("text", itemText); + m_pDropDown->UpdateMenuItem(itemID, itemText, kv, userData); + InvalidateLayout(); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Updates a current item to the drop down +// Input : wchar_t *itemText - name of dropdown menu item +//----------------------------------------------------------------------------- +bool ComboBox::IsItemIDValid( int itemID ) +{ + return m_pDropDown->IsValidMenuID(itemID); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ComboBox::SetItemEnabled(const char *itemText, bool state) +{ + m_pDropDown->SetItemEnabled(itemText, state); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ComboBox::SetItemEnabled(int itemID, bool state) +{ + m_pDropDown->SetItemEnabled(itemID, state); +} + +//----------------------------------------------------------------------------- +// Purpose: Remove all items from the drop down menu +//----------------------------------------------------------------------------- +void ComboBox::RemoveAll() +{ + m_pDropDown->DeleteAllItems(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ComboBox::GetItemCount() +{ + return m_pDropDown->GetItemCount(); +} + +int ComboBox::GetItemIDFromRow( int row ) +{ + // valid from [0, GetItemCount) + return m_pDropDown->GetMenuID( row ); +} + +//----------------------------------------------------------------------------- +// Purpose: Activate the item in the menu list, as if that menu item had been selected by the user +// Input : itemID - itemID from AddItem in list of dropdown items +//----------------------------------------------------------------------------- +void ComboBox::ActivateItem(int itemID) +{ + m_pDropDown->ActivateItem(itemID); +} + +//----------------------------------------------------------------------------- +// Purpose: Activate the item in the menu list, as if that menu item had been selected by the user +// Input : itemID - itemID from AddItem in list of dropdown items +//----------------------------------------------------------------------------- +void ComboBox::ActivateItemByRow(int row) +{ + m_pDropDown->ActivateItemByRow(row); +} + +//----------------------------------------------------------------------------- +// Purpose: Activate the item in the menu list, without sending a TextChanged message +// Input : row - row to activate +//----------------------------------------------------------------------------- +void ComboBox::SilentActivateItemByRow(int row) +{ + int itemID = GetItemIDFromRow( row ); + if ( itemID >= 0 ) + { + SilentActivateItem( itemID ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Activate the item in the menu list, without sending a TextChanged message +// Input : itemID - itemID from AddItem in list of dropdown items +//----------------------------------------------------------------------------- +void ComboBox::SilentActivateItem(int itemID) +{ + m_pDropDown->SilentActivateItem(itemID); + + // Now manually call our set text, with a wrapper to ensure we don't send the Text Changed message + wchar_t name[ 256 ]; + GetItemText( itemID, name, sizeof( name ) ); + + m_bPreventTextChangeMessage = true; + OnSetText( name ); + m_bPreventTextChangeMessage = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Allows a custom menu to be used with the combo box +//----------------------------------------------------------------------------- +void ComboBox::SetMenu( Menu *menu ) +{ + if ( m_pDropDown ) + { + m_pDropDown->MarkForDeletion(); + } + + m_pDropDown = menu; + if ( m_pDropDown ) + { + m_pDropDown->SetParent( this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Layout the format of the combo box for drawing on screen +//----------------------------------------------------------------------------- +void ComboBox::PerformLayout() +{ + int wide, tall; + GetPaintSize(wide, tall); + + BaseClass::PerformLayout(); + + HFont buttonFont = m_pButton->GetFont(); + int fontTall = surface()->GetFontTall( buttonFont ); + + int buttonSize = min( tall, fontTall ); + + int buttonY = ( ( tall - 1 ) - buttonSize ) / 2; + + // Some dropdown button icons in our games are wider than they are taller. We need to factor that in. + int button_wide, button_tall; + m_pButton->GetContentSize(button_wide, button_tall); + button_wide = max( buttonSize, button_wide ); + + m_pButton->SetBounds( wide - button_wide, buttonY, button_wide, buttonSize ); + if ( IsEditable() ) + { + SetCursor(dc_ibeam); + } + else + { + SetCursor(dc_arrow); + } + + m_pButton->SetEnabled(IsEnabled()); + + DoMenuLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ComboBox::DoMenuLayout() +{ + m_pDropDown->PositionRelativeToPanel( this, m_iDirection, m_iOpenOffsetY ); + + // reset the width of the drop down menu to be the width of the combo box + m_pDropDown->SetFixedWidth(GetWide()); + m_pDropDown->ForceCalculateWidth(); + +} + +//----------------------------------------------------------------------------- +// Purpose: Sorts the items in the list +//----------------------------------------------------------------------------- +void ComboBox::SortItems( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: return the index of the last selected item +//----------------------------------------------------------------------------- +int ComboBox::GetActiveItem() +{ + return m_pDropDown->GetActiveItem(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +KeyValues *ComboBox::GetActiveItemUserData() +{ + return m_pDropDown->GetItemUserData(GetActiveItem()); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +KeyValues *ComboBox::GetItemUserData(int itemID) +{ + return m_pDropDown->GetItemUserData(itemID); +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void ComboBox::GetItemText( int itemID, wchar_t *text, int bufLenInBytes ) +{ + m_pDropDown->GetItemText( itemID, text, bufLenInBytes ); +} + +void ComboBox::GetItemText( int itemID, char *text, int bufLenInBytes ) +{ + m_pDropDown->GetItemText( itemID, text, bufLenInBytes ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool ComboBox::IsDropdownVisible() +{ + return m_pDropDown->IsVisible(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *inResourceData - +//----------------------------------------------------------------------------- +void ComboBox::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetBorder( pScheme->GetBorder( m_szBorderOverride[0] ? m_szBorderOverride : "ComboBoxBorder" ) ); +} + +void ComboBox::ApplySettings( KeyValues *pInResourceData ) +{ + BaseClass::ApplySettings( pInResourceData ); + + const char *pBorderOverride = pInResourceData->GetString( "border_override", NULL ); + if ( pBorderOverride ) + { + V_strncpy( m_szBorderOverride, pBorderOverride, sizeof( m_szBorderOverride ) ); + } + + KeyValues *pKVButton = pInResourceData->FindKey( "Button" ); + if ( pKVButton && m_pButton ) + { + m_pButton->ApplySettings( pKVButton ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the visiblity of the drop down menu button. +//----------------------------------------------------------------------------- +void ComboBox::SetDropdownButtonVisible(bool state) +{ + m_pButton->SetVisible(state); +} + +//----------------------------------------------------------------------------- +// Purpose: overloads TextEntry MousePressed +//----------------------------------------------------------------------------- +void ComboBox::OnMousePressed(MouseCode code) +{ + if ( !m_pDropDown ) + return; + + if ( !IsEnabled() ) + return; + + // make sure it's getting pressed over us (it may not be due to mouse capture) + if ( !IsCursorOver() ) + { + HideMenu(); + return; + } + + if ( IsEditable() ) + { + BaseClass::OnMousePressed(code); + HideMenu(); + } + else + { + // clicking on a non-editable text box just activates the drop down menu + RequestFocus(); + DoClick(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Double-click acts the same as a single-click +//----------------------------------------------------------------------------- +void ComboBox::OnMouseDoublePressed(MouseCode code) +{ + if (IsEditable()) + { + BaseClass::OnMouseDoublePressed(code); + } + else + { + OnMousePressed(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called when a command is received from the menu +// Changes the label text to be that of the command +// Input : char *command - +//----------------------------------------------------------------------------- +void ComboBox::OnCommand( const char *command ) +{ + if (!stricmp(command, "ButtonClicked")) + { + // hide / show the menu underneath + DoClick(); + } + + Panel::OnCommand(command); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ComboBox::OnSetText(const wchar_t *newtext) +{ + // see if the combobox text has changed, and if so, post a message detailing the new text + const wchar_t *text = newtext; + + // check if the new text is a localized string, if so undo it + if (*text == '#') + { + char cbuf[255]; + g_pVGuiLocalize->ConvertUnicodeToANSI(text, cbuf, 255); + + // try lookup in localization tables + StringIndex_t unlocalizedTextSymbol = g_pVGuiLocalize->FindIndex(cbuf + 1); + + if (unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX) + { + // we have a new text value + text = g_pVGuiLocalize->GetValueByIndex(unlocalizedTextSymbol); + } + } + + wchar_t wbuf[255]; + GetText(wbuf, 254); + + if ( wcscmp(wbuf, text) ) + { + // text has changed + SetText(text); + + // fire off that things have changed + if ( !m_bPreventTextChangeMessage ) + { + PostActionSignal(new KeyValues("TextChanged", "text", text)); + } + Repaint(); + } + + // close the box + HideMenu(); +} + +//----------------------------------------------------------------------------- +// Purpose: hides the menu +//----------------------------------------------------------------------------- +void ComboBox::HideMenu(void) +{ + if ( !m_pDropDown ) + return; + + // hide the menu + m_pDropDown->SetVisible(false); + Repaint(); + OnHideMenu(m_pDropDown); +} + +//----------------------------------------------------------------------------- +// Purpose: shows the menu +//----------------------------------------------------------------------------- +void ComboBox::ShowMenu(void) +{ + if ( !m_pDropDown ) + return; + + // hide the menu + m_pDropDown->SetVisible(false); + DoClick(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the window loses focus; hides the menu +//----------------------------------------------------------------------------- +void ComboBox::OnKillFocus() +{ + SelectNoText(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the menu is closed +//----------------------------------------------------------------------------- +void ComboBox::OnMenuClose() +{ + HideMenu(); + + if ( HasFocus() ) + { + SelectAllText(false); + } + else if ( m_bHighlight ) + { + m_bHighlight = false; + // we want the text to be highlighted when we request the focus +// SelectAllOnFirstFocus(true); + RequestFocus(); + } + // if cursor is in this box or the arrow box + else if ( IsCursorOver() )// make sure it's getting pressed over us (it may not be due to mouse capture) + { + SelectAllText(false); + OnCursorEntered(); + // Get focus so the box will unhighlight if we click somewhere else. + RequestFocus(); + } + else + { + m_pButton->SetArmed(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handles hotkey accesses +// FIXME: make this open different directions as necessary see menubutton. +//----------------------------------------------------------------------------- +void ComboBox::DoClick() +{ + // menu is already visible, hide the menu + if ( m_pDropDown->IsVisible() ) + { + HideMenu(); + return; + } + + // do nothing if menu is not enabled + if ( !m_pDropDown->IsEnabled() ) + { + return; + } + // force the menu to Think + m_pDropDown->PerformLayout(); + + // make sure we're at the top of the draw order (and therefore our children as well) + // RequestFocus(); + + // We want the item that is shown in the combo box to show as selected + int itemToSelect = -1; + int i; + wchar_t comboBoxContents[255]; + GetText(comboBoxContents, 255); + for ( i = 0 ; i < m_pDropDown->GetItemCount() ; i++ ) + { + wchar_t menuItemName[255]; + int menuID = m_pDropDown->GetMenuID(i); + m_pDropDown->GetMenuItem(menuID)->GetText(menuItemName, 255); + if (!wcscmp(menuItemName, comboBoxContents)) + { + itemToSelect = i; + break; + } + } + // if we found a match, highlight it on opening the menu + if ( itemToSelect >= 0 ) + { + m_pDropDown->SetCurrentlyHighlightedItem( m_pDropDown->GetMenuID(itemToSelect) ); + } + + // reset the dropdown's position + DoMenuLayout(); + + + // make sure we're at the top of the draw order (and therefore our children as well) + // this important to make sure the menu will be drawn in the foreground + MoveToFront(); + + // !KLUDGE! Force alpha to solid. Otherwise, + // we run into weird VGUI problems with pops + // and the stencil test + Color c = m_pDropDown->GetBgColor(); + c[3] = 255; + m_pDropDown->SetBgColor( c ); + + // notify + OnShowMenu(m_pDropDown); + + // show the menu + m_pDropDown->SetVisible(true); + + // bring to focus + m_pDropDown->RequestFocus(); + + // no text is highlighted when the menu is opened + SelectNoText(); + + // highlight the arrow while menu is open + m_pButton->SetArmed(true); + + Repaint(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Brighten the arrow on the button when entering the box +//----------------------------------------------------------------------------- +void ComboBox::OnCursorEntered() +{ + // want the arrow to go white when we enter the box + m_pButton->OnCursorEntered(); + TextEntry::OnCursorEntered(); +} + +//----------------------------------------------------------------------------- +// Purpose: Dim the arrow on the button when exiting the box +//----------------------------------------------------------------------------- +void ComboBox::OnCursorExited() +{ + // want the arrow to go grey when we exit the box if the menu is not open + if ( !m_pDropDown->IsVisible() ) + { + m_pButton->SetArmed(false); + TextEntry::OnCursorExited(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#ifdef _X360 +void ComboBox::OnMenuItemSelected() +{ + m_bHighlight = true; + // For editable cbs, fill in the text field from whatever is chosen from the dropdown... + + //============================================================================= + // HPE_BEGIN: + // [pfreese] The text for the combo box should be updated regardless of its + // editable state, and in any case, the member variable below was never + // correctly initialized. + //============================================================================= + + // if ( m_bAllowEdit ) + + //============================================================================= + // HPE_END + //============================================================================= + { + int idx = GetActiveItem(); + if ( idx >= 0 ) + { + wchar_t name[ 256 ]; + GetItemText( idx, name, sizeof( name ) ); + + OnSetText( name ); + } + } + + Repaint(); + + // go to the next control + if(!NavigateDown()) + { + NavigateUp(); + } +} +#else +void ComboBox::OnMenuItemSelected() +{ + m_bHighlight = true; + // For editable cbs, fill in the text field from whatever is chosen from the dropdown... + //if ( m_bAllowEdit ) + { + int idx = GetActiveItem(); + if ( idx >= 0 ) + { + wchar_t name[ 256 ]; + GetItemText( idx, name, sizeof( name ) ); + + OnSetText( name ); + } + } + + Repaint(); +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ComboBox::OnSizeChanged(int wide, int tall) +{ + BaseClass::OnSizeChanged( wide, tall); + + // set the drawwidth. + int bwide, btall; + PerformLayout(); + m_pButton->GetSize( bwide, btall); + SetDrawWidth( wide - bwide ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#ifdef _X360 +void ComboBox::OnSetFocus() +{ + BaseClass::OnSetFocus(); + + GotoTextEnd(); + SelectAllText(true); +} +#else +void ComboBox::OnSetFocus() +{ + BaseClass::OnSetFocus(); + + GotoTextEnd(); + SelectAllText(false); +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#ifdef _X360 +void ComboBox::OnKeyCodePressed(KeyCode code) +{ + switch ( GetBaseButtonCode( code ) ) + { + case KEY_XBUTTON_A: + DoClick(); + break; + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case KEY_XSTICK2_UP: + if(m_pDropDown->IsVisible()) + { + MoveAlongMenuItemList(-1); + } + else + { + BaseClass::OnKeyCodePressed(code); + } + break; + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case KEY_XSTICK2_DOWN: + if(m_pDropDown->IsVisible()) + { + MoveAlongMenuItemList(1); + } + else + { + BaseClass::OnKeyCodePressed(code); + } + break; + default: + BaseClass::OnKeyCodePressed(code); + break; + } +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Handles up/down arrows +//----------------------------------------------------------------------------- +void ComboBox::OnKeyCodeTyped(KeyCode code) +{ + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + + if (alt) + { + switch (code) + { + case KEY_UP: + case KEY_DOWN: + { + DoClick(); + break; + } + default: + { + BaseClass::OnKeyCodeTyped(code); + break; + } + } + } + else + { + switch (code) + { + case KEY_HOME: + case KEY_END: + case KEY_PAGEUP: + case KEY_PAGEDOWN: + case KEY_UP: + case KEY_DOWN: + { + int itemSelected = m_pDropDown->GetCurrentlyHighlightedItem(); + m_pDropDown->OnKeyCodeTyped(code); + int itemToSelect = m_pDropDown->GetCurrentlyHighlightedItem(); + + if ( itemToSelect != itemSelected ) + { + SelectMenuItem(itemToSelect); + } + break; + } + + case KEY_ENTER: + { + int itemToSelect = m_pDropDown->GetCurrentlyHighlightedItem(); + m_pDropDown->ActivateItem(itemToSelect); + break; + } + + default: + { + BaseClass::OnKeyCodeTyped(code); + break; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: handles key input +//----------------------------------------------------------------------------- +void ComboBox::OnKeyTyped(wchar_t unichar) +{ + if ( IsEditable() || unichar == '\t') // don't play with key presses in edit mode + { + BaseClass::OnKeyTyped( unichar ); + return; + } + + int itemSelected = m_pDropDown->GetCurrentlyHighlightedItem(); + m_pDropDown->OnKeyTyped(unichar); + int itemToSelect = m_pDropDown->GetCurrentlyHighlightedItem(); + + if ( itemToSelect != itemSelected ) + { + SelectMenuItem(itemToSelect); + } + else + { + BaseClass::OnKeyTyped( unichar ); + } +} + +void ComboBox::SelectMenuItem(int itemToSelect) +{ + // if we found this item, then we scroll up or down + if ( itemToSelect >= 0 && itemToSelect < m_pDropDown->GetItemCount() ) + { + wchar_t menuItemName[255]; + + int menuID = m_pDropDown->GetMenuID(itemToSelect); + m_pDropDown->GetMenuItem(menuID)->GetText(menuItemName, 254); + OnSetText(menuItemName); + SelectAllText(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ComboBox::MoveAlongMenuItemList(int direction) +{ + // We want the item that is shown in the combo box to show as selected + int itemToSelect = -1; + wchar_t menuItemName[255]; + int i; + + wchar_t comboBoxContents[255]; + GetText(comboBoxContents, 254); + for ( i = 0 ; i < m_pDropDown->GetItemCount() ; i++ ) + { + int menuID = m_pDropDown->GetMenuID(i); + m_pDropDown->GetMenuItem(menuID)->GetText(menuItemName, 254); + + if ( !wcscmp(menuItemName, comboBoxContents) ) + { + itemToSelect = i; + break; + } + } + + if ( itemToSelect >= 0 ) + { + int newItem = itemToSelect + direction; + if ( newItem < 0 ) + { + newItem = 0; + } + else if ( newItem >= m_pDropDown->GetItemCount() ) + { + newItem = m_pDropDown->GetItemCount() - 1; + } + SelectMenuItem(newItem); + } + +} + +void ComboBox::MoveToFirstMenuItem() +{ + SelectMenuItem(0); +} + +void ComboBox::MoveToLastMenuItem() +{ + SelectMenuItem(m_pDropDown->GetItemCount() - 1); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets the direction from the menu button the menu should open +//----------------------------------------------------------------------------- +void ComboBox::SetOpenDirection(Menu::MenuDirection_e direction) +{ + m_iDirection = direction; +} + +void ComboBox::SetFont( HFont font ) +{ + BaseClass::SetFont( font ); + + m_pDropDown->SetFont( font ); +} + + +void ComboBox::SetUseFallbackFont( bool bState, HFont hFallback ) +{ + BaseClass::SetUseFallbackFont( bState, hFallback ); + m_pDropDown->SetUseFallbackFont( bState, hFallback ); +} diff --git a/mp/src/vgui2/vgui_controls/ControllerMap.cpp b/mp/src/vgui2/vgui_controls/ControllerMap.cpp index a0c5d8fb..d4859c11 100644 --- a/mp/src/vgui2/vgui_controls/ControllerMap.cpp +++ b/mp/src/vgui2/vgui_controls/ControllerMap.cpp @@ -1,160 +1,160 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "vgui_controls/ControllerMap.h" -#include "vgui/ISurface.h" -#include "vgui/KeyCode.h" -#include "KeyValues.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -using namespace vgui; - -struct keystring_t -{ - int code; - const char *name; -}; - -static const keystring_t s_ControllerButtons[] = { { KEY_XBUTTON_UP, "KEY_XBUTTON_UP" }, - { KEY_XBUTTON_DOWN, "KEY_XBUTTON_DOWN" }, - { KEY_XBUTTON_LEFT, "KEY_XBUTTON_LEFT" }, - { KEY_XBUTTON_RIGHT, "KEY_XBUTTON_RIGHT" }, - { KEY_XBUTTON_START, "KEY_XBUTTON_START" }, - { KEY_XBUTTON_BACK, "KEY_XBUTTON_BACK" }, - { KEY_XBUTTON_STICK1, "KEY_XBUTTON_STICK1" }, - { KEY_XBUTTON_STICK2, "KEY_XBUTTON_STICK2" }, - { KEY_XBUTTON_A, "KEY_XBUTTON_A" }, - { KEY_XBUTTON_B, "KEY_XBUTTON_B" }, - { KEY_XBUTTON_X, "KEY_XBUTTON_X" }, - { KEY_XBUTTON_Y, "KEY_XBUTTON_Y" }, - { KEY_XBUTTON_LEFT_SHOULDER, "KEY_XBUTTON_LEFT_SHOULDER" }, - { KEY_XBUTTON_RIGHT_SHOULDER, "KEY_XBUTTON_RIGHT_SHOULDER" }, - { KEY_XBUTTON_LTRIGGER, "KEY_XBUTTON_LTRIGGER" }, - { KEY_XBUTTON_RTRIGGER, "KEY_XBUTTON_RTRIGGER" }, - { KEY_XSTICK1_UP, "KEY_XSTICK1_UP" }, - { KEY_XSTICK1_DOWN, "KEY_XSTICK1_DOWN" }, - { KEY_XSTICK1_LEFT, "KEY_XSTICK1_LEFT" }, - { KEY_XSTICK1_RIGHT, "KEY_XSTICK1_RIGHT" }, - { KEY_XSTICK2_UP, "KEY_XSTICK2_UP" }, - { KEY_XSTICK2_DOWN, "KEY_XSTICK2_DOWN" }, - { KEY_XSTICK2_LEFT, "KEY_XSTICK2_LEFT" }, - { KEY_XSTICK2_RIGHT, "KEY_XSTICK2_RIGHT" } }; - -//----------------------------------------------------------------------------- -// Purpose: for the UtlMap -//----------------------------------------------------------------------------- -bool lessFunc( const int &lhs, const int &rhs ) -{ - return lhs < rhs; -} - -//----------------------------------------------------------------------------- -// Purpose: converts a button name string to the equivalent keycode -//----------------------------------------------------------------------------- -int StringToButtonCode( const char *name ) -{ - for ( int i = 0; i < ARRAYSIZE( s_ControllerButtons ); ++i ) - { - if ( !Q_stricmp( s_ControllerButtons[i].name, name ) ) - return s_ControllerButtons[i].code; - } - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: intercepts the keycode from its parent, and handles it according to -// the button map. If the keycode isn't handled, it gets passed on to the parent. -//----------------------------------------------------------------------------- -void CControllerMap::OnKeyCodeTyped( vgui::KeyCode code ) -{ - int idx = m_buttonMap.Find( code ); - if ( idx != m_buttonMap.InvalidIndex() ) - { - GetParent()->OnCommand( m_buttonMap[idx].cmd.String() ); - } - else - { - // Disable input before forwarding the message - // so it doesn't feed back here again. - SetKeyBoardInputEnabled( false ); - GetParent()->OnKeyCodeTyped( code ); - SetKeyBoardInputEnabled( true ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: constructor -//----------------------------------------------------------------------------- -CControllerMap::CControllerMap( vgui::Panel *parent, const char *name ) : BaseClass( parent, name ) -{ - m_buttonMap.SetLessFunc( lessFunc ); -} - -//----------------------------------------------------------------------------- -// Purpose: sets up the button/command bindings -//----------------------------------------------------------------------------- -void CControllerMap::ApplySettings( KeyValues *inResourceData ) -{ - BaseClass::ApplySettings( inResourceData ); - - // loop through all the data adding items to the menu - for (KeyValues *dat = inResourceData->GetFirstSubKey(); dat != NULL; dat = dat->GetNextKey()) - { - if ( !Q_stricmp( dat->GetName(), "button" ) ) - { - const char *buttonName = dat->GetString( "name", "" ); - int keycode = StringToButtonCode( buttonName ); - if ( keycode != -1 ) - { - button_t b; - b.cmd = CUtlSymbol( dat->GetString( "command", "" ) ); - - // text and icon are optional - their existence means this button - // should be displayed in the footer panel. - const char *helpText = dat->GetString( "text", NULL ); - if ( helpText ) - { - b.text = CUtlSymbol( helpText ); - b.icon = CUtlSymbol( dat->GetString( "icon", NULL ) ); - } - - m_buttonMap.Insert( keycode, b ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: gets the help text for a binding, if it exists -//----------------------------------------------------------------------------- -const char *CControllerMap::GetBindingText( int idx ) -{ - CUtlSymbol s = m_buttonMap[idx].text; - if ( s.IsValid() ) - { - return s.String(); - } - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: gets the icon for a binding, if it exists -//----------------------------------------------------------------------------- -const char *CControllerMap::GetBindingIcon( int idx ) -{ - CUtlSymbol s = m_buttonMap[idx].icon; - if ( s.IsValid() ) - { - return s.String(); - } - return NULL; -} - -DECLARE_BUILD_FACTORY( CControllerMap ); - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "vgui_controls/ControllerMap.h" +#include "vgui/ISurface.h" +#include "vgui/KeyCode.h" +#include "KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +struct keystring_t +{ + int code; + const char *name; +}; + +static const keystring_t s_ControllerButtons[] = { { KEY_XBUTTON_UP, "KEY_XBUTTON_UP" }, + { KEY_XBUTTON_DOWN, "KEY_XBUTTON_DOWN" }, + { KEY_XBUTTON_LEFT, "KEY_XBUTTON_LEFT" }, + { KEY_XBUTTON_RIGHT, "KEY_XBUTTON_RIGHT" }, + { KEY_XBUTTON_START, "KEY_XBUTTON_START" }, + { KEY_XBUTTON_BACK, "KEY_XBUTTON_BACK" }, + { KEY_XBUTTON_STICK1, "KEY_XBUTTON_STICK1" }, + { KEY_XBUTTON_STICK2, "KEY_XBUTTON_STICK2" }, + { KEY_XBUTTON_A, "KEY_XBUTTON_A" }, + { KEY_XBUTTON_B, "KEY_XBUTTON_B" }, + { KEY_XBUTTON_X, "KEY_XBUTTON_X" }, + { KEY_XBUTTON_Y, "KEY_XBUTTON_Y" }, + { KEY_XBUTTON_LEFT_SHOULDER, "KEY_XBUTTON_LEFT_SHOULDER" }, + { KEY_XBUTTON_RIGHT_SHOULDER, "KEY_XBUTTON_RIGHT_SHOULDER" }, + { KEY_XBUTTON_LTRIGGER, "KEY_XBUTTON_LTRIGGER" }, + { KEY_XBUTTON_RTRIGGER, "KEY_XBUTTON_RTRIGGER" }, + { KEY_XSTICK1_UP, "KEY_XSTICK1_UP" }, + { KEY_XSTICK1_DOWN, "KEY_XSTICK1_DOWN" }, + { KEY_XSTICK1_LEFT, "KEY_XSTICK1_LEFT" }, + { KEY_XSTICK1_RIGHT, "KEY_XSTICK1_RIGHT" }, + { KEY_XSTICK2_UP, "KEY_XSTICK2_UP" }, + { KEY_XSTICK2_DOWN, "KEY_XSTICK2_DOWN" }, + { KEY_XSTICK2_LEFT, "KEY_XSTICK2_LEFT" }, + { KEY_XSTICK2_RIGHT, "KEY_XSTICK2_RIGHT" } }; + +//----------------------------------------------------------------------------- +// Purpose: for the UtlMap +//----------------------------------------------------------------------------- +bool lessFunc( const int &lhs, const int &rhs ) +{ + return lhs < rhs; +} + +//----------------------------------------------------------------------------- +// Purpose: converts a button name string to the equivalent keycode +//----------------------------------------------------------------------------- +int StringToButtonCode( const char *name ) +{ + for ( int i = 0; i < ARRAYSIZE( s_ControllerButtons ); ++i ) + { + if ( !Q_stricmp( s_ControllerButtons[i].name, name ) ) + return s_ControllerButtons[i].code; + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: intercepts the keycode from its parent, and handles it according to +// the button map. If the keycode isn't handled, it gets passed on to the parent. +//----------------------------------------------------------------------------- +void CControllerMap::OnKeyCodeTyped( vgui::KeyCode code ) +{ + int idx = m_buttonMap.Find( code ); + if ( idx != m_buttonMap.InvalidIndex() ) + { + GetParent()->OnCommand( m_buttonMap[idx].cmd.String() ); + } + else + { + // Disable input before forwarding the message + // so it doesn't feed back here again. + SetKeyBoardInputEnabled( false ); + GetParent()->OnKeyCodeTyped( code ); + SetKeyBoardInputEnabled( true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CControllerMap::CControllerMap( vgui::Panel *parent, const char *name ) : BaseClass( parent, name ) +{ + m_buttonMap.SetLessFunc( lessFunc ); +} + +//----------------------------------------------------------------------------- +// Purpose: sets up the button/command bindings +//----------------------------------------------------------------------------- +void CControllerMap::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + // loop through all the data adding items to the menu + for (KeyValues *dat = inResourceData->GetFirstSubKey(); dat != NULL; dat = dat->GetNextKey()) + { + if ( !Q_stricmp( dat->GetName(), "button" ) ) + { + const char *buttonName = dat->GetString( "name", "" ); + int keycode = StringToButtonCode( buttonName ); + if ( keycode != -1 ) + { + button_t b; + b.cmd = CUtlSymbol( dat->GetString( "command", "" ) ); + + // text and icon are optional - their existence means this button + // should be displayed in the footer panel. + const char *helpText = dat->GetString( "text", NULL ); + if ( helpText ) + { + b.text = CUtlSymbol( helpText ); + b.icon = CUtlSymbol( dat->GetString( "icon", NULL ) ); + } + + m_buttonMap.Insert( keycode, b ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: gets the help text for a binding, if it exists +//----------------------------------------------------------------------------- +const char *CControllerMap::GetBindingText( int idx ) +{ + CUtlSymbol s = m_buttonMap[idx].text; + if ( s.IsValid() ) + { + return s.String(); + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the icon for a binding, if it exists +//----------------------------------------------------------------------------- +const char *CControllerMap::GetBindingIcon( int idx ) +{ + CUtlSymbol s = m_buttonMap[idx].icon; + if ( s.IsValid() ) + { + return s.String(); + } + return NULL; +} + +DECLARE_BUILD_FACTORY( CControllerMap ); + diff --git a/mp/src/vgui2/vgui_controls/DirectorySelectDialog.cpp b/mp/src/vgui2/vgui_controls/DirectorySelectDialog.cpp index a7474fb1..0e9eac52 100644 --- a/mp/src/vgui2/vgui_controls/DirectorySelectDialog.cpp +++ b/mp/src/vgui2/vgui_controls/DirectorySelectDialog.cpp @@ -1,594 +1,594 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#define PROTECTED_THINGS_DISABLE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef WIN32 -#include -#include -#include -#endif -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DirectoryTreeView::DirectoryTreeView(DirectorySelectDialog *parent, const char *name) : TreeView(parent, name) -{ - m_pParent = parent; -} - -void DirectoryTreeView::GenerateChildrenOfNode(int itemIndex) -{ - m_pParent->GenerateChildrenOfDirectoryNode(itemIndex); -} - -//----------------------------------------------------------------------------- -// Purpose: Used to prompt the user to create a directory -//----------------------------------------------------------------------------- -class CreateDirectoryDialog : public Frame -{ - DECLARE_CLASS_SIMPLE(CreateDirectoryDialog, Frame); - -public: - CreateDirectoryDialog(Panel *parent, const char *defaultCreateDirName) : BaseClass(parent, NULL) - { - SetSize(320, 100); - SetSizeable(false); - SetTitle("Choose directory name", false); - MoveToCenterOfScreen(); - - m_pOKButton = new Button(this, "OKButton", "#vgui_ok"); - m_pCancelButton = new Button(this, "OKButton", "#vgui_cancel"); - m_pNameEntry = new TextEntry(this, "NameEntry"); - - m_pOKButton->SetCommand("OK"); - m_pCancelButton->SetCommand("Close"); - m_pNameEntry->SetText(defaultCreateDirName); - m_pNameEntry->RequestFocus(); - m_pNameEntry->SelectAllText(true); - - // If some other window was hogging the input focus, then we have to hog it or else we'll never get input. - m_PrevAppFocusPanel = vgui::input()->GetAppModalSurface(); - if ( m_PrevAppFocusPanel ) - vgui::input()->SetAppModalSurface( GetVPanel() ); - } - - ~CreateDirectoryDialog() - { - if ( m_PrevAppFocusPanel ) - vgui::input()->SetAppModalSurface( m_PrevAppFocusPanel ); - } - - virtual void PerformLayout() - { - BaseClass::PerformLayout(); - - m_pNameEntry->SetBounds(24, 32, GetWide() - 48, 24); - m_pOKButton->SetBounds(GetWide() - 176, 64, 72, 24); - m_pCancelButton->SetBounds(GetWide() - 94, 64, 72, 24); - } - - virtual void OnCommand(const char *command) - { - if (!stricmp(command, "OK")) - { - PostActionSignal(new KeyValues("CreateDirectory", "dir", GetControlString("NameEntry"))); - Close(); - } - else - { - BaseClass::OnCommand(command); - } - } - - virtual void OnClose() - { - BaseClass::OnClose(); - MarkForDeletion(); - } - -private: - vgui::Button *m_pOKButton; - vgui::Button *m_pCancelButton; - vgui::TextEntry *m_pNameEntry; - vgui::VPANEL m_PrevAppFocusPanel; -}; - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -DirectorySelectDialog::DirectorySelectDialog(vgui::Panel *parent, const char *title) : Frame(parent, NULL) -{ - SetTitle(title, true); - SetSize(320, 360); - SetMinimumSize(300, 240); - m_szCurrentDir[0] = 0; - m_szDefaultCreateDirName[0] = 0; - - m_pDirTree = new DirectoryTreeView(this, "DirTree"); - m_pDriveCombo = new ComboBox(this, "DriveCombo", 6, false); - m_pCancelButton = new Button(this, "CancelButton", "#VGui_Cancel"); - m_pSelectButton = new Button(this, "SelectButton", "#VGui_Select"); - m_pCreateButton = new Button(this, "CreateButton", "#VGui_CreateFolder"); - m_pCancelButton->SetCommand("Cancel"); - m_pSelectButton->SetCommand("Select"); - m_pCreateButton->SetCommand("Create"); -} - -//----------------------------------------------------------------------------- -// Purpose: lays out controls -//----------------------------------------------------------------------------- -void DirectorySelectDialog::PerformLayout() -{ - BaseClass::PerformLayout(); - - // lay out all the controls - m_pDriveCombo->SetBounds(24, 30, GetWide() - 48, 24); - m_pDirTree->SetBounds(24, 64, GetWide() - 48, GetTall() - 128); - - m_pCreateButton->SetBounds(24, GetTall() - 48, 104, 24); - m_pSelectButton->SetBounds(GetWide() - 172, GetTall() - 48, 72, 24); - m_pCancelButton->SetBounds(GetWide() - 96, GetTall() - 48, 72, 24); -} - -//----------------------------------------------------------------------------- -// Purpose: lays out controls -//----------------------------------------------------------------------------- -void DirectorySelectDialog::ApplySchemeSettings(IScheme *pScheme) -{ - ImageList *imageList = new ImageList(false); - imageList->AddImage(scheme()->GetImage("Resource/icon_folder", false)); - imageList->AddImage(scheme()->GetImage("Resource/icon_folder_selected", false)); - m_pDirTree->SetImageList(imageList, true); - - BaseClass::ApplySchemeSettings(pScheme); -} - -//----------------------------------------------------------------------------- -// Purpose: Move the start string forward until we hit a slash and return the -// the first character past the trailing slash -//----------------------------------------------------------------------------- -inline const char *MoveToNextSubDir( const char *pStart, int *nCount ) -{ - int nMoved = 0; - - // Move past pre-pended slash - if ( pStart[nMoved] == '\\' ) - { - nMoved++; - } - - // Move past the current block of text until we've hit the next path seperator (or end) - while ( pStart[nMoved] != '\\' && pStart[nMoved] != '\0' ) - { - nMoved++; - } - - // Move past trailing slash - if ( pStart[nMoved] == '\\' ) - { - nMoved++; - } - - // Give back a count if they've supplied a pointer - if ( nCount != NULL ) - { - *nCount = nMoved; - } - - // The beginning of the next string, past slash - return (pStart+nMoved); -} - -//----------------------------------------------------------------------------- -// Purpose: Walk through our directory structure given a path as our guide, while expanding -// and populating the nodes of the tree view to match -// Input : *path - path (with drive letter) to show -//----------------------------------------------------------------------------- -void DirectorySelectDialog::ExpandTreeToPath( const char *lpszPath, bool bSelectFinalDirectory /*= true*/ ) -{ - // Make sure our slashes are correct! - char workPath[MAX_PATH]; - Q_strncpy( workPath, lpszPath, sizeof(workPath) ); - Q_FixSlashes( workPath ); - - // Set us to the work drive - SetStartDirectory( workPath ); - - // Check that the path is valid - if ( workPath[0] == '\0' || DoesDirectoryHaveSubdirectories( m_szCurrentDrive, "" ) == false ) - { - // Failing, start in C: - SetStartDirectory( "C:\\" ); - } - - // Start at the root of our tree - int nItemIndex = m_pDirTree->GetRootItemIndex(); - - // Move past the drive letter to the first subdir - int nPathPos = 0; - const char *lpszSubDirName = MoveToNextSubDir( workPath, &nPathPos ); - const char *lpszLastSubDirName = NULL; - int nPathIncr = 0; - char subDirName[MAX_PATH]; - - // While there are subdirectory names present, expand and populate the tree with their subdirectories - while ( lpszSubDirName[0] != '\0' ) - { - // Move our string pointer forward while keeping where our last subdir started off - lpszLastSubDirName = lpszSubDirName; - lpszSubDirName = MoveToNextSubDir( lpszSubDirName, &nPathIncr ); - - // Get the span between the last subdir and the new one - Q_StrLeft( lpszLastSubDirName, nPathIncr, subDirName, sizeof(subDirName) ); - Q_StripTrailingSlash( subDirName ); - - // Increment where we are in the string for use later - nPathPos += nPathIncr; - - // Run through the list and expand to our currently selected directory - for ( int i = 0; i < m_pDirTree->GetNumChildren( nItemIndex ); i++ ) - { - // Get the child and data for it - int nChild = m_pDirTree->GetChild( nItemIndex, i ); - KeyValues *pValues = m_pDirTree->GetItemData( nChild ); - - // See if this matches - if ( Q_stricmp( pValues->GetString( "Text" ), subDirName ) == 0 ) - { - // This is the new root item - nItemIndex = nChild; - - // Get the full path (starting from the drive letter) up to our current subdir - Q_strncpy( subDirName, workPath, nPathPos ); - Q_AppendSlash( subDirName, sizeof(subDirName) ); - - // Expand the tree node and populate its subdirs for our next iteration - ExpandTreeNode( subDirName, nItemIndex ); - break; - } - } - } - - // Select our last directory if we've been asked to (and it's valid) - if ( bSelectFinalDirectory && m_pDirTree->IsItemIDValid( nItemIndex ) ) - { - // If we don't call this once before selecting an item, the tree will not be properly expanded - // before it calculates how to show the selected item in the view - PerformLayout(); - - // Select that item - m_pDirTree->AddSelectedItem( nItemIndex, true ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets where it should start searching -//----------------------------------------------------------------------------- -void DirectorySelectDialog::SetStartDirectory(const char *path) -{ - strncpy(m_szCurrentDir, path, sizeof(m_szCurrentDir)); - strncpy(m_szCurrentDrive, path, sizeof(m_szCurrentDrive)); - m_szCurrentDrive[sizeof(m_szCurrentDrive) - 1] = 0; - char *firstSlash = strstr(m_szCurrentDrive, "\\"); - if (firstSlash) - { - firstSlash[1] = 0; - } - - BuildDirTree(); - BuildDriveChoices(); - - // update state of create directory button - int selectedIndex = m_pDirTree->GetFirstSelectedItem(); - if (m_pDirTree->IsItemIDValid(selectedIndex)) - { - m_pCreateButton->SetEnabled(true); - } - else - { - m_pCreateButton->SetEnabled(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets what name should show up by default in the create directory dialog -//----------------------------------------------------------------------------- -void DirectorySelectDialog::SetDefaultCreateDirectoryName(const char *defaultCreateDirName) -{ - strncpy(m_szDefaultCreateDirName, defaultCreateDirName, sizeof(m_szDefaultCreateDirName)); - m_szDefaultCreateDirName[sizeof(m_szDefaultCreateDirName) - 1] = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: opens the dialog -//----------------------------------------------------------------------------- -void DirectorySelectDialog::DoModal() -{ - input()->SetAppModalSurface(GetVPanel()); - BaseClass::Activate(); - MoveToCenterOfScreen(); -} - -//----------------------------------------------------------------------------- -// Purpose: Builds drive choices -//----------------------------------------------------------------------------- -void DirectorySelectDialog::BuildDriveChoices() -{ - m_pDriveCombo->DeleteAllItems(); - - char drives[256] = { 0 }; - int len = system()->GetAvailableDrives(drives, sizeof(drives)); - char *pBuf = drives; - KeyValues *kv = new KeyValues("drive"); - for (int i = 0; i < len / 4; i++) - { - kv->SetString("drive", pBuf); - int itemID = m_pDriveCombo->AddItem(pBuf, kv); - if (!stricmp(pBuf, m_szCurrentDrive)) - { - m_pDriveCombo->ActivateItem(itemID); - } - - pBuf += 4; - } - kv->deleteThis(); -} - -//----------------------------------------------------------------------------- -// Purpose: Builds the base tree directory -//----------------------------------------------------------------------------- -void DirectorySelectDialog::BuildDirTree() -{ - // clear current tree - m_pDirTree->RemoveAll(); - - // add in a root - int rootIndex = m_pDirTree->AddItem(new KeyValues("root", "Text", m_szCurrentDrive), -1); - - // build first level of the tree - ExpandTreeNode(m_szCurrentDrive, rootIndex); - - // start the root expanded - m_pDirTree->ExpandItem(rootIndex, true); -} - -//----------------------------------------------------------------------------- -// Purpose: expands a path -//----------------------------------------------------------------------------- -void DirectorySelectDialog::ExpandTreeNode(const char *path, int parentNodeIndex) -{ - // set the small wait cursor - surface()->SetCursor(dc_waitarrow); - - // get all the subfolders of the current drive - char searchString[512]; - sprintf(searchString, "%s*.*", path); - - FileFindHandle_t h; - const char *pFileName = g_pFullFileSystem->FindFirstEx( searchString, NULL, &h ); - for ( ; pFileName; pFileName = g_pFullFileSystem->FindNext( h ) ) - { - if ( !Q_stricmp( pFileName, ".." ) || !Q_stricmp( pFileName, "." ) ) - continue; - - KeyValues *kv = new KeyValues("item"); - kv->SetString("Text", pFileName); - // set the folder image - kv->SetInt("Image", 1); - kv->SetInt("SelectedImage", 1); - kv->SetInt("Expand", DoesDirectoryHaveSubdirectories(path, pFileName)); - m_pDirTree->AddItem(kv, parentNodeIndex); - } - g_pFullFileSystem->FindClose( h ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool DirectorySelectDialog::DoesDirectoryHaveSubdirectories(const char *path, const char *dir) -{ - char searchString[512]; - sprintf(searchString, "%s%s\\*.*", path, dir); - - FileFindHandle_t h; - const char *pFileName = g_pFullFileSystem->FindFirstEx( searchString, NULL, &h ); - for ( ; pFileName; pFileName = g_pFullFileSystem->FindNext( h ) ) - { - char szFullPath[ MAX_PATH ]; - Q_snprintf( szFullPath, sizeof(szFullPath), "%s\\%s", path, pFileName ); - Q_FixSlashes( szFullPath ); - if ( g_pFullFileSystem->IsDirectory( szFullPath ) ) - { - g_pFullFileSystem->FindClose( h ); - return true; - } - } - g_pFullFileSystem->FindClose( h ); - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Generates the children for the specified node -//----------------------------------------------------------------------------- -void DirectorySelectDialog::GenerateChildrenOfDirectoryNode(int nodeIndex) -{ - // generate path - char path[512]; - GenerateFullPathForNode(nodeIndex, path, sizeof(path)); - - // expand out - ExpandTreeNode(path, nodeIndex); -} - -//----------------------------------------------------------------------------- -// Purpose: creates the full path for a node -//----------------------------------------------------------------------------- -void DirectorySelectDialog::GenerateFullPathForNode(int nodeIndex, char *path, int pathBufferSize) -{ - // get all the nodes - CUtlLinkedList nodes; - nodes.AddToTail(nodeIndex); - int parentIndex = nodeIndex; - while (1) - { - parentIndex = m_pDirTree->GetItemParent(parentIndex); - if (parentIndex == -1) - break; - nodes.AddToHead(parentIndex); - } - - // walk the nodes, adding to the path - path[0] = 0; - bool bFirst = true; - FOR_EACH_LL( nodes, i ) - { - KeyValues *kv = m_pDirTree->GetItemData( nodes[i] ); - strcat(path, kv->GetString("Text")); - - if (!bFirst) - { - strcat(path, "\\"); - } - bFirst = false; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handles combo box changes -//----------------------------------------------------------------------------- -void DirectorySelectDialog::OnTextChanged() -{ - KeyValues *kv = m_pDriveCombo->GetActiveItemUserData(); - if (!kv) - return; - const char *newDrive = kv->GetString("drive"); - if (stricmp(newDrive, m_szCurrentDrive)) - { - // drive changed, reset - SetStartDirectory(newDrive); - } -} - -//----------------------------------------------------------------------------- -// Purpose: creates a directory -//----------------------------------------------------------------------------- -void DirectorySelectDialog::OnCreateDirectory(const char *dir) -{ - int selectedIndex = m_pDirTree->GetFirstSelectedItem(); - if (m_pDirTree->IsItemIDValid(selectedIndex)) - { - char fullPath[512]; - GenerateFullPathForNode(selectedIndex, fullPath, sizeof(fullPath)); - - // create the new directory underneath - strcat(fullPath, dir); - if (_mkdir(fullPath) == 0) - { - // add new path to tree view - KeyValues *kv = new KeyValues("item"); - kv->SetString("Text", dir); - // set the folder image - kv->SetInt("Image", 1); - kv->SetInt("SelectedImage", 1); - int itemID = m_pDirTree->AddItem(kv, selectedIndex); - - // select the item - m_pDirTree->AddSelectedItem( itemID, true ); - } - else - { - // print error message - MessageBox *box = new MessageBox("#vgui_CreateDirectoryFail_Title", "#vgui_CreateDirectoryFail_Info"); - box->DoModal(this); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: dialog closes -//----------------------------------------------------------------------------- -void DirectorySelectDialog::OnClose() -{ - BaseClass::OnClose(); - MarkForDeletion(); -} - -//----------------------------------------------------------------------------- -// Purpose: handles button commands -//----------------------------------------------------------------------------- -void DirectorySelectDialog::OnCommand(const char *command) -{ - if (!stricmp(command, "Cancel")) - { - Close(); - } - else if (!stricmp(command, "Select")) - { - // path selected - int selectedIndex = m_pDirTree->GetFirstSelectedItem(); - if (m_pDirTree->IsItemIDValid(selectedIndex)) - { - char fullPath[512]; - GenerateFullPathForNode(selectedIndex, fullPath, sizeof(fullPath)); - PostActionSignal(new KeyValues("DirectorySelected", "dir", fullPath)); - Close(); - } - } - else if (!stricmp(command, "Create")) - { - int selectedIndex = m_pDirTree->GetFirstSelectedItem(); - if (m_pDirTree->IsItemIDValid(selectedIndex)) - { - CreateDirectoryDialog *dlg = new CreateDirectoryDialog(this, m_szDefaultCreateDirName); - dlg->AddActionSignalTarget(this); - dlg->Activate(); - } - } - else - { - BaseClass::OnCommand(command); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Update the text in the combo -//----------------------------------------------------------------------------- -void DirectorySelectDialog::OnTreeViewItemSelected() -{ - int selectedIndex = m_pDirTree->GetFirstSelectedItem(); - if (!m_pDirTree->IsItemIDValid(selectedIndex)) - { - m_pCreateButton->SetEnabled(false); - return; - } - m_pCreateButton->SetEnabled(true); - - // build the string - char fullPath[512]; - GenerateFullPathForNode(selectedIndex, fullPath, sizeof(fullPath)); - - int itemID = m_pDriveCombo->GetActiveItem(); - m_pDriveCombo->UpdateItem(itemID, fullPath, NULL); - m_pDriveCombo->SetText(fullPath); +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#define PROTECTED_THINGS_DISABLE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#include +#include +#endif +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DirectoryTreeView::DirectoryTreeView(DirectorySelectDialog *parent, const char *name) : TreeView(parent, name) +{ + m_pParent = parent; +} + +void DirectoryTreeView::GenerateChildrenOfNode(int itemIndex) +{ + m_pParent->GenerateChildrenOfDirectoryNode(itemIndex); +} + +//----------------------------------------------------------------------------- +// Purpose: Used to prompt the user to create a directory +//----------------------------------------------------------------------------- +class CreateDirectoryDialog : public Frame +{ + DECLARE_CLASS_SIMPLE(CreateDirectoryDialog, Frame); + +public: + CreateDirectoryDialog(Panel *parent, const char *defaultCreateDirName) : BaseClass(parent, NULL) + { + SetSize(320, 100); + SetSizeable(false); + SetTitle("Choose directory name", false); + MoveToCenterOfScreen(); + + m_pOKButton = new Button(this, "OKButton", "#vgui_ok"); + m_pCancelButton = new Button(this, "OKButton", "#vgui_cancel"); + m_pNameEntry = new TextEntry(this, "NameEntry"); + + m_pOKButton->SetCommand("OK"); + m_pCancelButton->SetCommand("Close"); + m_pNameEntry->SetText(defaultCreateDirName); + m_pNameEntry->RequestFocus(); + m_pNameEntry->SelectAllText(true); + + // If some other window was hogging the input focus, then we have to hog it or else we'll never get input. + m_PrevAppFocusPanel = vgui::input()->GetAppModalSurface(); + if ( m_PrevAppFocusPanel ) + vgui::input()->SetAppModalSurface( GetVPanel() ); + } + + ~CreateDirectoryDialog() + { + if ( m_PrevAppFocusPanel ) + vgui::input()->SetAppModalSurface( m_PrevAppFocusPanel ); + } + + virtual void PerformLayout() + { + BaseClass::PerformLayout(); + + m_pNameEntry->SetBounds(24, 32, GetWide() - 48, 24); + m_pOKButton->SetBounds(GetWide() - 176, 64, 72, 24); + m_pCancelButton->SetBounds(GetWide() - 94, 64, 72, 24); + } + + virtual void OnCommand(const char *command) + { + if (!stricmp(command, "OK")) + { + PostActionSignal(new KeyValues("CreateDirectory", "dir", GetControlString("NameEntry"))); + Close(); + } + else + { + BaseClass::OnCommand(command); + } + } + + virtual void OnClose() + { + BaseClass::OnClose(); + MarkForDeletion(); + } + +private: + vgui::Button *m_pOKButton; + vgui::Button *m_pCancelButton; + vgui::TextEntry *m_pNameEntry; + vgui::VPANEL m_PrevAppFocusPanel; +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +DirectorySelectDialog::DirectorySelectDialog(vgui::Panel *parent, const char *title) : Frame(parent, NULL) +{ + SetTitle(title, true); + SetSize(320, 360); + SetMinimumSize(300, 240); + m_szCurrentDir[0] = 0; + m_szDefaultCreateDirName[0] = 0; + + m_pDirTree = new DirectoryTreeView(this, "DirTree"); + m_pDriveCombo = new ComboBox(this, "DriveCombo", 6, false); + m_pCancelButton = new Button(this, "CancelButton", "#VGui_Cancel"); + m_pSelectButton = new Button(this, "SelectButton", "#VGui_Select"); + m_pCreateButton = new Button(this, "CreateButton", "#VGui_CreateFolder"); + m_pCancelButton->SetCommand("Cancel"); + m_pSelectButton->SetCommand("Select"); + m_pCreateButton->SetCommand("Create"); +} + +//----------------------------------------------------------------------------- +// Purpose: lays out controls +//----------------------------------------------------------------------------- +void DirectorySelectDialog::PerformLayout() +{ + BaseClass::PerformLayout(); + + // lay out all the controls + m_pDriveCombo->SetBounds(24, 30, GetWide() - 48, 24); + m_pDirTree->SetBounds(24, 64, GetWide() - 48, GetTall() - 128); + + m_pCreateButton->SetBounds(24, GetTall() - 48, 104, 24); + m_pSelectButton->SetBounds(GetWide() - 172, GetTall() - 48, 72, 24); + m_pCancelButton->SetBounds(GetWide() - 96, GetTall() - 48, 72, 24); +} + +//----------------------------------------------------------------------------- +// Purpose: lays out controls +//----------------------------------------------------------------------------- +void DirectorySelectDialog::ApplySchemeSettings(IScheme *pScheme) +{ + ImageList *imageList = new ImageList(false); + imageList->AddImage(scheme()->GetImage("Resource/icon_folder", false)); + imageList->AddImage(scheme()->GetImage("Resource/icon_folder_selected", false)); + m_pDirTree->SetImageList(imageList, true); + + BaseClass::ApplySchemeSettings(pScheme); +} + +//----------------------------------------------------------------------------- +// Purpose: Move the start string forward until we hit a slash and return the +// the first character past the trailing slash +//----------------------------------------------------------------------------- +inline const char *MoveToNextSubDir( const char *pStart, int *nCount ) +{ + int nMoved = 0; + + // Move past pre-pended slash + if ( pStart[nMoved] == '\\' ) + { + nMoved++; + } + + // Move past the current block of text until we've hit the next path seperator (or end) + while ( pStart[nMoved] != '\\' && pStart[nMoved] != '\0' ) + { + nMoved++; + } + + // Move past trailing slash + if ( pStart[nMoved] == '\\' ) + { + nMoved++; + } + + // Give back a count if they've supplied a pointer + if ( nCount != NULL ) + { + *nCount = nMoved; + } + + // The beginning of the next string, past slash + return (pStart+nMoved); +} + +//----------------------------------------------------------------------------- +// Purpose: Walk through our directory structure given a path as our guide, while expanding +// and populating the nodes of the tree view to match +// Input : *path - path (with drive letter) to show +//----------------------------------------------------------------------------- +void DirectorySelectDialog::ExpandTreeToPath( const char *lpszPath, bool bSelectFinalDirectory /*= true*/ ) +{ + // Make sure our slashes are correct! + char workPath[MAX_PATH]; + Q_strncpy( workPath, lpszPath, sizeof(workPath) ); + Q_FixSlashes( workPath ); + + // Set us to the work drive + SetStartDirectory( workPath ); + + // Check that the path is valid + if ( workPath[0] == '\0' || DoesDirectoryHaveSubdirectories( m_szCurrentDrive, "" ) == false ) + { + // Failing, start in C: + SetStartDirectory( "C:\\" ); + } + + // Start at the root of our tree + int nItemIndex = m_pDirTree->GetRootItemIndex(); + + // Move past the drive letter to the first subdir + int nPathPos = 0; + const char *lpszSubDirName = MoveToNextSubDir( workPath, &nPathPos ); + const char *lpszLastSubDirName = NULL; + int nPathIncr = 0; + char subDirName[MAX_PATH]; + + // While there are subdirectory names present, expand and populate the tree with their subdirectories + while ( lpszSubDirName[0] != '\0' ) + { + // Move our string pointer forward while keeping where our last subdir started off + lpszLastSubDirName = lpszSubDirName; + lpszSubDirName = MoveToNextSubDir( lpszSubDirName, &nPathIncr ); + + // Get the span between the last subdir and the new one + Q_StrLeft( lpszLastSubDirName, nPathIncr, subDirName, sizeof(subDirName) ); + Q_StripTrailingSlash( subDirName ); + + // Increment where we are in the string for use later + nPathPos += nPathIncr; + + // Run through the list and expand to our currently selected directory + for ( int i = 0; i < m_pDirTree->GetNumChildren( nItemIndex ); i++ ) + { + // Get the child and data for it + int nChild = m_pDirTree->GetChild( nItemIndex, i ); + KeyValues *pValues = m_pDirTree->GetItemData( nChild ); + + // See if this matches + if ( Q_stricmp( pValues->GetString( "Text" ), subDirName ) == 0 ) + { + // This is the new root item + nItemIndex = nChild; + + // Get the full path (starting from the drive letter) up to our current subdir + Q_strncpy( subDirName, workPath, nPathPos ); + Q_AppendSlash( subDirName, sizeof(subDirName) ); + + // Expand the tree node and populate its subdirs for our next iteration + ExpandTreeNode( subDirName, nItemIndex ); + break; + } + } + } + + // Select our last directory if we've been asked to (and it's valid) + if ( bSelectFinalDirectory && m_pDirTree->IsItemIDValid( nItemIndex ) ) + { + // If we don't call this once before selecting an item, the tree will not be properly expanded + // before it calculates how to show the selected item in the view + PerformLayout(); + + // Select that item + m_pDirTree->AddSelectedItem( nItemIndex, true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets where it should start searching +//----------------------------------------------------------------------------- +void DirectorySelectDialog::SetStartDirectory(const char *path) +{ + strncpy(m_szCurrentDir, path, sizeof(m_szCurrentDir)); + strncpy(m_szCurrentDrive, path, sizeof(m_szCurrentDrive)); + m_szCurrentDrive[sizeof(m_szCurrentDrive) - 1] = 0; + char *firstSlash = strstr(m_szCurrentDrive, "\\"); + if (firstSlash) + { + firstSlash[1] = 0; + } + + BuildDirTree(); + BuildDriveChoices(); + + // update state of create directory button + int selectedIndex = m_pDirTree->GetFirstSelectedItem(); + if (m_pDirTree->IsItemIDValid(selectedIndex)) + { + m_pCreateButton->SetEnabled(true); + } + else + { + m_pCreateButton->SetEnabled(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets what name should show up by default in the create directory dialog +//----------------------------------------------------------------------------- +void DirectorySelectDialog::SetDefaultCreateDirectoryName(const char *defaultCreateDirName) +{ + strncpy(m_szDefaultCreateDirName, defaultCreateDirName, sizeof(m_szDefaultCreateDirName)); + m_szDefaultCreateDirName[sizeof(m_szDefaultCreateDirName) - 1] = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: opens the dialog +//----------------------------------------------------------------------------- +void DirectorySelectDialog::DoModal() +{ + input()->SetAppModalSurface(GetVPanel()); + BaseClass::Activate(); + MoveToCenterOfScreen(); +} + +//----------------------------------------------------------------------------- +// Purpose: Builds drive choices +//----------------------------------------------------------------------------- +void DirectorySelectDialog::BuildDriveChoices() +{ + m_pDriveCombo->DeleteAllItems(); + + char drives[256] = { 0 }; + int len = system()->GetAvailableDrives(drives, sizeof(drives)); + char *pBuf = drives; + KeyValues *kv = new KeyValues("drive"); + for (int i = 0; i < len / 4; i++) + { + kv->SetString("drive", pBuf); + int itemID = m_pDriveCombo->AddItem(pBuf, kv); + if (!stricmp(pBuf, m_szCurrentDrive)) + { + m_pDriveCombo->ActivateItem(itemID); + } + + pBuf += 4; + } + kv->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: Builds the base tree directory +//----------------------------------------------------------------------------- +void DirectorySelectDialog::BuildDirTree() +{ + // clear current tree + m_pDirTree->RemoveAll(); + + // add in a root + int rootIndex = m_pDirTree->AddItem(new KeyValues("root", "Text", m_szCurrentDrive), -1); + + // build first level of the tree + ExpandTreeNode(m_szCurrentDrive, rootIndex); + + // start the root expanded + m_pDirTree->ExpandItem(rootIndex, true); +} + +//----------------------------------------------------------------------------- +// Purpose: expands a path +//----------------------------------------------------------------------------- +void DirectorySelectDialog::ExpandTreeNode(const char *path, int parentNodeIndex) +{ + // set the small wait cursor + surface()->SetCursor(dc_waitarrow); + + // get all the subfolders of the current drive + char searchString[512]; + sprintf(searchString, "%s*.*", path); + + FileFindHandle_t h; + const char *pFileName = g_pFullFileSystem->FindFirstEx( searchString, NULL, &h ); + for ( ; pFileName; pFileName = g_pFullFileSystem->FindNext( h ) ) + { + if ( !Q_stricmp( pFileName, ".." ) || !Q_stricmp( pFileName, "." ) ) + continue; + + KeyValues *kv = new KeyValues("item"); + kv->SetString("Text", pFileName); + // set the folder image + kv->SetInt("Image", 1); + kv->SetInt("SelectedImage", 1); + kv->SetInt("Expand", DoesDirectoryHaveSubdirectories(path, pFileName)); + m_pDirTree->AddItem(kv, parentNodeIndex); + } + g_pFullFileSystem->FindClose( h ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool DirectorySelectDialog::DoesDirectoryHaveSubdirectories(const char *path, const char *dir) +{ + char searchString[512]; + sprintf(searchString, "%s%s\\*.*", path, dir); + + FileFindHandle_t h; + const char *pFileName = g_pFullFileSystem->FindFirstEx( searchString, NULL, &h ); + for ( ; pFileName; pFileName = g_pFullFileSystem->FindNext( h ) ) + { + char szFullPath[ MAX_PATH ]; + Q_snprintf( szFullPath, sizeof(szFullPath), "%s\\%s", path, pFileName ); + Q_FixSlashes( szFullPath ); + if ( g_pFullFileSystem->IsDirectory( szFullPath ) ) + { + g_pFullFileSystem->FindClose( h ); + return true; + } + } + g_pFullFileSystem->FindClose( h ); + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Generates the children for the specified node +//----------------------------------------------------------------------------- +void DirectorySelectDialog::GenerateChildrenOfDirectoryNode(int nodeIndex) +{ + // generate path + char path[512]; + GenerateFullPathForNode(nodeIndex, path, sizeof(path)); + + // expand out + ExpandTreeNode(path, nodeIndex); +} + +//----------------------------------------------------------------------------- +// Purpose: creates the full path for a node +//----------------------------------------------------------------------------- +void DirectorySelectDialog::GenerateFullPathForNode(int nodeIndex, char *path, int pathBufferSize) +{ + // get all the nodes + CUtlLinkedList nodes; + nodes.AddToTail(nodeIndex); + int parentIndex = nodeIndex; + while (1) + { + parentIndex = m_pDirTree->GetItemParent(parentIndex); + if (parentIndex == -1) + break; + nodes.AddToHead(parentIndex); + } + + // walk the nodes, adding to the path + path[0] = 0; + bool bFirst = true; + FOR_EACH_LL( nodes, i ) + { + KeyValues *kv = m_pDirTree->GetItemData( nodes[i] ); + strcat(path, kv->GetString("Text")); + + if (!bFirst) + { + strcat(path, "\\"); + } + bFirst = false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handles combo box changes +//----------------------------------------------------------------------------- +void DirectorySelectDialog::OnTextChanged() +{ + KeyValues *kv = m_pDriveCombo->GetActiveItemUserData(); + if (!kv) + return; + const char *newDrive = kv->GetString("drive"); + if (stricmp(newDrive, m_szCurrentDrive)) + { + // drive changed, reset + SetStartDirectory(newDrive); + } +} + +//----------------------------------------------------------------------------- +// Purpose: creates a directory +//----------------------------------------------------------------------------- +void DirectorySelectDialog::OnCreateDirectory(const char *dir) +{ + int selectedIndex = m_pDirTree->GetFirstSelectedItem(); + if (m_pDirTree->IsItemIDValid(selectedIndex)) + { + char fullPath[512]; + GenerateFullPathForNode(selectedIndex, fullPath, sizeof(fullPath)); + + // create the new directory underneath + strcat(fullPath, dir); + if (_mkdir(fullPath) == 0) + { + // add new path to tree view + KeyValues *kv = new KeyValues("item"); + kv->SetString("Text", dir); + // set the folder image + kv->SetInt("Image", 1); + kv->SetInt("SelectedImage", 1); + int itemID = m_pDirTree->AddItem(kv, selectedIndex); + + // select the item + m_pDirTree->AddSelectedItem( itemID, true ); + } + else + { + // print error message + MessageBox *box = new MessageBox("#vgui_CreateDirectoryFail_Title", "#vgui_CreateDirectoryFail_Info"); + box->DoModal(this); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: dialog closes +//----------------------------------------------------------------------------- +void DirectorySelectDialog::OnClose() +{ + BaseClass::OnClose(); + MarkForDeletion(); +} + +//----------------------------------------------------------------------------- +// Purpose: handles button commands +//----------------------------------------------------------------------------- +void DirectorySelectDialog::OnCommand(const char *command) +{ + if (!stricmp(command, "Cancel")) + { + Close(); + } + else if (!stricmp(command, "Select")) + { + // path selected + int selectedIndex = m_pDirTree->GetFirstSelectedItem(); + if (m_pDirTree->IsItemIDValid(selectedIndex)) + { + char fullPath[512]; + GenerateFullPathForNode(selectedIndex, fullPath, sizeof(fullPath)); + PostActionSignal(new KeyValues("DirectorySelected", "dir", fullPath)); + Close(); + } + } + else if (!stricmp(command, "Create")) + { + int selectedIndex = m_pDirTree->GetFirstSelectedItem(); + if (m_pDirTree->IsItemIDValid(selectedIndex)) + { + CreateDirectoryDialog *dlg = new CreateDirectoryDialog(this, m_szDefaultCreateDirName); + dlg->AddActionSignalTarget(this); + dlg->Activate(); + } + } + else + { + BaseClass::OnCommand(command); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Update the text in the combo +//----------------------------------------------------------------------------- +void DirectorySelectDialog::OnTreeViewItemSelected() +{ + int selectedIndex = m_pDirTree->GetFirstSelectedItem(); + if (!m_pDirTree->IsItemIDValid(selectedIndex)) + { + m_pCreateButton->SetEnabled(false); + return; + } + m_pCreateButton->SetEnabled(true); + + // build the string + char fullPath[512]; + GenerateFullPathForNode(selectedIndex, fullPath, sizeof(fullPath)); + + int itemID = m_pDriveCombo->GetActiveItem(); + m_pDriveCombo->UpdateItem(itemID, fullPath, NULL); + m_pDriveCombo->SetText(fullPath); } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/Divider.cpp b/mp/src/vgui2/vgui_controls/Divider.cpp index 26b843e7..b705d6d5 100644 --- a/mp/src/vgui2/vgui_controls/Divider.cpp +++ b/mp/src/vgui2/vgui_controls/Divider.cpp @@ -1,41 +1,41 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -using namespace vgui; - -DECLARE_BUILD_FACTORY( Divider ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -Divider::Divider(Panel *parent, const char *name) : Panel(parent, name) -{ - SetSize(128, 2); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -Divider::~Divider() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: sets up the border as the line to draw as a divider -//----------------------------------------------------------------------------- -void Divider::ApplySchemeSettings(IScheme *pScheme) -{ - SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); - BaseClass::ApplySchemeSettings(pScheme); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +DECLARE_BUILD_FACTORY( Divider ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Divider::Divider(Panel *parent, const char *name) : Panel(parent, name) +{ + SetSize(128, 2); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +Divider::~Divider() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: sets up the border as the line to draw as a divider +//----------------------------------------------------------------------------- +void Divider::ApplySchemeSettings(IScheme *pScheme) +{ + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); + BaseClass::ApplySchemeSettings(pScheme); +} diff --git a/mp/src/vgui2/vgui_controls/EditablePanel.cpp b/mp/src/vgui2/vgui_controls/EditablePanel.cpp index 1980db6c..e1ad3dbd 100644 --- a/mp/src/vgui2/vgui_controls/EditablePanel.cpp +++ b/mp/src/vgui2/vgui_controls/EditablePanel.cpp @@ -1,1069 +1,1069 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - - -#include -#include -#include -#include -#include -#include "vgui/IVGui.h" - -#include -#include -#include - -// these includes are all for the virtual contruction factory Dialog::CreateControlByName() -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "filesystem.h" -#include "fmtstr.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY( EditablePanel ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -#pragma warning( disable : 4355 ) - -EditablePanel::EditablePanel(Panel *parent, const char *panelName) : Panel(parent, panelName), m_NavGroup(this) -{ - _buildGroup = new BuildGroup(this, this); - m_pszConfigName = NULL; - m_iConfigID = 0; - m_pDialogVariables = NULL; - m_bShouldSkipAutoResize = false; - - // add ourselves to the build group - SetBuildGroup(GetBuildGroup()); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -EditablePanel::EditablePanel(Panel *parent, const char *panelName, HScheme hScheme) : Panel(parent, panelName, hScheme), m_NavGroup(this) -{ - _buildGroup = new BuildGroup(this, this); - m_pszConfigName = NULL; - m_iConfigID = 0; - m_pDialogVariables = NULL; - m_bShouldSkipAutoResize = false; - - // add ourselves to the build group - SetBuildGroup(GetBuildGroup()); -} - -#pragma warning( default : 4355 ) - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -EditablePanel::~EditablePanel() -{ - delete [] m_pszConfigName; - delete _buildGroup; - - if (m_pDialogVariables) - { - m_pDialogVariables->deleteThis(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Called when a child is added to the panel. -//----------------------------------------------------------------------------- -void EditablePanel::OnChildAdded(VPANEL child) -{ - BaseClass::OnChildAdded(child); - - // add only if we're in the same module - Panel *panel = ipanel()->GetPanel(child, GetModuleName()); - if (panel) - { - panel->SetBuildGroup(_buildGroup); - panel->AddActionSignalTarget(this); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void EditablePanel::OnKeyCodePressed( KeyCode code ) -{ - static ConVarRef vgui_nav_lock_default_button( "vgui_nav_lock_default_button" ); - if ( !vgui_nav_lock_default_button.IsValid() || vgui_nav_lock_default_button.GetInt() == 0 ) - { - ButtonCode_t nButtonCode = GetBaseButtonCode( code ); - - // check for a default button - VPANEL panel = GetFocusNavGroup().GetCurrentDefaultButton(); - if ( panel && !IsConsoleStylePanel() ) - { - switch ( nButtonCode ) - { - case KEY_XBUTTON_UP: - case KEY_XSTICK1_UP: - case KEY_XSTICK2_UP: - case KEY_UP: - case KEY_XBUTTON_DOWN: - case KEY_XSTICK1_DOWN: - case KEY_XSTICK2_DOWN: - case KEY_DOWN: - case KEY_XBUTTON_LEFT: - case KEY_XSTICK1_LEFT: - case KEY_XSTICK2_LEFT: - case KEY_LEFT: - case KEY_XBUTTON_RIGHT: - case KEY_XSTICK1_RIGHT: - case KEY_XSTICK2_RIGHT: - case KEY_RIGHT: - case KEY_XBUTTON_B: - // Navigating menus - vgui_nav_lock_default_button.SetValue( 1 ); - PostMessage( panel, new KeyValues( "KeyCodePressed", "code", code ) ); - return; - - case KEY_XBUTTON_A: - case KEY_ENTER: - if ( ipanel()->IsVisible( panel ) && ipanel()->IsEnabled( panel ) ) - { - // Activate the button - PostMessage( panel, new KeyValues( "Hotkey" ) ); - return; - } - } - } - } - - if ( !m_PassUnhandledInput ) - return; - - // Nothing to do with the button - BaseClass::OnKeyCodePressed( code ); -} - - - -//----------------------------------------------------------------------------- -// Purpose: Callback for when the panel size has been changed -//----------------------------------------------------------------------------- -void EditablePanel::OnSizeChanged(int wide, int tall) -{ - BaseClass::OnSizeChanged(wide, tall); - InvalidateLayout(); - - for (int i = 0; i < GetChildCount(); i++) - { - // perform auto-layout on the child panel - Panel *child = GetChild(i); - if ( !child ) - continue; - - int x, y, w, h; - child->GetBounds( x, y, w, h ); - - int px, py; - child->GetPinOffset( px, py ); - - int ox, oy; - child->GetResizeOffset( ox, oy ); - - int ex; - int ey; - - AutoResize_e resize = child->GetAutoResize(); - bool bResizeHoriz = ( resize == AUTORESIZE_RIGHT || resize == AUTORESIZE_DOWNANDRIGHT ); - bool bResizeVert = ( resize == AUTORESIZE_DOWN || resize == AUTORESIZE_DOWNANDRIGHT ); - - // The correct version of this code would say: - // if ( resize != AUTORESIZE_NO ) - // but we're very close to shipping and this causes artifacts in other vgui panels that now - // depend on this bug. So, I've added m_bShouldSkipAutoResize, which defaults to false but can - // be set using "skip_autoresize" in a .res file - if ( !m_bShouldSkipAutoResize ) - { - PinCorner_e pinCorner = child->GetPinCorner(); - if ( pinCorner == PIN_TOPRIGHT || pinCorner == PIN_BOTTOMRIGHT ) - { - // move along with the right edge - ex = wide + px; - x = bResizeHoriz ? ox : ex - w; - } - else - { - x = px; - ex = bResizeHoriz ? wide + ox : px + w; - } - - if ( pinCorner == PIN_BOTTOMLEFT || pinCorner == PIN_BOTTOMRIGHT ) - { - // move along with the right edge - ey = tall + py; - y = bResizeVert ? oy : ey - h; - } - else - { - y = py; - ey = bResizeVert ? tall + oy : py + h; - } - - // Clamp.. - if ( ex < x ) - { - ex = x; - } - if ( ey < y ) - { - ey = y; - } - - child->SetBounds( x, y, ex - x, ey - y ); - child->InvalidateLayout(); - } - } - Repaint(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void EditablePanel::OnCurrentDefaultButtonSet( VPANEL defaultButton ) -{ - m_NavGroup.SetCurrentDefaultButton( defaultButton, false ); - - // forward the message up - if (GetVParent()) - { - KeyValues *msg = new KeyValues("CurrentDefaultButtonSet"); - msg->SetInt("button", ivgui()->PanelToHandle( defaultButton ) ); - PostMessage(GetVParent(), msg); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void EditablePanel::OnDefaultButtonSet( VPANEL defaultButton ) -{ - Panel *panel = ipanel()->GetPanel( defaultButton, GetModuleName() ); - - m_NavGroup.SetDefaultButton(panel); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void EditablePanel::OnFindDefaultButton() -{ - if (m_NavGroup.GetDefaultButton()) - { - m_NavGroup.SetCurrentDefaultButton(m_NavGroup.GetDefaultButton()); - } - else - { - if (GetVParent()) - { - PostMessage(GetVParent(), new KeyValues("FindDefaultButton")); - } - } -} - -struct leaf_t -{ - short x, y, wide, tall; - unsigned char split; // 0 no split; 1 x-axis, 2 y-axis - bool filled; // true if this is already filled - short splitpos; // place of split - - leaf_t *left; - leaf_t *right; -}; - -leaf_t g_Leaves[256]; -int g_iNextLeaf; - -inline leaf_t *AllocLeaf() -{ - Assert(g_iNextLeaf < 255); - - return &g_Leaves[g_iNextLeaf++]; -} - -void AddSolidToTree(leaf_t *leaf, int x, int y, int wide, int tall) -{ - // clip to this leaf - if (x < leaf->x) - { - wide -= (leaf->x - x); - if (wide < 1) - return; - x = leaf->x; - } - if (y < leaf->y) - { - tall -= (leaf->y - y); - if (tall < 1) - return; - y = leaf->y; - } - if (x + wide > leaf->x + leaf->wide) - { - wide -= ((x + wide) - (leaf->x + leaf->wide)); - if (wide < 1) - return; - } - if (y + tall > leaf->y + leaf->tall) - { - tall -= ((y + tall) - (leaf->y + leaf->tall)); - if (tall < 1) - return; - } - - // the rect should now be completely within the leaf - if (leaf->split == 1) - { - // see if it is to the left or the right of the split - if (x < leaf->splitpos) - { - // it's to the left - AddSolidToTree(leaf->left, x, y, wide, tall); - } - else if (x + wide > leaf->splitpos) - { - // it's to the right - AddSolidToTree(leaf->right, x, y, wide, tall); - } - } - else if (leaf->split == 2) - { - // check y - // see if it is to the left (above) or the right (below) of the split - if (y < leaf->splitpos) - { - // it's above - AddSolidToTree(leaf->left, x, y, wide, tall); - } - else if (y + tall > leaf->splitpos) - { - // it's below - AddSolidToTree(leaf->right, x, y, wide, tall); - } - } - else - { - // this leaf is unsplit, make the first split against the first edge we find - if (x > leaf->x) - { - // split the left side of the rect - leaf->split = 1; - leaf->splitpos = (short)x; - - // create 2 new leaves - leaf_t *left = AllocLeaf(); - leaf_t *right = AllocLeaf(); - memset(left, 0, sizeof(leaf_t)); - memset(right, 0, sizeof(leaf_t)); - leaf->left = left; - leaf->right = right; - - left->x = leaf->x; - left->y = leaf->y; - left->wide = (short)(leaf->splitpos - leaf->x); - left->tall = leaf->tall; - - right->x = leaf->splitpos; - right->y = leaf->y; - right->wide = (short)(leaf->wide - left->wide); - right->tall = leaf->tall; - - // split the right leaf by the current rect - AddSolidToTree(leaf->right, x, y, wide, tall); - } - else if (y > leaf->y) - { - // split the top edge - leaf->split = 2; - leaf->splitpos = (short)y; - - // create 2 new leaves (facing to the east) - leaf_t *left = AllocLeaf(); - leaf_t *right = AllocLeaf(); - memset(left, 0, sizeof(leaf_t)); - memset(right, 0, sizeof(leaf_t)); - leaf->left = left; - leaf->right = right; - - left->x = leaf->x; - left->y = leaf->y; - left->wide = leaf->wide; - left->tall = (short)(y - leaf->y); - - right->x = leaf->x; - right->y = leaf->splitpos; - right->wide = leaf->wide; - right->tall = (short)(leaf->tall + leaf->y - right->y); - - // split the right leaf by the current rect - AddSolidToTree(leaf->right, x, y, wide, tall); - } - else if (x + wide < leaf->x + leaf->wide) - { - // split the right edge - leaf->split = 1; - leaf->splitpos = (short)(x + wide); - - // create 2 new leaves - leaf_t *left = AllocLeaf(); - leaf_t *right = AllocLeaf(); - memset(left, 0, sizeof(leaf_t)); - memset(right, 0, sizeof(leaf_t)); - leaf->left = left; - leaf->right = right; - - left->x = leaf->x; - left->y = leaf->y; - left->wide = (short)(leaf->splitpos - leaf->x); - left->tall = leaf->tall; - - right->x = leaf->splitpos; - right->y = leaf->y; - right->wide = (short)(leaf->wide - left->wide); - right->tall = leaf->tall; - - // split the left leaf by the current rect - AddSolidToTree(leaf->left, x, y, wide, tall); - } - else if (y + tall < leaf->y + leaf->tall) - { - // split the bottom edge - leaf->split = 2; - leaf->splitpos = (short)(y + tall); - - // create 2 new leaves (facing to the east) - leaf_t *left = AllocLeaf(); - leaf_t *right = AllocLeaf(); - memset(left, 0, sizeof(leaf_t)); - memset(right, 0, sizeof(leaf_t)); - leaf->left = left; - leaf->right = right; - - left->x = leaf->x; - left->y = leaf->y; - left->wide = leaf->wide; - left->tall = (short)(leaf->splitpos - leaf->y); - - right->x = leaf->x; - right->y = leaf->splitpos; - right->wide = leaf->wide; - right->tall = (short)(leaf->tall - left->tall); - - // split the left leaf by the current rect - AddSolidToTree(leaf->left, x, y, wide, tall); - } - else - { - // this is the exact same rect! don't draw this leaf - leaf->filled = true; - return; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Fills the panel background, clipping if possible -//----------------------------------------------------------------------------- -void EditablePanel::PaintBackground() -{ - BaseClass::PaintBackground(); - return; - -/* - test code, using a screenspace bsp tree to reduce overdraw in vgui - not yet fully functional - -// test: fill background with obnoxious color to show holes -// surface()->DrawSetColor(Color(255, 0, 0, 255)); -// surface()->DrawFilledRect(0, 0, GetWide(), GetTall()); -// return; - - // reset the leaf memory - g_iNextLeaf = 0; - - leaf_t *headNode = AllocLeaf(); - memset(headNode, 0, sizeof(leaf_t)); - - headNode->wide = (short)GetWide(); - headNode->tall = (short)GetTall(); - - // split the leaf by the first child - for (int i = 0; i < GetChildCount(); i++) - { - Panel *child = GetChild(i); - if (child->IsOpaque()) - { - int x, y, wide, tall; - child->GetBounds(x, y, wide, tall); - - // ignore small children - if (wide + tall < 100) - continue; - - AddSolidToTree(headNode, x, y, wide, tall); - } - } - - // walk the built tree, painting the background - Color col = GetBgColor(); - surface()->DrawSetColor(col); - for (i = 0; i < g_iNextLeaf; i++) - { - leaf_t *leaf = g_Leaves + i; - if (leaf->splitpos || leaf->filled) - continue; - surface()->DrawFilledRect(leaf->x, leaf->y, leaf->x + leaf->wide, leaf->y + leaf->tall); - } -*/ -} - -//----------------------------------------------------------------------------- -// Purpose: Activates the build mode dialog for editing panels. -//----------------------------------------------------------------------------- -void EditablePanel::ActivateBuildMode() -{ - _buildGroup->SetEnabled(true); -} - -//----------------------------------------------------------------------------- -// Purpose: Loads panel settings from a resource file. -//----------------------------------------------------------------------------- -void EditablePanel::LoadControlSettings(const char *resourceName, const char *pathID, KeyValues *pKeyValues, KeyValues *pConditions) -{ -#if defined( DBGFLAG_ASSERT ) && !defined(OSX) && !defined(LINUX) - extern IFileSystem *g_pFullFileSystem; - // Since nobody wants to fix this assert, I'm making it a Msg instead: - // editablepanel.cpp (535) : Resource file "resource\DebugOptionsPanel.res" not found on disk! - // AssertMsg( g_pFullFileSystem->FileExists( resourceName ), CFmtStr( "Resource file \"%s\" not found on disk!", resourceName ).Access() ); - if ( !g_pFullFileSystem->FileExists( resourceName ) ) - { - Msg( "Resource file \"%s\" not found on disk!", resourceName ); - } -#endif - _buildGroup->LoadControlSettings(resourceName, pathID, pKeyValues, pConditions); - ForceSubPanelsToUpdateWithNewDialogVariables(); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: registers a file in the list of control settings, so the vgui dialog can choose between them to edit -//----------------------------------------------------------------------------- -void EditablePanel::RegisterControlSettingsFile(const char *resourceName, const char *pathID) -{ - _buildGroup->RegisterControlSettingsFile(resourceName, pathID); -} - -//----------------------------------------------------------------------------- -// Purpose: sets the name of this dialog so it can be saved in the user config area -//----------------------------------------------------------------------------- -void EditablePanel::LoadUserConfig(const char *configName, int dialogID) -{ - KeyValues *data = system()->GetUserConfigFileData(configName, dialogID); - - delete [] m_pszConfigName; - int len = Q_strlen(configName) + 1; - m_pszConfigName = new char[ len ]; - Q_strncpy(m_pszConfigName, configName, len ); - m_iConfigID = dialogID; - - // apply our user config settings (this will recurse through our children) - if (data) - { - ApplyUserConfigSettings(data); - } -} - -//----------------------------------------------------------------------------- -// Purpose: saves all the settings to the document -//----------------------------------------------------------------------------- -void EditablePanel::SaveUserConfig() -{ - if (m_pszConfigName) - { - KeyValues *data = system()->GetUserConfigFileData(m_pszConfigName, m_iConfigID); - - // get our user config settings (this will recurse through our children) - if (data) - { - GetUserConfigSettings(data); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: combines both of the above, LoadControlSettings & LoadUserConfig -//----------------------------------------------------------------------------- -void EditablePanel::LoadControlSettingsAndUserConfig(const char *dialogResourceName, int dialogID) -{ - LoadControlSettings(dialogResourceName); - LoadUserConfig(dialogResourceName, dialogID); -} - -//----------------------------------------------------------------------------- -// Purpose: applies the user config settings to all the children -//----------------------------------------------------------------------------- -void EditablePanel::ApplyUserConfigSettings(KeyValues *userConfig) -{ - for (int i = 0; i < GetChildCount(); i++) - { - Panel *child = GetChild(i); - if (child->HasUserConfigSettings()) - { - const char *name = child->GetName(); - if (name && *name) - { - child->ApplyUserConfigSettings(userConfig->FindKey(name, true)); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: gets all the children's user config settings -//----------------------------------------------------------------------------- -void EditablePanel::GetUserConfigSettings(KeyValues *userConfig) -{ - for (int i = 0; i < GetChildCount(); i++) - { - Panel *child = GetChild(i); - if (child->HasUserConfigSettings()) - { - const char *name = child->GetName(); - if (name && *name) - { - child->GetUserConfigSettings(userConfig->FindKey(name, true)); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Save user config settings -//----------------------------------------------------------------------------- -void EditablePanel::OnClose() -{ - SaveUserConfig(); -} - -//----------------------------------------------------------------------------- -// Purpose: Handle information requests -//----------------------------------------------------------------------------- -bool EditablePanel::RequestInfo(KeyValues *data) -{ - if (!stricmp(data->GetName(), "BuildDialog")) - { - // a build dialog is being requested, give it one - // a bit hacky, but this is a case where vgui.dll needs to reach out - data->SetPtr("PanelPtr", new BuildModeDialog( (BuildGroup *)data->GetPtr("BuildGroupPtr"))); - return true; - } - else if (!stricmp(data->GetName(), "ControlFactory")) - { - Panel *newPanel = CreateControlByName(data->GetString("ControlName")); - if (newPanel) - { - data->SetPtr("PanelPtr", newPanel); - return true; - } - } - return BaseClass::RequestInfo(data); -} - -//----------------------------------------------------------------------------- -// Purpose: Return the buildgroup that this panel is part of. -// Input : -// Output : BuildGroup -//----------------------------------------------------------------------------- -BuildGroup *EditablePanel::GetBuildGroup() -{ - return _buildGroup; -} - -//----------------------------------------------------------------------------- -// Purpose: Return a pointer to the nav group -// Output : FocusNavGroup -//----------------------------------------------------------------------------- -FocusNavGroup &EditablePanel::GetFocusNavGroup() -{ - return m_NavGroup; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool EditablePanel::RequestFocusNext(VPANEL panel) -{ - bool bRet = m_NavGroup.RequestFocusNext(panel); - if ( IsPC() && !bRet && IsConsoleStylePanel() ) - { - NavigateDown(); - } - return bRet; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool EditablePanel::RequestFocusPrev(VPANEL panel) -{ - bool bRet = m_NavGroup.RequestFocusPrev(panel); - if ( IsPC() && !bRet && IsConsoleStylePanel() ) - { - NavigateUp(); - } - return bRet; -} - -//----------------------------------------------------------------------------- -// Purpose: Delegates focus to a sub panel -// Input : direction - the direction in which focus travelled to arrive at this panel; forward = 1, back = -1 -//----------------------------------------------------------------------------- -void EditablePanel::RequestFocus(int direction) -{ - // we must be a sub panel for this to be called - // delegate focus - if (direction == 1) - { - RequestFocusNext(NULL); - } - else if (direction == -1) - { - RequestFocusPrev(NULL); - } - else - { - BaseClass::RequestFocus(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Pass the focus down onto the last used panel -//----------------------------------------------------------------------------- -void EditablePanel::OnSetFocus() -{ - Panel *focus = m_NavGroup.GetCurrentFocus(); - if (focus && focus != this) - { - focus->RequestFocus(); - } - else - { - focus = m_NavGroup.GetDefaultPanel(); - if (focus) - { - focus->RequestFocus(); - focus->OnSetFocus(); - } - } - - BaseClass::OnSetFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: Called when the resource file is loaded to set up the panel state -// Input : *inResourceData - -//----------------------------------------------------------------------------- -void EditablePanel::ApplySettings(KeyValues *inResourceData) -{ - BaseClass::ApplySettings(inResourceData); - - _buildGroup->ApplySettings(inResourceData); - - m_bShouldSkipAutoResize = inResourceData->GetBool( "skip_autoresize", false ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Update focus info for navigation -//----------------------------------------------------------------------------- -void EditablePanel::OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel) -{ - if (!ipanel()->IsPopup(subFocus)) - { - defaultPanel = m_NavGroup.SetCurrentFocus(subFocus, defaultPanel); - } - BaseClass::OnRequestFocus(GetVPanel(), defaultPanel); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the panel that currently has keyfocus -//----------------------------------------------------------------------------- -VPANEL EditablePanel::GetCurrentKeyFocus() -{ - Panel *focus = m_NavGroup.GetCurrentFocus(); - if (focus == this) - return NULL; - - if (focus) - { - if (focus->IsPopup()) - return BaseClass::GetCurrentKeyFocus(); - - // chain down the editpanel hierarchy - VPANEL subFocus = focus->GetCurrentKeyFocus(); - if (subFocus) - return subFocus; - - // hit a leaf panel, return that - return focus->GetVPanel(); - } - return BaseClass::GetCurrentKeyFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: Gets the panel with the specified hotkey -//----------------------------------------------------------------------------- -Panel *EditablePanel::HasHotkey(wchar_t key) -{ - if( !IsVisible() || !IsEnabled()) // not visible, so can't respond to a hot key - { - return NULL; - } - - for (int i = 0; i < GetChildCount(); i++) - { - Panel *hot = GetChild(i)->HasHotkey(key); - if (hot && hot->IsVisible() && hot->IsEnabled()) - { - return hot; - } - } - - return NULL; - -} - -//----------------------------------------------------------------------------- -// Purpose: Shortcut function to setting enabled state of control -//----------------------------------------------------------------------------- -void EditablePanel::SetControlEnabled(const char *controlName, bool enabled) -{ - Panel *control = FindChildByName(controlName); - if (control) - { - control->SetEnabled(enabled); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Shortcut function to setting visibility state of control -//----------------------------------------------------------------------------- -void EditablePanel::SetControlVisible(const char *controlName, bool visible) -{ - Panel *control = FindChildByName(controlName); - if (control) - { - control->SetVisible(visible); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Shortcut function to set data in child controls -//----------------------------------------------------------------------------- -void EditablePanel::SetControlString(const char *controlName, const char *string) -{ - Panel *control = FindChildByName(controlName); - if (control) - { - if (string[0] == '#') - { - const wchar_t *wszText = g_pVGuiLocalize->Find(string); - if (wszText) - { - PostMessage(control, new KeyValues("SetText", "text", wszText)); - } - } - else - { - PostMessage(control, new KeyValues("SetText", "text", string)); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Shortcut function to set data in child controls -//----------------------------------------------------------------------------- -void EditablePanel::SetControlString(const char *controlName, const wchar_t *string) -{ - Panel *control = FindChildByName(controlName); - if (control) - { - PostMessage(control, new KeyValues("SetText", "text", string)); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Shortcut function to set data in child controls -//----------------------------------------------------------------------------- -void EditablePanel::SetControlInt(const char *controlName, int state) -{ - Panel *control = FindChildByName(controlName); - if (control) - { - PostMessage(control, new KeyValues("SetState", "state", state)); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Shortcut function to get data in child controls -//----------------------------------------------------------------------------- -int EditablePanel::GetControlInt(const char *controlName, int defaultState) -{ - Panel *control = FindChildByName(controlName); - if (control) - { - KeyValues *data = new KeyValues("GetState"); - if (control->RequestInfo(data)) - { - int state = data->GetInt("state", defaultState); - data->deleteThis(); - return state; - } - } - return defaultState; -} - -//----------------------------------------------------------------------------- -// Purpose: Shortcut function to get data in child controls -//----------------------------------------------------------------------------- -const char *EditablePanel::GetControlString(const char *controlName, const char *defaultString) -{ - static char buf[512]; - GetControlString(controlName, buf, sizeof(buf) - 1, defaultString); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: Shortcut function to get data in child controls -//----------------------------------------------------------------------------- -void EditablePanel::GetControlString(const char *controlName, char *buf, int bufSize, const char *defaultString) -{ - Panel *control = FindChildByName(controlName); - KeyValues *data = new KeyValues("GetText"); - if (control && control->RequestInfo(data)) - { - Q_strncpy(buf, data->GetString("text", defaultString), bufSize); - } - else - { - // no value found, copy in default text - Q_strncpy(buf, defaultString, bufSize); - } - - // ensure null termination of string - buf[bufSize - 1] = 0; - - // free - data->deleteThis(); -} - -//----------------------------------------------------------------------------- -// Purpose: localization variables (used in constructing UI strings) -//----------------------------------------------------------------------------- -void EditablePanel::SetDialogVariable(const char *varName, const char *value) -{ - GetDialogVariables()->SetString(varName, value); - ForceSubPanelsToUpdateWithNewDialogVariables(); -} - -//----------------------------------------------------------------------------- -// Purpose: localization variables (used in constructing UI strings) -//----------------------------------------------------------------------------- -void EditablePanel::SetDialogVariable(const char *varName, const wchar_t *value) -{ - GetDialogVariables()->SetWString(varName, value); - ForceSubPanelsToUpdateWithNewDialogVariables(); -} - -//----------------------------------------------------------------------------- -// Purpose: localization variables (used in constructing UI strings) -//----------------------------------------------------------------------------- -void EditablePanel::SetDialogVariable(const char *varName, int value) -{ - GetDialogVariables()->SetInt(varName, value); - ForceSubPanelsToUpdateWithNewDialogVariables(); -} - -//----------------------------------------------------------------------------- -// Purpose: localization variables (used in constructing UI strings) -//----------------------------------------------------------------------------- -void EditablePanel::SetDialogVariable(const char *varName, float value) -{ - GetDialogVariables()->SetFloat(varName, value); - ForceSubPanelsToUpdateWithNewDialogVariables(); -} - -//----------------------------------------------------------------------------- -// Purpose: redraws child panels with new localization vars -//----------------------------------------------------------------------------- -void EditablePanel::ForceSubPanelsToUpdateWithNewDialogVariables() -{ - if (m_pDialogVariables) - { - ipanel()->SendMessage(GetVPanel(), m_pDialogVariables, GetVPanel()); - for (int i = 0; i < ipanel()->GetChildCount(GetVPanel()); i++) - { - ipanel()->SendMessage(ipanel()->GetChild(GetVPanel(), i), m_pDialogVariables, GetVPanel()); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: lazy creation of localization vars object -//----------------------------------------------------------------------------- -KeyValues *EditablePanel::GetDialogVariables() -{ - if (m_pDialogVariables) - return m_pDialogVariables; - - m_pDialogVariables = new KeyValues("DialogVariables"); - return m_pDialogVariables; -} - -//----------------------------------------------------------------------------- -// Purpose: Virtual factory for control creation -//----------------------------------------------------------------------------- -Panel *EditablePanel::CreateControlByName(const char *controlName) -{ - Panel *fromFactory = CBuildFactoryHelper::InstancePanel( controlName ); - if ( fromFactory ) - { - return fromFactory; - } - - return NULL; -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include +#include +#include +#include +#include +#include "vgui/IVGui.h" + +#include +#include +#include + +// these includes are all for the virtual contruction factory Dialog::CreateControlByName() +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "filesystem.h" +#include "fmtstr.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY( EditablePanel ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +#pragma warning( disable : 4355 ) + +EditablePanel::EditablePanel(Panel *parent, const char *panelName) : Panel(parent, panelName), m_NavGroup(this) +{ + _buildGroup = new BuildGroup(this, this); + m_pszConfigName = NULL; + m_iConfigID = 0; + m_pDialogVariables = NULL; + m_bShouldSkipAutoResize = false; + + // add ourselves to the build group + SetBuildGroup(GetBuildGroup()); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +EditablePanel::EditablePanel(Panel *parent, const char *panelName, HScheme hScheme) : Panel(parent, panelName, hScheme), m_NavGroup(this) +{ + _buildGroup = new BuildGroup(this, this); + m_pszConfigName = NULL; + m_iConfigID = 0; + m_pDialogVariables = NULL; + m_bShouldSkipAutoResize = false; + + // add ourselves to the build group + SetBuildGroup(GetBuildGroup()); +} + +#pragma warning( default : 4355 ) + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +EditablePanel::~EditablePanel() +{ + delete [] m_pszConfigName; + delete _buildGroup; + + if (m_pDialogVariables) + { + m_pDialogVariables->deleteThis(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called when a child is added to the panel. +//----------------------------------------------------------------------------- +void EditablePanel::OnChildAdded(VPANEL child) +{ + BaseClass::OnChildAdded(child); + + // add only if we're in the same module + Panel *panel = ipanel()->GetPanel(child, GetModuleName()); + if (panel) + { + panel->SetBuildGroup(_buildGroup); + panel->AddActionSignalTarget(this); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void EditablePanel::OnKeyCodePressed( KeyCode code ) +{ + static ConVarRef vgui_nav_lock_default_button( "vgui_nav_lock_default_button" ); + if ( !vgui_nav_lock_default_button.IsValid() || vgui_nav_lock_default_button.GetInt() == 0 ) + { + ButtonCode_t nButtonCode = GetBaseButtonCode( code ); + + // check for a default button + VPANEL panel = GetFocusNavGroup().GetCurrentDefaultButton(); + if ( panel && !IsConsoleStylePanel() ) + { + switch ( nButtonCode ) + { + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case KEY_XSTICK2_UP: + case KEY_UP: + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case KEY_XSTICK2_DOWN: + case KEY_DOWN: + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case KEY_XSTICK2_LEFT: + case KEY_LEFT: + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + case KEY_XSTICK2_RIGHT: + case KEY_RIGHT: + case KEY_XBUTTON_B: + // Navigating menus + vgui_nav_lock_default_button.SetValue( 1 ); + PostMessage( panel, new KeyValues( "KeyCodePressed", "code", code ) ); + return; + + case KEY_XBUTTON_A: + case KEY_ENTER: + if ( ipanel()->IsVisible( panel ) && ipanel()->IsEnabled( panel ) ) + { + // Activate the button + PostMessage( panel, new KeyValues( "Hotkey" ) ); + return; + } + } + } + } + + if ( !m_PassUnhandledInput ) + return; + + // Nothing to do with the button + BaseClass::OnKeyCodePressed( code ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Callback for when the panel size has been changed +//----------------------------------------------------------------------------- +void EditablePanel::OnSizeChanged(int wide, int tall) +{ + BaseClass::OnSizeChanged(wide, tall); + InvalidateLayout(); + + for (int i = 0; i < GetChildCount(); i++) + { + // perform auto-layout on the child panel + Panel *child = GetChild(i); + if ( !child ) + continue; + + int x, y, w, h; + child->GetBounds( x, y, w, h ); + + int px, py; + child->GetPinOffset( px, py ); + + int ox, oy; + child->GetResizeOffset( ox, oy ); + + int ex; + int ey; + + AutoResize_e resize = child->GetAutoResize(); + bool bResizeHoriz = ( resize == AUTORESIZE_RIGHT || resize == AUTORESIZE_DOWNANDRIGHT ); + bool bResizeVert = ( resize == AUTORESIZE_DOWN || resize == AUTORESIZE_DOWNANDRIGHT ); + + // The correct version of this code would say: + // if ( resize != AUTORESIZE_NO ) + // but we're very close to shipping and this causes artifacts in other vgui panels that now + // depend on this bug. So, I've added m_bShouldSkipAutoResize, which defaults to false but can + // be set using "skip_autoresize" in a .res file + if ( !m_bShouldSkipAutoResize ) + { + PinCorner_e pinCorner = child->GetPinCorner(); + if ( pinCorner == PIN_TOPRIGHT || pinCorner == PIN_BOTTOMRIGHT ) + { + // move along with the right edge + ex = wide + px; + x = bResizeHoriz ? ox : ex - w; + } + else + { + x = px; + ex = bResizeHoriz ? wide + ox : px + w; + } + + if ( pinCorner == PIN_BOTTOMLEFT || pinCorner == PIN_BOTTOMRIGHT ) + { + // move along with the right edge + ey = tall + py; + y = bResizeVert ? oy : ey - h; + } + else + { + y = py; + ey = bResizeVert ? tall + oy : py + h; + } + + // Clamp.. + if ( ex < x ) + { + ex = x; + } + if ( ey < y ) + { + ey = y; + } + + child->SetBounds( x, y, ex - x, ey - y ); + child->InvalidateLayout(); + } + } + Repaint(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void EditablePanel::OnCurrentDefaultButtonSet( VPANEL defaultButton ) +{ + m_NavGroup.SetCurrentDefaultButton( defaultButton, false ); + + // forward the message up + if (GetVParent()) + { + KeyValues *msg = new KeyValues("CurrentDefaultButtonSet"); + msg->SetInt("button", ivgui()->PanelToHandle( defaultButton ) ); + PostMessage(GetVParent(), msg); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void EditablePanel::OnDefaultButtonSet( VPANEL defaultButton ) +{ + Panel *panel = ipanel()->GetPanel( defaultButton, GetModuleName() ); + + m_NavGroup.SetDefaultButton(panel); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void EditablePanel::OnFindDefaultButton() +{ + if (m_NavGroup.GetDefaultButton()) + { + m_NavGroup.SetCurrentDefaultButton(m_NavGroup.GetDefaultButton()); + } + else + { + if (GetVParent()) + { + PostMessage(GetVParent(), new KeyValues("FindDefaultButton")); + } + } +} + +struct leaf_t +{ + short x, y, wide, tall; + unsigned char split; // 0 no split; 1 x-axis, 2 y-axis + bool filled; // true if this is already filled + short splitpos; // place of split + + leaf_t *left; + leaf_t *right; +}; + +leaf_t g_Leaves[256]; +int g_iNextLeaf; + +inline leaf_t *AllocLeaf() +{ + Assert(g_iNextLeaf < 255); + + return &g_Leaves[g_iNextLeaf++]; +} + +void AddSolidToTree(leaf_t *leaf, int x, int y, int wide, int tall) +{ + // clip to this leaf + if (x < leaf->x) + { + wide -= (leaf->x - x); + if (wide < 1) + return; + x = leaf->x; + } + if (y < leaf->y) + { + tall -= (leaf->y - y); + if (tall < 1) + return; + y = leaf->y; + } + if (x + wide > leaf->x + leaf->wide) + { + wide -= ((x + wide) - (leaf->x + leaf->wide)); + if (wide < 1) + return; + } + if (y + tall > leaf->y + leaf->tall) + { + tall -= ((y + tall) - (leaf->y + leaf->tall)); + if (tall < 1) + return; + } + + // the rect should now be completely within the leaf + if (leaf->split == 1) + { + // see if it is to the left or the right of the split + if (x < leaf->splitpos) + { + // it's to the left + AddSolidToTree(leaf->left, x, y, wide, tall); + } + else if (x + wide > leaf->splitpos) + { + // it's to the right + AddSolidToTree(leaf->right, x, y, wide, tall); + } + } + else if (leaf->split == 2) + { + // check y + // see if it is to the left (above) or the right (below) of the split + if (y < leaf->splitpos) + { + // it's above + AddSolidToTree(leaf->left, x, y, wide, tall); + } + else if (y + tall > leaf->splitpos) + { + // it's below + AddSolidToTree(leaf->right, x, y, wide, tall); + } + } + else + { + // this leaf is unsplit, make the first split against the first edge we find + if (x > leaf->x) + { + // split the left side of the rect + leaf->split = 1; + leaf->splitpos = (short)x; + + // create 2 new leaves + leaf_t *left = AllocLeaf(); + leaf_t *right = AllocLeaf(); + memset(left, 0, sizeof(leaf_t)); + memset(right, 0, sizeof(leaf_t)); + leaf->left = left; + leaf->right = right; + + left->x = leaf->x; + left->y = leaf->y; + left->wide = (short)(leaf->splitpos - leaf->x); + left->tall = leaf->tall; + + right->x = leaf->splitpos; + right->y = leaf->y; + right->wide = (short)(leaf->wide - left->wide); + right->tall = leaf->tall; + + // split the right leaf by the current rect + AddSolidToTree(leaf->right, x, y, wide, tall); + } + else if (y > leaf->y) + { + // split the top edge + leaf->split = 2; + leaf->splitpos = (short)y; + + // create 2 new leaves (facing to the east) + leaf_t *left = AllocLeaf(); + leaf_t *right = AllocLeaf(); + memset(left, 0, sizeof(leaf_t)); + memset(right, 0, sizeof(leaf_t)); + leaf->left = left; + leaf->right = right; + + left->x = leaf->x; + left->y = leaf->y; + left->wide = leaf->wide; + left->tall = (short)(y - leaf->y); + + right->x = leaf->x; + right->y = leaf->splitpos; + right->wide = leaf->wide; + right->tall = (short)(leaf->tall + leaf->y - right->y); + + // split the right leaf by the current rect + AddSolidToTree(leaf->right, x, y, wide, tall); + } + else if (x + wide < leaf->x + leaf->wide) + { + // split the right edge + leaf->split = 1; + leaf->splitpos = (short)(x + wide); + + // create 2 new leaves + leaf_t *left = AllocLeaf(); + leaf_t *right = AllocLeaf(); + memset(left, 0, sizeof(leaf_t)); + memset(right, 0, sizeof(leaf_t)); + leaf->left = left; + leaf->right = right; + + left->x = leaf->x; + left->y = leaf->y; + left->wide = (short)(leaf->splitpos - leaf->x); + left->tall = leaf->tall; + + right->x = leaf->splitpos; + right->y = leaf->y; + right->wide = (short)(leaf->wide - left->wide); + right->tall = leaf->tall; + + // split the left leaf by the current rect + AddSolidToTree(leaf->left, x, y, wide, tall); + } + else if (y + tall < leaf->y + leaf->tall) + { + // split the bottom edge + leaf->split = 2; + leaf->splitpos = (short)(y + tall); + + // create 2 new leaves (facing to the east) + leaf_t *left = AllocLeaf(); + leaf_t *right = AllocLeaf(); + memset(left, 0, sizeof(leaf_t)); + memset(right, 0, sizeof(leaf_t)); + leaf->left = left; + leaf->right = right; + + left->x = leaf->x; + left->y = leaf->y; + left->wide = leaf->wide; + left->tall = (short)(leaf->splitpos - leaf->y); + + right->x = leaf->x; + right->y = leaf->splitpos; + right->wide = leaf->wide; + right->tall = (short)(leaf->tall - left->tall); + + // split the left leaf by the current rect + AddSolidToTree(leaf->left, x, y, wide, tall); + } + else + { + // this is the exact same rect! don't draw this leaf + leaf->filled = true; + return; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Fills the panel background, clipping if possible +//----------------------------------------------------------------------------- +void EditablePanel::PaintBackground() +{ + BaseClass::PaintBackground(); + return; + +/* + test code, using a screenspace bsp tree to reduce overdraw in vgui + not yet fully functional + +// test: fill background with obnoxious color to show holes +// surface()->DrawSetColor(Color(255, 0, 0, 255)); +// surface()->DrawFilledRect(0, 0, GetWide(), GetTall()); +// return; + + // reset the leaf memory + g_iNextLeaf = 0; + + leaf_t *headNode = AllocLeaf(); + memset(headNode, 0, sizeof(leaf_t)); + + headNode->wide = (short)GetWide(); + headNode->tall = (short)GetTall(); + + // split the leaf by the first child + for (int i = 0; i < GetChildCount(); i++) + { + Panel *child = GetChild(i); + if (child->IsOpaque()) + { + int x, y, wide, tall; + child->GetBounds(x, y, wide, tall); + + // ignore small children + if (wide + tall < 100) + continue; + + AddSolidToTree(headNode, x, y, wide, tall); + } + } + + // walk the built tree, painting the background + Color col = GetBgColor(); + surface()->DrawSetColor(col); + for (i = 0; i < g_iNextLeaf; i++) + { + leaf_t *leaf = g_Leaves + i; + if (leaf->splitpos || leaf->filled) + continue; + surface()->DrawFilledRect(leaf->x, leaf->y, leaf->x + leaf->wide, leaf->y + leaf->tall); + } +*/ +} + +//----------------------------------------------------------------------------- +// Purpose: Activates the build mode dialog for editing panels. +//----------------------------------------------------------------------------- +void EditablePanel::ActivateBuildMode() +{ + _buildGroup->SetEnabled(true); +} + +//----------------------------------------------------------------------------- +// Purpose: Loads panel settings from a resource file. +//----------------------------------------------------------------------------- +void EditablePanel::LoadControlSettings(const char *resourceName, const char *pathID, KeyValues *pKeyValues, KeyValues *pConditions) +{ +#if defined( DBGFLAG_ASSERT ) && !defined(OSX) && !defined(LINUX) + extern IFileSystem *g_pFullFileSystem; + // Since nobody wants to fix this assert, I'm making it a Msg instead: + // editablepanel.cpp (535) : Resource file "resource\DebugOptionsPanel.res" not found on disk! + // AssertMsg( g_pFullFileSystem->FileExists( resourceName ), CFmtStr( "Resource file \"%s\" not found on disk!", resourceName ).Access() ); + if ( !g_pFullFileSystem->FileExists( resourceName ) ) + { + Msg( "Resource file \"%s\" not found on disk!", resourceName ); + } +#endif + _buildGroup->LoadControlSettings(resourceName, pathID, pKeyValues, pConditions); + ForceSubPanelsToUpdateWithNewDialogVariables(); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: registers a file in the list of control settings, so the vgui dialog can choose between them to edit +//----------------------------------------------------------------------------- +void EditablePanel::RegisterControlSettingsFile(const char *resourceName, const char *pathID) +{ + _buildGroup->RegisterControlSettingsFile(resourceName, pathID); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the name of this dialog so it can be saved in the user config area +//----------------------------------------------------------------------------- +void EditablePanel::LoadUserConfig(const char *configName, int dialogID) +{ + KeyValues *data = system()->GetUserConfigFileData(configName, dialogID); + + delete [] m_pszConfigName; + int len = Q_strlen(configName) + 1; + m_pszConfigName = new char[ len ]; + Q_strncpy(m_pszConfigName, configName, len ); + m_iConfigID = dialogID; + + // apply our user config settings (this will recurse through our children) + if (data) + { + ApplyUserConfigSettings(data); + } +} + +//----------------------------------------------------------------------------- +// Purpose: saves all the settings to the document +//----------------------------------------------------------------------------- +void EditablePanel::SaveUserConfig() +{ + if (m_pszConfigName) + { + KeyValues *data = system()->GetUserConfigFileData(m_pszConfigName, m_iConfigID); + + // get our user config settings (this will recurse through our children) + if (data) + { + GetUserConfigSettings(data); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: combines both of the above, LoadControlSettings & LoadUserConfig +//----------------------------------------------------------------------------- +void EditablePanel::LoadControlSettingsAndUserConfig(const char *dialogResourceName, int dialogID) +{ + LoadControlSettings(dialogResourceName); + LoadUserConfig(dialogResourceName, dialogID); +} + +//----------------------------------------------------------------------------- +// Purpose: applies the user config settings to all the children +//----------------------------------------------------------------------------- +void EditablePanel::ApplyUserConfigSettings(KeyValues *userConfig) +{ + for (int i = 0; i < GetChildCount(); i++) + { + Panel *child = GetChild(i); + if (child->HasUserConfigSettings()) + { + const char *name = child->GetName(); + if (name && *name) + { + child->ApplyUserConfigSettings(userConfig->FindKey(name, true)); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: gets all the children's user config settings +//----------------------------------------------------------------------------- +void EditablePanel::GetUserConfigSettings(KeyValues *userConfig) +{ + for (int i = 0; i < GetChildCount(); i++) + { + Panel *child = GetChild(i); + if (child->HasUserConfigSettings()) + { + const char *name = child->GetName(); + if (name && *name) + { + child->GetUserConfigSettings(userConfig->FindKey(name, true)); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Save user config settings +//----------------------------------------------------------------------------- +void EditablePanel::OnClose() +{ + SaveUserConfig(); +} + +//----------------------------------------------------------------------------- +// Purpose: Handle information requests +//----------------------------------------------------------------------------- +bool EditablePanel::RequestInfo(KeyValues *data) +{ + if (!stricmp(data->GetName(), "BuildDialog")) + { + // a build dialog is being requested, give it one + // a bit hacky, but this is a case where vgui.dll needs to reach out + data->SetPtr("PanelPtr", new BuildModeDialog( (BuildGroup *)data->GetPtr("BuildGroupPtr"))); + return true; + } + else if (!stricmp(data->GetName(), "ControlFactory")) + { + Panel *newPanel = CreateControlByName(data->GetString("ControlName")); + if (newPanel) + { + data->SetPtr("PanelPtr", newPanel); + return true; + } + } + return BaseClass::RequestInfo(data); +} + +//----------------------------------------------------------------------------- +// Purpose: Return the buildgroup that this panel is part of. +// Input : +// Output : BuildGroup +//----------------------------------------------------------------------------- +BuildGroup *EditablePanel::GetBuildGroup() +{ + return _buildGroup; +} + +//----------------------------------------------------------------------------- +// Purpose: Return a pointer to the nav group +// Output : FocusNavGroup +//----------------------------------------------------------------------------- +FocusNavGroup &EditablePanel::GetFocusNavGroup() +{ + return m_NavGroup; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool EditablePanel::RequestFocusNext(VPANEL panel) +{ + bool bRet = m_NavGroup.RequestFocusNext(panel); + if ( IsPC() && !bRet && IsConsoleStylePanel() ) + { + NavigateDown(); + } + return bRet; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool EditablePanel::RequestFocusPrev(VPANEL panel) +{ + bool bRet = m_NavGroup.RequestFocusPrev(panel); + if ( IsPC() && !bRet && IsConsoleStylePanel() ) + { + NavigateUp(); + } + return bRet; +} + +//----------------------------------------------------------------------------- +// Purpose: Delegates focus to a sub panel +// Input : direction - the direction in which focus travelled to arrive at this panel; forward = 1, back = -1 +//----------------------------------------------------------------------------- +void EditablePanel::RequestFocus(int direction) +{ + // we must be a sub panel for this to be called + // delegate focus + if (direction == 1) + { + RequestFocusNext(NULL); + } + else if (direction == -1) + { + RequestFocusPrev(NULL); + } + else + { + BaseClass::RequestFocus(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Pass the focus down onto the last used panel +//----------------------------------------------------------------------------- +void EditablePanel::OnSetFocus() +{ + Panel *focus = m_NavGroup.GetCurrentFocus(); + if (focus && focus != this) + { + focus->RequestFocus(); + } + else + { + focus = m_NavGroup.GetDefaultPanel(); + if (focus) + { + focus->RequestFocus(); + focus->OnSetFocus(); + } + } + + BaseClass::OnSetFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the resource file is loaded to set up the panel state +// Input : *inResourceData - +//----------------------------------------------------------------------------- +void EditablePanel::ApplySettings(KeyValues *inResourceData) +{ + BaseClass::ApplySettings(inResourceData); + + _buildGroup->ApplySettings(inResourceData); + + m_bShouldSkipAutoResize = inResourceData->GetBool( "skip_autoresize", false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Update focus info for navigation +//----------------------------------------------------------------------------- +void EditablePanel::OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel) +{ + if (!ipanel()->IsPopup(subFocus)) + { + defaultPanel = m_NavGroup.SetCurrentFocus(subFocus, defaultPanel); + } + BaseClass::OnRequestFocus(GetVPanel(), defaultPanel); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the panel that currently has keyfocus +//----------------------------------------------------------------------------- +VPANEL EditablePanel::GetCurrentKeyFocus() +{ + Panel *focus = m_NavGroup.GetCurrentFocus(); + if (focus == this) + return NULL; + + if (focus) + { + if (focus->IsPopup()) + return BaseClass::GetCurrentKeyFocus(); + + // chain down the editpanel hierarchy + VPANEL subFocus = focus->GetCurrentKeyFocus(); + if (subFocus) + return subFocus; + + // hit a leaf panel, return that + return focus->GetVPanel(); + } + return BaseClass::GetCurrentKeyFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the panel with the specified hotkey +//----------------------------------------------------------------------------- +Panel *EditablePanel::HasHotkey(wchar_t key) +{ + if( !IsVisible() || !IsEnabled()) // not visible, so can't respond to a hot key + { + return NULL; + } + + for (int i = 0; i < GetChildCount(); i++) + { + Panel *hot = GetChild(i)->HasHotkey(key); + if (hot && hot->IsVisible() && hot->IsEnabled()) + { + return hot; + } + } + + return NULL; + +} + +//----------------------------------------------------------------------------- +// Purpose: Shortcut function to setting enabled state of control +//----------------------------------------------------------------------------- +void EditablePanel::SetControlEnabled(const char *controlName, bool enabled) +{ + Panel *control = FindChildByName(controlName); + if (control) + { + control->SetEnabled(enabled); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Shortcut function to setting visibility state of control +//----------------------------------------------------------------------------- +void EditablePanel::SetControlVisible(const char *controlName, bool visible) +{ + Panel *control = FindChildByName(controlName); + if (control) + { + control->SetVisible(visible); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Shortcut function to set data in child controls +//----------------------------------------------------------------------------- +void EditablePanel::SetControlString(const char *controlName, const char *string) +{ + Panel *control = FindChildByName(controlName); + if (control) + { + if (string[0] == '#') + { + const wchar_t *wszText = g_pVGuiLocalize->Find(string); + if (wszText) + { + PostMessage(control, new KeyValues("SetText", "text", wszText)); + } + } + else + { + PostMessage(control, new KeyValues("SetText", "text", string)); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Shortcut function to set data in child controls +//----------------------------------------------------------------------------- +void EditablePanel::SetControlString(const char *controlName, const wchar_t *string) +{ + Panel *control = FindChildByName(controlName); + if (control) + { + PostMessage(control, new KeyValues("SetText", "text", string)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Shortcut function to set data in child controls +//----------------------------------------------------------------------------- +void EditablePanel::SetControlInt(const char *controlName, int state) +{ + Panel *control = FindChildByName(controlName); + if (control) + { + PostMessage(control, new KeyValues("SetState", "state", state)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Shortcut function to get data in child controls +//----------------------------------------------------------------------------- +int EditablePanel::GetControlInt(const char *controlName, int defaultState) +{ + Panel *control = FindChildByName(controlName); + if (control) + { + KeyValues *data = new KeyValues("GetState"); + if (control->RequestInfo(data)) + { + int state = data->GetInt("state", defaultState); + data->deleteThis(); + return state; + } + } + return defaultState; +} + +//----------------------------------------------------------------------------- +// Purpose: Shortcut function to get data in child controls +//----------------------------------------------------------------------------- +const char *EditablePanel::GetControlString(const char *controlName, const char *defaultString) +{ + static char buf[512]; + GetControlString(controlName, buf, sizeof(buf) - 1, defaultString); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: Shortcut function to get data in child controls +//----------------------------------------------------------------------------- +void EditablePanel::GetControlString(const char *controlName, char *buf, int bufSize, const char *defaultString) +{ + Panel *control = FindChildByName(controlName); + KeyValues *data = new KeyValues("GetText"); + if (control && control->RequestInfo(data)) + { + Q_strncpy(buf, data->GetString("text", defaultString), bufSize); + } + else + { + // no value found, copy in default text + Q_strncpy(buf, defaultString, bufSize); + } + + // ensure null termination of string + buf[bufSize - 1] = 0; + + // free + data->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: localization variables (used in constructing UI strings) +//----------------------------------------------------------------------------- +void EditablePanel::SetDialogVariable(const char *varName, const char *value) +{ + GetDialogVariables()->SetString(varName, value); + ForceSubPanelsToUpdateWithNewDialogVariables(); +} + +//----------------------------------------------------------------------------- +// Purpose: localization variables (used in constructing UI strings) +//----------------------------------------------------------------------------- +void EditablePanel::SetDialogVariable(const char *varName, const wchar_t *value) +{ + GetDialogVariables()->SetWString(varName, value); + ForceSubPanelsToUpdateWithNewDialogVariables(); +} + +//----------------------------------------------------------------------------- +// Purpose: localization variables (used in constructing UI strings) +//----------------------------------------------------------------------------- +void EditablePanel::SetDialogVariable(const char *varName, int value) +{ + GetDialogVariables()->SetInt(varName, value); + ForceSubPanelsToUpdateWithNewDialogVariables(); +} + +//----------------------------------------------------------------------------- +// Purpose: localization variables (used in constructing UI strings) +//----------------------------------------------------------------------------- +void EditablePanel::SetDialogVariable(const char *varName, float value) +{ + GetDialogVariables()->SetFloat(varName, value); + ForceSubPanelsToUpdateWithNewDialogVariables(); +} + +//----------------------------------------------------------------------------- +// Purpose: redraws child panels with new localization vars +//----------------------------------------------------------------------------- +void EditablePanel::ForceSubPanelsToUpdateWithNewDialogVariables() +{ + if (m_pDialogVariables) + { + ipanel()->SendMessage(GetVPanel(), m_pDialogVariables, GetVPanel()); + for (int i = 0; i < ipanel()->GetChildCount(GetVPanel()); i++) + { + ipanel()->SendMessage(ipanel()->GetChild(GetVPanel(), i), m_pDialogVariables, GetVPanel()); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: lazy creation of localization vars object +//----------------------------------------------------------------------------- +KeyValues *EditablePanel::GetDialogVariables() +{ + if (m_pDialogVariables) + return m_pDialogVariables; + + m_pDialogVariables = new KeyValues("DialogVariables"); + return m_pDialogVariables; +} + +//----------------------------------------------------------------------------- +// Purpose: Virtual factory for control creation +//----------------------------------------------------------------------------- +Panel *EditablePanel::CreateControlByName(const char *controlName) +{ + Panel *fromFactory = CBuildFactoryHelper::InstancePanel( controlName ); + if ( fromFactory ) + { + return fromFactory; + } + + return NULL; +} diff --git a/mp/src/vgui2/vgui_controls/ExpandButton.cpp b/mp/src/vgui2/vgui_controls/ExpandButton.cpp index 5c891acc..a4489d0e 100644 --- a/mp/src/vgui2/vgui_controls/ExpandButton.cpp +++ b/mp/src/vgui2/vgui_controls/ExpandButton.cpp @@ -1,114 +1,114 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//===========================================================================// - -#include -#include - -#include -#include -#include - -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY( ExpandButton ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -ExpandButton::ExpandButton( Panel *parent, const char *panelName ) : ToggleButton( parent, panelName, "" ) -{ - m_bExpandable = true; - m_hFont = INVALID_FONT; -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -ExpandButton::~ExpandButton() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ExpandButton::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - m_Color = GetSchemeColor( "ExpandButton.Color", pScheme ); - m_hFont = pScheme->GetFont("Marlett", IsProportional() ); - - // don't draw a background - SetPaintBackgroundEnabled(false); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -IBorder *ExpandButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) -{ - return NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: Expand the button -//----------------------------------------------------------------------------- -void ExpandButton::SetSelected(bool state) -{ - if ( m_bExpandable && ( state != IsSelected() ) ) - { - // send a message saying we've been checked - KeyValues *msg = new KeyValues("Expanded", "state", (int)state); - PostActionSignal(msg); - - BaseClass::SetSelected(state); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: sets whether or not the state of the check can be changed -//----------------------------------------------------------------------------- -void ExpandButton::SetExpandable(bool state) -{ - m_bExpandable = state; - Repaint(); -} - - -void ExpandButton::Paint() -{ - surface()->DrawSetTextFont( m_hFont ); - - wchar_t code = IsSelected( ) ? L'6' : L'4'; - wchar_t pString[2] = { code, 0 }; - - // draw selected check - int tw, th, w, h; - GetSize( w, h ); - surface()->GetTextSize( m_hFont, pString, tw, th ); - surface()->DrawSetTextColor( m_Color ); - surface()->DrawSetTextPos( ( w - tw ) / 2, ( h - th ) / 2 ); - surface()->DrawUnicodeChar( code ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ExpandButton::OnExpanded(Panel *panel) -{ -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include +#include + +#include +#include +#include + +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY( ExpandButton ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ExpandButton::ExpandButton( Panel *parent, const char *panelName ) : ToggleButton( parent, panelName, "" ) +{ + m_bExpandable = true; + m_hFont = INVALID_FONT; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +ExpandButton::~ExpandButton() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ExpandButton::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + m_Color = GetSchemeColor( "ExpandButton.Color", pScheme ); + m_hFont = pScheme->GetFont("Marlett", IsProportional() ); + + // don't draw a background + SetPaintBackgroundEnabled(false); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IBorder *ExpandButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) +{ + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Expand the button +//----------------------------------------------------------------------------- +void ExpandButton::SetSelected(bool state) +{ + if ( m_bExpandable && ( state != IsSelected() ) ) + { + // send a message saying we've been checked + KeyValues *msg = new KeyValues("Expanded", "state", (int)state); + PostActionSignal(msg); + + BaseClass::SetSelected(state); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: sets whether or not the state of the check can be changed +//----------------------------------------------------------------------------- +void ExpandButton::SetExpandable(bool state) +{ + m_bExpandable = state; + Repaint(); +} + + +void ExpandButton::Paint() +{ + surface()->DrawSetTextFont( m_hFont ); + + wchar_t code = IsSelected( ) ? L'6' : L'4'; + wchar_t pString[2] = { code, 0 }; + + // draw selected check + int tw, th, w, h; + GetSize( w, h ); + surface()->GetTextSize( m_hFont, pString, tw, th ); + surface()->DrawSetTextColor( m_Color ); + surface()->DrawSetTextPos( ( w - tw ) / 2, ( h - th ) / 2 ); + surface()->DrawUnicodeChar( code ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ExpandButton::OnExpanded(Panel *panel) +{ +} diff --git a/mp/src/vgui2/vgui_controls/FileOpenDialog.cpp b/mp/src/vgui2/vgui_controls/FileOpenDialog.cpp index 8a1af699..ebe5eb0c 100644 --- a/mp/src/vgui2/vgui_controls/FileOpenDialog.cpp +++ b/mp/src/vgui2/vgui_controls/FileOpenDialog.cpp @@ -1,1701 +1,1701 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: Implementation of vgui generic open file dialog -// -// $NoKeywords: $ -//===========================================================================// - - -#define PROTECTED_THINGS_DISABLE - -#if !defined( _X360 ) && defined( WIN32 ) -#include "winlite.h" -#include -#elif defined( POSIX ) -#include -#define _stat stat -#define _wcsnicmp wcsncmp -#elif defined( _X360 ) -#else -#error -#endif - -#undef GetCurrentDirectory -#include "filesystem.h" -#include - -#include "tier1/utldict.h" -#include "tier1/utlstring.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined( _X360 ) -#include "xbox/xbox_win32stubs.h" -#undef GetCurrentDirectory -#endif - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -static int s_nLastSortColumn = 0; - -static int ListFileNameSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) -{ - NOTE_UNUSED( pPanel ); - - bool dir1 = item1.kv->GetInt("directory") == 1; - bool dir2 = item2.kv->GetInt("directory") == 1; - - // if they're both not directories of files, return if dir1 is a directory (before files) - if (dir1 != dir2) - { - return dir1 ? -1 : 1; - } - - const char *string1 = item1.kv->GetString("text"); - const char *string2 = item2.kv->GetString("text"); - - // YWB: Mimic windows behavior where filenames starting with numbers are sorted based on numeric part - int num1 = Q_atoi( string1 ); - int num2 = Q_atoi( string2 ); - - if ( num1 != 0 && - num2 != 0 ) - { - if ( num1 < num2 ) - return -1; - else if ( num1 > num2 ) - return 1; - } - - // Push numbers before everything else - if ( num1 != 0 ) - { - return -1; - } - - // Push numbers before everything else - if ( num2 != 0 ) - { - return 1; - } - - return Q_stricmp( string1, string2 ); -} - -static int ListBaseStringSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName ) -{ - bool dir1 = item1.kv->GetInt("directory") == 1; - bool dir2 = item2.kv->GetInt("directory") == 1; - - // if they're both not directories of files, return if dir1 is a directory (before files) - if (dir1 != dir2) - { - return -1; - } - - const char *string1 = item1.kv->GetString(fieldName); - const char *string2 = item2.kv->GetString(fieldName); - int cval = Q_stricmp(string1, string2); - if ( cval == 0 ) - { - // Use filename to break ties - return ListFileNameSortFunc( pPanel, item1, item2 ); - } - - return cval; -} - -static int ListBaseIntegerSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName ) -{ - bool dir1 = item1.kv->GetInt("directory") == 1; - bool dir2 = item2.kv->GetInt("directory") == 1; - - // if they're both not directories of files, return if dir1 is a directory (before files) - if (dir1 != dir2) - { - return -1; - } - - int i1 = item1.kv->GetInt(fieldName); - int i2 = item2.kv->GetInt(fieldName); - if ( i1 == i2 ) - { - // Use filename to break ties - return ListFileNameSortFunc( pPanel, item1, item2 ); - } - - return ( i1 < i2 ) ? -1 : 1; -} - -static int ListBaseInteger64SortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *lowfield, char const *highfield ) -{ - bool dir1 = item1.kv->GetInt("directory") == 1; - bool dir2 = item2.kv->GetInt("directory") == 1; - - // if they're both not directories of files, return if dir1 is a directory (before files) - if (dir1 != dir2) - { - return dir1 ? -1 : 1; - } - - uint32 l1 = item1.kv->GetInt(lowfield); - uint32 h1 = item1.kv->GetInt(highfield); - uint32 l2 = item2.kv->GetInt(lowfield); - uint32 h2 = item2.kv->GetInt(highfield); - uint64 i1 = (uint64)( (uint64)l1 | ( (uint64)h1 << 32 ) ); - uint64 i2 = (uint64)( (uint64)l2 | ( (uint64)h2 << 32 ) ); - - if ( i1 == i2 ) - { - // Use filename to break ties - return ListFileNameSortFunc( pPanel, item1, item2 ); - } - - return ( i1 < i2 ) ? -1 : 1; -} - - -static int ListFileSizeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) -{ - return ListBaseIntegerSortFunc( pPanel, item1, item2, "filesizeint" ); -} - -static int ListFileModifiedSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) -{ - // NOTE: Backward order to get most recent files first - return ListBaseInteger64SortFunc( pPanel, item2, item1, "modifiedint_low", "modifiedint_high" ); -} -static int ListFileCreatedSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) -{ - // NOTE: Backward order to get most recent files first - return ListBaseInteger64SortFunc( pPanel, item2, item1, "createdint_low", "createdint_high" ); -} -static int ListFileAttributesSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) -{ - return ListBaseStringSortFunc( pPanel, item1, item2, "attributes" ); -} -static int ListFileTypeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) -{ - return ListBaseStringSortFunc( pPanel, item1, item2, "type" ); -} - - - -namespace vgui -{ - -class FileCompletionMenu : public Menu -{ -public: - FileCompletionMenu(Panel *parent, const char *panelName) : Menu(parent, panelName) - { - } - - // override it so it doesn't request focus - virtual void SetVisible(bool state) - { - Panel::SetVisible(state); - } - -}; - - -//----------------------------------------------------------------------------- -// File completion edit text entry -//----------------------------------------------------------------------------- -class FileCompletionEdit : public TextEntry -{ - DECLARE_CLASS_SIMPLE( FileCompletionEdit, TextEntry ); - -public: - FileCompletionEdit(Panel *parent); - ~FileCompletionEdit(); - - int AddItem(const char *itemText, KeyValues *userData); - int AddItem(const wchar_t *itemText, KeyValues *userData); - void DeleteAllItems(); - int GetItemCount(); - int GetItemIDFromRow(int row); - int GetRowFromItemID(int itemID); - virtual void PerformLayout(); - void OnSetText(const wchar_t *newtext); - virtual void OnKillFocus(); - void HideMenu(void); - void ShowMenu(void); - virtual void OnKeyCodeTyped(KeyCode code); - MESSAGE_FUNC_INT( OnMenuItemHighlight, "MenuItemHighlight", itemID ); - -private: - FileCompletionMenu *m_pDropDown; -}; - - - -FileCompletionEdit::FileCompletionEdit(Panel *parent) : TextEntry(parent, NULL) -{ - m_pDropDown = new FileCompletionMenu(this, NULL); - m_pDropDown->AddActionSignalTarget(this); -} - -FileCompletionEdit::~FileCompletionEdit() -{ - delete m_pDropDown; -} - -int FileCompletionEdit::AddItem(const char *itemText, KeyValues *userData) -{ - // when the menu item is selected it will send the custom message "SetText" - return m_pDropDown->AddMenuItem(itemText, new KeyValues("SetText", "text", itemText), this, userData); -} -int FileCompletionEdit::AddItem(const wchar_t *itemText, KeyValues *userData) -{ - // add the element to the menu - // when the menu item is selected it will send the custom message "SetText" - KeyValues *kv = new KeyValues("SetText"); - kv->SetWString("text", itemText); - - // get an ansi version for the menuitem name - char ansi[128]; - g_pVGuiLocalize->ConvertUnicodeToANSI(itemText, ansi, sizeof(ansi)); - return m_pDropDown->AddMenuItem(ansi, kv, this, userData); -} - -void FileCompletionEdit::DeleteAllItems() -{ - m_pDropDown->DeleteAllItems(); -} - -int FileCompletionEdit::GetItemCount() -{ - return m_pDropDown->GetItemCount(); -} - -int FileCompletionEdit::GetItemIDFromRow(int row) -{ - // valid from [0, GetItemCount) - return m_pDropDown->GetMenuID(row); -} - -int FileCompletionEdit::GetRowFromItemID(int itemID) -{ - int i; - for (i=0;iGetMenuID(i) == itemID) - return i; - } - return -1; -} - -void FileCompletionEdit::PerformLayout() -{ - BaseClass::PerformLayout(); - - m_pDropDown->PositionRelativeToPanel( this, Menu::DOWN, 0 ); - - // reset the width of the drop down menu to be the width of this edit box - m_pDropDown->SetFixedWidth(GetWide()); - m_pDropDown->ForceCalculateWidth(); -} - -void FileCompletionEdit::OnSetText(const wchar_t *newtext) -{ - // see if the combobox text has changed, and if so, post a message detailing the new text - wchar_t wbuf[255]; - GetText( wbuf, 254 ); - - if ( wcscmp(wbuf, newtext) ) - { - // text has changed - SetText(newtext); - - // fire off that things have changed - PostActionSignal(new KeyValues("TextChanged", "text", newtext)); - Repaint(); - } -} - -void FileCompletionEdit::OnKillFocus() -{ - HideMenu(); - BaseClass::OnKillFocus(); -} - -void FileCompletionEdit::HideMenu(void) -{ - // hide the menu - m_pDropDown->SetVisible(false); -} - -void FileCompletionEdit::ShowMenu(void) -{ - // reset the dropdown's position - m_pDropDown->InvalidateLayout(); - - // make sure we're at the top of the draw order (and therefore our children as well) - // this important to make sure the menu will be drawn in the foreground - MoveToFront(); - - // reset the drop down - m_pDropDown->ClearCurrentlyHighlightedItem(); - - // limit it to only 6 - if (m_pDropDown->GetItemCount() > 6) - { - m_pDropDown->SetNumberOfVisibleItems(6); - } - else - { - m_pDropDown->SetNumberOfVisibleItems(m_pDropDown->GetItemCount()); - } - // show the menu - m_pDropDown->SetVisible(true); - - Repaint(); -} - -void FileCompletionEdit::OnKeyCodeTyped(KeyCode code) -{ - if ( code == KEY_DOWN ) - { - if (m_pDropDown->GetItemCount() > 0) - { - int menuID = m_pDropDown->GetCurrentlyHighlightedItem(); - int row = -1; - if ( menuID == -1 ) - { - row = m_pDropDown->GetItemCount() - 1; - } - else - { - row = GetRowFromItemID(menuID); - } - row++; - if (row == m_pDropDown->GetItemCount()) - { - row = 0; - } - menuID = GetItemIDFromRow(row); - m_pDropDown->SetCurrentlyHighlightedItem(menuID); - return; - } - } - else if ( code == KEY_UP ) - { - if (m_pDropDown->GetItemCount() > 0) - { - int menuID = m_pDropDown->GetCurrentlyHighlightedItem(); - int row = -1; - if ( menuID == -1 ) - { - row = 0; - } - else - { - row = GetRowFromItemID(menuID); - } - row--; - if ( row < 0 ) - { - row = m_pDropDown->GetItemCount() - 1; - } - menuID = GetItemIDFromRow(row); - m_pDropDown->SetCurrentlyHighlightedItem(menuID); - return; - } - } - else if ( code == KEY_ESCAPE ) - { - if ( m_pDropDown->IsVisible() ) - { - HideMenu(); - return; - } - } - BaseClass::OnKeyCodeTyped(code); - return; -} - -void FileCompletionEdit::OnMenuItemHighlight( int itemID ) -{ - char wbuf[80]; - if ( m_pDropDown->IsValidMenuID(itemID) ) - { - m_pDropDown->GetMenuItem(itemID)->GetText(wbuf, 80); - } - else - { - wbuf[0] = 0; - } - SetText(wbuf); - RequestFocus(); - GotoTextEnd(); -} - - -} // namespace vgui - - -//----------------------------------------------------------------------------- -// Dictionary of start dir contexts -//----------------------------------------------------------------------------- -static CUtlDict< CUtlString, unsigned short > s_StartDirContexts; - -struct ColumnInfo_t -{ - char const *columnName; - char const *columnText; - int startingWidth; - int minWidth; - int maxWidth; - int flags; - SortFunc *pfnSort; - Label::Alignment alignment; -}; - -static ColumnInfo_t g_ColInfo[] = -{ - { "text", "#FileOpenDialog_Col_Name", 175, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileNameSortFunc , Label::a_west }, - { "filesize", "#FileOpenDialog_Col_Size", 100, 20, 10000, 0, &ListFileSizeSortFunc , Label::a_east }, - { "type", "#FileOpenDialog_Col_Type", 150, 20, 10000, 0, &ListFileTypeSortFunc , Label::a_west }, - { "modified", "#FileOpenDialog_Col_DateModified", 125, 20, 10000, 0, &ListFileModifiedSortFunc , Label::a_west }, -// { "created", "#FileOpenDialog_Col_DateCreated", 125, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileCreatedSortFunc , Label::a_west }, - { "attributes", "#FileOpenDialog_Col_Attributes", 50, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileAttributesSortFunc , Label::a_west }, -}; - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -FileOpenDialog::FileOpenDialog(Panel *parent, const char *title, bool bOpenOnly, KeyValues* pContextKeyValues ) : - Frame( parent, "FileOpenDialog" ) -{ - m_DialogType = bOpenOnly ? FOD_OPEN : FOD_SAVE; - Init( title, pContextKeyValues ); -} - - -FileOpenDialog::FileOpenDialog( Panel *parent, const char *title, FileOpenDialogType_t type, KeyValues *pContextKeyValues ) : - Frame( parent, "FileOpenDialog" ) -{ - m_DialogType = type; - Init( title, pContextKeyValues ); -} - -void FileOpenDialog::Init( const char *title, KeyValues *pContextKeyValues ) -{ - m_bFileSelected = false; - SetTitle(title, true); - SetMinimizeButtonVisible(false); - -#ifdef POSIX - Q_strncpy(m_szLastPath, "/", sizeof( m_szLastPath ) ); -#else - Q_strncpy(m_szLastPath, "c:\\", sizeof( m_szLastPath ) ); -#endif - - m_pContextKeyValues = pContextKeyValues; - - // Get the list of available drives and put them in a menu here. - // Start with the directory we are in. - m_pFullPathEdit = new ComboBox(this, "FullPathEdit", 6, false); - m_pFullPathEdit->GetTooltip()->SetTooltipFormatToSingleLine(); - - // list panel - m_pFileList = new ListPanel(this, "FileList"); - for ( int i = 0; i < ARRAYSIZE( g_ColInfo ); ++i ) - { - const ColumnInfo_t& info = g_ColInfo[ i ]; - - m_pFileList->AddColumnHeader( i, info.columnName, info.columnText, info.startingWidth, info.minWidth, info.maxWidth, info.flags ); - m_pFileList->SetSortFunc( i, info.pfnSort ); - m_pFileList->SetColumnTextAlignment( i, info.alignment ); - } - - m_pFileList->SetSortColumn( s_nLastSortColumn ); - m_pFileList->SetMultiselectEnabled( false ); - - // file name edit box - m_pFileNameEdit = new FileCompletionEdit(this); - m_pFileNameEdit->AddActionSignalTarget(this); - - m_pFileTypeCombo = new ComboBox( this, "FileTypeCombo", 6, false ); - - switch ( m_DialogType ) - { - case FOD_OPEN: - m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this ); - break; - case FOD_SAVE: - m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Save", this ); - break; - case FOD_SELECT_DIRECTORY: - m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Select", this ); - m_pFileTypeCombo->SetVisible( false ); - break; - } - - m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this ); - m_pFolderUpButton = new Button( this, "FolderUpButton", "", this ); - m_pFolderUpButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_Up" ); - m_pNewFolderButton = new Button( this, "NewFolderButton", "", this ); - m_pNewFolderButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_NewFolder" ); - m_pOpenInExplorerButton = new Button( this, "OpenInExplorerButton", "", this ); - -#if defined ( OSX ) - m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInFinderButton" ); -#elif defined ( POSIX ) - m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInDesktopManagerButton" ); -#else // Assume Windows / Explorer - m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInExplorerButton" ); -#endif - - Label *lookIn = new Label( this, "LookInLabel", "#FileOpenDialog_Look_in" ); - Label *fileName = new Label( this, "FileNameLabel", - ( m_DialogType != FOD_SELECT_DIRECTORY ) ? "#FileOpenDialog_File_name" : "#FileOpenDialog_Directory_Name" ); - - m_pFolderIcon = new ImagePanel(NULL, "FolderIcon"); - - // set up the control's initial positions - SetSize( 600, 260 ); - - int nFileEditLeftSide = ( m_DialogType != FOD_SELECT_DIRECTORY ) ? 84 : 100; - int nFileNameWidth = ( m_DialogType != FOD_SELECT_DIRECTORY ) ? 72 : 82; - - m_pFullPathEdit->SetBounds(67, 32, 310, 24); - m_pFolderUpButton->SetBounds(362, 32, 24, 24); - m_pNewFolderButton->SetBounds(392, 32, 24, 24); - m_pOpenInExplorerButton->SetBounds(332, 32, 24, 24); - m_pFileList->SetBounds(10, 60, 406, 130); - m_pFileNameEdit->SetBounds( nFileEditLeftSide, 194, 238, 24); - m_pFileTypeCombo->SetBounds( nFileEditLeftSide, 224, 238, 24); - m_pOpenButton->SetBounds(336, 194, 74, 24); - m_pCancelButton->SetBounds(336, 224, 74, 24); - lookIn->SetBounds(10, 32, 55, 24); - fileName->SetBounds(10, 194, nFileNameWidth, 24); - - // set autolayout parameters - m_pFullPathEdit->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_RIGHT, 67, 32, -100, 0 ); - m_pFileNameEdit->SetAutoResize( Panel::PIN_BOTTOMLEFT, Panel::AUTORESIZE_RIGHT, nFileEditLeftSide, -42, -104, 0 ); - m_pFileTypeCombo->SetAutoResize( Panel::PIN_BOTTOMLEFT, Panel::AUTORESIZE_RIGHT, nFileEditLeftSide, -12, -104, 0 ); - m_pFileList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 10, 60, -10, -70 ); - - m_pFolderUpButton->SetPinCorner( Panel::PIN_TOPRIGHT, -40, 32 ); - m_pNewFolderButton->SetPinCorner( Panel::PIN_TOPRIGHT, -10, 32 ); - m_pOpenInExplorerButton->SetPinCorner( Panel::PIN_TOPRIGHT, -70, 32 ); - m_pOpenButton->SetPinCorner( Panel::PIN_BOTTOMRIGHT, -16, -42 ); - m_pCancelButton->SetPinCorner( Panel::PIN_BOTTOMRIGHT, -16, -12 ); - lookIn->SetPinCorner( Panel::PIN_TOPLEFT, 10, 32 ); - fileName->SetPinCorner( Panel::PIN_BOTTOMLEFT, 10, -42 ); - - // label settings - lookIn->SetContentAlignment(Label::a_west); - fileName->SetContentAlignment(Label::a_west); - - lookIn->SetAssociatedControl(m_pFullPathEdit); - fileName->SetAssociatedControl(m_pFileNameEdit); - - if ( m_DialogType != FOD_SELECT_DIRECTORY ) - { - Label *fileType = new Label(this, "FileTypeLabel", "#FileOpenDialog_File_type"); - fileType->SetBounds(10, 224, 72, 24); - fileType->SetPinCorner( Panel::PIN_BOTTOMLEFT, 10, -12 ); - fileType->SetContentAlignment(Label::a_west); - fileType->SetAssociatedControl( m_pFileTypeCombo ); - } - - // set tab positions - GetFocusNavGroup().SetDefaultButton(m_pOpenButton); - - m_pFileNameEdit->SetTabPosition(1); - m_pFileTypeCombo->SetTabPosition(2); - m_pOpenButton->SetTabPosition(3); - m_pCancelButton->SetTabPosition(4); - m_pFullPathEdit->SetTabPosition(5); - m_pFileList->SetTabPosition(6); - - m_pOpenButton->SetCommand( ( m_DialogType != FOD_SELECT_DIRECTORY ) ? new KeyValues( "OnOpen" ) : new KeyValues( "SelectFolder" ) ); - m_pCancelButton->SetCommand( "CloseModal" ); - m_pFolderUpButton->SetCommand( new KeyValues( "OnFolderUp" ) ); - m_pNewFolderButton->SetCommand( new KeyValues( "OnNewFolder" ) ); - m_pOpenInExplorerButton->SetCommand( new KeyValues( "OpenInExplorer" ) ); - - SetSize( 600, 384 ); - - m_nStartDirContext = s_StartDirContexts.InvalidIndex(); - - // Set our starting path to the current directory - char pLocalPath[255]; - g_pFullFileSystem->GetCurrentDirectory( pLocalPath , 255 ); - if ( !pLocalPath[0] || ( IsOSX() && V_strlen(pLocalPath) <= 2 ) ) - { - const char *pszHomeDir = getenv( "HOME" ); - V_strcpy_safe( pLocalPath, pszHomeDir ); - } - - SetStartDirectory( pLocalPath ); - - // Because these call through virtual functions, we can't issue them in the constructor, so we post a message to ourselves instead!! - PostMessage( GetVPanel(), new KeyValues( "PopulateFileList" ) ); - PostMessage( GetVPanel(), new KeyValues( "PopulateDriveList" ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -FileOpenDialog::~FileOpenDialog() -{ - s_nLastSortColumn = m_pFileList->GetSortColumn(); - if ( m_pContextKeyValues ) - { - m_pContextKeyValues->deleteThis(); - m_pContextKeyValues = NULL; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Apply scheme settings -//----------------------------------------------------------------------------- -void FileOpenDialog::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - m_pFolderIcon->SetImage(scheme()->GetImage("resource/icon_folder", false)); - m_pFolderUpButton->AddImage(scheme()->GetImage("resource/icon_folderup", false), -3); - m_pNewFolderButton->AddImage( scheme()->GetImage("resource/icon_newfolder", false), -3 ); - m_pOpenInExplorerButton->AddImage( scheme()->GetImage("resource/icon_play_once", false), -3 ); - - ImageList *imageList = new ImageList(false); - imageList->AddImage(scheme()->GetImage("resource/icon_file", false)); - imageList->AddImage(scheme()->GetImage("resource/icon_folder", false)); - imageList->AddImage(scheme()->GetImage("resource/icon_folder_selected", false)); - - m_pFileList->SetImageList(imageList, true); -} - - -//----------------------------------------------------------------------------- -// Prevent default button ('select') from getting triggered -// when selecting directories. Instead, open the directory -//----------------------------------------------------------------------------- -void FileOpenDialog::OnKeyCodeTyped(KeyCode code) -{ - if ( m_DialogType == FOD_SELECT_DIRECTORY && code == KEY_ENTER ) - { - OnOpen(); - } - else - { - BaseClass::OnKeyCodeTyped( code ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void FileOpenDialog::PopulateDriveList() -{ - char fullpath[MAX_PATH * 4]; - char subDirPath[MAX_PATH * 4]; - GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH); - Q_strncpy(subDirPath, fullpath, sizeof( subDirPath ) ); - - m_pFullPathEdit->DeleteAllItems(); - -#ifdef WIN32 - // populate the drive list - char buf[512]; - int len = system()->GetAvailableDrives(buf, 512); - char *pBuf = buf; - for (int i=0; i < len / 4; i++) - { - m_pFullPathEdit->AddItem(pBuf, NULL); - - // is this our drive - add all subdirectories - if (!_strnicmp(pBuf, fullpath, 2)) - { - int indent = 0; - char *pData = fullpath; - while (*pData) - { - if ( *pData == CORRECT_PATH_SEPARATOR ) - { - if (indent > 0) - { - memset(subDirPath, ' ', indent); - memcpy(subDirPath+indent, fullpath, pData-fullpath); - subDirPath[indent+pData-fullpath] = 0; - - m_pFullPathEdit->AddItem(subDirPath, NULL); - } - indent += 2; - } - pData++; - } - } - pBuf += 4; - } -#else - m_pFullPathEdit->AddItem("/", NULL); - - char *pData = fullpath; - int indent = 0; - while (*pData) - { - if (*pData == '/' && ( pData[1] != '\0' ) ) - { - if (indent > 0) - { - memset(subDirPath, ' ', indent); - memcpy(subDirPath+indent, fullpath, pData-fullpath); - subDirPath[indent+pData-fullpath] = 0; - - m_pFullPathEdit->AddItem(subDirPath, NULL); - } - indent += 2; - } - pData++; - } -#endif -} - - -//----------------------------------------------------------------------------- -// Purpose: Delete self on close -//----------------------------------------------------------------------------- -void FileOpenDialog::OnClose() -{ - s_nLastSortColumn = m_pFileList->GetSortColumn(); - if ( !m_bFileSelected ) - { - KeyValues *pKeyValues = new KeyValues( "FileSelectionCancelled" ); - PostActionSignal( pKeyValues ); - m_bFileSelected = true; - } - - m_pFileNameEdit->SetText(""); - m_pFileNameEdit->HideMenu(); - - if ( vgui::input()->GetAppModalSurface() == GetVPanel() ) - { - input()->SetAppModalSurface(NULL); - } - - BaseClass::OnClose(); -} - -void FileOpenDialog::OnFolderUp() -{ - MoveUpFolder(); - OnOpen(); -} - -void FileOpenDialog::OnInputCompleted( KeyValues *data ) -{ - if ( m_hInputDialog.Get() ) - { - delete m_hInputDialog.Get(); - } - - input()->SetAppModalSurface( m_SaveModal ); - m_SaveModal = 0; - - NewFolder( data->GetString( "text" ) ); - OnOpen(); -} - -void FileOpenDialog::OnInputCanceled() -{ - input()->SetAppModalSurface( m_SaveModal ); - m_SaveModal = 0; -} - -void FileOpenDialog::OnNewFolder() -{ - if ( m_hInputDialog.Get() ) - delete m_hInputDialog.Get(); - - m_hInputDialog = new InputDialog( this, "#FileOpenDialog_NewFolder_InputTitle", "#FileOpenDialog_NewFolderPrompt", "#FileOpenDialog_NewFolder_DefaultName" ); - if ( m_hInputDialog.Get() ) - { - m_SaveModal = input()->GetAppModalSurface(); - - KeyValues *pContextKeyValues = new KeyValues( "NewFolder" ); - m_hInputDialog->SetSmallCaption( true ); - m_hInputDialog->SetMultiline( false ); - m_hInputDialog->DoModal( pContextKeyValues ); - } -} - - -//----------------------------------------------------------------------------- -// Opens the current file/folder in explorer -//----------------------------------------------------------------------------- -void FileOpenDialog::OnOpenInExplorer() -{ - char pCurrentDirectory[MAX_PATH]; - GetCurrentDirectory( pCurrentDirectory, sizeof(pCurrentDirectory) ); -#if !defined( _X360 ) && defined( WIN32 ) - ShellExecute( NULL, NULL, pCurrentDirectory, NULL, NULL, SW_SHOWNORMAL ); -#elif defined( OSX ) - char szCmd[ MAX_PATH * 2]; - Q_snprintf( szCmd, sizeof(szCmd), "/usr/bin/open \"%s\"", pCurrentDirectory ); - ::system( szCmd ); -#elif defined( LINUX ) - char szCmd[ MAX_PATH * 2 ]; - Q_snprintf( szCmd, sizeof(szCmd), "xdg-open \"%s\" &", pCurrentDirectory ); - ::system( szCmd ); -#endif -} - - -//----------------------------------------------------------------------------- -// Purpose: Handle for button commands -//----------------------------------------------------------------------------- -void FileOpenDialog::OnCommand(const char *command) -{ - if (!stricmp(command, "Cancel")) - { - Close(); - } - else - { - BaseClass::OnCommand(command); - } -} - - -//----------------------------------------------------------------------------- -// Sets the start directory context (and resets the start directory in the process) -//----------------------------------------------------------------------------- -void FileOpenDialog::SetStartDirectoryContext( const char *pStartDirContext, const char *pDefaultDir ) -{ - bool bUseCurrentDirectory = true; - if ( pStartDirContext ) - { - m_nStartDirContext = s_StartDirContexts.Find( pStartDirContext ); - if ( m_nStartDirContext == s_StartDirContexts.InvalidIndex() ) - { - m_nStartDirContext = s_StartDirContexts.Insert( pStartDirContext, pDefaultDir ); - bUseCurrentDirectory = ( pDefaultDir == NULL ); - } - else - { - bUseCurrentDirectory = false; - } - } - else - { - m_nStartDirContext = s_StartDirContexts.InvalidIndex(); - } - - if ( !bUseCurrentDirectory ) - { - SetStartDirectory( s_StartDirContexts[m_nStartDirContext].Get() ); - } - else - { - // Set our starting path to the current directory - char pLocalPath[255]; - g_pFullFileSystem->GetCurrentDirectory( pLocalPath, 255 ); - SetStartDirectory( pLocalPath ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Set the starting directory of the file search. -//----------------------------------------------------------------------------- -void FileOpenDialog::SetStartDirectory( const char *dir ) -{ - m_pFullPathEdit->SetText(dir); - - // ensure it's validity - ValidatePath(); - - // Store this in the start directory list - if ( m_nStartDirContext != s_StartDirContexts.InvalidIndex() ) - { - char pDirBuf[MAX_PATH]; - GetCurrentDirectory( pDirBuf, sizeof(pDirBuf) ); - s_StartDirContexts[ m_nStartDirContext ] = pDirBuf; - } - - PopulateDriveList(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Add filters for the drop down combo box -//----------------------------------------------------------------------------- -void FileOpenDialog::AddFilter( const char *filter, const char *filterName, bool bActive, const char *pFilterInfo ) -{ - KeyValues *kv = new KeyValues("item"); - kv->SetString( "filter", filter ); - kv->SetString( "filterinfo", pFilterInfo ); - int itemID = m_pFileTypeCombo->AddItem(filterName, kv); - if ( bActive ) - { - m_pFileTypeCombo->ActivateItem(itemID); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Activate the dialog -//----------------------------------------------------------------------------- -void FileOpenDialog::DoModal( bool bUnused ) -{ - m_bFileSelected = false; - m_pFileNameEdit->RequestFocus(); - BaseClass::DoModal(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Gets the directory this is currently in -//----------------------------------------------------------------------------- -void FileOpenDialog::GetCurrentDirectory(char *buf, int bufSize) -{ - // get the text from the text entry - m_pFullPathEdit->GetText(buf, bufSize); -} - - -//----------------------------------------------------------------------------- -// Purpose: Get the last selected file name -//----------------------------------------------------------------------------- -void FileOpenDialog::GetSelectedFileName(char *buf, int bufSize) -{ - m_pFileNameEdit->GetText(buf, bufSize); -} - - -//----------------------------------------------------------------------------- -// Creates a new folder -//----------------------------------------------------------------------------- -void FileOpenDialog::NewFolder( char const *folderName ) -{ - char pCurrentDirectory[MAX_PATH]; - GetCurrentDirectory( pCurrentDirectory, sizeof(pCurrentDirectory) ); - - char pFullPath[MAX_PATH]; - char pNewFolderName[MAX_PATH]; - Q_strncpy( pNewFolderName, folderName, sizeof(pNewFolderName) ); - int i = 2; - do - { - Q_MakeAbsolutePath( pFullPath, sizeof(pFullPath), pNewFolderName, pCurrentDirectory ); - if ( !g_pFullFileSystem->FileExists( pFullPath, NULL ) && - !g_pFullFileSystem->IsDirectory( pFullPath, NULL ) ) - { - g_pFullFileSystem->CreateDirHierarchy( pFullPath, NULL ); - m_pFileNameEdit->SetText( pNewFolderName ); - return; - } - - Q_snprintf( pNewFolderName, sizeof(pNewFolderName), "%s%d", folderName, i ); - ++i; - } while ( i <= 999 ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Move the directory structure up -//----------------------------------------------------------------------------- -void FileOpenDialog::MoveUpFolder() -{ - char fullpath[MAX_PATH * 4]; - GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH); - - Q_StripLastDir( fullpath, sizeof( fullpath ) ); - // append a trailing slash - Q_AppendSlash( fullpath, sizeof( fullpath ) ); - - SetStartDirectory(fullpath); - PopulateFileList(); - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Validate that the current path is valid -//----------------------------------------------------------------------------- -void FileOpenDialog::ValidatePath() -{ - char fullpath[MAX_PATH * 4]; - GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH); - Q_RemoveDotSlashes( fullpath ); - - // when statting a directory on Windows, you want to include - // the terminal slash exactly when you are statting a root - // directory. PKMN. -#ifdef _WIN32 - if ( Q_strlen( fullpath ) != 3 ) - { - Q_StripTrailingSlash( fullpath ); - } -#endif - // cleanup the path, we format tabs into the list to make it pretty in the UI - Q_StripPrecedingAndTrailingWhitespace( fullpath ); - - struct _stat buf; - if ( ( 0 == _stat( fullpath, &buf ) ) && - ( 0 != ( buf.st_mode & S_IFDIR ) ) ) - { - Q_AppendSlash( fullpath, sizeof( fullpath ) ); - Q_strncpy(m_szLastPath, fullpath, sizeof(m_szLastPath)); - } - else - { - // failed to load file, use the previously successful path - } - - m_pFullPathEdit->SetText(m_szLastPath); - m_pFullPathEdit->GetTooltip()->SetText(m_szLastPath); -} - -#ifdef WIN32 -const char *GetAttributesAsString( DWORD dwAttributes ) -{ - static char out[ 256 ]; - out[ 0 ] = 0; - if ( dwAttributes & FILE_ATTRIBUTE_ARCHIVE ) - { - Q_strncat( out, "A", sizeof( out ), COPY_ALL_CHARACTERS ); - } - if ( dwAttributes & FILE_ATTRIBUTE_COMPRESSED ) - { - Q_strncat( out, "C", sizeof( out ), COPY_ALL_CHARACTERS ); - } - if ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY ) - { - Q_strncat( out, "D", sizeof( out ), COPY_ALL_CHARACTERS ); - } - if ( dwAttributes & FILE_ATTRIBUTE_HIDDEN ) - { - Q_strncat( out, "H", sizeof( out ), COPY_ALL_CHARACTERS ); - } - if ( dwAttributes & FILE_ATTRIBUTE_READONLY ) - { - Q_strncat( out, "R", sizeof( out ), COPY_ALL_CHARACTERS ); - } - if ( dwAttributes & FILE_ATTRIBUTE_SYSTEM ) - { - Q_strncat( out, "S", sizeof( out ), COPY_ALL_CHARACTERS ); - } - if ( dwAttributes & FILE_ATTRIBUTE_TEMPORARY ) - { - Q_strncat( out, "T", sizeof( out ), COPY_ALL_CHARACTERS ); - } - return out; -} - -const char *GetFileTimetamp( FILETIME ft ) -{ - SYSTEMTIME local; - FILETIME localFileTime; - FileTimeToLocalFileTime( &ft, &localFileTime ); - FileTimeToSystemTime( &localFileTime, &local ); - - static char out[ 256 ]; - - bool am = true; - WORD hour = local.wHour; - if ( hour >= 12 ) - { - am = false; - // 12:42 pm displays as 12:42 pm - // 13:42 pm displays as 1:42 pm - if ( hour > 12 ) - { - hour -= 12; - } - } - Q_snprintf( out, sizeof( out ), "%d/%02d/%04d %d:%02d %s", - local.wMonth, - local.wDay, - local.wYear, - hour, - local.wMinute, - am ? "AM" : "PM" // TODO: Localize this? - ); - return out; -} -#endif - -//----------------------------------------------------------------------------- -// Purpose: Fill the filelist with the names of all the files in the current directory -//----------------------------------------------------------------------------- -#define MAX_FILTER_LENGTH 255 -void FileOpenDialog::PopulateFileList() -{ - // clear the current list - m_pFileList->DeleteAllItems(); - - FileFindHandle_t findHandle; - char pszFileModified[64]; - - // get the current directory - char currentDir[MAX_PATH * 4]; - char dir[MAX_PATH * 4]; - char filterList[MAX_FILTER_LENGTH+1]; - GetCurrentDirectory(currentDir, sizeof(dir)); - - KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData(); - if (combokv) - { - Q_strncpy(filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH); - } - else - { - // add wildcard for search - Q_strncpy(filterList, "*\0", MAX_FILTER_LENGTH); - } - - - char *filterPtr = filterList; - KeyValues *kv = new KeyValues("item"); - - if ( m_DialogType != FOD_SELECT_DIRECTORY ) - { - while ((filterPtr != NULL) && (*filterPtr != 0)) - { - // parse the next filter in the list. - char curFilter[MAX_FILTER_LENGTH]; - curFilter[0] = 0; - int i = 0; - while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' '))) - { - ++filterPtr; - } - while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' ')) - { - curFilter[i++] = *(filterPtr++); - } - curFilter[i] = 0; - - if (curFilter[0] == 0) - { - break; - } - - Q_snprintf( dir, MAX_PATH*4, "%s%s", currentDir, curFilter ); - - // Open the directory and walk it, loading files - const char *pszFileName = g_pFullFileSystem->FindFirst( dir, &findHandle ); - while ( pszFileName ) - { - if ( !g_pFullFileSystem->FindIsDirectory( findHandle ) - || !IsOSX() - || ( IsOSX() && g_pFullFileSystem->FindIsDirectory( findHandle ) && Q_stristr( pszFileName, ".app" ) ) ) - { - char pFullPath[MAX_PATH]; - Q_snprintf( pFullPath, MAX_PATH, "%s%s", currentDir, pszFileName ); - - // add the file to the list - kv->SetString( "text", pszFileName ); - - kv->SetInt( "image", 1 ); - - IImage *image = surface()->GetIconImageForFullPath( pFullPath ); - - if ( image ) - { - kv->SetPtr( "iconImage", (void *)image ); - } - - kv->SetInt("imageSelected", 1); - kv->SetInt("directory", 0); - - kv->SetString( "filesize", Q_pretifymem( g_pFullFileSystem->Size( pFullPath ), 0, true ) ); - Q_FixSlashes( pFullPath ); - wchar_t fileType[ 80 ]; - g_pFullFileSystem->GetFileTypeForFullPath( pFullPath, fileType, sizeof( fileType ) ); - kv->SetWString( "type", fileType ); - - kv->SetString( "attributes", g_pFullFileSystem->IsFileWritable( pFullPath )? "" : "R" ); - - long fileModified = g_pFullFileSystem->GetFileTime( pFullPath ); - g_pFullFileSystem->FileTimeToString( pszFileModified, sizeof( pszFileModified ), fileModified ); - kv->SetString( "modified", pszFileModified ); - -// kv->SetString( "created", GetFileTimetamp( findData.ftCreationTime ) ); - - m_pFileList->AddItem(kv, 0, false, false); - } - - pszFileName = g_pFullFileSystem->FindNext( findHandle ); - } - g_pFullFileSystem->FindClose( findHandle ); - } - } - - // find all the directories - GetCurrentDirectory( dir, sizeof(dir) ); - Q_strncat(dir, "*", sizeof( dir ), COPY_ALL_CHARACTERS); - - const char *pszFileName = g_pFullFileSystem->FindFirst( dir, &findHandle ); - while ( pszFileName ) - { - if ( pszFileName[0] != '.' && g_pFullFileSystem->FindIsDirectory( findHandle ) - && ( !IsOSX() || ( IsOSX() && !Q_stristr( pszFileName, ".app" ) ) ) ) - { - char pFullPath[MAX_PATH]; - Q_snprintf( pFullPath, MAX_PATH, "%s%s", currentDir, pszFileName ); - - kv->SetString("text", pszFileName ); - kv->SetPtr( "iconImage", (void *)NULL ); - kv->SetInt("image", 2); - kv->SetInt("imageSelected", 3); - kv->SetInt("directory", 1); - - kv->SetString( "filesize", "" ); - kv->SetString( "type", "#FileOpenDialog_FileType_Folder" ); - - kv->SetString( "attributes", g_pFullFileSystem->IsFileWritable( pFullPath )? "" : "R" ); - - long fileModified = g_pFullFileSystem->GetFileTime( pFullPath ); - g_pFullFileSystem->FileTimeToString( pszFileModified, sizeof( pszFileModified ), fileModified ); - kv->SetString( "modified", pszFileModified ); - -// kv->SetString( "created", GetFileTimetamp( findData.ftCreationTime ) ); - - m_pFileList->AddItem( kv, 0, false, false ); - } - - pszFileName = g_pFullFileSystem->FindNext( findHandle ); - } - g_pFullFileSystem->FindClose( findHandle ); - - kv->deleteThis(); - m_pFileList->SortList(); -} - - -//----------------------------------------------------------------------------- -// Does the specified extension match something in the filter list? -//----------------------------------------------------------------------------- -bool FileOpenDialog::ExtensionMatchesFilter( const char *pExt ) -{ - KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData(); - if ( !combokv ) - return true; - - char filterList[MAX_FILTER_LENGTH+1]; - Q_strncpy( filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH ); - - char *filterPtr = filterList; - while ((filterPtr != NULL) && (*filterPtr != 0)) - { - // parse the next filter in the list. - char curFilter[MAX_FILTER_LENGTH]; - curFilter[0] = 0; - int i = 0; - while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' '))) - { - ++filterPtr; - } - while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' ')) - { - curFilter[i++] = *(filterPtr++); - } - curFilter[i] = 0; - - if (curFilter[0] == 0) - break; - - if ( !Q_stricmp( curFilter, "*" ) || !Q_stricmp( curFilter, "*.*" ) ) - return true; - - // FIXME: This isn't exactly right, but tough cookies; - // it assumes the first two characters of the filter are *. - Assert( curFilter[0] == '*' && curFilter[1] == '.' ); - if ( !Q_stricmp( &curFilter[2], pExt ) ) - return true; - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Choose the first non *.* filter in the filter list -//----------------------------------------------------------------------------- -void FileOpenDialog::ChooseExtension( char *pExt, int nBufLen ) -{ - pExt[0] = 0; - - KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData(); - if ( !combokv ) - return; - - char filterList[MAX_FILTER_LENGTH+1]; - Q_strncpy( filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH ); - - char *filterPtr = filterList; - while ((filterPtr != NULL) && (*filterPtr != 0)) - { - // parse the next filter in the list. - char curFilter[MAX_FILTER_LENGTH]; - curFilter[0] = 0; - int i = 0; - while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' '))) - { - ++filterPtr; - } - while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' ')) - { - curFilter[i++] = *(filterPtr++); - } - curFilter[i] = 0; - - if (curFilter[0] == 0) - break; - - if ( !Q_stricmp( curFilter, "*" ) || !Q_stricmp( curFilter, "*.*" ) ) - continue; - - // FIXME: This isn't exactly right, but tough cookies; - // it assumes the first two characters of the filter are *. - Assert( curFilter[0] == '*' && curFilter[1] == '.' ); - Q_strncpy( pExt, &curFilter[1], nBufLen ); - break; - } -} - - -//----------------------------------------------------------------------------- -// Saves the file to the start dir context -//----------------------------------------------------------------------------- -void FileOpenDialog::SaveFileToStartDirContext( const char *pFullPath ) -{ - if ( m_nStartDirContext == s_StartDirContexts.InvalidIndex() ) - return; - - char pPath[MAX_PATH]; - pPath[0] = 0; - Q_ExtractFilePath( pFullPath, pPath, sizeof(pPath) ); - s_StartDirContexts[ m_nStartDirContext ] = pPath; -} - - -//----------------------------------------------------------------------------- -// Posts a file selected message -//----------------------------------------------------------------------------- -void FileOpenDialog::PostFileSelectedMessage( const char *pFileName ) -{ - m_bFileSelected = true; - - // open the file! - KeyValues *pKeyValues = new KeyValues( "FileSelected", "fullpath", pFileName ); - KeyValues *pFilterKeys = m_pFileTypeCombo->GetActiveItemUserData(); - const char *pFilterInfo = pFilterKeys ? pFilterKeys->GetString( "filterinfo", NULL ) : NULL; - if ( pFilterInfo ) - { - pKeyValues->SetString( "filterinfo", pFilterInfo ); - } - if ( m_pContextKeyValues ) - { - pKeyValues->AddSubKey( m_pContextKeyValues ); - m_pContextKeyValues = NULL; - } - PostActionSignal( pKeyValues ); - CloseModal(); -} - - -//----------------------------------------------------------------------------- -// Selects the current folder -//----------------------------------------------------------------------------- -void FileOpenDialog::OnSelectFolder() -{ - ValidatePath(); - - // construct a file path - char pFileName[MAX_PATH]; - GetSelectedFileName( pFileName, sizeof( pFileName ) ); - - Q_StripTrailingSlash( pFileName ); - - if ( !stricmp(pFileName, "..") ) - { - MoveUpFolder(); - - // clear the name text - m_pFileNameEdit->SetText(""); - return; - } - - if ( !stricmp(pFileName, ".") ) - { - // clear the name text - m_pFileNameEdit->SetText(""); - return; - } - - // Compute the full path - char pFullPath[MAX_PATH * 4]; - if ( !Q_IsAbsolutePath( pFileName ) ) - { - GetCurrentDirectory(pFullPath, sizeof(pFullPath) - MAX_PATH); - strcat( pFullPath, pFileName ); - if ( !pFileName[0] ) - { - Q_StripTrailingSlash( pFullPath ); - } - } - else - { - Q_strncpy( pFullPath, pFileName, sizeof(pFullPath) ); - } - - if ( g_pFullFileSystem->FileExists( pFullPath ) ) - { - // open the file! - SaveFileToStartDirContext( pFullPath ); - PostFileSelectedMessage( pFullPath ); - return; - } - - PopulateDriveList(); - PopulateFileList(); - InvalidateLayout(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Handle the open button being pressed -// checks on what has changed and acts accordingly -//----------------------------------------------------------------------------- -void FileOpenDialog::OnOpen() -{ - ValidatePath(); - - // construct a file path - char pFileName[MAX_PATH]; - GetSelectedFileName( pFileName, sizeof( pFileName ) ); - - int nLen = Q_strlen( pFileName ); - bool bSpecifiedDirectory = ( pFileName[nLen-1] == '/' || pFileName[nLen-1] == '\\' ) && (!IsOSX() || ( IsOSX() && !Q_stristr( pFileName, ".app" ) ) ); - Q_StripTrailingSlash( pFileName ); - - if ( !stricmp(pFileName, "..") ) - { - MoveUpFolder(); - - // clear the name text - m_pFileNameEdit->SetText(""); - return; - } - - if ( !stricmp(pFileName, ".") ) - { - // clear the name text - m_pFileNameEdit->SetText(""); - return; - } - - // Compute the full path - char pFullPath[MAX_PATH * 4]; - if ( !Q_IsAbsolutePath( pFileName ) ) - { - GetCurrentDirectory(pFullPath, sizeof(pFullPath) - MAX_PATH); - Q_AppendSlash( pFullPath, sizeof( pFullPath ) ); - strcat(pFullPath, pFileName); - if ( !pFileName[0] ) - { - Q_StripTrailingSlash( pFullPath ); - } - } - else - { - Q_strncpy( pFullPath, pFileName, sizeof(pFullPath) ); - } - - Q_StripTrailingSlash( pFullPath ); - - // when statting a directory on Windows, you want to include - // the terminal slash exactly when you are statting a root - // directory. PKMN. -#ifdef _WIN32 - if ( Q_strlen( pFullPath ) == 2 ) - { - Q_AppendSlash( pFullPath, Q_ARRAYSIZE( pFullPath ) ); - } -#endif - - - // If the name specified is a directory, then change directory - if ( g_pFullFileSystem->IsDirectory( pFullPath, NULL ) && - ( !IsOSX() || ( IsOSX() && !Q_stristr( pFullPath, ".app" ) ) ) ) - { - // it's a directory; change to the specified directory - if ( !bSpecifiedDirectory ) - { - Q_AppendSlash( pFullPath, Q_ARRAYSIZE( pFullPath ) ); - } - SetStartDirectory( pFullPath ); - - // clear the name text - m_pFileNameEdit->SetText(""); - m_pFileNameEdit->HideMenu(); - - PopulateDriveList(); - PopulateFileList(); - InvalidateLayout(); - return; - } - else if ( bSpecifiedDirectory ) - { - PopulateDriveList(); - PopulateFileList(); - InvalidateLayout(); - return; - } - - // Append suffix of the first filter that isn't *.* - char extension[512]; - Q_ExtractFileExtension( pFullPath, extension, sizeof(extension) ); - if ( !ExtensionMatchesFilter( extension ) ) - { - ChooseExtension( extension, sizeof(extension) ); - Q_SetExtension( pFullPath, extension, sizeof(pFullPath) ); - } - - if ( g_pFullFileSystem->FileExists( pFullPath ) ) - { - // open the file! - SaveFileToStartDirContext( pFullPath ); - PostFileSelectedMessage( pFullPath ); - return; - } - - // file not found - if ( ( m_DialogType == FOD_SAVE ) && pFileName[0] ) - { - // open the file! - SaveFileToStartDirContext( pFullPath ); - PostFileSelectedMessage( pFullPath ); - return; - } - - PopulateDriveList(); - PopulateFileList(); - InvalidateLayout(); -} - - -//----------------------------------------------------------------------------- -// Purpose: using the file edit box as a prefix, create a menu of all possible files -//----------------------------------------------------------------------------- -void FileOpenDialog::PopulateFileNameCompletion() -{ - char buf[80]; - m_pFileNameEdit->GetText(buf, 80); - wchar_t wbuf[80]; - m_pFileNameEdit->GetText(wbuf, 80); - int bufLen = wcslen(wbuf); - - // delete all items before we check if there's even a string - m_pFileNameEdit->DeleteAllItems(); - - // no string at all - don't show even bother showing it - if (bufLen == 0) - { - m_pFileNameEdit->HideMenu(); - return; - } - - // what files use current string as a prefix? - int nCount = m_pFileList->GetItemCount(); - int i; - for ( i = 0 ; i < nCount ; i++ ) - { - KeyValues *kv = m_pFileList->GetItem(m_pFileList->GetItemIDFromRow(i)); - const wchar_t *wszString = kv->GetWString("text"); - if ( !_wcsnicmp(wbuf, wszString, bufLen) ) - { - m_pFileNameEdit->AddItem(wszString, NULL); - } - } - - // if there are any items - show the menu - if ( m_pFileNameEdit->GetItemCount() > 0 ) - { - m_pFileNameEdit->ShowMenu(); - } - else - { - m_pFileNameEdit->HideMenu(); - } - - m_pFileNameEdit->InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Handle an item in the list being selected -//----------------------------------------------------------------------------- -void FileOpenDialog::OnItemSelected() -{ - // make sure only one item is selected - if (m_pFileList->GetSelectedItemsCount() != 1) - { - m_pFileNameEdit->SetText(""); - } - else - { - // put the file name into the text edit box - KeyValues *data = m_pFileList->GetItem(m_pFileList->GetSelectedItem(0)); - m_pFileNameEdit->SetText(data->GetString("text")); - } - - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Handle an item in the Drive combo box being selected -//----------------------------------------------------------------------------- -void FileOpenDialog::OnTextChanged(KeyValues *kv) -{ - Panel *pPanel = (Panel *) kv->GetPtr("panel", NULL); - - // first check which control had its text changed! - if (pPanel == m_pFullPathEdit) - { - m_pFileNameEdit->HideMenu(); - m_pFileNameEdit->SetText(""); - OnOpen(); - } - else if (pPanel == m_pFileNameEdit) - { - PopulateFileNameCompletion(); - } - else if (pPanel == m_pFileTypeCombo) - { - m_pFileNameEdit->HideMenu(); - PopulateFileList(); - } -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Implementation of vgui generic open file dialog +// +// $NoKeywords: $ +//===========================================================================// + + +#define PROTECTED_THINGS_DISABLE + +#if !defined( _X360 ) && defined( WIN32 ) +#include "winlite.h" +#include +#elif defined( POSIX ) +#include +#define _stat stat +#define _wcsnicmp wcsncmp +#elif defined( _X360 ) +#else +#error +#endif + +#undef GetCurrentDirectory +#include "filesystem.h" +#include + +#include "tier1/utldict.h" +#include "tier1/utlstring.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#undef GetCurrentDirectory +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +static int s_nLastSortColumn = 0; + +static int ListFileNameSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + NOTE_UNUSED( pPanel ); + + bool dir1 = item1.kv->GetInt("directory") == 1; + bool dir2 = item2.kv->GetInt("directory") == 1; + + // if they're both not directories of files, return if dir1 is a directory (before files) + if (dir1 != dir2) + { + return dir1 ? -1 : 1; + } + + const char *string1 = item1.kv->GetString("text"); + const char *string2 = item2.kv->GetString("text"); + + // YWB: Mimic windows behavior where filenames starting with numbers are sorted based on numeric part + int num1 = Q_atoi( string1 ); + int num2 = Q_atoi( string2 ); + + if ( num1 != 0 && + num2 != 0 ) + { + if ( num1 < num2 ) + return -1; + else if ( num1 > num2 ) + return 1; + } + + // Push numbers before everything else + if ( num1 != 0 ) + { + return -1; + } + + // Push numbers before everything else + if ( num2 != 0 ) + { + return 1; + } + + return Q_stricmp( string1, string2 ); +} + +static int ListBaseStringSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName ) +{ + bool dir1 = item1.kv->GetInt("directory") == 1; + bool dir2 = item2.kv->GetInt("directory") == 1; + + // if they're both not directories of files, return if dir1 is a directory (before files) + if (dir1 != dir2) + { + return -1; + } + + const char *string1 = item1.kv->GetString(fieldName); + const char *string2 = item2.kv->GetString(fieldName); + int cval = Q_stricmp(string1, string2); + if ( cval == 0 ) + { + // Use filename to break ties + return ListFileNameSortFunc( pPanel, item1, item2 ); + } + + return cval; +} + +static int ListBaseIntegerSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName ) +{ + bool dir1 = item1.kv->GetInt("directory") == 1; + bool dir2 = item2.kv->GetInt("directory") == 1; + + // if they're both not directories of files, return if dir1 is a directory (before files) + if (dir1 != dir2) + { + return -1; + } + + int i1 = item1.kv->GetInt(fieldName); + int i2 = item2.kv->GetInt(fieldName); + if ( i1 == i2 ) + { + // Use filename to break ties + return ListFileNameSortFunc( pPanel, item1, item2 ); + } + + return ( i1 < i2 ) ? -1 : 1; +} + +static int ListBaseInteger64SortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *lowfield, char const *highfield ) +{ + bool dir1 = item1.kv->GetInt("directory") == 1; + bool dir2 = item2.kv->GetInt("directory") == 1; + + // if they're both not directories of files, return if dir1 is a directory (before files) + if (dir1 != dir2) + { + return dir1 ? -1 : 1; + } + + uint32 l1 = item1.kv->GetInt(lowfield); + uint32 h1 = item1.kv->GetInt(highfield); + uint32 l2 = item2.kv->GetInt(lowfield); + uint32 h2 = item2.kv->GetInt(highfield); + uint64 i1 = (uint64)( (uint64)l1 | ( (uint64)h1 << 32 ) ); + uint64 i2 = (uint64)( (uint64)l2 | ( (uint64)h2 << 32 ) ); + + if ( i1 == i2 ) + { + // Use filename to break ties + return ListFileNameSortFunc( pPanel, item1, item2 ); + } + + return ( i1 < i2 ) ? -1 : 1; +} + + +static int ListFileSizeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + return ListBaseIntegerSortFunc( pPanel, item1, item2, "filesizeint" ); +} + +static int ListFileModifiedSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + // NOTE: Backward order to get most recent files first + return ListBaseInteger64SortFunc( pPanel, item2, item1, "modifiedint_low", "modifiedint_high" ); +} +static int ListFileCreatedSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + // NOTE: Backward order to get most recent files first + return ListBaseInteger64SortFunc( pPanel, item2, item1, "createdint_low", "createdint_high" ); +} +static int ListFileAttributesSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + return ListBaseStringSortFunc( pPanel, item1, item2, "attributes" ); +} +static int ListFileTypeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + return ListBaseStringSortFunc( pPanel, item1, item2, "type" ); +} + + + +namespace vgui +{ + +class FileCompletionMenu : public Menu +{ +public: + FileCompletionMenu(Panel *parent, const char *panelName) : Menu(parent, panelName) + { + } + + // override it so it doesn't request focus + virtual void SetVisible(bool state) + { + Panel::SetVisible(state); + } + +}; + + +//----------------------------------------------------------------------------- +// File completion edit text entry +//----------------------------------------------------------------------------- +class FileCompletionEdit : public TextEntry +{ + DECLARE_CLASS_SIMPLE( FileCompletionEdit, TextEntry ); + +public: + FileCompletionEdit(Panel *parent); + ~FileCompletionEdit(); + + int AddItem(const char *itemText, KeyValues *userData); + int AddItem(const wchar_t *itemText, KeyValues *userData); + void DeleteAllItems(); + int GetItemCount(); + int GetItemIDFromRow(int row); + int GetRowFromItemID(int itemID); + virtual void PerformLayout(); + void OnSetText(const wchar_t *newtext); + virtual void OnKillFocus(); + void HideMenu(void); + void ShowMenu(void); + virtual void OnKeyCodeTyped(KeyCode code); + MESSAGE_FUNC_INT( OnMenuItemHighlight, "MenuItemHighlight", itemID ); + +private: + FileCompletionMenu *m_pDropDown; +}; + + + +FileCompletionEdit::FileCompletionEdit(Panel *parent) : TextEntry(parent, NULL) +{ + m_pDropDown = new FileCompletionMenu(this, NULL); + m_pDropDown->AddActionSignalTarget(this); +} + +FileCompletionEdit::~FileCompletionEdit() +{ + delete m_pDropDown; +} + +int FileCompletionEdit::AddItem(const char *itemText, KeyValues *userData) +{ + // when the menu item is selected it will send the custom message "SetText" + return m_pDropDown->AddMenuItem(itemText, new KeyValues("SetText", "text", itemText), this, userData); +} +int FileCompletionEdit::AddItem(const wchar_t *itemText, KeyValues *userData) +{ + // add the element to the menu + // when the menu item is selected it will send the custom message "SetText" + KeyValues *kv = new KeyValues("SetText"); + kv->SetWString("text", itemText); + + // get an ansi version for the menuitem name + char ansi[128]; + g_pVGuiLocalize->ConvertUnicodeToANSI(itemText, ansi, sizeof(ansi)); + return m_pDropDown->AddMenuItem(ansi, kv, this, userData); +} + +void FileCompletionEdit::DeleteAllItems() +{ + m_pDropDown->DeleteAllItems(); +} + +int FileCompletionEdit::GetItemCount() +{ + return m_pDropDown->GetItemCount(); +} + +int FileCompletionEdit::GetItemIDFromRow(int row) +{ + // valid from [0, GetItemCount) + return m_pDropDown->GetMenuID(row); +} + +int FileCompletionEdit::GetRowFromItemID(int itemID) +{ + int i; + for (i=0;iGetMenuID(i) == itemID) + return i; + } + return -1; +} + +void FileCompletionEdit::PerformLayout() +{ + BaseClass::PerformLayout(); + + m_pDropDown->PositionRelativeToPanel( this, Menu::DOWN, 0 ); + + // reset the width of the drop down menu to be the width of this edit box + m_pDropDown->SetFixedWidth(GetWide()); + m_pDropDown->ForceCalculateWidth(); +} + +void FileCompletionEdit::OnSetText(const wchar_t *newtext) +{ + // see if the combobox text has changed, and if so, post a message detailing the new text + wchar_t wbuf[255]; + GetText( wbuf, 254 ); + + if ( wcscmp(wbuf, newtext) ) + { + // text has changed + SetText(newtext); + + // fire off that things have changed + PostActionSignal(new KeyValues("TextChanged", "text", newtext)); + Repaint(); + } +} + +void FileCompletionEdit::OnKillFocus() +{ + HideMenu(); + BaseClass::OnKillFocus(); +} + +void FileCompletionEdit::HideMenu(void) +{ + // hide the menu + m_pDropDown->SetVisible(false); +} + +void FileCompletionEdit::ShowMenu(void) +{ + // reset the dropdown's position + m_pDropDown->InvalidateLayout(); + + // make sure we're at the top of the draw order (and therefore our children as well) + // this important to make sure the menu will be drawn in the foreground + MoveToFront(); + + // reset the drop down + m_pDropDown->ClearCurrentlyHighlightedItem(); + + // limit it to only 6 + if (m_pDropDown->GetItemCount() > 6) + { + m_pDropDown->SetNumberOfVisibleItems(6); + } + else + { + m_pDropDown->SetNumberOfVisibleItems(m_pDropDown->GetItemCount()); + } + // show the menu + m_pDropDown->SetVisible(true); + + Repaint(); +} + +void FileCompletionEdit::OnKeyCodeTyped(KeyCode code) +{ + if ( code == KEY_DOWN ) + { + if (m_pDropDown->GetItemCount() > 0) + { + int menuID = m_pDropDown->GetCurrentlyHighlightedItem(); + int row = -1; + if ( menuID == -1 ) + { + row = m_pDropDown->GetItemCount() - 1; + } + else + { + row = GetRowFromItemID(menuID); + } + row++; + if (row == m_pDropDown->GetItemCount()) + { + row = 0; + } + menuID = GetItemIDFromRow(row); + m_pDropDown->SetCurrentlyHighlightedItem(menuID); + return; + } + } + else if ( code == KEY_UP ) + { + if (m_pDropDown->GetItemCount() > 0) + { + int menuID = m_pDropDown->GetCurrentlyHighlightedItem(); + int row = -1; + if ( menuID == -1 ) + { + row = 0; + } + else + { + row = GetRowFromItemID(menuID); + } + row--; + if ( row < 0 ) + { + row = m_pDropDown->GetItemCount() - 1; + } + menuID = GetItemIDFromRow(row); + m_pDropDown->SetCurrentlyHighlightedItem(menuID); + return; + } + } + else if ( code == KEY_ESCAPE ) + { + if ( m_pDropDown->IsVisible() ) + { + HideMenu(); + return; + } + } + BaseClass::OnKeyCodeTyped(code); + return; +} + +void FileCompletionEdit::OnMenuItemHighlight( int itemID ) +{ + char wbuf[80]; + if ( m_pDropDown->IsValidMenuID(itemID) ) + { + m_pDropDown->GetMenuItem(itemID)->GetText(wbuf, 80); + } + else + { + wbuf[0] = 0; + } + SetText(wbuf); + RequestFocus(); + GotoTextEnd(); +} + + +} // namespace vgui + + +//----------------------------------------------------------------------------- +// Dictionary of start dir contexts +//----------------------------------------------------------------------------- +static CUtlDict< CUtlString, unsigned short > s_StartDirContexts; + +struct ColumnInfo_t +{ + char const *columnName; + char const *columnText; + int startingWidth; + int minWidth; + int maxWidth; + int flags; + SortFunc *pfnSort; + Label::Alignment alignment; +}; + +static ColumnInfo_t g_ColInfo[] = +{ + { "text", "#FileOpenDialog_Col_Name", 175, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileNameSortFunc , Label::a_west }, + { "filesize", "#FileOpenDialog_Col_Size", 100, 20, 10000, 0, &ListFileSizeSortFunc , Label::a_east }, + { "type", "#FileOpenDialog_Col_Type", 150, 20, 10000, 0, &ListFileTypeSortFunc , Label::a_west }, + { "modified", "#FileOpenDialog_Col_DateModified", 125, 20, 10000, 0, &ListFileModifiedSortFunc , Label::a_west }, +// { "created", "#FileOpenDialog_Col_DateCreated", 125, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileCreatedSortFunc , Label::a_west }, + { "attributes", "#FileOpenDialog_Col_Attributes", 50, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileAttributesSortFunc , Label::a_west }, +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +FileOpenDialog::FileOpenDialog(Panel *parent, const char *title, bool bOpenOnly, KeyValues* pContextKeyValues ) : + Frame( parent, "FileOpenDialog" ) +{ + m_DialogType = bOpenOnly ? FOD_OPEN : FOD_SAVE; + Init( title, pContextKeyValues ); +} + + +FileOpenDialog::FileOpenDialog( Panel *parent, const char *title, FileOpenDialogType_t type, KeyValues *pContextKeyValues ) : + Frame( parent, "FileOpenDialog" ) +{ + m_DialogType = type; + Init( title, pContextKeyValues ); +} + +void FileOpenDialog::Init( const char *title, KeyValues *pContextKeyValues ) +{ + m_bFileSelected = false; + SetTitle(title, true); + SetMinimizeButtonVisible(false); + +#ifdef POSIX + Q_strncpy(m_szLastPath, "/", sizeof( m_szLastPath ) ); +#else + Q_strncpy(m_szLastPath, "c:\\", sizeof( m_szLastPath ) ); +#endif + + m_pContextKeyValues = pContextKeyValues; + + // Get the list of available drives and put them in a menu here. + // Start with the directory we are in. + m_pFullPathEdit = new ComboBox(this, "FullPathEdit", 6, false); + m_pFullPathEdit->GetTooltip()->SetTooltipFormatToSingleLine(); + + // list panel + m_pFileList = new ListPanel(this, "FileList"); + for ( int i = 0; i < ARRAYSIZE( g_ColInfo ); ++i ) + { + const ColumnInfo_t& info = g_ColInfo[ i ]; + + m_pFileList->AddColumnHeader( i, info.columnName, info.columnText, info.startingWidth, info.minWidth, info.maxWidth, info.flags ); + m_pFileList->SetSortFunc( i, info.pfnSort ); + m_pFileList->SetColumnTextAlignment( i, info.alignment ); + } + + m_pFileList->SetSortColumn( s_nLastSortColumn ); + m_pFileList->SetMultiselectEnabled( false ); + + // file name edit box + m_pFileNameEdit = new FileCompletionEdit(this); + m_pFileNameEdit->AddActionSignalTarget(this); + + m_pFileTypeCombo = new ComboBox( this, "FileTypeCombo", 6, false ); + + switch ( m_DialogType ) + { + case FOD_OPEN: + m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this ); + break; + case FOD_SAVE: + m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Save", this ); + break; + case FOD_SELECT_DIRECTORY: + m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Select", this ); + m_pFileTypeCombo->SetVisible( false ); + break; + } + + m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this ); + m_pFolderUpButton = new Button( this, "FolderUpButton", "", this ); + m_pFolderUpButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_Up" ); + m_pNewFolderButton = new Button( this, "NewFolderButton", "", this ); + m_pNewFolderButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_NewFolder" ); + m_pOpenInExplorerButton = new Button( this, "OpenInExplorerButton", "", this ); + +#if defined ( OSX ) + m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInFinderButton" ); +#elif defined ( POSIX ) + m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInDesktopManagerButton" ); +#else // Assume Windows / Explorer + m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInExplorerButton" ); +#endif + + Label *lookIn = new Label( this, "LookInLabel", "#FileOpenDialog_Look_in" ); + Label *fileName = new Label( this, "FileNameLabel", + ( m_DialogType != FOD_SELECT_DIRECTORY ) ? "#FileOpenDialog_File_name" : "#FileOpenDialog_Directory_Name" ); + + m_pFolderIcon = new ImagePanel(NULL, "FolderIcon"); + + // set up the control's initial positions + SetSize( 600, 260 ); + + int nFileEditLeftSide = ( m_DialogType != FOD_SELECT_DIRECTORY ) ? 84 : 100; + int nFileNameWidth = ( m_DialogType != FOD_SELECT_DIRECTORY ) ? 72 : 82; + + m_pFullPathEdit->SetBounds(67, 32, 310, 24); + m_pFolderUpButton->SetBounds(362, 32, 24, 24); + m_pNewFolderButton->SetBounds(392, 32, 24, 24); + m_pOpenInExplorerButton->SetBounds(332, 32, 24, 24); + m_pFileList->SetBounds(10, 60, 406, 130); + m_pFileNameEdit->SetBounds( nFileEditLeftSide, 194, 238, 24); + m_pFileTypeCombo->SetBounds( nFileEditLeftSide, 224, 238, 24); + m_pOpenButton->SetBounds(336, 194, 74, 24); + m_pCancelButton->SetBounds(336, 224, 74, 24); + lookIn->SetBounds(10, 32, 55, 24); + fileName->SetBounds(10, 194, nFileNameWidth, 24); + + // set autolayout parameters + m_pFullPathEdit->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_RIGHT, 67, 32, -100, 0 ); + m_pFileNameEdit->SetAutoResize( Panel::PIN_BOTTOMLEFT, Panel::AUTORESIZE_RIGHT, nFileEditLeftSide, -42, -104, 0 ); + m_pFileTypeCombo->SetAutoResize( Panel::PIN_BOTTOMLEFT, Panel::AUTORESIZE_RIGHT, nFileEditLeftSide, -12, -104, 0 ); + m_pFileList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 10, 60, -10, -70 ); + + m_pFolderUpButton->SetPinCorner( Panel::PIN_TOPRIGHT, -40, 32 ); + m_pNewFolderButton->SetPinCorner( Panel::PIN_TOPRIGHT, -10, 32 ); + m_pOpenInExplorerButton->SetPinCorner( Panel::PIN_TOPRIGHT, -70, 32 ); + m_pOpenButton->SetPinCorner( Panel::PIN_BOTTOMRIGHT, -16, -42 ); + m_pCancelButton->SetPinCorner( Panel::PIN_BOTTOMRIGHT, -16, -12 ); + lookIn->SetPinCorner( Panel::PIN_TOPLEFT, 10, 32 ); + fileName->SetPinCorner( Panel::PIN_BOTTOMLEFT, 10, -42 ); + + // label settings + lookIn->SetContentAlignment(Label::a_west); + fileName->SetContentAlignment(Label::a_west); + + lookIn->SetAssociatedControl(m_pFullPathEdit); + fileName->SetAssociatedControl(m_pFileNameEdit); + + if ( m_DialogType != FOD_SELECT_DIRECTORY ) + { + Label *fileType = new Label(this, "FileTypeLabel", "#FileOpenDialog_File_type"); + fileType->SetBounds(10, 224, 72, 24); + fileType->SetPinCorner( Panel::PIN_BOTTOMLEFT, 10, -12 ); + fileType->SetContentAlignment(Label::a_west); + fileType->SetAssociatedControl( m_pFileTypeCombo ); + } + + // set tab positions + GetFocusNavGroup().SetDefaultButton(m_pOpenButton); + + m_pFileNameEdit->SetTabPosition(1); + m_pFileTypeCombo->SetTabPosition(2); + m_pOpenButton->SetTabPosition(3); + m_pCancelButton->SetTabPosition(4); + m_pFullPathEdit->SetTabPosition(5); + m_pFileList->SetTabPosition(6); + + m_pOpenButton->SetCommand( ( m_DialogType != FOD_SELECT_DIRECTORY ) ? new KeyValues( "OnOpen" ) : new KeyValues( "SelectFolder" ) ); + m_pCancelButton->SetCommand( "CloseModal" ); + m_pFolderUpButton->SetCommand( new KeyValues( "OnFolderUp" ) ); + m_pNewFolderButton->SetCommand( new KeyValues( "OnNewFolder" ) ); + m_pOpenInExplorerButton->SetCommand( new KeyValues( "OpenInExplorer" ) ); + + SetSize( 600, 384 ); + + m_nStartDirContext = s_StartDirContexts.InvalidIndex(); + + // Set our starting path to the current directory + char pLocalPath[255]; + g_pFullFileSystem->GetCurrentDirectory( pLocalPath , 255 ); + if ( !pLocalPath[0] || ( IsOSX() && V_strlen(pLocalPath) <= 2 ) ) + { + const char *pszHomeDir = getenv( "HOME" ); + V_strcpy_safe( pLocalPath, pszHomeDir ); + } + + SetStartDirectory( pLocalPath ); + + // Because these call through virtual functions, we can't issue them in the constructor, so we post a message to ourselves instead!! + PostMessage( GetVPanel(), new KeyValues( "PopulateFileList" ) ); + PostMessage( GetVPanel(), new KeyValues( "PopulateDriveList" ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +FileOpenDialog::~FileOpenDialog() +{ + s_nLastSortColumn = m_pFileList->GetSortColumn(); + if ( m_pContextKeyValues ) + { + m_pContextKeyValues->deleteThis(); + m_pContextKeyValues = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Apply scheme settings +//----------------------------------------------------------------------------- +void FileOpenDialog::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + m_pFolderIcon->SetImage(scheme()->GetImage("resource/icon_folder", false)); + m_pFolderUpButton->AddImage(scheme()->GetImage("resource/icon_folderup", false), -3); + m_pNewFolderButton->AddImage( scheme()->GetImage("resource/icon_newfolder", false), -3 ); + m_pOpenInExplorerButton->AddImage( scheme()->GetImage("resource/icon_play_once", false), -3 ); + + ImageList *imageList = new ImageList(false); + imageList->AddImage(scheme()->GetImage("resource/icon_file", false)); + imageList->AddImage(scheme()->GetImage("resource/icon_folder", false)); + imageList->AddImage(scheme()->GetImage("resource/icon_folder_selected", false)); + + m_pFileList->SetImageList(imageList, true); +} + + +//----------------------------------------------------------------------------- +// Prevent default button ('select') from getting triggered +// when selecting directories. Instead, open the directory +//----------------------------------------------------------------------------- +void FileOpenDialog::OnKeyCodeTyped(KeyCode code) +{ + if ( m_DialogType == FOD_SELECT_DIRECTORY && code == KEY_ENTER ) + { + OnOpen(); + } + else + { + BaseClass::OnKeyCodeTyped( code ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void FileOpenDialog::PopulateDriveList() +{ + char fullpath[MAX_PATH * 4]; + char subDirPath[MAX_PATH * 4]; + GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH); + Q_strncpy(subDirPath, fullpath, sizeof( subDirPath ) ); + + m_pFullPathEdit->DeleteAllItems(); + +#ifdef WIN32 + // populate the drive list + char buf[512]; + int len = system()->GetAvailableDrives(buf, 512); + char *pBuf = buf; + for (int i=0; i < len / 4; i++) + { + m_pFullPathEdit->AddItem(pBuf, NULL); + + // is this our drive - add all subdirectories + if (!_strnicmp(pBuf, fullpath, 2)) + { + int indent = 0; + char *pData = fullpath; + while (*pData) + { + if ( *pData == CORRECT_PATH_SEPARATOR ) + { + if (indent > 0) + { + memset(subDirPath, ' ', indent); + memcpy(subDirPath+indent, fullpath, pData-fullpath); + subDirPath[indent+pData-fullpath] = 0; + + m_pFullPathEdit->AddItem(subDirPath, NULL); + } + indent += 2; + } + pData++; + } + } + pBuf += 4; + } +#else + m_pFullPathEdit->AddItem("/", NULL); + + char *pData = fullpath; + int indent = 0; + while (*pData) + { + if (*pData == '/' && ( pData[1] != '\0' ) ) + { + if (indent > 0) + { + memset(subDirPath, ' ', indent); + memcpy(subDirPath+indent, fullpath, pData-fullpath); + subDirPath[indent+pData-fullpath] = 0; + + m_pFullPathEdit->AddItem(subDirPath, NULL); + } + indent += 2; + } + pData++; + } +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: Delete self on close +//----------------------------------------------------------------------------- +void FileOpenDialog::OnClose() +{ + s_nLastSortColumn = m_pFileList->GetSortColumn(); + if ( !m_bFileSelected ) + { + KeyValues *pKeyValues = new KeyValues( "FileSelectionCancelled" ); + PostActionSignal( pKeyValues ); + m_bFileSelected = true; + } + + m_pFileNameEdit->SetText(""); + m_pFileNameEdit->HideMenu(); + + if ( vgui::input()->GetAppModalSurface() == GetVPanel() ) + { + input()->SetAppModalSurface(NULL); + } + + BaseClass::OnClose(); +} + +void FileOpenDialog::OnFolderUp() +{ + MoveUpFolder(); + OnOpen(); +} + +void FileOpenDialog::OnInputCompleted( KeyValues *data ) +{ + if ( m_hInputDialog.Get() ) + { + delete m_hInputDialog.Get(); + } + + input()->SetAppModalSurface( m_SaveModal ); + m_SaveModal = 0; + + NewFolder( data->GetString( "text" ) ); + OnOpen(); +} + +void FileOpenDialog::OnInputCanceled() +{ + input()->SetAppModalSurface( m_SaveModal ); + m_SaveModal = 0; +} + +void FileOpenDialog::OnNewFolder() +{ + if ( m_hInputDialog.Get() ) + delete m_hInputDialog.Get(); + + m_hInputDialog = new InputDialog( this, "#FileOpenDialog_NewFolder_InputTitle", "#FileOpenDialog_NewFolderPrompt", "#FileOpenDialog_NewFolder_DefaultName" ); + if ( m_hInputDialog.Get() ) + { + m_SaveModal = input()->GetAppModalSurface(); + + KeyValues *pContextKeyValues = new KeyValues( "NewFolder" ); + m_hInputDialog->SetSmallCaption( true ); + m_hInputDialog->SetMultiline( false ); + m_hInputDialog->DoModal( pContextKeyValues ); + } +} + + +//----------------------------------------------------------------------------- +// Opens the current file/folder in explorer +//----------------------------------------------------------------------------- +void FileOpenDialog::OnOpenInExplorer() +{ + char pCurrentDirectory[MAX_PATH]; + GetCurrentDirectory( pCurrentDirectory, sizeof(pCurrentDirectory) ); +#if !defined( _X360 ) && defined( WIN32 ) + ShellExecute( NULL, NULL, pCurrentDirectory, NULL, NULL, SW_SHOWNORMAL ); +#elif defined( OSX ) + char szCmd[ MAX_PATH * 2]; + Q_snprintf( szCmd, sizeof(szCmd), "/usr/bin/open \"%s\"", pCurrentDirectory ); + ::system( szCmd ); +#elif defined( LINUX ) + char szCmd[ MAX_PATH * 2 ]; + Q_snprintf( szCmd, sizeof(szCmd), "xdg-open \"%s\" &", pCurrentDirectory ); + ::system( szCmd ); +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: Handle for button commands +//----------------------------------------------------------------------------- +void FileOpenDialog::OnCommand(const char *command) +{ + if (!stricmp(command, "Cancel")) + { + Close(); + } + else + { + BaseClass::OnCommand(command); + } +} + + +//----------------------------------------------------------------------------- +// Sets the start directory context (and resets the start directory in the process) +//----------------------------------------------------------------------------- +void FileOpenDialog::SetStartDirectoryContext( const char *pStartDirContext, const char *pDefaultDir ) +{ + bool bUseCurrentDirectory = true; + if ( pStartDirContext ) + { + m_nStartDirContext = s_StartDirContexts.Find( pStartDirContext ); + if ( m_nStartDirContext == s_StartDirContexts.InvalidIndex() ) + { + m_nStartDirContext = s_StartDirContexts.Insert( pStartDirContext, pDefaultDir ); + bUseCurrentDirectory = ( pDefaultDir == NULL ); + } + else + { + bUseCurrentDirectory = false; + } + } + else + { + m_nStartDirContext = s_StartDirContexts.InvalidIndex(); + } + + if ( !bUseCurrentDirectory ) + { + SetStartDirectory( s_StartDirContexts[m_nStartDirContext].Get() ); + } + else + { + // Set our starting path to the current directory + char pLocalPath[255]; + g_pFullFileSystem->GetCurrentDirectory( pLocalPath, 255 ); + SetStartDirectory( pLocalPath ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Set the starting directory of the file search. +//----------------------------------------------------------------------------- +void FileOpenDialog::SetStartDirectory( const char *dir ) +{ + m_pFullPathEdit->SetText(dir); + + // ensure it's validity + ValidatePath(); + + // Store this in the start directory list + if ( m_nStartDirContext != s_StartDirContexts.InvalidIndex() ) + { + char pDirBuf[MAX_PATH]; + GetCurrentDirectory( pDirBuf, sizeof(pDirBuf) ); + s_StartDirContexts[ m_nStartDirContext ] = pDirBuf; + } + + PopulateDriveList(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Add filters for the drop down combo box +//----------------------------------------------------------------------------- +void FileOpenDialog::AddFilter( const char *filter, const char *filterName, bool bActive, const char *pFilterInfo ) +{ + KeyValues *kv = new KeyValues("item"); + kv->SetString( "filter", filter ); + kv->SetString( "filterinfo", pFilterInfo ); + int itemID = m_pFileTypeCombo->AddItem(filterName, kv); + if ( bActive ) + { + m_pFileTypeCombo->ActivateItem(itemID); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Activate the dialog +//----------------------------------------------------------------------------- +void FileOpenDialog::DoModal( bool bUnused ) +{ + m_bFileSelected = false; + m_pFileNameEdit->RequestFocus(); + BaseClass::DoModal(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Gets the directory this is currently in +//----------------------------------------------------------------------------- +void FileOpenDialog::GetCurrentDirectory(char *buf, int bufSize) +{ + // get the text from the text entry + m_pFullPathEdit->GetText(buf, bufSize); +} + + +//----------------------------------------------------------------------------- +// Purpose: Get the last selected file name +//----------------------------------------------------------------------------- +void FileOpenDialog::GetSelectedFileName(char *buf, int bufSize) +{ + m_pFileNameEdit->GetText(buf, bufSize); +} + + +//----------------------------------------------------------------------------- +// Creates a new folder +//----------------------------------------------------------------------------- +void FileOpenDialog::NewFolder( char const *folderName ) +{ + char pCurrentDirectory[MAX_PATH]; + GetCurrentDirectory( pCurrentDirectory, sizeof(pCurrentDirectory) ); + + char pFullPath[MAX_PATH]; + char pNewFolderName[MAX_PATH]; + Q_strncpy( pNewFolderName, folderName, sizeof(pNewFolderName) ); + int i = 2; + do + { + Q_MakeAbsolutePath( pFullPath, sizeof(pFullPath), pNewFolderName, pCurrentDirectory ); + if ( !g_pFullFileSystem->FileExists( pFullPath, NULL ) && + !g_pFullFileSystem->IsDirectory( pFullPath, NULL ) ) + { + g_pFullFileSystem->CreateDirHierarchy( pFullPath, NULL ); + m_pFileNameEdit->SetText( pNewFolderName ); + return; + } + + Q_snprintf( pNewFolderName, sizeof(pNewFolderName), "%s%d", folderName, i ); + ++i; + } while ( i <= 999 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Move the directory structure up +//----------------------------------------------------------------------------- +void FileOpenDialog::MoveUpFolder() +{ + char fullpath[MAX_PATH * 4]; + GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH); + + Q_StripLastDir( fullpath, sizeof( fullpath ) ); + // append a trailing slash + Q_AppendSlash( fullpath, sizeof( fullpath ) ); + + SetStartDirectory(fullpath); + PopulateFileList(); + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Validate that the current path is valid +//----------------------------------------------------------------------------- +void FileOpenDialog::ValidatePath() +{ + char fullpath[MAX_PATH * 4]; + GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH); + Q_RemoveDotSlashes( fullpath ); + + // when statting a directory on Windows, you want to include + // the terminal slash exactly when you are statting a root + // directory. PKMN. +#ifdef _WIN32 + if ( Q_strlen( fullpath ) != 3 ) + { + Q_StripTrailingSlash( fullpath ); + } +#endif + // cleanup the path, we format tabs into the list to make it pretty in the UI + Q_StripPrecedingAndTrailingWhitespace( fullpath ); + + struct _stat buf; + if ( ( 0 == _stat( fullpath, &buf ) ) && + ( 0 != ( buf.st_mode & S_IFDIR ) ) ) + { + Q_AppendSlash( fullpath, sizeof( fullpath ) ); + Q_strncpy(m_szLastPath, fullpath, sizeof(m_szLastPath)); + } + else + { + // failed to load file, use the previously successful path + } + + m_pFullPathEdit->SetText(m_szLastPath); + m_pFullPathEdit->GetTooltip()->SetText(m_szLastPath); +} + +#ifdef WIN32 +const char *GetAttributesAsString( DWORD dwAttributes ) +{ + static char out[ 256 ]; + out[ 0 ] = 0; + if ( dwAttributes & FILE_ATTRIBUTE_ARCHIVE ) + { + Q_strncat( out, "A", sizeof( out ), COPY_ALL_CHARACTERS ); + } + if ( dwAttributes & FILE_ATTRIBUTE_COMPRESSED ) + { + Q_strncat( out, "C", sizeof( out ), COPY_ALL_CHARACTERS ); + } + if ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY ) + { + Q_strncat( out, "D", sizeof( out ), COPY_ALL_CHARACTERS ); + } + if ( dwAttributes & FILE_ATTRIBUTE_HIDDEN ) + { + Q_strncat( out, "H", sizeof( out ), COPY_ALL_CHARACTERS ); + } + if ( dwAttributes & FILE_ATTRIBUTE_READONLY ) + { + Q_strncat( out, "R", sizeof( out ), COPY_ALL_CHARACTERS ); + } + if ( dwAttributes & FILE_ATTRIBUTE_SYSTEM ) + { + Q_strncat( out, "S", sizeof( out ), COPY_ALL_CHARACTERS ); + } + if ( dwAttributes & FILE_ATTRIBUTE_TEMPORARY ) + { + Q_strncat( out, "T", sizeof( out ), COPY_ALL_CHARACTERS ); + } + return out; +} + +const char *GetFileTimetamp( FILETIME ft ) +{ + SYSTEMTIME local; + FILETIME localFileTime; + FileTimeToLocalFileTime( &ft, &localFileTime ); + FileTimeToSystemTime( &localFileTime, &local ); + + static char out[ 256 ]; + + bool am = true; + WORD hour = local.wHour; + if ( hour >= 12 ) + { + am = false; + // 12:42 pm displays as 12:42 pm + // 13:42 pm displays as 1:42 pm + if ( hour > 12 ) + { + hour -= 12; + } + } + Q_snprintf( out, sizeof( out ), "%d/%02d/%04d %d:%02d %s", + local.wMonth, + local.wDay, + local.wYear, + hour, + local.wMinute, + am ? "AM" : "PM" // TODO: Localize this? + ); + return out; +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Fill the filelist with the names of all the files in the current directory +//----------------------------------------------------------------------------- +#define MAX_FILTER_LENGTH 255 +void FileOpenDialog::PopulateFileList() +{ + // clear the current list + m_pFileList->DeleteAllItems(); + + FileFindHandle_t findHandle; + char pszFileModified[64]; + + // get the current directory + char currentDir[MAX_PATH * 4]; + char dir[MAX_PATH * 4]; + char filterList[MAX_FILTER_LENGTH+1]; + GetCurrentDirectory(currentDir, sizeof(dir)); + + KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData(); + if (combokv) + { + Q_strncpy(filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH); + } + else + { + // add wildcard for search + Q_strncpy(filterList, "*\0", MAX_FILTER_LENGTH); + } + + + char *filterPtr = filterList; + KeyValues *kv = new KeyValues("item"); + + if ( m_DialogType != FOD_SELECT_DIRECTORY ) + { + while ((filterPtr != NULL) && (*filterPtr != 0)) + { + // parse the next filter in the list. + char curFilter[MAX_FILTER_LENGTH]; + curFilter[0] = 0; + int i = 0; + while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' '))) + { + ++filterPtr; + } + while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' ')) + { + curFilter[i++] = *(filterPtr++); + } + curFilter[i] = 0; + + if (curFilter[0] == 0) + { + break; + } + + Q_snprintf( dir, MAX_PATH*4, "%s%s", currentDir, curFilter ); + + // Open the directory and walk it, loading files + const char *pszFileName = g_pFullFileSystem->FindFirst( dir, &findHandle ); + while ( pszFileName ) + { + if ( !g_pFullFileSystem->FindIsDirectory( findHandle ) + || !IsOSX() + || ( IsOSX() && g_pFullFileSystem->FindIsDirectory( findHandle ) && Q_stristr( pszFileName, ".app" ) ) ) + { + char pFullPath[MAX_PATH]; + Q_snprintf( pFullPath, MAX_PATH, "%s%s", currentDir, pszFileName ); + + // add the file to the list + kv->SetString( "text", pszFileName ); + + kv->SetInt( "image", 1 ); + + IImage *image = surface()->GetIconImageForFullPath( pFullPath ); + + if ( image ) + { + kv->SetPtr( "iconImage", (void *)image ); + } + + kv->SetInt("imageSelected", 1); + kv->SetInt("directory", 0); + + kv->SetString( "filesize", Q_pretifymem( g_pFullFileSystem->Size( pFullPath ), 0, true ) ); + Q_FixSlashes( pFullPath ); + wchar_t fileType[ 80 ]; + g_pFullFileSystem->GetFileTypeForFullPath( pFullPath, fileType, sizeof( fileType ) ); + kv->SetWString( "type", fileType ); + + kv->SetString( "attributes", g_pFullFileSystem->IsFileWritable( pFullPath )? "" : "R" ); + + long fileModified = g_pFullFileSystem->GetFileTime( pFullPath ); + g_pFullFileSystem->FileTimeToString( pszFileModified, sizeof( pszFileModified ), fileModified ); + kv->SetString( "modified", pszFileModified ); + +// kv->SetString( "created", GetFileTimetamp( findData.ftCreationTime ) ); + + m_pFileList->AddItem(kv, 0, false, false); + } + + pszFileName = g_pFullFileSystem->FindNext( findHandle ); + } + g_pFullFileSystem->FindClose( findHandle ); + } + } + + // find all the directories + GetCurrentDirectory( dir, sizeof(dir) ); + Q_strncat(dir, "*", sizeof( dir ), COPY_ALL_CHARACTERS); + + const char *pszFileName = g_pFullFileSystem->FindFirst( dir, &findHandle ); + while ( pszFileName ) + { + if ( pszFileName[0] != '.' && g_pFullFileSystem->FindIsDirectory( findHandle ) + && ( !IsOSX() || ( IsOSX() && !Q_stristr( pszFileName, ".app" ) ) ) ) + { + char pFullPath[MAX_PATH]; + Q_snprintf( pFullPath, MAX_PATH, "%s%s", currentDir, pszFileName ); + + kv->SetString("text", pszFileName ); + kv->SetPtr( "iconImage", (void *)NULL ); + kv->SetInt("image", 2); + kv->SetInt("imageSelected", 3); + kv->SetInt("directory", 1); + + kv->SetString( "filesize", "" ); + kv->SetString( "type", "#FileOpenDialog_FileType_Folder" ); + + kv->SetString( "attributes", g_pFullFileSystem->IsFileWritable( pFullPath )? "" : "R" ); + + long fileModified = g_pFullFileSystem->GetFileTime( pFullPath ); + g_pFullFileSystem->FileTimeToString( pszFileModified, sizeof( pszFileModified ), fileModified ); + kv->SetString( "modified", pszFileModified ); + +// kv->SetString( "created", GetFileTimetamp( findData.ftCreationTime ) ); + + m_pFileList->AddItem( kv, 0, false, false ); + } + + pszFileName = g_pFullFileSystem->FindNext( findHandle ); + } + g_pFullFileSystem->FindClose( findHandle ); + + kv->deleteThis(); + m_pFileList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Does the specified extension match something in the filter list? +//----------------------------------------------------------------------------- +bool FileOpenDialog::ExtensionMatchesFilter( const char *pExt ) +{ + KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData(); + if ( !combokv ) + return true; + + char filterList[MAX_FILTER_LENGTH+1]; + Q_strncpy( filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH ); + + char *filterPtr = filterList; + while ((filterPtr != NULL) && (*filterPtr != 0)) + { + // parse the next filter in the list. + char curFilter[MAX_FILTER_LENGTH]; + curFilter[0] = 0; + int i = 0; + while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' '))) + { + ++filterPtr; + } + while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' ')) + { + curFilter[i++] = *(filterPtr++); + } + curFilter[i] = 0; + + if (curFilter[0] == 0) + break; + + if ( !Q_stricmp( curFilter, "*" ) || !Q_stricmp( curFilter, "*.*" ) ) + return true; + + // FIXME: This isn't exactly right, but tough cookies; + // it assumes the first two characters of the filter are *. + Assert( curFilter[0] == '*' && curFilter[1] == '.' ); + if ( !Q_stricmp( &curFilter[2], pExt ) ) + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Choose the first non *.* filter in the filter list +//----------------------------------------------------------------------------- +void FileOpenDialog::ChooseExtension( char *pExt, int nBufLen ) +{ + pExt[0] = 0; + + KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData(); + if ( !combokv ) + return; + + char filterList[MAX_FILTER_LENGTH+1]; + Q_strncpy( filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH ); + + char *filterPtr = filterList; + while ((filterPtr != NULL) && (*filterPtr != 0)) + { + // parse the next filter in the list. + char curFilter[MAX_FILTER_LENGTH]; + curFilter[0] = 0; + int i = 0; + while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' '))) + { + ++filterPtr; + } + while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' ')) + { + curFilter[i++] = *(filterPtr++); + } + curFilter[i] = 0; + + if (curFilter[0] == 0) + break; + + if ( !Q_stricmp( curFilter, "*" ) || !Q_stricmp( curFilter, "*.*" ) ) + continue; + + // FIXME: This isn't exactly right, but tough cookies; + // it assumes the first two characters of the filter are *. + Assert( curFilter[0] == '*' && curFilter[1] == '.' ); + Q_strncpy( pExt, &curFilter[1], nBufLen ); + break; + } +} + + +//----------------------------------------------------------------------------- +// Saves the file to the start dir context +//----------------------------------------------------------------------------- +void FileOpenDialog::SaveFileToStartDirContext( const char *pFullPath ) +{ + if ( m_nStartDirContext == s_StartDirContexts.InvalidIndex() ) + return; + + char pPath[MAX_PATH]; + pPath[0] = 0; + Q_ExtractFilePath( pFullPath, pPath, sizeof(pPath) ); + s_StartDirContexts[ m_nStartDirContext ] = pPath; +} + + +//----------------------------------------------------------------------------- +// Posts a file selected message +//----------------------------------------------------------------------------- +void FileOpenDialog::PostFileSelectedMessage( const char *pFileName ) +{ + m_bFileSelected = true; + + // open the file! + KeyValues *pKeyValues = new KeyValues( "FileSelected", "fullpath", pFileName ); + KeyValues *pFilterKeys = m_pFileTypeCombo->GetActiveItemUserData(); + const char *pFilterInfo = pFilterKeys ? pFilterKeys->GetString( "filterinfo", NULL ) : NULL; + if ( pFilterInfo ) + { + pKeyValues->SetString( "filterinfo", pFilterInfo ); + } + if ( m_pContextKeyValues ) + { + pKeyValues->AddSubKey( m_pContextKeyValues ); + m_pContextKeyValues = NULL; + } + PostActionSignal( pKeyValues ); + CloseModal(); +} + + +//----------------------------------------------------------------------------- +// Selects the current folder +//----------------------------------------------------------------------------- +void FileOpenDialog::OnSelectFolder() +{ + ValidatePath(); + + // construct a file path + char pFileName[MAX_PATH]; + GetSelectedFileName( pFileName, sizeof( pFileName ) ); + + Q_StripTrailingSlash( pFileName ); + + if ( !stricmp(pFileName, "..") ) + { + MoveUpFolder(); + + // clear the name text + m_pFileNameEdit->SetText(""); + return; + } + + if ( !stricmp(pFileName, ".") ) + { + // clear the name text + m_pFileNameEdit->SetText(""); + return; + } + + // Compute the full path + char pFullPath[MAX_PATH * 4]; + if ( !Q_IsAbsolutePath( pFileName ) ) + { + GetCurrentDirectory(pFullPath, sizeof(pFullPath) - MAX_PATH); + strcat( pFullPath, pFileName ); + if ( !pFileName[0] ) + { + Q_StripTrailingSlash( pFullPath ); + } + } + else + { + Q_strncpy( pFullPath, pFileName, sizeof(pFullPath) ); + } + + if ( g_pFullFileSystem->FileExists( pFullPath ) ) + { + // open the file! + SaveFileToStartDirContext( pFullPath ); + PostFileSelectedMessage( pFullPath ); + return; + } + + PopulateDriveList(); + PopulateFileList(); + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Handle the open button being pressed +// checks on what has changed and acts accordingly +//----------------------------------------------------------------------------- +void FileOpenDialog::OnOpen() +{ + ValidatePath(); + + // construct a file path + char pFileName[MAX_PATH]; + GetSelectedFileName( pFileName, sizeof( pFileName ) ); + + int nLen = Q_strlen( pFileName ); + bool bSpecifiedDirectory = ( pFileName[nLen-1] == '/' || pFileName[nLen-1] == '\\' ) && (!IsOSX() || ( IsOSX() && !Q_stristr( pFileName, ".app" ) ) ); + Q_StripTrailingSlash( pFileName ); + + if ( !stricmp(pFileName, "..") ) + { + MoveUpFolder(); + + // clear the name text + m_pFileNameEdit->SetText(""); + return; + } + + if ( !stricmp(pFileName, ".") ) + { + // clear the name text + m_pFileNameEdit->SetText(""); + return; + } + + // Compute the full path + char pFullPath[MAX_PATH * 4]; + if ( !Q_IsAbsolutePath( pFileName ) ) + { + GetCurrentDirectory(pFullPath, sizeof(pFullPath) - MAX_PATH); + Q_AppendSlash( pFullPath, sizeof( pFullPath ) ); + strcat(pFullPath, pFileName); + if ( !pFileName[0] ) + { + Q_StripTrailingSlash( pFullPath ); + } + } + else + { + Q_strncpy( pFullPath, pFileName, sizeof(pFullPath) ); + } + + Q_StripTrailingSlash( pFullPath ); + + // when statting a directory on Windows, you want to include + // the terminal slash exactly when you are statting a root + // directory. PKMN. +#ifdef _WIN32 + if ( Q_strlen( pFullPath ) == 2 ) + { + Q_AppendSlash( pFullPath, Q_ARRAYSIZE( pFullPath ) ); + } +#endif + + + // If the name specified is a directory, then change directory + if ( g_pFullFileSystem->IsDirectory( pFullPath, NULL ) && + ( !IsOSX() || ( IsOSX() && !Q_stristr( pFullPath, ".app" ) ) ) ) + { + // it's a directory; change to the specified directory + if ( !bSpecifiedDirectory ) + { + Q_AppendSlash( pFullPath, Q_ARRAYSIZE( pFullPath ) ); + } + SetStartDirectory( pFullPath ); + + // clear the name text + m_pFileNameEdit->SetText(""); + m_pFileNameEdit->HideMenu(); + + PopulateDriveList(); + PopulateFileList(); + InvalidateLayout(); + return; + } + else if ( bSpecifiedDirectory ) + { + PopulateDriveList(); + PopulateFileList(); + InvalidateLayout(); + return; + } + + // Append suffix of the first filter that isn't *.* + char extension[512]; + Q_ExtractFileExtension( pFullPath, extension, sizeof(extension) ); + if ( !ExtensionMatchesFilter( extension ) ) + { + ChooseExtension( extension, sizeof(extension) ); + Q_SetExtension( pFullPath, extension, sizeof(pFullPath) ); + } + + if ( g_pFullFileSystem->FileExists( pFullPath ) ) + { + // open the file! + SaveFileToStartDirContext( pFullPath ); + PostFileSelectedMessage( pFullPath ); + return; + } + + // file not found + if ( ( m_DialogType == FOD_SAVE ) && pFileName[0] ) + { + // open the file! + SaveFileToStartDirContext( pFullPath ); + PostFileSelectedMessage( pFullPath ); + return; + } + + PopulateDriveList(); + PopulateFileList(); + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Purpose: using the file edit box as a prefix, create a menu of all possible files +//----------------------------------------------------------------------------- +void FileOpenDialog::PopulateFileNameCompletion() +{ + char buf[80]; + m_pFileNameEdit->GetText(buf, 80); + wchar_t wbuf[80]; + m_pFileNameEdit->GetText(wbuf, 80); + int bufLen = wcslen(wbuf); + + // delete all items before we check if there's even a string + m_pFileNameEdit->DeleteAllItems(); + + // no string at all - don't show even bother showing it + if (bufLen == 0) + { + m_pFileNameEdit->HideMenu(); + return; + } + + // what files use current string as a prefix? + int nCount = m_pFileList->GetItemCount(); + int i; + for ( i = 0 ; i < nCount ; i++ ) + { + KeyValues *kv = m_pFileList->GetItem(m_pFileList->GetItemIDFromRow(i)); + const wchar_t *wszString = kv->GetWString("text"); + if ( !_wcsnicmp(wbuf, wszString, bufLen) ) + { + m_pFileNameEdit->AddItem(wszString, NULL); + } + } + + // if there are any items - show the menu + if ( m_pFileNameEdit->GetItemCount() > 0 ) + { + m_pFileNameEdit->ShowMenu(); + } + else + { + m_pFileNameEdit->HideMenu(); + } + + m_pFileNameEdit->InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Handle an item in the list being selected +//----------------------------------------------------------------------------- +void FileOpenDialog::OnItemSelected() +{ + // make sure only one item is selected + if (m_pFileList->GetSelectedItemsCount() != 1) + { + m_pFileNameEdit->SetText(""); + } + else + { + // put the file name into the text edit box + KeyValues *data = m_pFileList->GetItem(m_pFileList->GetSelectedItem(0)); + m_pFileNameEdit->SetText(data->GetString("text")); + } + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Handle an item in the Drive combo box being selected +//----------------------------------------------------------------------------- +void FileOpenDialog::OnTextChanged(KeyValues *kv) +{ + Panel *pPanel = (Panel *) kv->GetPtr("panel", NULL); + + // first check which control had its text changed! + if (pPanel == m_pFullPathEdit) + { + m_pFileNameEdit->HideMenu(); + m_pFileNameEdit->SetText(""); + OnOpen(); + } + else if (pPanel == m_pFileNameEdit) + { + PopulateFileNameCompletion(); + } + else if (pPanel == m_pFileTypeCombo) + { + m_pFileNameEdit->HideMenu(); + PopulateFileList(); + } +} diff --git a/mp/src/vgui2/vgui_controls/FileOpenStateMachine.cpp b/mp/src/vgui2/vgui_controls/FileOpenStateMachine.cpp index efb91532..c611c11d 100644 --- a/mp/src/vgui2/vgui_controls/FileOpenStateMachine.cpp +++ b/mp/src/vgui2/vgui_controls/FileOpenStateMachine.cpp @@ -1,497 +1,497 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// This is a helper class designed to help with the chains of modal dialogs -// encountered when trying to open or save a particular file -// -//============================================================================= - -#include "vgui_controls/FileOpenStateMachine.h" -#include "tier1/KeyValues.h" -#include "vgui_controls/FileOpenDialog.h" -#include "vgui_controls/MessageBox.h" -#include "vgui_controls/perforcefilelistframe.h" -#include "vgui_controls/savedocumentquery.h" -#include "filesystem.h" -#include "p4lib/ip4.h" -#include "tier2/tier2.h" -#include "tier0/icommandline.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - - -using namespace vgui; - - -//----------------------------------------------------------------------------- -// Constructor -//----------------------------------------------------------------------------- -FileOpenStateMachine::FileOpenStateMachine( vgui::Panel *pParent, IFileOpenStateMachineClient *pClient ) : BaseClass( pParent, "FileOpenStateMachine" ) -{ - m_pClient = pClient; - m_CompletionState = SUCCESSFUL; - m_CurrentState = STATE_NONE; - m_pContextKeyValues = NULL; - SetVisible( false ); -} - -FileOpenStateMachine::~FileOpenStateMachine() -{ - CleanUpContextKeyValues(); -} - - -//----------------------------------------------------------------------------- -// Cleans up keyvalues -//----------------------------------------------------------------------------- -void FileOpenStateMachine::CleanUpContextKeyValues() -{ - if ( m_pContextKeyValues ) - { - m_pContextKeyValues->deleteThis(); - m_pContextKeyValues = NULL; - } -} - - -//----------------------------------------------------------------------------- -// Returns the state machine completion state -//----------------------------------------------------------------------------- -FileOpenStateMachine::CompletionState_t FileOpenStateMachine::GetCompletionState() -{ - return m_CompletionState; -} - - -//----------------------------------------------------------------------------- -// Utility to set the completion state -//----------------------------------------------------------------------------- -void FileOpenStateMachine::SetCompletionState( FileOpenStateMachine::CompletionState_t state ) -{ - m_CompletionState = state; - if ( m_CompletionState == IN_PROGRESS ) - return; - - m_CurrentState = STATE_NONE; - - KeyValues *kv = new KeyValues( "FileStateMachineFinished" ); - kv->SetInt( "completionState", m_CompletionState ); - kv->SetInt( "wroteFile", m_bWroteFile ); - kv->SetString( "fullPath", m_FileName.Get() ); - kv->SetString( "fileType", m_bIsOpeningFile ? m_OpenFileType.Get() : m_SaveFileType.Get() ); - if ( m_pContextKeyValues ) - { - kv->AddSubKey( m_pContextKeyValues ); - m_pContextKeyValues = NULL; - } - PostActionSignal( kv ); -} - - -//----------------------------------------------------------------------------- -// Called by the message box in OverwriteFileDialog -//----------------------------------------------------------------------------- -void FileOpenStateMachine::OnOverwriteFile( ) -{ - CheckOutDialog( ); -} - -void FileOpenStateMachine::OnCancelOverwriteFile( ) -{ - SetCompletionState( FILE_NOT_OVERWRITTEN ); -} - - -//----------------------------------------------------------------------------- -// Shows the overwrite existing file dialog -//----------------------------------------------------------------------------- -void FileOpenStateMachine::OverwriteFileDialog( ) -{ - if ( !g_pFullFileSystem->FileExists( m_FileName ) ) - { - CheckOutDialog( ); - return; - } - - m_CurrentState = STATE_SHOWING_OVERWRITE_DIALOG; - - char pBuf[1024]; - Q_snprintf( pBuf, sizeof(pBuf), "File already exists. Overwrite it?\n\n\"%s\"\n", m_FileName.Get() ); - vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Overwrite Existing File?", pBuf, GetParent() ); - pMessageBox->AddActionSignalTarget( this ); - pMessageBox->SetOKButtonVisible( true ); - pMessageBox->SetOKButtonText( "Yes" ); - pMessageBox->SetCancelButtonVisible( true ); - pMessageBox->SetCancelButtonText( "No" ); - pMessageBox->SetCloseButtonVisible( false ); - pMessageBox->SetCommand( new KeyValues( "OverwriteFile" ) ); - pMessageBox->SetCancelCommand( new KeyValues( "CancelOverwriteFile" ) ); - pMessageBox->DoModal(); -} - - -//----------------------------------------------------------------------------- -// Used to open a particular file in perforce, and deal with all the lovely dialogs -//----------------------------------------------------------------------------- -void FileOpenStateMachine::OnFileSelectionCancelled() -{ - if ( m_CurrentState == STATE_SHOWING_SAVE_DIALOG ) - { - SetCompletionState( FILE_SAVE_NAME_NOT_SPECIFIED ); - return; - } - - if ( m_CurrentState == STATE_SHOWING_OPEN_DIALOG ) - { - SetCompletionState( FILE_OPEN_NAME_NOT_SPECIFIED ); - return; - } - - Assert(0); -} - - -//----------------------------------------------------------------------------- -// Used to open a particular file in perforce, and deal with all the lovely dialogs -//----------------------------------------------------------------------------- -void FileOpenStateMachine::OnFileSelected( KeyValues *pKeyValues ) -{ - if ( m_CurrentState == STATE_SHOWING_SAVE_DIALOG ) - { - m_FileName = pKeyValues->GetString( "fullpath" ); - const char *pFilterInfo = pKeyValues->GetString( "filterinfo" ); - if ( pFilterInfo ) - { - m_SaveFileType = pFilterInfo; - } - OverwriteFileDialog(); - return; - } - - if ( m_CurrentState == STATE_SHOWING_OPEN_DIALOG ) - { - m_FileName = pKeyValues->GetString( "fullpath" ); - const char *pFilterInfo = pKeyValues->GetString( "filterinfo" ); - if ( pFilterInfo ) - { - m_OpenFileType = pFilterInfo; - } - ReadFile( ); - return; - } - - Assert(0); -} - - -//----------------------------------------------------------------------------- -// Writes the file out -//----------------------------------------------------------------------------- -void FileOpenStateMachine::WriteFile() -{ - m_CurrentState = STATE_WRITING_FILE; - if ( !m_pClient->OnWriteFileToDisk( m_FileName, m_SaveFileType, m_pContextKeyValues ) ) - { - SetCompletionState( ERROR_WRITING_FILE ); - return; - } - - m_bWroteFile = true; - if ( m_bShowPerforceDialogs ) - { - m_CurrentState = STATE_SHOWING_PERFORCE_ADD_DIALOG; - ShowPerforceQuery( GetParent(), m_FileName, this, NULL, PERFORCE_ACTION_FILE_ADD ); - return; - } - - if ( !m_bIsOpeningFile ) - { - SetCompletionState( SUCCESSFUL ); - return; - } - - OpenFileDialog(); -} - - -//----------------------------------------------------------------------------- -// Called by the message box in MakeFileWriteableDialog -//----------------------------------------------------------------------------- -void FileOpenStateMachine::OnMakeFileWriteable( ) -{ - if ( !g_pFullFileSystem->SetFileWritable( m_FileName, true ) ) - { - SetCompletionState( ERROR_MAKING_FILE_WRITEABLE ); - return; - } - - WriteFile(); -} - -void FileOpenStateMachine::OnCancelMakeFileWriteable( ) -{ - SetCompletionState( FILE_NOT_MADE_WRITEABLE ); -} - - -//----------------------------------------------------------------------------- -// Shows the make file writeable dialog -//----------------------------------------------------------------------------- -void FileOpenStateMachine::MakeFileWriteableDialog( ) -{ - // If the file is writeable, write it! - if ( !g_pFullFileSystem->FileExists( m_FileName ) || g_pFullFileSystem->IsFileWritable( m_FileName ) ) - { - WriteFile(); - return; - } - - // If it's in perforce, and not checked out, then we must abort. - bool bIsInPerforce = p4->IsFileInPerforce( m_FileName ); - bool bIsOpened = ( p4->GetFileState( m_FileName ) != P4FILE_UNOPENED ); - if ( bIsInPerforce && !bIsOpened ) - { - SetCompletionState( FILE_NOT_CHECKED_OUT ); - return; - } - - m_CurrentState = STATE_SHOWING_MAKE_FILE_WRITEABLE_DIALOG; - - char pBuf[1024]; - Q_snprintf( pBuf, sizeof(pBuf), "Encountered read-only file. Should it be made writeable?\n\n\"%s\"\n", m_FileName.Get() ); - vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Make File Writeable?", pBuf, GetParent() ); - pMessageBox->AddActionSignalTarget( this ); - pMessageBox->SetOKButtonVisible( true ); - pMessageBox->SetOKButtonText( "Yes" ); - pMessageBox->SetCancelButtonVisible( true ); - pMessageBox->SetCancelButtonText( "No" ); - pMessageBox->SetCloseButtonVisible( false ); - pMessageBox->SetCommand( new KeyValues( "MakeFileWriteable" ) ); - pMessageBox->SetCancelCommand( new KeyValues( "CancelMakeFileWriteable" ) ); - pMessageBox->DoModal(); -} - - -//----------------------------------------------------------------------------- -// Called when ShowPerforceQuery completes -//----------------------------------------------------------------------------- -void FileOpenStateMachine::OnPerforceQueryCompleted( KeyValues *pKeyValues ) -{ - if ( m_CurrentState == STATE_SHOWING_CHECK_OUT_DIALOG ) - { - if ( pKeyValues->GetInt( "operationPerformed" ) == 0 ) - { - SetCompletionState( FILE_NOT_CHECKED_OUT ); - return; - } - - MakeFileWriteableDialog(); - return; - } - - if ( m_CurrentState == STATE_SHOWING_PERFORCE_ADD_DIALOG ) - { - if ( !m_bIsOpeningFile ) - { - SetCompletionState( SUCCESSFUL ); - return; - } - - OpenFileDialog(); - return; - } - - Assert(0); -} - - -//----------------------------------------------------------------------------- -// Used to open a particular file in perforce, and deal with all the lovely dialogs -//----------------------------------------------------------------------------- -void FileOpenStateMachine::CheckOutDialog( ) -{ - if ( m_bShowPerforceDialogs ) - { - m_CurrentState = STATE_SHOWING_CHECK_OUT_DIALOG; - ShowPerforceQuery( GetParent(), m_FileName, this, NULL, PERFORCE_ACTION_FILE_EDIT ); - return; - } - - WriteFile(); -} - - -//----------------------------------------------------------------------------- -// These 3 messages come from the savedocumentquery dialog -//----------------------------------------------------------------------------- -void FileOpenStateMachine::OnSaveFile() -{ - if ( !m_FileName[0] || !Q_IsAbsolutePath( m_FileName ) ) - { - m_CurrentState = STATE_SHOWING_SAVE_DIALOG; - - FileOpenDialog *pDialog = new FileOpenDialog( GetParent(), "Save As", false ); - m_pClient->SetupFileOpenDialog( pDialog, false, m_SaveFileType, m_pContextKeyValues ); - pDialog->SetDeleteSelfOnClose( true ); - pDialog->AddActionSignalTarget( this ); - pDialog->DoModal( ); - return; - } - - CheckOutDialog( ); -} - -void FileOpenStateMachine::OnMarkNotDirty() -{ - if ( !m_bIsOpeningFile ) - { - SetCompletionState( SUCCESSFUL ); - return; - } - - // Jump right to opening the file - OpenFileDialog( ); -} - -void FileOpenStateMachine::OnCancelSaveDocument() -{ - SetCompletionState( FILE_SAVE_CANCELLED ); -} - - -//----------------------------------------------------------------------------- -// Show the save document query dialog -//----------------------------------------------------------------------------- -void FileOpenStateMachine::ShowSaveQuery( ) -{ - m_CurrentState = STATE_SHOWING_SAVE_DIRTY_FILE_DIALOG; - ShowSaveDocumentQuery( GetParent(), m_FileName, m_SaveFileType, 0, this, NULL ); -} - - -//----------------------------------------------------------------------------- -// Used to save a specified file, and deal with all the lovely dialogs -//----------------------------------------------------------------------------- -void FileOpenStateMachine::SaveFile( KeyValues *pContextKeyValues, const char *pFileName, const char *pFileType, int nFlags ) -{ - CleanUpContextKeyValues(); - SetCompletionState( IN_PROGRESS ); - m_pContextKeyValues = pContextKeyValues; - m_FileName = pFileName; - m_SaveFileType = pFileType; - m_OpenFileType = NULL; - m_OpenFileName = NULL; - - // Clear the P4 dialog flag for SDK users and licensees without Perforce - if ( CommandLine()->FindParm( "-nop4" ) ) - { - nFlags &= ~FOSM_SHOW_PERFORCE_DIALOGS; - } - - m_bShowPerforceDialogs = ( nFlags & FOSM_SHOW_PERFORCE_DIALOGS ) != 0; - m_bShowSaveQuery = ( nFlags & FOSM_SHOW_SAVE_QUERY ) != 0; - m_bIsOpeningFile = false; - m_bWroteFile = false; - - if ( m_bShowSaveQuery ) - { - ShowSaveQuery(); - return; - } - - OnSaveFile(); -} - - -//----------------------------------------------------------------------------- -// Reads the file in -//----------------------------------------------------------------------------- -void FileOpenStateMachine::ReadFile() -{ - m_CurrentState = STATE_READING_FILE; - if ( !m_pClient->OnReadFileFromDisk( m_FileName, m_OpenFileType, m_pContextKeyValues ) ) - { - SetCompletionState( ERROR_READING_FILE ); - return; - } - - SetCompletionState( SUCCESSFUL ); -} - - -//----------------------------------------------------------------------------- -// Shows the open file dialog -//----------------------------------------------------------------------------- -void FileOpenStateMachine::OpenFileDialog( ) -{ - m_CurrentState = STATE_SHOWING_OPEN_DIALOG; - - if ( m_OpenFileName.IsEmpty() ) - { - FileOpenDialog *pDialog = new FileOpenDialog( GetParent(), "Open", true ); - m_pClient->SetupFileOpenDialog( pDialog, true, m_OpenFileType, m_pContextKeyValues ); - pDialog->SetDeleteSelfOnClose( true ); - pDialog->AddActionSignalTarget( this ); - pDialog->DoModal( ); - } - else - { - m_FileName = m_OpenFileName; - ReadFile(); - } -} - - -//----------------------------------------------------------------------------- -// Opens a file, saves an existing one if necessary -//----------------------------------------------------------------------------- -void FileOpenStateMachine::OpenFile( const char *pOpenFileType, KeyValues *pContextKeyValues, const char *pSaveFileName, const char *pSaveFileType, int nFlags ) -{ - CleanUpContextKeyValues(); - SetCompletionState( IN_PROGRESS ); - m_pContextKeyValues = pContextKeyValues; - m_FileName = pSaveFileName; - m_SaveFileType = pSaveFileType; - m_OpenFileType = pOpenFileType; - m_OpenFileName = NULL; - m_bShowPerforceDialogs = ( nFlags & FOSM_SHOW_PERFORCE_DIALOGS ) != 0; - m_bShowSaveQuery = ( nFlags & FOSM_SHOW_SAVE_QUERY ) != 0; - m_bIsOpeningFile = true; - m_bWroteFile = false; - - if ( m_bShowSaveQuery ) - { - ShowSaveQuery(); - return; - } - - OpenFileDialog(); -} - - -//----------------------------------------------------------------------------- -// Version of OpenFile that skips browsing for a particular file to open -//----------------------------------------------------------------------------- -void FileOpenStateMachine::OpenFile( const char *pOpenFileName, const char *pOpenFileType, KeyValues *pContextKeyValues, const char *pSaveFileName, const char *pSaveFileType, int nFlags ) -{ - CleanUpContextKeyValues(); - SetCompletionState( IN_PROGRESS ); - m_pContextKeyValues = pContextKeyValues; - m_FileName = pSaveFileName; - m_SaveFileType = pSaveFileType; - m_OpenFileType = pOpenFileType; - m_bShowPerforceDialogs = ( nFlags & FOSM_SHOW_PERFORCE_DIALOGS ) != 0; - m_bShowSaveQuery = ( nFlags & FOSM_SHOW_SAVE_QUERY ) != 0; - m_bIsOpeningFile = true; - m_bWroteFile = false; - m_OpenFileName = pOpenFileName; - if ( m_bShowSaveQuery ) - { - ShowSaveQuery(); - return; - } - - OpenFileDialog(); -} - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// This is a helper class designed to help with the chains of modal dialogs +// encountered when trying to open or save a particular file +// +//============================================================================= + +#include "vgui_controls/FileOpenStateMachine.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/FileOpenDialog.h" +#include "vgui_controls/MessageBox.h" +#include "vgui_controls/perforcefilelistframe.h" +#include "vgui_controls/savedocumentquery.h" +#include "filesystem.h" +#include "p4lib/ip4.h" +#include "tier2/tier2.h" +#include "tier0/icommandline.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +FileOpenStateMachine::FileOpenStateMachine( vgui::Panel *pParent, IFileOpenStateMachineClient *pClient ) : BaseClass( pParent, "FileOpenStateMachine" ) +{ + m_pClient = pClient; + m_CompletionState = SUCCESSFUL; + m_CurrentState = STATE_NONE; + m_pContextKeyValues = NULL; + SetVisible( false ); +} + +FileOpenStateMachine::~FileOpenStateMachine() +{ + CleanUpContextKeyValues(); +} + + +//----------------------------------------------------------------------------- +// Cleans up keyvalues +//----------------------------------------------------------------------------- +void FileOpenStateMachine::CleanUpContextKeyValues() +{ + if ( m_pContextKeyValues ) + { + m_pContextKeyValues->deleteThis(); + m_pContextKeyValues = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Returns the state machine completion state +//----------------------------------------------------------------------------- +FileOpenStateMachine::CompletionState_t FileOpenStateMachine::GetCompletionState() +{ + return m_CompletionState; +} + + +//----------------------------------------------------------------------------- +// Utility to set the completion state +//----------------------------------------------------------------------------- +void FileOpenStateMachine::SetCompletionState( FileOpenStateMachine::CompletionState_t state ) +{ + m_CompletionState = state; + if ( m_CompletionState == IN_PROGRESS ) + return; + + m_CurrentState = STATE_NONE; + + KeyValues *kv = new KeyValues( "FileStateMachineFinished" ); + kv->SetInt( "completionState", m_CompletionState ); + kv->SetInt( "wroteFile", m_bWroteFile ); + kv->SetString( "fullPath", m_FileName.Get() ); + kv->SetString( "fileType", m_bIsOpeningFile ? m_OpenFileType.Get() : m_SaveFileType.Get() ); + if ( m_pContextKeyValues ) + { + kv->AddSubKey( m_pContextKeyValues ); + m_pContextKeyValues = NULL; + } + PostActionSignal( kv ); +} + + +//----------------------------------------------------------------------------- +// Called by the message box in OverwriteFileDialog +//----------------------------------------------------------------------------- +void FileOpenStateMachine::OnOverwriteFile( ) +{ + CheckOutDialog( ); +} + +void FileOpenStateMachine::OnCancelOverwriteFile( ) +{ + SetCompletionState( FILE_NOT_OVERWRITTEN ); +} + + +//----------------------------------------------------------------------------- +// Shows the overwrite existing file dialog +//----------------------------------------------------------------------------- +void FileOpenStateMachine::OverwriteFileDialog( ) +{ + if ( !g_pFullFileSystem->FileExists( m_FileName ) ) + { + CheckOutDialog( ); + return; + } + + m_CurrentState = STATE_SHOWING_OVERWRITE_DIALOG; + + char pBuf[1024]; + Q_snprintf( pBuf, sizeof(pBuf), "File already exists. Overwrite it?\n\n\"%s\"\n", m_FileName.Get() ); + vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Overwrite Existing File?", pBuf, GetParent() ); + pMessageBox->AddActionSignalTarget( this ); + pMessageBox->SetOKButtonVisible( true ); + pMessageBox->SetOKButtonText( "Yes" ); + pMessageBox->SetCancelButtonVisible( true ); + pMessageBox->SetCancelButtonText( "No" ); + pMessageBox->SetCloseButtonVisible( false ); + pMessageBox->SetCommand( new KeyValues( "OverwriteFile" ) ); + pMessageBox->SetCancelCommand( new KeyValues( "CancelOverwriteFile" ) ); + pMessageBox->DoModal(); +} + + +//----------------------------------------------------------------------------- +// Used to open a particular file in perforce, and deal with all the lovely dialogs +//----------------------------------------------------------------------------- +void FileOpenStateMachine::OnFileSelectionCancelled() +{ + if ( m_CurrentState == STATE_SHOWING_SAVE_DIALOG ) + { + SetCompletionState( FILE_SAVE_NAME_NOT_SPECIFIED ); + return; + } + + if ( m_CurrentState == STATE_SHOWING_OPEN_DIALOG ) + { + SetCompletionState( FILE_OPEN_NAME_NOT_SPECIFIED ); + return; + } + + Assert(0); +} + + +//----------------------------------------------------------------------------- +// Used to open a particular file in perforce, and deal with all the lovely dialogs +//----------------------------------------------------------------------------- +void FileOpenStateMachine::OnFileSelected( KeyValues *pKeyValues ) +{ + if ( m_CurrentState == STATE_SHOWING_SAVE_DIALOG ) + { + m_FileName = pKeyValues->GetString( "fullpath" ); + const char *pFilterInfo = pKeyValues->GetString( "filterinfo" ); + if ( pFilterInfo ) + { + m_SaveFileType = pFilterInfo; + } + OverwriteFileDialog(); + return; + } + + if ( m_CurrentState == STATE_SHOWING_OPEN_DIALOG ) + { + m_FileName = pKeyValues->GetString( "fullpath" ); + const char *pFilterInfo = pKeyValues->GetString( "filterinfo" ); + if ( pFilterInfo ) + { + m_OpenFileType = pFilterInfo; + } + ReadFile( ); + return; + } + + Assert(0); +} + + +//----------------------------------------------------------------------------- +// Writes the file out +//----------------------------------------------------------------------------- +void FileOpenStateMachine::WriteFile() +{ + m_CurrentState = STATE_WRITING_FILE; + if ( !m_pClient->OnWriteFileToDisk( m_FileName, m_SaveFileType, m_pContextKeyValues ) ) + { + SetCompletionState( ERROR_WRITING_FILE ); + return; + } + + m_bWroteFile = true; + if ( m_bShowPerforceDialogs ) + { + m_CurrentState = STATE_SHOWING_PERFORCE_ADD_DIALOG; + ShowPerforceQuery( GetParent(), m_FileName, this, NULL, PERFORCE_ACTION_FILE_ADD ); + return; + } + + if ( !m_bIsOpeningFile ) + { + SetCompletionState( SUCCESSFUL ); + return; + } + + OpenFileDialog(); +} + + +//----------------------------------------------------------------------------- +// Called by the message box in MakeFileWriteableDialog +//----------------------------------------------------------------------------- +void FileOpenStateMachine::OnMakeFileWriteable( ) +{ + if ( !g_pFullFileSystem->SetFileWritable( m_FileName, true ) ) + { + SetCompletionState( ERROR_MAKING_FILE_WRITEABLE ); + return; + } + + WriteFile(); +} + +void FileOpenStateMachine::OnCancelMakeFileWriteable( ) +{ + SetCompletionState( FILE_NOT_MADE_WRITEABLE ); +} + + +//----------------------------------------------------------------------------- +// Shows the make file writeable dialog +//----------------------------------------------------------------------------- +void FileOpenStateMachine::MakeFileWriteableDialog( ) +{ + // If the file is writeable, write it! + if ( !g_pFullFileSystem->FileExists( m_FileName ) || g_pFullFileSystem->IsFileWritable( m_FileName ) ) + { + WriteFile(); + return; + } + + // If it's in perforce, and not checked out, then we must abort. + bool bIsInPerforce = p4->IsFileInPerforce( m_FileName ); + bool bIsOpened = ( p4->GetFileState( m_FileName ) != P4FILE_UNOPENED ); + if ( bIsInPerforce && !bIsOpened ) + { + SetCompletionState( FILE_NOT_CHECKED_OUT ); + return; + } + + m_CurrentState = STATE_SHOWING_MAKE_FILE_WRITEABLE_DIALOG; + + char pBuf[1024]; + Q_snprintf( pBuf, sizeof(pBuf), "Encountered read-only file. Should it be made writeable?\n\n\"%s\"\n", m_FileName.Get() ); + vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Make File Writeable?", pBuf, GetParent() ); + pMessageBox->AddActionSignalTarget( this ); + pMessageBox->SetOKButtonVisible( true ); + pMessageBox->SetOKButtonText( "Yes" ); + pMessageBox->SetCancelButtonVisible( true ); + pMessageBox->SetCancelButtonText( "No" ); + pMessageBox->SetCloseButtonVisible( false ); + pMessageBox->SetCommand( new KeyValues( "MakeFileWriteable" ) ); + pMessageBox->SetCancelCommand( new KeyValues( "CancelMakeFileWriteable" ) ); + pMessageBox->DoModal(); +} + + +//----------------------------------------------------------------------------- +// Called when ShowPerforceQuery completes +//----------------------------------------------------------------------------- +void FileOpenStateMachine::OnPerforceQueryCompleted( KeyValues *pKeyValues ) +{ + if ( m_CurrentState == STATE_SHOWING_CHECK_OUT_DIALOG ) + { + if ( pKeyValues->GetInt( "operationPerformed" ) == 0 ) + { + SetCompletionState( FILE_NOT_CHECKED_OUT ); + return; + } + + MakeFileWriteableDialog(); + return; + } + + if ( m_CurrentState == STATE_SHOWING_PERFORCE_ADD_DIALOG ) + { + if ( !m_bIsOpeningFile ) + { + SetCompletionState( SUCCESSFUL ); + return; + } + + OpenFileDialog(); + return; + } + + Assert(0); +} + + +//----------------------------------------------------------------------------- +// Used to open a particular file in perforce, and deal with all the lovely dialogs +//----------------------------------------------------------------------------- +void FileOpenStateMachine::CheckOutDialog( ) +{ + if ( m_bShowPerforceDialogs ) + { + m_CurrentState = STATE_SHOWING_CHECK_OUT_DIALOG; + ShowPerforceQuery( GetParent(), m_FileName, this, NULL, PERFORCE_ACTION_FILE_EDIT ); + return; + } + + WriteFile(); +} + + +//----------------------------------------------------------------------------- +// These 3 messages come from the savedocumentquery dialog +//----------------------------------------------------------------------------- +void FileOpenStateMachine::OnSaveFile() +{ + if ( !m_FileName[0] || !Q_IsAbsolutePath( m_FileName ) ) + { + m_CurrentState = STATE_SHOWING_SAVE_DIALOG; + + FileOpenDialog *pDialog = new FileOpenDialog( GetParent(), "Save As", false ); + m_pClient->SetupFileOpenDialog( pDialog, false, m_SaveFileType, m_pContextKeyValues ); + pDialog->SetDeleteSelfOnClose( true ); + pDialog->AddActionSignalTarget( this ); + pDialog->DoModal( ); + return; + } + + CheckOutDialog( ); +} + +void FileOpenStateMachine::OnMarkNotDirty() +{ + if ( !m_bIsOpeningFile ) + { + SetCompletionState( SUCCESSFUL ); + return; + } + + // Jump right to opening the file + OpenFileDialog( ); +} + +void FileOpenStateMachine::OnCancelSaveDocument() +{ + SetCompletionState( FILE_SAVE_CANCELLED ); +} + + +//----------------------------------------------------------------------------- +// Show the save document query dialog +//----------------------------------------------------------------------------- +void FileOpenStateMachine::ShowSaveQuery( ) +{ + m_CurrentState = STATE_SHOWING_SAVE_DIRTY_FILE_DIALOG; + ShowSaveDocumentQuery( GetParent(), m_FileName, m_SaveFileType, 0, this, NULL ); +} + + +//----------------------------------------------------------------------------- +// Used to save a specified file, and deal with all the lovely dialogs +//----------------------------------------------------------------------------- +void FileOpenStateMachine::SaveFile( KeyValues *pContextKeyValues, const char *pFileName, const char *pFileType, int nFlags ) +{ + CleanUpContextKeyValues(); + SetCompletionState( IN_PROGRESS ); + m_pContextKeyValues = pContextKeyValues; + m_FileName = pFileName; + m_SaveFileType = pFileType; + m_OpenFileType = NULL; + m_OpenFileName = NULL; + + // Clear the P4 dialog flag for SDK users and licensees without Perforce + if ( CommandLine()->FindParm( "-nop4" ) ) + { + nFlags &= ~FOSM_SHOW_PERFORCE_DIALOGS; + } + + m_bShowPerforceDialogs = ( nFlags & FOSM_SHOW_PERFORCE_DIALOGS ) != 0; + m_bShowSaveQuery = ( nFlags & FOSM_SHOW_SAVE_QUERY ) != 0; + m_bIsOpeningFile = false; + m_bWroteFile = false; + + if ( m_bShowSaveQuery ) + { + ShowSaveQuery(); + return; + } + + OnSaveFile(); +} + + +//----------------------------------------------------------------------------- +// Reads the file in +//----------------------------------------------------------------------------- +void FileOpenStateMachine::ReadFile() +{ + m_CurrentState = STATE_READING_FILE; + if ( !m_pClient->OnReadFileFromDisk( m_FileName, m_OpenFileType, m_pContextKeyValues ) ) + { + SetCompletionState( ERROR_READING_FILE ); + return; + } + + SetCompletionState( SUCCESSFUL ); +} + + +//----------------------------------------------------------------------------- +// Shows the open file dialog +//----------------------------------------------------------------------------- +void FileOpenStateMachine::OpenFileDialog( ) +{ + m_CurrentState = STATE_SHOWING_OPEN_DIALOG; + + if ( m_OpenFileName.IsEmpty() ) + { + FileOpenDialog *pDialog = new FileOpenDialog( GetParent(), "Open", true ); + m_pClient->SetupFileOpenDialog( pDialog, true, m_OpenFileType, m_pContextKeyValues ); + pDialog->SetDeleteSelfOnClose( true ); + pDialog->AddActionSignalTarget( this ); + pDialog->DoModal( ); + } + else + { + m_FileName = m_OpenFileName; + ReadFile(); + } +} + + +//----------------------------------------------------------------------------- +// Opens a file, saves an existing one if necessary +//----------------------------------------------------------------------------- +void FileOpenStateMachine::OpenFile( const char *pOpenFileType, KeyValues *pContextKeyValues, const char *pSaveFileName, const char *pSaveFileType, int nFlags ) +{ + CleanUpContextKeyValues(); + SetCompletionState( IN_PROGRESS ); + m_pContextKeyValues = pContextKeyValues; + m_FileName = pSaveFileName; + m_SaveFileType = pSaveFileType; + m_OpenFileType = pOpenFileType; + m_OpenFileName = NULL; + m_bShowPerforceDialogs = ( nFlags & FOSM_SHOW_PERFORCE_DIALOGS ) != 0; + m_bShowSaveQuery = ( nFlags & FOSM_SHOW_SAVE_QUERY ) != 0; + m_bIsOpeningFile = true; + m_bWroteFile = false; + + if ( m_bShowSaveQuery ) + { + ShowSaveQuery(); + return; + } + + OpenFileDialog(); +} + + +//----------------------------------------------------------------------------- +// Version of OpenFile that skips browsing for a particular file to open +//----------------------------------------------------------------------------- +void FileOpenStateMachine::OpenFile( const char *pOpenFileName, const char *pOpenFileType, KeyValues *pContextKeyValues, const char *pSaveFileName, const char *pSaveFileType, int nFlags ) +{ + CleanUpContextKeyValues(); + SetCompletionState( IN_PROGRESS ); + m_pContextKeyValues = pContextKeyValues; + m_FileName = pSaveFileName; + m_SaveFileType = pSaveFileType; + m_OpenFileType = pOpenFileType; + m_bShowPerforceDialogs = ( nFlags & FOSM_SHOW_PERFORCE_DIALOGS ) != 0; + m_bShowSaveQuery = ( nFlags & FOSM_SHOW_SAVE_QUERY ) != 0; + m_bIsOpeningFile = true; + m_bWroteFile = false; + m_OpenFileName = pOpenFileName; + if ( m_bShowSaveQuery ) + { + ShowSaveQuery(); + return; + } + + OpenFileDialog(); +} + + diff --git a/mp/src/vgui2/vgui_controls/FocusNavGroup.cpp b/mp/src/vgui2/vgui_controls/FocusNavGroup.cpp index 10fc88ad..8bfb807d 100644 --- a/mp/src/vgui2/vgui_controls/FocusNavGroup.cpp +++ b/mp/src/vgui2/vgui_controls/FocusNavGroup.cpp @@ -1,433 +1,433 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: Constructor -// Input : *panel - parent panel -//----------------------------------------------------------------------------- -FocusNavGroup::FocusNavGroup(Panel *panel) : _mainPanel(panel) -{ - _currentFocus = NULL; - _topLevelFocus = false; - _defaultButton = NULL; - _currentDefaultButton = NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -FocusNavGroup::~FocusNavGroup() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the focus to the previous panel in the tab order -// Input : *panel - panel currently with focus -//----------------------------------------------------------------------------- -bool FocusNavGroup::RequestFocusPrev(VPANEL panel) -{ - if(panel==0) - return false; - - _currentFocus = NULL; - int newPosition = 9999999; - if (panel) - { - newPosition = ipanel()->GetTabPosition(panel); - } - - bool bFound = false; - bool bRepeat = true; - Panel *best = NULL; - while (1) - { - newPosition--; - if (newPosition > 0) - { - int bestPosition = 0; - - // look for the next tab position - for (int i = 0; i < _mainPanel->GetChildCount(); i++) - { - Panel *child = _mainPanel->GetChild(i); - if (child && child->IsVisible() && child->IsEnabled() && child->GetTabPosition()) - { - int tabPosition = child->GetTabPosition(); - if (tabPosition == newPosition) - { - // we've found the right tab - best = child; - bestPosition = newPosition; - - // don't loop anymore since we've found the correct panel - break; - } - else if (tabPosition < newPosition && tabPosition > bestPosition) - { - // record the match since this is the closest so far - bestPosition = tabPosition; - best = child; - } - } - } - - if (!bRepeat) - break; - - if (best) - break; - } - else - { - // reset new position for next loop - newPosition = 9999999; - } - - // haven't found an item - - if (!_topLevelFocus) - { - // check to see if we should push the focus request up - if (_mainPanel->GetVParent() && _mainPanel->GetVParent() != surface()->GetEmbeddedPanel()) - { - // we're not a top level panel, so forward up the request instead of looping - if (ipanel()->RequestFocusPrev(_mainPanel->GetVParent(), _mainPanel->GetVPanel())) - { - bFound = true; - SetCurrentDefaultButton(NULL); - break; - } - } - } - - // not found an item, loop back - newPosition = 9999999; - bRepeat = false; - } - - if (best) - { - _currentFocus = best->GetVPanel(); - best->RequestFocus(-1); - bFound = true; - - if (!CanButtonBeDefault(best->GetVPanel())) - { - if (_defaultButton) - { - SetCurrentDefaultButton(_defaultButton); - } - else - { - SetCurrentDefaultButton(NULL); - - // we need to ask the parent to set its default button - if (_mainPanel->GetVParent()) - { - ivgui()->PostMessage(_mainPanel->GetVParent(), new KeyValues("FindDefaultButton"), NULL); - } - } - } - else - { - SetCurrentDefaultButton(best->GetVPanel()); - } - } - return bFound; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the focus to the previous panel in the tab order -// Input : *panel - panel currently with focus -//----------------------------------------------------------------------------- -bool FocusNavGroup::RequestFocusNext(VPANEL panel) -{ - // basic recursion guard, in case user has set up a bad focus hierarchy - static int stack_depth = 0; - stack_depth++; - - _currentFocus = NULL; - int newPosition = 0; - if (panel) - { - newPosition = ipanel()->GetTabPosition(panel); - } - - bool bFound = false; - bool bRepeat = true; - Panel *best = NULL; - while (1) - { - newPosition++; - int bestPosition = 999999; - - // look for the next tab position - for (int i = 0; i < _mainPanel->GetChildCount(); i++) - { - Panel *child = _mainPanel->GetChild(i); - if ( !child ) - continue; - - if (child && child->IsVisible() && child->IsEnabled() && child->GetTabPosition()) - { - int tabPosition = child->GetTabPosition(); - if (tabPosition == newPosition) - { - // we've found the right tab - best = child; - bestPosition = newPosition; - - // don't loop anymore since we've found the correct panel - break; - } - else if (tabPosition > newPosition && tabPosition < bestPosition) - { - // record the match since this is the closest so far - bestPosition = tabPosition; - best = child; - } - } - } - - if (!bRepeat) - break; - - if (best) - break; - - // haven't found an item - - // check to see if we should push the focus request up - if (!_topLevelFocus) - { - if (_mainPanel->GetVParent() && _mainPanel->GetVParent() != surface()->GetEmbeddedPanel()) - { - // we're not a top level panel, so forward up the request instead of looping - if (stack_depth < 15) - { - if (ipanel()->RequestFocusNext(_mainPanel->GetVParent(), _mainPanel->GetVPanel())) - { - bFound = true; - SetCurrentDefaultButton(NULL); - break; - } - - // if we find one then we break, otherwise we loop - } - } - } - - // loop back - newPosition = 0; - bRepeat = false; - } - - if (best) - { - _currentFocus = best->GetVPanel(); - best->RequestFocus(1); - bFound = true; - - if (!CanButtonBeDefault(best->GetVPanel())) - { - if (_defaultButton) - { - SetCurrentDefaultButton(_defaultButton); - } - else - { - SetCurrentDefaultButton(NULL); - - // we need to ask the parent to set its default button - if (_mainPanel->GetVParent()) - { - ivgui()->PostMessage(_mainPanel->GetVParent(), new KeyValues("FindDefaultButton"), NULL); - } - } - } - else - { - SetCurrentDefaultButton(best->GetVPanel()); - } - } - - stack_depth--; - return bFound; -} - -//----------------------------------------------------------------------------- -// Purpose: sets the panel that owns this FocusNavGroup to be the root in the focus traversal heirarchy -//----------------------------------------------------------------------------- -void FocusNavGroup::SetFocusTopLevel(bool state) -{ - _topLevelFocus = state; -} - -//----------------------------------------------------------------------------- -// Purpose: sets panel which receives input when ENTER is hit -//----------------------------------------------------------------------------- -void FocusNavGroup::SetDefaultButton(Panel *panel) -{ - VPANEL vpanel = panel ? panel->GetVPanel() : NULL; - if ( vpanel == _defaultButton.Get() ) - return; - -// Assert(CanButtonBeDefault(vpanel)); - - _defaultButton = vpanel; - SetCurrentDefaultButton(_defaultButton); -} - -//----------------------------------------------------------------------------- -// Purpose: sets panel which receives input when ENTER is hit -//----------------------------------------------------------------------------- -void FocusNavGroup::SetCurrentDefaultButton(VPANEL panel, bool sendCurrentDefaultButtonMessage) -{ - if (panel == _currentDefaultButton.Get()) - return; - - if ( sendCurrentDefaultButtonMessage && _currentDefaultButton.Get() != 0) - { - ivgui()->PostMessage(_currentDefaultButton, new KeyValues("SetAsCurrentDefaultButton", "state", 0), NULL); - } - - _currentDefaultButton = panel; - - if ( sendCurrentDefaultButtonMessage && _currentDefaultButton.Get() != 0) - { - ivgui()->PostMessage(_currentDefaultButton, new KeyValues("SetAsCurrentDefaultButton", "state", 1), NULL); - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets panel which receives input when ENTER is hit -//----------------------------------------------------------------------------- -VPANEL FocusNavGroup::GetCurrentDefaultButton() -{ - return _currentDefaultButton; -} - -//----------------------------------------------------------------------------- -// Purpose: sets panel which receives input when ENTER is hit -//----------------------------------------------------------------------------- -VPANEL FocusNavGroup::GetDefaultButton() -{ - return _defaultButton; -} - -//----------------------------------------------------------------------------- -// Purpose: finds the panel which is activated by the specified key -// Input : code - the keycode of the hotkey -// Output : Panel * - NULL if no panel found -//----------------------------------------------------------------------------- -Panel *FocusNavGroup::FindPanelByHotkey(wchar_t key) -{ - for (int i = 0; i < _mainPanel->GetChildCount(); i++) - { - Panel *child = _mainPanel->GetChild(i); - if ( !child ) - continue; - - Panel *hot = child->HasHotkey(key); - if (hot && hot->IsVisible() && hot->IsEnabled()) - { - return hot; - } - } - - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Panel *FocusNavGroup::GetDefaultPanel() -{ - for (int i = 0; i < _mainPanel->GetChildCount(); i++) - { - Panel *child = _mainPanel->GetChild(i); - if ( !child ) - continue; - - if (child->GetTabPosition() == 1) - { - return child; - } - } - - return NULL; // no specific panel set -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Panel *FocusNavGroup::GetCurrentFocus() -{ - return _currentFocus ? ipanel()->GetPanel(_currentFocus, vgui::GetControlsModuleName()) : NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the current focus -//----------------------------------------------------------------------------- -VPANEL FocusNavGroup::SetCurrentFocus(VPANEL focus, VPANEL defaultPanel) -{ - _currentFocus = focus; - - // if we haven't found a default panel yet, let's see if we know of one - if (defaultPanel == 0) - { - // can this focus itself by the default - if (CanButtonBeDefault(focus)) - { - defaultPanel = focus; - } - else if (_defaultButton) // do we know of a default button - { - defaultPanel = _defaultButton; - } - } - - SetCurrentDefaultButton(defaultPanel); - return defaultPanel; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns true if the specified panel can be the default -//----------------------------------------------------------------------------- -bool FocusNavGroup::CanButtonBeDefault(VPANEL panel) -{ - if( panel == 0 ) - return false; - - KeyValues *data = new KeyValues("CanBeDefaultButton"); - - bool bResult = false; - if (ipanel()->RequestInfo(panel, data)) - { - bResult = (data->GetInt("result") == 1); - } - data->deleteThis(); - return bResult; +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +// Input : *panel - parent panel +//----------------------------------------------------------------------------- +FocusNavGroup::FocusNavGroup(Panel *panel) : _mainPanel(panel) +{ + _currentFocus = NULL; + _topLevelFocus = false; + _defaultButton = NULL; + _currentDefaultButton = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +FocusNavGroup::~FocusNavGroup() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the focus to the previous panel in the tab order +// Input : *panel - panel currently with focus +//----------------------------------------------------------------------------- +bool FocusNavGroup::RequestFocusPrev(VPANEL panel) +{ + if(panel==0) + return false; + + _currentFocus = NULL; + int newPosition = 9999999; + if (panel) + { + newPosition = ipanel()->GetTabPosition(panel); + } + + bool bFound = false; + bool bRepeat = true; + Panel *best = NULL; + while (1) + { + newPosition--; + if (newPosition > 0) + { + int bestPosition = 0; + + // look for the next tab position + for (int i = 0; i < _mainPanel->GetChildCount(); i++) + { + Panel *child = _mainPanel->GetChild(i); + if (child && child->IsVisible() && child->IsEnabled() && child->GetTabPosition()) + { + int tabPosition = child->GetTabPosition(); + if (tabPosition == newPosition) + { + // we've found the right tab + best = child; + bestPosition = newPosition; + + // don't loop anymore since we've found the correct panel + break; + } + else if (tabPosition < newPosition && tabPosition > bestPosition) + { + // record the match since this is the closest so far + bestPosition = tabPosition; + best = child; + } + } + } + + if (!bRepeat) + break; + + if (best) + break; + } + else + { + // reset new position for next loop + newPosition = 9999999; + } + + // haven't found an item + + if (!_topLevelFocus) + { + // check to see if we should push the focus request up + if (_mainPanel->GetVParent() && _mainPanel->GetVParent() != surface()->GetEmbeddedPanel()) + { + // we're not a top level panel, so forward up the request instead of looping + if (ipanel()->RequestFocusPrev(_mainPanel->GetVParent(), _mainPanel->GetVPanel())) + { + bFound = true; + SetCurrentDefaultButton(NULL); + break; + } + } + } + + // not found an item, loop back + newPosition = 9999999; + bRepeat = false; + } + + if (best) + { + _currentFocus = best->GetVPanel(); + best->RequestFocus(-1); + bFound = true; + + if (!CanButtonBeDefault(best->GetVPanel())) + { + if (_defaultButton) + { + SetCurrentDefaultButton(_defaultButton); + } + else + { + SetCurrentDefaultButton(NULL); + + // we need to ask the parent to set its default button + if (_mainPanel->GetVParent()) + { + ivgui()->PostMessage(_mainPanel->GetVParent(), new KeyValues("FindDefaultButton"), NULL); + } + } + } + else + { + SetCurrentDefaultButton(best->GetVPanel()); + } + } + return bFound; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the focus to the previous panel in the tab order +// Input : *panel - panel currently with focus +//----------------------------------------------------------------------------- +bool FocusNavGroup::RequestFocusNext(VPANEL panel) +{ + // basic recursion guard, in case user has set up a bad focus hierarchy + static int stack_depth = 0; + stack_depth++; + + _currentFocus = NULL; + int newPosition = 0; + if (panel) + { + newPosition = ipanel()->GetTabPosition(panel); + } + + bool bFound = false; + bool bRepeat = true; + Panel *best = NULL; + while (1) + { + newPosition++; + int bestPosition = 999999; + + // look for the next tab position + for (int i = 0; i < _mainPanel->GetChildCount(); i++) + { + Panel *child = _mainPanel->GetChild(i); + if ( !child ) + continue; + + if (child && child->IsVisible() && child->IsEnabled() && child->GetTabPosition()) + { + int tabPosition = child->GetTabPosition(); + if (tabPosition == newPosition) + { + // we've found the right tab + best = child; + bestPosition = newPosition; + + // don't loop anymore since we've found the correct panel + break; + } + else if (tabPosition > newPosition && tabPosition < bestPosition) + { + // record the match since this is the closest so far + bestPosition = tabPosition; + best = child; + } + } + } + + if (!bRepeat) + break; + + if (best) + break; + + // haven't found an item + + // check to see if we should push the focus request up + if (!_topLevelFocus) + { + if (_mainPanel->GetVParent() && _mainPanel->GetVParent() != surface()->GetEmbeddedPanel()) + { + // we're not a top level panel, so forward up the request instead of looping + if (stack_depth < 15) + { + if (ipanel()->RequestFocusNext(_mainPanel->GetVParent(), _mainPanel->GetVPanel())) + { + bFound = true; + SetCurrentDefaultButton(NULL); + break; + } + + // if we find one then we break, otherwise we loop + } + } + } + + // loop back + newPosition = 0; + bRepeat = false; + } + + if (best) + { + _currentFocus = best->GetVPanel(); + best->RequestFocus(1); + bFound = true; + + if (!CanButtonBeDefault(best->GetVPanel())) + { + if (_defaultButton) + { + SetCurrentDefaultButton(_defaultButton); + } + else + { + SetCurrentDefaultButton(NULL); + + // we need to ask the parent to set its default button + if (_mainPanel->GetVParent()) + { + ivgui()->PostMessage(_mainPanel->GetVParent(), new KeyValues("FindDefaultButton"), NULL); + } + } + } + else + { + SetCurrentDefaultButton(best->GetVPanel()); + } + } + + stack_depth--; + return bFound; +} + +//----------------------------------------------------------------------------- +// Purpose: sets the panel that owns this FocusNavGroup to be the root in the focus traversal heirarchy +//----------------------------------------------------------------------------- +void FocusNavGroup::SetFocusTopLevel(bool state) +{ + _topLevelFocus = state; +} + +//----------------------------------------------------------------------------- +// Purpose: sets panel which receives input when ENTER is hit +//----------------------------------------------------------------------------- +void FocusNavGroup::SetDefaultButton(Panel *panel) +{ + VPANEL vpanel = panel ? panel->GetVPanel() : NULL; + if ( vpanel == _defaultButton.Get() ) + return; + +// Assert(CanButtonBeDefault(vpanel)); + + _defaultButton = vpanel; + SetCurrentDefaultButton(_defaultButton); +} + +//----------------------------------------------------------------------------- +// Purpose: sets panel which receives input when ENTER is hit +//----------------------------------------------------------------------------- +void FocusNavGroup::SetCurrentDefaultButton(VPANEL panel, bool sendCurrentDefaultButtonMessage) +{ + if (panel == _currentDefaultButton.Get()) + return; + + if ( sendCurrentDefaultButtonMessage && _currentDefaultButton.Get() != 0) + { + ivgui()->PostMessage(_currentDefaultButton, new KeyValues("SetAsCurrentDefaultButton", "state", 0), NULL); + } + + _currentDefaultButton = panel; + + if ( sendCurrentDefaultButtonMessage && _currentDefaultButton.Get() != 0) + { + ivgui()->PostMessage(_currentDefaultButton, new KeyValues("SetAsCurrentDefaultButton", "state", 1), NULL); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets panel which receives input when ENTER is hit +//----------------------------------------------------------------------------- +VPANEL FocusNavGroup::GetCurrentDefaultButton() +{ + return _currentDefaultButton; +} + +//----------------------------------------------------------------------------- +// Purpose: sets panel which receives input when ENTER is hit +//----------------------------------------------------------------------------- +VPANEL FocusNavGroup::GetDefaultButton() +{ + return _defaultButton; +} + +//----------------------------------------------------------------------------- +// Purpose: finds the panel which is activated by the specified key +// Input : code - the keycode of the hotkey +// Output : Panel * - NULL if no panel found +//----------------------------------------------------------------------------- +Panel *FocusNavGroup::FindPanelByHotkey(wchar_t key) +{ + for (int i = 0; i < _mainPanel->GetChildCount(); i++) + { + Panel *child = _mainPanel->GetChild(i); + if ( !child ) + continue; + + Panel *hot = child->HasHotkey(key); + if (hot && hot->IsVisible() && hot->IsEnabled()) + { + return hot; + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Panel *FocusNavGroup::GetDefaultPanel() +{ + for (int i = 0; i < _mainPanel->GetChildCount(); i++) + { + Panel *child = _mainPanel->GetChild(i); + if ( !child ) + continue; + + if (child->GetTabPosition() == 1) + { + return child; + } + } + + return NULL; // no specific panel set +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Panel *FocusNavGroup::GetCurrentFocus() +{ + return _currentFocus ? ipanel()->GetPanel(_currentFocus, vgui::GetControlsModuleName()) : NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the current focus +//----------------------------------------------------------------------------- +VPANEL FocusNavGroup::SetCurrentFocus(VPANEL focus, VPANEL defaultPanel) +{ + _currentFocus = focus; + + // if we haven't found a default panel yet, let's see if we know of one + if (defaultPanel == 0) + { + // can this focus itself by the default + if (CanButtonBeDefault(focus)) + { + defaultPanel = focus; + } + else if (_defaultButton) // do we know of a default button + { + defaultPanel = _defaultButton; + } + } + + SetCurrentDefaultButton(defaultPanel); + return defaultPanel; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the specified panel can be the default +//----------------------------------------------------------------------------- +bool FocusNavGroup::CanButtonBeDefault(VPANEL panel) +{ + if( panel == 0 ) + return false; + + KeyValues *data = new KeyValues("CanBeDefaultButton"); + + bool bResult = false; + if (ipanel()->RequestInfo(panel, data)) + { + bResult = (data->GetInt("result") == 1); + } + data->deleteThis(); + return bResult; } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/Frame.cpp b/mp/src/vgui2/vgui_controls/Frame.cpp index 3d4718ad..35b5d761 100644 --- a/mp/src/vgui2/vgui_controls/Frame.cpp +++ b/mp/src/vgui2/vgui_controls/Frame.cpp @@ -1,2396 +1,2396 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//===========================================================================// - -#include -#include // for ceil() -#define PROTECTED_THINGS_DISABLE - -#include "tier1/utlstring.h" -#include "vgui/Cursor.h" -#include "vgui/MouseCode.h" -#include "vgui/IBorder.h" -#include "vgui/IInput.h" -#include "vgui/ILocalize.h" -#include "vgui/IPanel.h" -#include "vgui/ISurface.h" -#include "vgui/IScheme.h" -#include "vgui/KeyCode.h" - -#include "vgui_controls/AnimationController.h" -#include "vgui_controls/Controls.h" -#include "vgui_controls/Frame.h" -#include "vgui_controls/Button.h" -#include "vgui_controls/Menu.h" -#include "vgui_controls/MenuButton.h" -#include "vgui_controls/TextImage.h" - -#include "KeyValues.h" - -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -using namespace vgui; - -static const int DEFAULT_SNAP_RANGE = 10; // number of pixels distance before the frame will snap to an edge -static const int CAPTION_TITLE_BORDER = 7; -static const int CAPTION_TITLE_BORDER_SMALL = 0; - -namespace -{ - //----------------------------------------------------------------------------- - // Purpose: Invisible panel to handle dragging/resizing frames - //----------------------------------------------------------------------------- - class GripPanel : public Panel - { - public: - GripPanel(Frame *dragFrame, const char *name, int xdir, int ydir) : Panel(dragFrame, name) - { - _frame = dragFrame; - _dragging = false; - _dragMultX = xdir; - _dragMultY = ydir; - SetPaintEnabled(false); - SetPaintBackgroundEnabled(false); - SetPaintBorderEnabled(false); - m_iSnapRange = DEFAULT_SNAP_RANGE; - - if (xdir == 1 && ydir == 1) - { - // bottom-right grip gets an image - SetPaintEnabled(true); - SetPaintBackgroundEnabled(true); - } - - SetBlockDragChaining( true ); - } - - // Purpose- handle window resizing - // Input- dx, dy, the offet of the mouse pointer from where we started dragging - virtual void moved(int dx, int dy) - { - if (!_frame->IsSizeable()) - return; - - // Start off with x, y at the coords of where we started to drag - int newX = _dragOrgPos[0], newY =_dragOrgPos[1]; - // Start off with width and tall equal from window when we started to drag - int newWide = _dragOrgSize[0], newTall = _dragOrgSize[1]; - - // get window's minimum size - int minWide, minTall; - _frame->GetMinimumSize( minWide, minTall); - - // Handle width resizing - newWide += (dx * _dragMultX); - // Handle the position of the corner x position - if (_dragMultX == -1) - { - // only move if we are not at the minimum - // if we are at min we have to force the proper offset (dx) - if (newWide < minWide) - { - dx=_dragOrgSize[0]-minWide; - } - newX += dx; // move window to its new position - } - - // Handle height resizing - newTall += (dy * _dragMultY); - // Handle position of corner y position - if (_dragMultY == -1) - { - if (newTall < minTall) - { - dy=_dragOrgSize[1]-minTall; - } - newY += dy; - } - - if ( _frame->GetClipToParent() ) - { - // If any coordinate is out of range, snap it back - if ( newX < 0 ) - newX = 0; - if ( newY < 0 ) - newY = 0; - - int sx, sy; - surface()->GetScreenSize( sx, sy ); - - int w, h; - _frame->GetSize( w, h ); - if ( newX + w > sx ) - { - newX = sx - w; - } - if ( newY + h > sy ) - { - newY = sy - h; - } - } - - // set new position - _frame->SetPos(newX, newY); - // set the new size - // if window is below min size it will automatically pop to min size - _frame->SetSize(newWide, newTall); - _frame->InvalidateLayout(); - _frame->Repaint(); - } - - void OnCursorMoved(int x, int y) - { - if (!_dragging) - return; - - if (!input()->IsMouseDown(MOUSE_LEFT)) - { - // for some reason we're marked as dragging when the mouse is released - // trigger a release - OnMouseReleased(MOUSE_LEFT); - return; - } - - input()->GetCursorPos(x, y); - moved((x - _dragStart[0]), ( y - _dragStart[1])); - _frame->Repaint(); - } - - void OnMousePressed(MouseCode code) - { - if (code == MOUSE_LEFT) - { - _dragging=true; - int x,y; - input()->GetCursorPos(x,y); - _dragStart[0]=x; - _dragStart[1]=y; - _frame->GetPos(_dragOrgPos[0],_dragOrgPos[1]); - _frame->GetSize(_dragOrgSize[0],_dragOrgSize[1]); - input()->SetMouseCapture(GetVPanel()); - - // if a child doesn't have focus, get it for ourselves - VPANEL focus = input()->GetFocus(); - if (!focus || !ipanel()->HasParent(focus, _frame->GetVPanel())) - { - _frame->RequestFocus(); - } - _frame->Repaint(); - } - else - { - GetParent()->OnMousePressed(code); - } - } - - void OnMouseDoublePressed(MouseCode code) - { - GetParent()->OnMouseDoublePressed(code); - } - - void Paint() - { - // draw the grab handle in the bottom right of the frame - surface()->DrawSetTextFont(_marlettFont); - surface()->DrawSetTextPos(0, 0); - - // thin highlight lines - surface()->DrawSetTextColor(GetFgColor()); - surface()->DrawUnicodeChar('p'); - } - - void PaintBackground() - { - // draw the grab handle in the bottom right of the frame - surface()->DrawSetTextFont(_marlettFont); - surface()->DrawSetTextPos(0, 0); - - // thick shadow lines - surface()->DrawSetTextColor(GetBgColor()); - surface()->DrawUnicodeChar('o'); - } - - void OnMouseReleased(MouseCode code) - { - _dragging = false; - input()->SetMouseCapture(NULL); - } - - void OnMouseCaptureLost() - { - Panel::OnMouseCaptureLost(); - _dragging = false; - } - - void ApplySchemeSettings(IScheme *pScheme) - { - Panel::ApplySchemeSettings(pScheme); - bool isSmall = ((Frame *)GetParent())->IsSmallCaption(); - - _marlettFont = pScheme->GetFont( isSmall ? "MarlettSmall" : "Marlett", IsProportional()); - SetFgColor(GetSchemeColor("FrameGrip.Color1", pScheme)); - SetBgColor(GetSchemeColor("FrameGrip.Color2", pScheme)); - - const char *snapRange = pScheme->GetResourceString("Frame.AutoSnapRange"); - if (snapRange && *snapRange) - { - m_iSnapRange = atoi(snapRange); - } - } - - protected: - Frame *_frame; - int _dragMultX; - int _dragMultY; - bool _dragging; - int _dragOrgPos[2]; - int _dragOrgSize[2]; - int _dragStart[2]; - int m_iSnapRange; - HFont _marlettFont; - }; - - //----------------------------------------------------------------------------- - // Purpose: Handles caption grip input for moving dialogs around - //----------------------------------------------------------------------------- - class CaptionGripPanel : public GripPanel - { - public: - CaptionGripPanel(Frame* frame, const char *name) : GripPanel(frame, name, 0, 0) - { - } - - void moved(int dx, int dy) - { - if (!_frame->IsMoveable()) - return; - - int newX = _dragOrgPos[0] + dx; - int newY = _dragOrgPos[1] + dy; - - if (m_iSnapRange) - { - // first check docking to desktop - int wx, wy, ww, wt; - surface()->GetWorkspaceBounds(wx, wy, ww, wt); - getInsideSnapPosition(wx, wy, ww, wt, newX, newY); - - // now lets check all windows and see if we snap to those - // root panel - VPANEL root = surface()->GetEmbeddedPanel(); - // cycle through panels - // look for panels that are visible and are popups that we can dock to - for (int i = 0; i < ipanel()->GetChildCount(root); ++i) - { - VPANEL child = ipanel()->GetChild(root, i); - tryToDock (child, newX, newY); - } - } - - if ( _frame->GetClipToParent() ) - { - // If any coordinate is out of range, snap it back - if ( newX < 0 ) - newX = 0; - if ( newY < 0 ) - newY = 0; - - int sx, sy; - surface()->GetScreenSize( sx, sy ); - - int w, h; - _frame->GetSize( w, h ); - if ( newX + w > sx ) - { - newX = sx - w; - } - if ( newY + h > sy ) - { - newY = sy - h; - } - } - - _frame->SetPos(newX, newY); - - } - - void tryToDock(VPANEL window, int &newX, int & newY) - { - // bail if child is this window - if ( window == _frame->GetVPanel()) - return; - - int cx, cy, cw, ct; - if ( (ipanel()->IsVisible(window)) && (ipanel()->IsPopup(window)) ) - { - // position - ipanel()->GetAbsPos(window, cx, cy); - // dimensions - ipanel()->GetSize(window, cw, ct); - bool snapped = getOutsideSnapPosition (cx, cy, cw, ct, newX, newY); - if (snapped) - { - // if we snapped, we're done with this path - // dont try to snap to kids - return; - } - } - - // check all children - for (int i = 0; i < ipanel()->GetChildCount(window); ++i) - { - VPANEL child = ipanel()->GetChild(window, i); - tryToDock(child, newX, newY); - } - - } - - // Purpose: To calculate the windows new x,y position if it snaps - // Will snap to the INSIDE of a window (eg desktop sides - // Input: boundX boundY, position of candidate window we are seeing if we snap to - // boundWide, boundTall, width and height of window we are seeing if we snap to - // Output: snapToX, snapToY new coords for window, unchanged if we dont snap - // Returns true if we snapped, false if we did not snap. - bool getInsideSnapPosition(int boundX, int boundY, int boundWide, int boundTall, - int &snapToX, int &snapToY) - { - - int wide, tall; - _frame->GetSize(wide, tall); - Assert (wide > 0); - Assert (tall > 0); - - bool snapped=false; - if (abs(snapToX - boundX) < m_iSnapRange) - { - snapToX = boundX; - snapped=true; - } - else if (abs((snapToX + wide) - (boundX + boundWide)) < m_iSnapRange) - { - snapToX = boundX + boundWide - wide; - snapped=true; - } - - if (abs(snapToY - boundY) < m_iSnapRange) - { - snapToY = boundY; - snapped=true; - } - else if (abs((snapToY + tall) - (boundY + boundTall)) < m_iSnapRange) - { - snapToY = boundY + boundTall - tall; - snapped=true; - } - return snapped; - - } - - // Purpose: To calculate the windows new x,y position if it snaps - // Will snap to the OUTSIDE edges of a window (i.e. will stick peers together - // Input: left, top, position of candidate window we are seeing if we snap to - // boundWide, boundTall, width and height of window we are seeing if we snap to - // Output: snapToX, snapToY new coords for window, unchanged if we dont snap - // Returns true if we snapped, false if we did not snap. - bool getOutsideSnapPosition(int left, int top, int boundWide, int boundTall, - int &snapToX, int &snapToY) - { - Assert (boundWide >= 0); - Assert (boundTall >= 0); - - bool snapped=false; - - int right=left+boundWide; - int bottom=top+boundTall; - - int wide, tall; - _frame->GetSize(wide, tall); - Assert (wide > 0); - Assert (tall > 0); - - // we now see if we are going to be able to snap to a window side, and not - // just snap to the "open air" - // want to make it so that if any part of the window can dock to the candidate, it will - - // is this window horizontally snappable to the candidate - bool horizSnappable=( - // top of window is in range - ((snapToY > top) && (snapToY < bottom)) - // bottom of window is in range - || ((snapToY+tall > top) && (snapToY+tall < bottom)) - // window is just plain bigger than the window we wanna dock to - || ((snapToY < top) && (snapToY+tall > bottom)) ); - - - // is this window vertically snappable to the candidate - bool vertSnappable= ( - // left of window is in range - ((snapToX > left) && (snapToX < right)) - // right of window is in range - || ((snapToX+wide > left) && (snapToX+wide < right)) - // window is just plain bigger than the window we wanna dock to - || ((snapToX < left) && (snapToX+wide > right)) ); - - // if neither, might as well bail - if ( !(horizSnappable || vertSnappable) ) - return false; - - //if we're within the snap threshold then snap - if ( (snapToX <= (right+m_iSnapRange)) && - (snapToX >= (right-m_iSnapRange)) ) - { - if (horizSnappable) - { - //disallow "open air" snaps - snapped=true; - snapToX = right; - } - } - else if ((snapToX + wide) >= (left-m_iSnapRange) && - (snapToX + wide) <= (left+m_iSnapRange)) - { - if (horizSnappable) - { - snapped=true; - snapToX = left-wide; - } - } - - if ( (snapToY <= (bottom+m_iSnapRange)) && - (snapToY >= (bottom-m_iSnapRange)) ) - { - if (vertSnappable) - { - snapped=true; - snapToY = bottom; - } - } - else if ((snapToY + tall) <= (top+m_iSnapRange) && - (snapToY + tall) >= (top-m_iSnapRange)) - { - if (vertSnappable) - { - snapped=true; - snapToY = top-tall; - } - } - return snapped; - } - }; - -} - -namespace vgui -{ - //----------------------------------------------------------------------------- - // Purpose: overrides normal button drawing to use different colors & borders - //----------------------------------------------------------------------------- - class FrameButton : public Button - { - private: - IBorder *_brightBorder, *_depressedBorder, *_disabledBorder; - Color _enabledFgColor, _enabledBgColor; - Color _disabledFgColor, _disabledBgColor; - bool _disabledLook; - - public: - - static int GetButtonSide( Frame *pFrame ) - { - if ( pFrame->IsSmallCaption() ) - { - return 12; - } - - return 18; - } - - - FrameButton(Panel *parent, const char *name, const char *text) : Button(parent, name, text) - { - SetSize( FrameButton::GetButtonSide( (Frame *)parent ), FrameButton::GetButtonSide( (Frame *)parent ) ); - _brightBorder = NULL; - _depressedBorder = NULL; - _disabledBorder = NULL; - _disabledLook = true; - SetContentAlignment(Label::a_northwest); - SetTextInset(2, 1); - SetBlockDragChaining( true ); - } - - virtual void ApplySchemeSettings(IScheme *pScheme) - { - Button::ApplySchemeSettings(pScheme); - - _enabledFgColor = GetSchemeColor("FrameTitleButton.FgColor", pScheme); - _enabledBgColor = GetSchemeColor("FrameTitleButton.BgColor", pScheme); - - _disabledFgColor = GetSchemeColor("FrameTitleButton.DisabledFgColor", pScheme); - _disabledBgColor = GetSchemeColor("FrameTitleButton.DisabledBgColor", pScheme); - - _brightBorder = pScheme->GetBorder("TitleButtonBorder"); - _depressedBorder = pScheme->GetBorder("TitleButtonDepressedBorder"); - _disabledBorder = pScheme->GetBorder("TitleButtonDisabledBorder"); - - SetDisabledLook(_disabledLook); - } - - virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) - { - if (_disabledLook) - { - return _disabledBorder; - } - - if (depressed) - { - return _depressedBorder; - } - - return _brightBorder; - } - - virtual void SetDisabledLook(bool state) - { - _disabledLook = state; - if (!_disabledLook) - { - SetDefaultColor(_enabledFgColor, _enabledBgColor); - SetArmedColor(_enabledFgColor, _enabledBgColor); - SetDepressedColor(_enabledFgColor, _enabledBgColor); - } - else - { - // setup disabled colors - SetDefaultColor(_disabledFgColor, _disabledBgColor); - SetArmedColor(_disabledFgColor, _disabledBgColor); - SetDepressedColor(_disabledFgColor, _disabledBgColor); - } - } - - virtual void PerformLayout() - { - Button::PerformLayout(); - Repaint(); - } - - // Don't request focus. - // This will keep items in the listpanel selected. - virtual void OnMousePressed(MouseCode code) - { - if (!IsEnabled()) - return; - - if (!IsMouseClickEnabled(code)) - return; - - if (IsUseCaptureMouseEnabled()) - { - { - SetSelected(true); - Repaint(); - } - - // lock mouse input to going to this button - input()->SetMouseCapture(GetVPanel()); - } - } -}; - - -//----------------------------------------------------------------------------- -// Purpose: icon button -//----------------------------------------------------------------------------- -class FrameSystemButton : public MenuButton -{ - DECLARE_CLASS_SIMPLE( FrameSystemButton, MenuButton ); - -private: - IImage *_enabled, *_disabled; - Color _enCol, _disCol; - bool _respond; - CUtlString m_EnabledImage; - CUtlString m_DisabledImage; - -public: - FrameSystemButton(Panel *parent, const char *panelName) : MenuButton(parent, panelName, "") - { - _disabled = _enabled = NULL; - _respond = true; - SetEnabled(false); - // This menu will open if we use the left or right mouse button - SetMouseClickEnabled( MOUSE_RIGHT, true ); - SetBlockDragChaining( true ); - } - - void SetImages( const char *pEnabledImage, const char *pDisabledImage = NULL ) - { - m_EnabledImage = pEnabledImage; - m_DisabledImage = pDisabledImage ? pDisabledImage : pEnabledImage; - } - - void GetImageSize( int &w, int &h ) - { - w = h = 0; - - int tw = 0, th = 0; - if ( _enabled ) - { - _enabled->GetSize( w, h ); - } - if ( _disabled ) - { - _disabled->GetSize( tw, th ); - } - if ( tw > w ) - { - w = tw; - } - if ( th > h ) - { - h = th; - } - } - - virtual void ApplySchemeSettings(IScheme *pScheme) - { - BaseClass::ApplySchemeSettings(pScheme); - - _enCol = GetSchemeColor("FrameSystemButton.FgColor", pScheme); - _disCol = GetSchemeColor("FrameSystemButton.BgColor", pScheme); - - const char *pEnabledImage = m_EnabledImage.Length() ? m_EnabledImage.Get() : - pScheme->GetResourceString( "FrameSystemButton.Icon" ); - const char *pDisabledImage = m_DisabledImage.Length() ? m_DisabledImage.Get() : - pScheme->GetResourceString( "FrameSystemButton.DisabledIcon" ); - _enabled = scheme()->GetImage( pEnabledImage, false); - _disabled = scheme()->GetImage( pDisabledImage, false); - - SetTextInset(0, 0); - - // get our iconic image - SetEnabled(IsEnabled()); - } - - virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) - { - return NULL; - } - - virtual void SetEnabled(bool state) - { - Button::SetEnabled(state); - - if (IsEnabled()) - { - if ( _enabled ) - { - SetImageAtIndex(0, _enabled, 0); - } - SetBgColor(_enCol); - SetDefaultColor(_enCol, _enCol); - SetArmedColor(_enCol, _enCol); - SetDepressedColor(_enCol, _enCol); - } - else - { - if ( _disabled ) - { - SetImageAtIndex(0, _disabled, 0); - } - SetBgColor(_disCol); - SetDefaultColor(_disCol, _disCol); - SetArmedColor(_disCol, _disCol); - SetDepressedColor(_disCol, _disCol); - } - } - - void SetResponsive(bool state) - { - _respond = state; - } - - virtual void OnMousePressed(MouseCode code) - { - // button may look enabled but not be responsive - if (!_respond) - return; - - BaseClass::OnMousePressed(code); - } - - virtual void OnMouseDoublePressed(MouseCode code) - { - // button may look enabled but not be responsive - if (!_respond) - return; - - // only close if left is double pressed - if (code == MOUSE_LEFT) - { - // double click on the icon closes the window - // But only if the menu contains a 'close' item - vgui::Menu *pMenu = GetMenu(); - if ( pMenu && pMenu->FindChildByName("Close") ) - { - PostMessage(GetVParent(), new KeyValues("CloseFrameButtonPressed")); - } - } - } - -}; - -} // namespace vgui -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -Frame::Frame(Panel *parent, const char *panelName, bool showTaskbarIcon /*=true*/, bool bPopup /*=true*/ ) : EditablePanel(parent, panelName) -{ - // frames start invisible, to avoid having window flicker in on taskbar - SetVisible(false); - if ( bPopup ) - { - MakePopup(showTaskbarIcon); - } - - m_hPreviousModal = 0; - - _title=null; - _moveable=true; - _sizeable=true; - m_bHasFocus=false; - _flashWindow=false; - _drawTitleBar = true; - m_bPreviouslyVisible = false; - m_bFadingOut = false; - m_bDisableFadeEffect = false; - m_flTransitionEffectTime = 0.0f; - m_flFocusTransitionEffectTime = 0.0f; - m_bDeleteSelfOnClose = false; - m_iClientInsetX = 5; - m_iClientInsetY = 5; - m_iClientInsetXOverridden = false; - m_iTitleTextInsetX = 28; - m_bClipToParent = false; - m_bSmallCaption = false; - m_bChainKeysToParent = false; - m_bPrimed = false; - m_hCustomTitleFont = INVALID_FONT; - - SetTitle("#Frame_Untitled", parent ? false : true); - - // add ourselves to the build group - SetBuildGroup(GetBuildGroup()); - - SetMinimumSize(128,66); - - GetFocusNavGroup().SetFocusTopLevel(true); - -#if !defined( _X360 ) - _sysMenu = NULL; - - // add dragging grips - _topGrip = new GripPanel(this, "frame_topGrip", 0, -1); - _bottomGrip = new GripPanel(this, "frame_bottomGrip", 0, 1); - _leftGrip = new GripPanel(this, "frame_leftGrip", -1, 0); - _rightGrip = new GripPanel(this, "frame_rightGrip", 1, 0); - _topLeftGrip = new GripPanel(this, "frame_tlGrip", -1, -1); - _topRightGrip = new GripPanel(this, "frame_trGrip", 1, -1); - _bottomLeftGrip = new GripPanel(this, "frame_blGrip", -1, 1); - _bottomRightGrip = new GripPanel(this, "frame_brGrip", 1, 1); - _captionGrip = new CaptionGripPanel(this, "frame_caption" ); - _captionGrip->SetCursor(dc_arrow); - - _minimizeButton = new FrameButton(this, "frame_minimize","0"); - _minimizeButton->AddActionSignalTarget(this); - _minimizeButton->SetCommand(new KeyValues("Minimize")); - - _maximizeButton = new FrameButton(this, "frame_maximize", "1"); - //!! no maximize handler implemented yet, so leave maximize button disabled - SetMaximizeButtonVisible(false); - - char str[] = { 0x6F, 0 }; - _minimizeToSysTrayButton = new FrameButton(this, "frame_mintosystray", str); - _minimizeToSysTrayButton->SetCommand("MinimizeToSysTray"); - SetMinimizeToSysTrayButtonVisible(false); - - _closeButton = new FrameButton(this, "frame_close", "r"); - _closeButton->AddActionSignalTarget(this); - _closeButton->SetCommand(new KeyValues("CloseFrameButtonPressed")); - - if (!surface()->SupportsFeature(ISurface::FRAME_MINIMIZE_MAXIMIZE)) - { - SetMinimizeButtonVisible(false); - SetMaximizeButtonVisible(false); - } - - if (parent) - { - // vgui doesn't support subwindow minimization - SetMinimizeButtonVisible(false); - SetMaximizeButtonVisible(false); - } - - _menuButton = new FrameSystemButton(this, "frame_menu"); - _menuButton->SetMenu(GetSysMenu()); -#endif - - SetupResizeCursors(); - - REGISTER_COLOR_AS_OVERRIDABLE( m_InFocusBgColor, "infocus_bgcolor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( m_OutOfFocusBgColor, "outoffocus_bgcolor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _titleBarBgColor, "titlebarbgcolor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _titleBarDisabledBgColor, "titlebardisabledbgcolor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _titleBarFgColor, "titlebarfgcolor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _titleBarDisabledFgColor, "titlebardisabledfgcolor_override" ); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -Frame::~Frame() -{ - if ( input()->GetAppModalSurface() == GetVPanel() ) - { - vgui::input()->ReleaseAppModalSurface(); - if ( m_hPreviousModal != 0 ) - { - vgui::input()->SetAppModalSurface( m_hPreviousModal ); - m_hPreviousModal = 0; - } - } - -#if !defined( _X360 ) - delete _topGrip; - delete _bottomGrip; - delete _leftGrip; - delete _rightGrip; - delete _topLeftGrip; - delete _topRightGrip; - delete _bottomLeftGrip; - delete _bottomRightGrip; - delete _captionGrip; - delete _minimizeButton; - delete _maximizeButton; - delete _closeButton; - delete _menuButton; - delete _minimizeToSysTrayButton; -#endif - delete _title; -} - -//----------------------------------------------------------------------------- -// Purpose: Setup the grips on the edges of the panel to resize it. -//----------------------------------------------------------------------------- -void Frame::SetupResizeCursors() -{ -#if !defined( _X360 ) - if (IsSizeable()) - { - _topGrip->SetCursor(dc_sizens); - _bottomGrip->SetCursor(dc_sizens); - _leftGrip->SetCursor(dc_sizewe); - _rightGrip->SetCursor(dc_sizewe); - _topLeftGrip->SetCursor(dc_sizenwse); - _topRightGrip->SetCursor(dc_sizenesw); - _bottomLeftGrip->SetCursor(dc_sizenesw); - _bottomRightGrip->SetCursor(dc_sizenwse); - - _bottomRightGrip->SetPaintEnabled(true); - _bottomRightGrip->SetPaintBackgroundEnabled(true); - } - else - { - // not resizable, so just use the default cursor - _topGrip->SetCursor(dc_arrow); - _bottomGrip->SetCursor(dc_arrow); - _leftGrip->SetCursor(dc_arrow); - _rightGrip->SetCursor(dc_arrow); - _topLeftGrip->SetCursor(dc_arrow); - _topRightGrip->SetCursor(dc_arrow); - _bottomLeftGrip->SetCursor(dc_arrow); - _bottomRightGrip->SetCursor(dc_arrow); - - _bottomRightGrip->SetPaintEnabled(false); - _bottomRightGrip->SetPaintBackgroundEnabled(false); - } -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: Bring the frame to the front and requests focus, ensures it's not minimized -//----------------------------------------------------------------------------- -void Frame::Activate() -{ - MoveToFront(); - if ( IsKeyBoardInputEnabled() ) - { - RequestFocus(); - } - SetVisible(true); - SetEnabled(true); - if (m_bFadingOut) - { - // we were fading out, make sure to fade back in - m_bFadingOut = false; - m_bPreviouslyVisible = false; - } - - surface()->SetMinimized(GetVPanel(), false); -} - - -//----------------------------------------------------------------------------- -// Sets up, cleans up modal dialogs -//----------------------------------------------------------------------------- -void Frame::DoModal( ) -{ - // move to the middle of the screen - MoveToCenterOfScreen(); - InvalidateLayout(); - Activate(); - m_hPreviousModal = vgui::input()->GetAppModalSurface(); - vgui::input()->SetAppModalSurface( GetVPanel() ); -} - - -//----------------------------------------------------------------------------- -// Closes a modal dialog -//----------------------------------------------------------------------------- -void Frame::CloseModal() -{ - vgui::input()->ReleaseAppModalSurface(); - if ( m_hPreviousModal != 0 ) - { - vgui::input()->SetAppModalSurface( m_hPreviousModal ); - m_hPreviousModal = 0; - } - PostMessage( this, new KeyValues("Close") ); -} - - -//----------------------------------------------------------------------------- -// Purpose: activates the dialog -// if dialog is not currently visible it starts it minimized and flashing in the taskbar -//----------------------------------------------------------------------------- -void Frame::ActivateMinimized() -{ - if ( ( IsVisible() && !IsMinimized() ) || !surface()->SupportsFeature( ISurface::FRAME_MINIMIZE_MAXIMIZE ) ) - { - Activate(); - } - else - { - ipanel()->MoveToBack(GetVPanel()); - surface()->SetMinimized(GetVPanel(), true); - SetVisible(true); - SetEnabled(true); - if (m_bFadingOut) - { - // we were fading out, make sure to fade back in - m_bFadingOut = false; - m_bPreviouslyVisible = false; - } - FlashWindow(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if the dialog is currently minimized -//----------------------------------------------------------------------------- -bool Frame::IsMinimized() -{ - return surface()->IsMinimized(GetVPanel()); -} - -//----------------------------------------------------------------------------- -// Purpose: Center the dialog on the screen -//----------------------------------------------------------------------------- -void Frame::MoveToCenterOfScreen() -{ - int wx, wy, ww, wt; - surface()->GetWorkspaceBounds(wx, wy, ww, wt); - SetPos((ww - GetWide()) / 2, (wt - GetTall()) / 2); -} - - -void Frame::LayoutProportional( FrameButton *bt ) -{ - float scale = 1.0; - - if( IsProportional() ) - { - int screenW, screenH; - surface()->GetScreenSize( screenW, screenH ); - - int proW,proH; - surface()->GetProportionalBase( proW, proH ); - - scale = ( (float)( screenH ) / (float)( proH ) ); - } - - bt->SetSize( (int)( FrameButton::GetButtonSide( this ) * scale ), (int)( FrameButton::GetButtonSide( this ) * scale ) ); - bt->SetTextInset( (int)( ceil( 2 * scale ) ), (int) ( ceil(1 * scale ) ) ); -} - -//----------------------------------------------------------------------------- -// Purpose: per-frame thinking, used for transition effects -// only gets called if the Frame is visible -//----------------------------------------------------------------------------- -void Frame::OnThink() -{ - BaseClass::OnThink(); - - // check for transition effects - if (IsVisible() && m_flTransitionEffectTime > 0 && ( !m_bDisableFadeEffect )) - { - if (m_bFadingOut) - { - // we're fading out, see if we're done so we can fully hide the window - if (GetAlpha() < ( IsX360() ? 64 : 1 )) - { - FinishClose(); - } - } - else if (!m_bPreviouslyVisible) - { - // need to fade-in - m_bPreviouslyVisible = true; - - // fade in - if (IsX360()) - { - SetAlpha(64); - } - else - { - SetAlpha(0); - } - GetAnimationController()->RunAnimationCommand(this, "alpha", 255.0f, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR); - } - } - - // check for focus changes - bool hasFocus = false; - - if (input()) - { - VPANEL focus = input()->GetFocus(); - if (focus && ipanel()->HasParent(focus, GetVPanel())) - { - if ( input()->GetAppModalSurface() == 0 || - input()->GetAppModalSurface() == GetVPanel() ) - { - hasFocus = true; - } - } - } - if (hasFocus != m_bHasFocus) - { - // Because vgui focus is message based, and focus gets reset to NULL when a focused panel is deleted, we defer the flashing/transition - // animation for an extra frame in case something is deleted, a message is sent, and then we become the focused panel again on the - // next frame - if ( !m_bPrimed ) - { - m_bPrimed = true; - return; - } - m_bPrimed = false; - m_bHasFocus = hasFocus; - OnFrameFocusChanged(m_bHasFocus); - } - else - { - m_bPrimed = false; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Called when the frame focus changes -//----------------------------------------------------------------------------- -void Frame::OnFrameFocusChanged(bool bHasFocus) -{ -#if !defined( _X360 ) - // enable/disable the frame buttons - _minimizeButton->SetDisabledLook(!bHasFocus); - _maximizeButton->SetDisabledLook(!bHasFocus); - _closeButton->SetDisabledLook(!bHasFocus); - _minimizeToSysTrayButton->SetDisabledLook(!bHasFocus); - _menuButton->SetEnabled(bHasFocus); - _minimizeButton->InvalidateLayout(); - _maximizeButton->InvalidateLayout(); - _minimizeToSysTrayButton->InvalidateLayout(); - _closeButton->InvalidateLayout(); - _menuButton->InvalidateLayout(); -#endif - - if (bHasFocus) - { - _title->SetColor(_titleBarFgColor); - } - else - { - _title->SetColor(_titleBarDisabledFgColor); - } - - // set our background color - if (bHasFocus) - { - if (m_flFocusTransitionEffectTime && ( !m_bDisableFadeEffect )) - { - GetAnimationController()->RunAnimationCommand(this, "BgColor", m_InFocusBgColor, 0.0f, m_bDisableFadeEffect ? 0.0f : m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR); - } - else - { - SetBgColor(m_InFocusBgColor); - } - } - else - { - if (m_flFocusTransitionEffectTime && ( !m_bDisableFadeEffect )) - { - GetAnimationController()->RunAnimationCommand(this, "BgColor", m_OutOfFocusBgColor, 0.0f, m_bDisableFadeEffect ? 0.0f : m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR); - } - else - { - SetBgColor(m_OutOfFocusBgColor); - } - } - - // Stop flashing when we get focus - if (bHasFocus && _flashWindow) - { - FlashWindowStop(); - } -} - -int Frame::GetDraggerSize() -{ - const int DRAGGER_SIZE = 5; - if ( m_bSmallCaption ) - { - return 3; - } - - return DRAGGER_SIZE; -} - -int Frame::GetCornerSize() -{ - const int CORNER_SIZE = 8; - if ( m_bSmallCaption ) - { - return 6; - } - - return CORNER_SIZE; -} - -int Frame::GetBottomRightSize() -{ - const int BOTTOMRIGHTSIZE = 18; - if ( m_bSmallCaption ) - { - return 12; - } - - return BOTTOMRIGHTSIZE; -} - -int Frame::GetCaptionHeight() -{ - const int CAPTIONHEIGHT = 23; - if ( m_bSmallCaption ) - { - return 12; - } - return CAPTIONHEIGHT; -} - -//----------------------------------------------------------------------------- -// Purpose: Recalculate the position of all items -//----------------------------------------------------------------------------- -void Frame::PerformLayout() -{ - // chain back - BaseClass::PerformLayout(); - - // move everything into place - int wide, tall; - GetSize(wide, tall); - -#if !defined( _X360 ) - int DRAGGER_SIZE = GetDraggerSize(); - int CORNER_SIZE = GetCornerSize(); - int CORNER_SIZE2 = CORNER_SIZE * 2; - int BOTTOMRIGHTSIZE = GetBottomRightSize(); - - _topGrip->SetBounds(CORNER_SIZE, 0, wide - CORNER_SIZE2, DRAGGER_SIZE); - _leftGrip->SetBounds(0, CORNER_SIZE, DRAGGER_SIZE, tall - CORNER_SIZE2); - _topLeftGrip->SetBounds(0, 0, CORNER_SIZE, CORNER_SIZE); - _topRightGrip->SetBounds(wide - CORNER_SIZE, 0, CORNER_SIZE, CORNER_SIZE); - _bottomLeftGrip->SetBounds(0, tall - CORNER_SIZE, CORNER_SIZE, CORNER_SIZE); - - // make the bottom-right grip larger - _bottomGrip->SetBounds(CORNER_SIZE, tall - DRAGGER_SIZE, wide - (CORNER_SIZE + BOTTOMRIGHTSIZE), DRAGGER_SIZE); - _rightGrip->SetBounds(wide - DRAGGER_SIZE, CORNER_SIZE, DRAGGER_SIZE, tall - (CORNER_SIZE + BOTTOMRIGHTSIZE)); - - _bottomRightGrip->SetBounds(wide - BOTTOMRIGHTSIZE, tall - BOTTOMRIGHTSIZE, BOTTOMRIGHTSIZE, BOTTOMRIGHTSIZE); - - _captionGrip->SetSize(wide-10,GetCaptionHeight()); - - _topGrip->MoveToFront(); - _bottomGrip->MoveToFront(); - _leftGrip->MoveToFront(); - _rightGrip->MoveToFront(); - _topLeftGrip->MoveToFront(); - _topRightGrip->MoveToFront(); - _bottomLeftGrip->MoveToFront(); - _bottomRightGrip->MoveToFront(); - - _maximizeButton->MoveToFront(); - _menuButton->MoveToFront(); - _minimizeButton->MoveToFront(); - _minimizeToSysTrayButton->MoveToFront(); - _menuButton->SetBounds(5+2, 5+3, GetCaptionHeight()-5, GetCaptionHeight()-5); -#endif - - float scale = 1; - if (IsProportional()) - { - int screenW, screenH; - surface()->GetScreenSize( screenW, screenH ); - - int proW,proH; - surface()->GetProportionalBase( proW, proH ); - - scale = ( (float)( screenH ) / (float)( proH ) ); - } - -#if !defined( _X360 ) - int offset_start = (int)( 20 * scale ); - int offset = offset_start; - - int top_border_offset = (int) ( ( 5+3 ) * scale ); - if ( m_bSmallCaption ) - { - top_border_offset = (int) ( ( 3 ) * scale ); - } - - int side_border_offset = (int) ( 5 * scale ); - // push the buttons against the east side - if (_closeButton->IsVisible()) - { - _closeButton->SetPos((wide-side_border_offset)-offset,top_border_offset); - offset += offset_start; - LayoutProportional( _closeButton ); - - } - if (_minimizeToSysTrayButton->IsVisible()) - { - _minimizeToSysTrayButton->SetPos((wide-side_border_offset)-offset,top_border_offset); - offset += offset_start; - LayoutProportional( _minimizeToSysTrayButton ); - } - if (_maximizeButton->IsVisible()) - { - _maximizeButton->SetPos((wide-side_border_offset)-offset,top_border_offset); - offset += offset_start; - LayoutProportional( _maximizeButton ); - } - if (_minimizeButton->IsVisible()) - { - _minimizeButton->SetPos((wide-side_border_offset)-offset,top_border_offset); - offset += offset_start; - LayoutProportional( _minimizeButton ); - } -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: Set the text in the title bar. -//----------------------------------------------------------------------------- -void Frame::SetTitle(const char *title, bool surfaceTitle) -{ - if (!_title) - { - _title = new TextImage( "" ); - } - - Assert(title); - _title->SetText(title); - - // see if the combobox text has changed, and if so, post a message detailing the new text - const char *newTitle = title; - - // check if the new text is a localized string, if so undo it - wchar_t unicodeText[128]; - unicodeText[0] = 0; - if (*newTitle == '#') - { - // try lookup in localization tables - StringIndex_t unlocalizedTextSymbol = g_pVGuiLocalize->FindIndex(newTitle + 1); - if (unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX) - { - // we have a new text value - wcsncpy( unicodeText, g_pVGuiLocalize->GetValueByIndex(unlocalizedTextSymbol), sizeof( unicodeText) / sizeof(wchar_t) ); - } - } - else - { - g_pVGuiLocalize->ConvertANSIToUnicode( newTitle, unicodeText, sizeof(unicodeText) ); - } - - if (surfaceTitle) - { - surface()->SetTitle(GetVPanel(), unicodeText); - } - - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the unicode text in the title bar -//----------------------------------------------------------------------------- -void Frame::SetTitle(const wchar_t *title, bool surfaceTitle) -{ - if (!_title) - { - _title = new TextImage( "" ); - } - _title->SetText(title); - if (surfaceTitle) - { - surface()->SetTitle(GetVPanel(), title); - } - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the text in the title bar. -//----------------------------------------------------------------------------- -void Frame::InternalSetTitle(const char *title) -{ - SetTitle(title, true); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the movability of the panel -//----------------------------------------------------------------------------- -void Frame::SetMoveable(bool state) -{ - _moveable=state; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the resizability of the panel -//----------------------------------------------------------------------------- -void Frame::SetSizeable(bool state) -{ - _sizeable=state; - - SetupResizeCursors(); -} - -// When moving via caption, don't let any part of window go outside parent's bounds -void Frame::SetClipToParent( bool state ) -{ - m_bClipToParent = state; -} - -bool Frame::GetClipToParent() const -{ - return m_bClipToParent; -} - -//----------------------------------------------------------------------------- -// Purpose: Check the movability of the panel -//----------------------------------------------------------------------------- -bool Frame::IsMoveable() -{ - return _moveable; -} - -//----------------------------------------------------------------------------- -// Purpose: Check the resizability of the panel -//----------------------------------------------------------------------------- -bool Frame::IsSizeable() -{ - return _sizeable; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the size of the panel inside the frame edges. -//----------------------------------------------------------------------------- -void Frame::GetClientArea(int &x, int &y, int &wide, int &tall) -{ - x = m_iClientInsetX; - - GetSize(wide, tall); - - if (_drawTitleBar) - { - int captionTall = surface()->GetFontTall(_title->GetFont()); - - int border = m_bSmallCaption ? CAPTION_TITLE_BORDER_SMALL : CAPTION_TITLE_BORDER; - int yinset = m_bSmallCaption ? 0 : m_iClientInsetY; - - yinset += m_iTitleTextInsetYOverride; - - y = yinset + captionTall + border + 1; - tall = (tall - yinset) - y; - } - - if ( m_bSmallCaption ) - { - tall -= 5; - } - - wide = (wide - m_iClientInsetX) - x; -} - -// -//----------------------------------------------------------------------------- -// Purpose: applies user configuration settings -//----------------------------------------------------------------------------- -void Frame::ApplyUserConfigSettings(KeyValues *userConfig) -{ - // calculate defaults - int wx, wy, ww, wt; - vgui::surface()->GetWorkspaceBounds(wx, wy, ww, wt); - - int x, y, wide, tall; - GetBounds(x, y, wide, tall); - bool bNoSettings = false; - if (_moveable) - { - // check to see if anything is set - if (!userConfig->FindKey("xpos", false)) - { - bNoSettings = true; - } - - // get the user config position - // default to where we're currently at - x = userConfig->GetInt("xpos", x); - y = userConfig->GetInt("ypos", y); - } - if (_sizeable) - { - wide = userConfig->GetInt("wide", wide); - tall = userConfig->GetInt("tall", tall); - - // Make sure it's no larger than the workspace - if ( wide > ww ) - { - wide = ww; - } - if ( tall > wt ) - { - tall = wt; - } - } - - // see if the dialog has a place on the screen it wants to start - if (bNoSettings && GetDefaultScreenPosition(x, y, wide, tall)) - { - bNoSettings = false; - } - - // make sure it conforms to the minimum size of the dialog - int minWide, minTall; - GetMinimumSize(minWide, minTall); - if (wide < minWide) - { - wide = minWide; - } - if (tall < minTall) - { - tall = minTall; - } - - // make sure it's on the screen - if (x + wide > ww) - { - x = wx + ww - wide; - } - if (y + tall > wt) - { - y = wy + wt - tall; - } - - if (x < wx) - { - x = wx; - } - if (y < wy) - { - y = wy; - } - - SetBounds(x, y, wide, tall); - - if (bNoSettings) - { - // since nothing was set, default our position to the middle of the screen - MoveToCenterOfScreen(); - } - - BaseClass::ApplyUserConfigSettings(userConfig); -} - -//----------------------------------------------------------------------------- -// Purpose: returns user config settings for this control -//----------------------------------------------------------------------------- -void Frame::GetUserConfigSettings(KeyValues *userConfig) -{ - if (_moveable) - { - int x, y; - GetPos(x, y); - userConfig->SetInt("xpos", x); - userConfig->SetInt("ypos", y); - } - if (_sizeable) - { - int w, t; - GetSize(w, t); - userConfig->SetInt("wide", w); - userConfig->SetInt("tall", t); - } - - BaseClass::GetUserConfigSettings(userConfig); -} - -//----------------------------------------------------------------------------- -// Purpose: optimization, return true if this control has any user config settings -//----------------------------------------------------------------------------- -bool Frame::HasUserConfigSettings() -{ - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: gets the default position and size on the screen to appear the first time (defaults to centered) -//----------------------------------------------------------------------------- -bool Frame::GetDefaultScreenPosition(int &x, int &y, int &wide, int &tall) -{ - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: draws title bar -//----------------------------------------------------------------------------- -void Frame::PaintBackground() -{ - // take the panel with focus and check up tree for this panel - // if you find it, than some child of you has the focus, so - // you should be focused - Color titleColor = _titleBarDisabledBgColor; - if (m_bHasFocus) - { - titleColor = _titleBarBgColor; - } - - BaseClass::PaintBackground(); - - if (_drawTitleBar) - { - int wide = GetWide(); - int tall = surface()->GetFontTall(_title->GetFont()); - - // caption - surface()->DrawSetColor(titleColor); - int inset = m_bSmallCaption ? 3 : 5; - int captionHeight = m_bSmallCaption ? 14: 28; - - surface()->DrawFilledRect(inset, inset, wide - inset, captionHeight ); - - if (_title) - { - int nTitleX = m_iTitleTextInsetXOverride ? m_iTitleTextInsetXOverride : m_iTitleTextInsetX; - int nTitleWidth = wide - 72; -#if !defined( _X360 ) - if ( _menuButton && _menuButton->IsVisible() ) - { - int mw, mh; - _menuButton->GetImageSize( mw, mh ); - nTitleX += mw; - nTitleWidth -= mw; - } -#endif - int nTitleY; - if ( m_iTitleTextInsetYOverride ) - { - nTitleY = m_iTitleTextInsetYOverride; - } - else - { - nTitleY = m_bSmallCaption ? 2 : 9; - } - _title->SetPos( nTitleX, nTitleY ); - _title->SetSize( nTitleWidth, tall); - _title->Paint(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Frame::ApplySchemeSettings(IScheme *pScheme) -{ - // always chain back - BaseClass::ApplySchemeSettings(pScheme); - - SetOverridableColor( &_titleBarFgColor, GetSchemeColor("FrameTitleBar.TextColor", pScheme) ); - SetOverridableColor( &_titleBarBgColor, GetSchemeColor("FrameTitleBar.BgColor", pScheme) ); - SetOverridableColor( &_titleBarDisabledFgColor, GetSchemeColor("FrameTitleBar.DisabledTextColor", pScheme) ); - SetOverridableColor( &_titleBarDisabledBgColor, GetSchemeColor("FrameTitleBar.DisabledBgColor", pScheme) ); - - const char *font = NULL; - if ( m_bSmallCaption ) - { - font = pScheme->GetResourceString("FrameTitleBar.SmallFont"); - } - else - { - font = pScheme->GetResourceString("FrameTitleBar.Font"); - } - - HFont titlefont; - if ( m_hCustomTitleFont ) - { - titlefont = m_hCustomTitleFont; - } - else - { - titlefont = pScheme->GetFont((font && *font) ? font : "Default", IsProportional()); - } - - _title->SetFont( titlefont ); - _title->ResizeImageToContent(); - -#if !defined( _X360 ) - HFont marfont = (HFont)0; - if ( m_bSmallCaption ) - { - marfont = pScheme->GetFont( "MarlettSmall", IsProportional() ); - } - else - { - marfont = pScheme->GetFont( "Marlett", IsProportional() ); - } - - _minimizeButton->SetFont(marfont); - _maximizeButton->SetFont(marfont); - _minimizeToSysTrayButton->SetFont(marfont); - _closeButton->SetFont(marfont); -#endif - - m_flTransitionEffectTime = atof(pScheme->GetResourceString("Frame.TransitionEffectTime")); - m_flFocusTransitionEffectTime = atof(pScheme->GetResourceString("Frame.FocusTransitionEffectTime")); - - SetOverridableColor( &m_InFocusBgColor, pScheme->GetColor("Frame.BgColor", GetBgColor()) ); - SetOverridableColor( &m_OutOfFocusBgColor, pScheme->GetColor("Frame.OutOfFocusBgColor", m_InFocusBgColor) ); - - const char *resourceString = pScheme->GetResourceString("Frame.ClientInsetX"); - if ( resourceString ) - { - m_iClientInsetX = atoi(resourceString); - } - resourceString = pScheme->GetResourceString("Frame.ClientInsetY"); - if ( resourceString ) - { - m_iClientInsetY = atoi(resourceString); - } - resourceString = pScheme->GetResourceString("Frame.TitleTextInsetX"); - if ( resourceString ) - { - m_iTitleTextInsetX = atoi(resourceString); - } - - SetBgColor(m_InFocusBgColor); - SetBorder(pScheme->GetBorder("FrameBorder")); - - OnFrameFocusChanged( m_bHasFocus ); -} - -// Disables the fade-in/out-effect even if configured in the scheme settings -void Frame::DisableFadeEffect( void ) -{ - m_flFocusTransitionEffectTime = 0.f; - m_flTransitionEffectTime = 0.f; -} - -void Frame::SetFadeEffectDisableOverride( bool disabled ) -{ - m_bDisableFadeEffect = disabled; -} - -//----------------------------------------------------------------------------- -// Purpose: Apply settings loaded from a resource file -//----------------------------------------------------------------------------- -void Frame::ApplySettings(KeyValues *inResourceData) -{ - // Don't change the frame's visibility, remove that setting from the config data - inResourceData->SetInt("visible", -1); - BaseClass::ApplySettings(inResourceData); - - SetCloseButtonVisible( inResourceData->GetBool( "setclosebuttonvisible", true ) ); - - if( !inResourceData->GetInt("settitlebarvisible", 1 ) ) // if "title" is "0" then don't draw the title bar - { - SetTitleBarVisible( false ); - } - - // set the title - const char *title = inResourceData->GetString("title", ""); - if (title && *title) - { - SetTitle(title, true); - } - - const char *titlefont = inResourceData->GetString("title_font", ""); - if ( titlefont && titlefont[0] ) - { - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - if ( pScheme ) - { - m_hCustomTitleFont = pScheme->GetFont( titlefont ); - } - } - - KeyValues *pKV = inResourceData->FindKey( "clientinsetx_override", false ); - if ( pKV ) - { - m_iClientInsetX = pKV->GetInt(); - m_iClientInsetXOverridden = true; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Apply settings loaded from a resource file -//----------------------------------------------------------------------------- -void Frame::GetSettings(KeyValues *outResourceData) -{ - BaseClass::GetSettings(outResourceData); - outResourceData->SetInt("settitlebarvisible", _drawTitleBar ); - - if (_title) - { - char buf[256]; - _title->GetUnlocalizedText( buf, 255 ); - if (buf[0]) - { - outResourceData->SetString("title", buf); - } - } - - if ( m_iClientInsetXOverridden ) - { - outResourceData->SetInt( "clientinsetx_override", m_iClientInsetX ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: returns a description of the settings possible for a frame -//----------------------------------------------------------------------------- -const char *Frame::GetDescription() -{ - static char buf[512]; - Q_snprintf(buf, sizeof(buf), "%s, string title", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: Go invisible when a close message is recieved. -//----------------------------------------------------------------------------- -void Frame::OnClose() -{ - // if we're modal, release that before we hide the window else the wrong window will get focus - if (input()->GetAppModalSurface() == GetVPanel()) - { - input()->ReleaseAppModalSurface(); - if ( m_hPreviousModal != 0 ) - { - vgui::input()->SetAppModalSurface( m_hPreviousModal ); - m_hPreviousModal = 0; - } - } - - BaseClass::OnClose(); - - if (m_flTransitionEffectTime && !m_bDisableFadeEffect) - { - // begin the hide transition effect - GetAnimationController()->RunAnimationCommand(this, "alpha", 0.0f, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR); - m_bFadingOut = true; - // move us to the back of the draw order (so that fading out over the top of other dialogs doesn't look wierd) - surface()->MovePopupToBack(GetVPanel()); - } - else - { - // hide us immediately - FinishClose(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Close button in frame pressed -//----------------------------------------------------------------------------- -void Frame::OnCloseFrameButtonPressed() -{ - OnCommand("Close"); -} - -//----------------------------------------------------------------------------- -// Purpose: Command handling -//----------------------------------------------------------------------------- -void Frame::OnCommand(const char *command) -{ - if (!stricmp(command, "Close")) - { - Close(); - } - else if (!stricmp(command, "CloseModal")) - { - CloseModal(); - } - else if (!stricmp(command, "Minimize")) - { - OnMinimize(); - } - else if (!stricmp(command, "MinimizeToSysTray")) - { - OnMinimizeToSysTray(); - } - else - { - BaseClass::OnCommand(command); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Get the system menu -//----------------------------------------------------------------------------- -Menu *Frame::GetSysMenu() -{ -#if !defined( _X360 ) - if (!_sysMenu) - { - _sysMenu = new Menu(this, NULL); - _sysMenu->SetVisible(false); - _sysMenu->AddActionSignalTarget(this); - - _sysMenu->AddMenuItem("Minimize", "#SysMenu_Minimize", "Minimize", this); - _sysMenu->AddMenuItem("Maximize", "#SysMenu_Maximize", "Maximize", this); - _sysMenu->AddMenuItem("Close", "#SysMenu_Close", "Close", this); - - // check for enabling/disabling menu items - // this might have to be done at other times as well. - Panel *menuItem = _sysMenu->FindChildByName("Minimize"); - if (menuItem) - { - menuItem->SetEnabled(_minimizeButton->IsVisible()); - } - menuItem = _sysMenu->FindChildByName("Maximize"); - if (menuItem) - { - menuItem->SetEnabled(_maximizeButton->IsVisible()); - } - menuItem = _sysMenu->FindChildByName("Close"); - if (menuItem) - { - menuItem->SetEnabled(_closeButton->IsVisible()); - } - } - - return _sysMenu; -#else - return NULL; -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: Set the system menu -//----------------------------------------------------------------------------- -void Frame::SetSysMenu(Menu *menu) -{ -#if !defined( _X360 ) - if (menu == _sysMenu) - return; - - _sysMenu->MarkForDeletion(); - _sysMenu = menu; - - _menuButton->SetMenu(_sysMenu); -#endif -} - - -//----------------------------------------------------------------------------- -// Set the system menu images -//----------------------------------------------------------------------------- -void Frame::SetImages( const char *pEnabledImage, const char *pDisabledImage ) -{ -#if !defined( _X360 ) - _menuButton->SetImages( pEnabledImage, pDisabledImage ); -#endif -} - - -//----------------------------------------------------------------------------- -// Purpose: Close the window -//----------------------------------------------------------------------------- -void Frame::Close() -{ - OnClose(); -} - -//----------------------------------------------------------------------------- -// Purpose: Finishes closing the dialog -//----------------------------------------------------------------------------- -void Frame::FinishClose() -{ - SetVisible(false); - m_bPreviouslyVisible = false; - m_bFadingOut = false; - - OnFinishedClose(); - - if (m_bDeleteSelfOnClose) - { - // Must be last because if vgui is not running then this will call delete this!!! - MarkForDeletion(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Frame::OnFinishedClose() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Minimize the window on the taskbar. -//----------------------------------------------------------------------------- -void Frame::OnMinimize() -{ - surface()->SetMinimized(GetVPanel(), true); -} - -//----------------------------------------------------------------------------- -// Purpose: Does nothing by default -//----------------------------------------------------------------------------- -void Frame::OnMinimizeToSysTray() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Respond to mouse presses -//----------------------------------------------------------------------------- -void Frame::OnMousePressed(MouseCode code) -{ - if (!IsBuildGroupEnabled()) - { - // if a child doesn't have focus, get it for ourselves - VPANEL focus = input()->GetFocus(); - if (!focus || !ipanel()->HasParent(focus, GetVPanel())) - { - RequestFocus(); - } - } - - BaseClass::OnMousePressed(code); -} - -//----------------------------------------------------------------------------- -// Purpose: Toggle visibility of the system menu button -//----------------------------------------------------------------------------- -void Frame::SetMenuButtonVisible(bool state) -{ -#if !defined( _X360 ) - _menuButton->SetVisible(state); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: Toggle respond of the system menu button -// it will look enabled or disabled in response to the title bar -// but may not activate. -//----------------------------------------------------------------------------- -void Frame::SetMenuButtonResponsive(bool state) -{ -#if !defined( _X360 ) - _menuButton->SetResponsive(state); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: Toggle visibility of the minimize button -//----------------------------------------------------------------------------- -void Frame::SetMinimizeButtonVisible(bool state) -{ -#if !defined( _X360 ) - _minimizeButton->SetVisible(state); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: Toggle visibility of the maximize button -//----------------------------------------------------------------------------- -void Frame::SetMaximizeButtonVisible(bool state) -{ -#if !defined( _X360 ) - _maximizeButton->SetVisible(state); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: Toggles visibility of the minimize-to-systray icon (defaults to false) -//----------------------------------------------------------------------------- -void Frame::SetMinimizeToSysTrayButtonVisible(bool state) -{ -#if !defined( _X360 ) - _minimizeToSysTrayButton->SetVisible(state); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: Toggle visibility of the close button -//----------------------------------------------------------------------------- -void Frame::SetCloseButtonVisible(bool state) -{ -#if !defined( _X360 ) - _closeButton->SetVisible(state); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: soaks up any remaining messages -//----------------------------------------------------------------------------- -void Frame::OnKeyCodeReleased(KeyCode code) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: soaks up any remaining messages -//----------------------------------------------------------------------------- -void Frame::OnKeyFocusTicked() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Toggles window flash state on a timer -//----------------------------------------------------------------------------- -void Frame::InternalFlashWindow() -{ - if (_flashWindow) - { - // toggle icon flashing - _nextFlashState = true; - surface()->FlashWindow(GetVPanel(), _nextFlashState); - _nextFlashState = !_nextFlashState; - - PostMessage(this, new KeyValues("FlashWindow"), 1.8f); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Adds the child to the focus nav group -//----------------------------------------------------------------------------- -void Frame::OnChildAdded(VPANEL child) -{ - BaseClass::OnChildAdded(child); -} - -//----------------------------------------------------------------------------- -// Purpose: Flash the window system tray button until the frame gets focus -//----------------------------------------------------------------------------- -void Frame::FlashWindow() -{ - _flashWindow = true; - _nextFlashState = true; - - InternalFlashWindow(); -} - -//----------------------------------------------------------------------------- -// Purpose: Stops any window flashing -//----------------------------------------------------------------------------- -void Frame::FlashWindowStop() -{ - surface()->FlashWindow(GetVPanel(), false); - _flashWindow = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: load the control settings - should be done after all the children are added to the dialog -//----------------------------------------------------------------------------- -void Frame::LoadControlSettings( const char *dialogResourceName, const char *pathID, KeyValues *pPreloadedKeyValues, KeyValues *pConditions ) -{ - BaseClass::LoadControlSettings( dialogResourceName, pathID, pPreloadedKeyValues, pConditions ); - - // set the focus on the default control - Panel *defaultFocus = GetFocusNavGroup().GetDefaultPanel(); - if (defaultFocus) - { - defaultFocus->RequestFocus(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Checks for ctrl+shift+b hits to enter build mode -// Activates any hotkeys / default buttons -// Swallows any unhandled input -//----------------------------------------------------------------------------- -void Frame::OnKeyCodeTyped(KeyCode code) -{ - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - - if ( IsX360() ) - { - vgui::Panel *pMap = FindChildByName( "ControllerMap" ); - if ( pMap && pMap->IsKeyBoardInputEnabled() ) - { - pMap->OnKeyCodeTyped( code ); - return; - } - } - - if ( ctrl && shift && alt && code == KEY_B) - { - // enable build mode - ActivateBuildMode(); - } - else if (ctrl && shift && alt && code == KEY_R) - { - // reload the scheme - VPANEL top = surface()->GetEmbeddedPanel(); - if (top) - { - // reload the data file - scheme()->ReloadSchemes(); - - Panel *panel = ipanel()->GetPanel(top, GetModuleName()); - if (panel) - { - // make the top-level panel reload it's scheme, it will chain down to all the child panels - panel->InvalidateLayout(false, true); - } - } - } - else if (alt && code == KEY_F4) - { - // user has hit the close - PostMessage(this, new KeyValues("CloseFrameButtonPressed")); - } - else if (code == KEY_ENTER) - { - // check for a default button - VPANEL panel = GetFocusNavGroup().GetCurrentDefaultButton(); - if (panel && ipanel()->IsVisible( panel ) && ipanel()->IsEnabled( panel )) - { - // Activate the button - PostMessage(panel, new KeyValues("Hotkey")); - } - } - else if ( code == KEY_ESCAPE && - surface()->SupportsFeature(ISurface::ESCAPE_KEY) && - input()->GetAppModalSurface() == GetVPanel() ) - { - // ESC cancels, unless we're in the engine - in the engine ESC flips between the UI and the game - CloseModal(); - } - // Usually don't chain back as Frames are the end of the line for key presses, unless - // m_bChainKeysToParent is set - else if ( m_bChainKeysToParent ) - { - BaseClass::OnKeyCodeTyped( code ); - } - else - { - input()->OnKeyCodeUnhandled( (int)code ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: If true, then OnKeyCodeTyped messages continue up past the Frame -// Input : state - -//----------------------------------------------------------------------------- -void Frame::SetChainKeysToParent( bool state ) -{ - m_bChainKeysToParent = state; -} - -//----------------------------------------------------------------------------- -// Purpose: If true, then OnKeyCodeTyped messages continue up past the Frame -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Frame::CanChainKeysToParent() const -{ - return m_bChainKeysToParent; -} - -//----------------------------------------------------------------------------- -// Purpose: Checks for ctrl+shift+b hits to enter build mode -// Activates any hotkeys / default buttons -// Swallows any unhandled input -//----------------------------------------------------------------------------- -void Frame::OnKeyTyped(wchar_t unichar) -{ - Panel *panel = GetFocusNavGroup().FindPanelByHotkey(unichar); - if (panel) - { - // tell the panel to Activate - PostMessage(panel, new KeyValues("Hotkey")); - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets all title bar controls -//----------------------------------------------------------------------------- -void Frame::SetTitleBarVisible( bool state ) -{ - _drawTitleBar = state; - SetMenuButtonVisible(state); - SetMinimizeButtonVisible(state); - SetMaximizeButtonVisible(state); - SetCloseButtonVisible(state); -} - -//----------------------------------------------------------------------------- -// Purpose: sets the frame to delete itself on close -//----------------------------------------------------------------------------- -void Frame::SetDeleteSelfOnClose( bool state ) -{ - m_bDeleteSelfOnClose = state; -} - -//----------------------------------------------------------------------------- -// Purpose: updates localized text -//----------------------------------------------------------------------------- -void Frame::OnDialogVariablesChanged( KeyValues *dialogVariables ) -{ - StringIndex_t index = _title->GetUnlocalizedTextSymbol(); - if (index != INVALID_LOCALIZE_STRING_INDEX) - { - // reconstruct the string from the variables - wchar_t buf[1024]; - g_pVGuiLocalize->ConstructString(buf, sizeof(buf), index, dialogVariables); - SetTitle(buf, true); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handles staying on screen when the screen size changes -//----------------------------------------------------------------------------- -void Frame::OnScreenSizeChanged(int iOldWide, int iOldTall) -{ - BaseClass::OnScreenSizeChanged(iOldWide, iOldTall); - - if (IsProportional()) - return; - - // make sure we're completely on screen - int iNewWide, iNewTall; - surface()->GetScreenSize(iNewWide, iNewTall); - - int x, y, wide, tall; - GetBounds(x, y, wide, tall); - - // make sure the bottom-right corner is on the screen first - if (x + wide > iNewWide) - { - x = iNewWide - wide; - } - if (y + tall > iNewTall) - { - y = iNewTall - tall; - } - - // make sure the top-left is visible - x = max( 0, x ); - y = max( 0, y ); - - // apply - SetPos(x, y); -} - -//----------------------------------------------------------------------------- -// Purpose: For supporting thin caption bars -// Input : state - -//----------------------------------------------------------------------------- -void Frame::SetSmallCaption( bool state ) -{ - m_bSmallCaption = state; - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Frame::IsSmallCaption() const -{ - return m_bSmallCaption; -} - - -//----------------------------------------------------------------------------- -// Purpose: Static method to place a frame under the cursor -//----------------------------------------------------------------------------- -void Frame::PlaceUnderCursor( ) -{ - // get cursor position, this is local to this text edit window - int cursorX, cursorY; - input()->GetCursorPos( cursorX, cursorY ); - - // relayout the menu immediately so that we know it's size - InvalidateLayout(true); - int w, h; - GetSize( w, h ); - - // work out where the cursor is and therefore the best place to put the frame - int sw, sh; - surface()->GetScreenSize( sw, sh ); - - // Try to center it first - int x, y; - x = cursorX - ( w / 2 ); - y = cursorY - ( h / 2 ); - - // Clamp to various sides - if ( x + w > sw ) - { - x = sw - w; - } - if ( y + h > sh ) - { - y = sh - h; - } - if ( x < 0 ) - { - x = 0; - } - if ( y < 0 ) - { - y = 0; - } - - SetPos( x, y ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include +#include // for ceil() +#define PROTECTED_THINGS_DISABLE + +#include "tier1/utlstring.h" +#include "vgui/Cursor.h" +#include "vgui/MouseCode.h" +#include "vgui/IBorder.h" +#include "vgui/IInput.h" +#include "vgui/ILocalize.h" +#include "vgui/IPanel.h" +#include "vgui/ISurface.h" +#include "vgui/IScheme.h" +#include "vgui/KeyCode.h" + +#include "vgui_controls/AnimationController.h" +#include "vgui_controls/Controls.h" +#include "vgui_controls/Frame.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/Menu.h" +#include "vgui_controls/MenuButton.h" +#include "vgui_controls/TextImage.h" + +#include "KeyValues.h" + +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +static const int DEFAULT_SNAP_RANGE = 10; // number of pixels distance before the frame will snap to an edge +static const int CAPTION_TITLE_BORDER = 7; +static const int CAPTION_TITLE_BORDER_SMALL = 0; + +namespace +{ + //----------------------------------------------------------------------------- + // Purpose: Invisible panel to handle dragging/resizing frames + //----------------------------------------------------------------------------- + class GripPanel : public Panel + { + public: + GripPanel(Frame *dragFrame, const char *name, int xdir, int ydir) : Panel(dragFrame, name) + { + _frame = dragFrame; + _dragging = false; + _dragMultX = xdir; + _dragMultY = ydir; + SetPaintEnabled(false); + SetPaintBackgroundEnabled(false); + SetPaintBorderEnabled(false); + m_iSnapRange = DEFAULT_SNAP_RANGE; + + if (xdir == 1 && ydir == 1) + { + // bottom-right grip gets an image + SetPaintEnabled(true); + SetPaintBackgroundEnabled(true); + } + + SetBlockDragChaining( true ); + } + + // Purpose- handle window resizing + // Input- dx, dy, the offet of the mouse pointer from where we started dragging + virtual void moved(int dx, int dy) + { + if (!_frame->IsSizeable()) + return; + + // Start off with x, y at the coords of where we started to drag + int newX = _dragOrgPos[0], newY =_dragOrgPos[1]; + // Start off with width and tall equal from window when we started to drag + int newWide = _dragOrgSize[0], newTall = _dragOrgSize[1]; + + // get window's minimum size + int minWide, minTall; + _frame->GetMinimumSize( minWide, minTall); + + // Handle width resizing + newWide += (dx * _dragMultX); + // Handle the position of the corner x position + if (_dragMultX == -1) + { + // only move if we are not at the minimum + // if we are at min we have to force the proper offset (dx) + if (newWide < minWide) + { + dx=_dragOrgSize[0]-minWide; + } + newX += dx; // move window to its new position + } + + // Handle height resizing + newTall += (dy * _dragMultY); + // Handle position of corner y position + if (_dragMultY == -1) + { + if (newTall < minTall) + { + dy=_dragOrgSize[1]-minTall; + } + newY += dy; + } + + if ( _frame->GetClipToParent() ) + { + // If any coordinate is out of range, snap it back + if ( newX < 0 ) + newX = 0; + if ( newY < 0 ) + newY = 0; + + int sx, sy; + surface()->GetScreenSize( sx, sy ); + + int w, h; + _frame->GetSize( w, h ); + if ( newX + w > sx ) + { + newX = sx - w; + } + if ( newY + h > sy ) + { + newY = sy - h; + } + } + + // set new position + _frame->SetPos(newX, newY); + // set the new size + // if window is below min size it will automatically pop to min size + _frame->SetSize(newWide, newTall); + _frame->InvalidateLayout(); + _frame->Repaint(); + } + + void OnCursorMoved(int x, int y) + { + if (!_dragging) + return; + + if (!input()->IsMouseDown(MOUSE_LEFT)) + { + // for some reason we're marked as dragging when the mouse is released + // trigger a release + OnMouseReleased(MOUSE_LEFT); + return; + } + + input()->GetCursorPos(x, y); + moved((x - _dragStart[0]), ( y - _dragStart[1])); + _frame->Repaint(); + } + + void OnMousePressed(MouseCode code) + { + if (code == MOUSE_LEFT) + { + _dragging=true; + int x,y; + input()->GetCursorPos(x,y); + _dragStart[0]=x; + _dragStart[1]=y; + _frame->GetPos(_dragOrgPos[0],_dragOrgPos[1]); + _frame->GetSize(_dragOrgSize[0],_dragOrgSize[1]); + input()->SetMouseCapture(GetVPanel()); + + // if a child doesn't have focus, get it for ourselves + VPANEL focus = input()->GetFocus(); + if (!focus || !ipanel()->HasParent(focus, _frame->GetVPanel())) + { + _frame->RequestFocus(); + } + _frame->Repaint(); + } + else + { + GetParent()->OnMousePressed(code); + } + } + + void OnMouseDoublePressed(MouseCode code) + { + GetParent()->OnMouseDoublePressed(code); + } + + void Paint() + { + // draw the grab handle in the bottom right of the frame + surface()->DrawSetTextFont(_marlettFont); + surface()->DrawSetTextPos(0, 0); + + // thin highlight lines + surface()->DrawSetTextColor(GetFgColor()); + surface()->DrawUnicodeChar('p'); + } + + void PaintBackground() + { + // draw the grab handle in the bottom right of the frame + surface()->DrawSetTextFont(_marlettFont); + surface()->DrawSetTextPos(0, 0); + + // thick shadow lines + surface()->DrawSetTextColor(GetBgColor()); + surface()->DrawUnicodeChar('o'); + } + + void OnMouseReleased(MouseCode code) + { + _dragging = false; + input()->SetMouseCapture(NULL); + } + + void OnMouseCaptureLost() + { + Panel::OnMouseCaptureLost(); + _dragging = false; + } + + void ApplySchemeSettings(IScheme *pScheme) + { + Panel::ApplySchemeSettings(pScheme); + bool isSmall = ((Frame *)GetParent())->IsSmallCaption(); + + _marlettFont = pScheme->GetFont( isSmall ? "MarlettSmall" : "Marlett", IsProportional()); + SetFgColor(GetSchemeColor("FrameGrip.Color1", pScheme)); + SetBgColor(GetSchemeColor("FrameGrip.Color2", pScheme)); + + const char *snapRange = pScheme->GetResourceString("Frame.AutoSnapRange"); + if (snapRange && *snapRange) + { + m_iSnapRange = atoi(snapRange); + } + } + + protected: + Frame *_frame; + int _dragMultX; + int _dragMultY; + bool _dragging; + int _dragOrgPos[2]; + int _dragOrgSize[2]; + int _dragStart[2]; + int m_iSnapRange; + HFont _marlettFont; + }; + + //----------------------------------------------------------------------------- + // Purpose: Handles caption grip input for moving dialogs around + //----------------------------------------------------------------------------- + class CaptionGripPanel : public GripPanel + { + public: + CaptionGripPanel(Frame* frame, const char *name) : GripPanel(frame, name, 0, 0) + { + } + + void moved(int dx, int dy) + { + if (!_frame->IsMoveable()) + return; + + int newX = _dragOrgPos[0] + dx; + int newY = _dragOrgPos[1] + dy; + + if (m_iSnapRange) + { + // first check docking to desktop + int wx, wy, ww, wt; + surface()->GetWorkspaceBounds(wx, wy, ww, wt); + getInsideSnapPosition(wx, wy, ww, wt, newX, newY); + + // now lets check all windows and see if we snap to those + // root panel + VPANEL root = surface()->GetEmbeddedPanel(); + // cycle through panels + // look for panels that are visible and are popups that we can dock to + for (int i = 0; i < ipanel()->GetChildCount(root); ++i) + { + VPANEL child = ipanel()->GetChild(root, i); + tryToDock (child, newX, newY); + } + } + + if ( _frame->GetClipToParent() ) + { + // If any coordinate is out of range, snap it back + if ( newX < 0 ) + newX = 0; + if ( newY < 0 ) + newY = 0; + + int sx, sy; + surface()->GetScreenSize( sx, sy ); + + int w, h; + _frame->GetSize( w, h ); + if ( newX + w > sx ) + { + newX = sx - w; + } + if ( newY + h > sy ) + { + newY = sy - h; + } + } + + _frame->SetPos(newX, newY); + + } + + void tryToDock(VPANEL window, int &newX, int & newY) + { + // bail if child is this window + if ( window == _frame->GetVPanel()) + return; + + int cx, cy, cw, ct; + if ( (ipanel()->IsVisible(window)) && (ipanel()->IsPopup(window)) ) + { + // position + ipanel()->GetAbsPos(window, cx, cy); + // dimensions + ipanel()->GetSize(window, cw, ct); + bool snapped = getOutsideSnapPosition (cx, cy, cw, ct, newX, newY); + if (snapped) + { + // if we snapped, we're done with this path + // dont try to snap to kids + return; + } + } + + // check all children + for (int i = 0; i < ipanel()->GetChildCount(window); ++i) + { + VPANEL child = ipanel()->GetChild(window, i); + tryToDock(child, newX, newY); + } + + } + + // Purpose: To calculate the windows new x,y position if it snaps + // Will snap to the INSIDE of a window (eg desktop sides + // Input: boundX boundY, position of candidate window we are seeing if we snap to + // boundWide, boundTall, width and height of window we are seeing if we snap to + // Output: snapToX, snapToY new coords for window, unchanged if we dont snap + // Returns true if we snapped, false if we did not snap. + bool getInsideSnapPosition(int boundX, int boundY, int boundWide, int boundTall, + int &snapToX, int &snapToY) + { + + int wide, tall; + _frame->GetSize(wide, tall); + Assert (wide > 0); + Assert (tall > 0); + + bool snapped=false; + if (abs(snapToX - boundX) < m_iSnapRange) + { + snapToX = boundX; + snapped=true; + } + else if (abs((snapToX + wide) - (boundX + boundWide)) < m_iSnapRange) + { + snapToX = boundX + boundWide - wide; + snapped=true; + } + + if (abs(snapToY - boundY) < m_iSnapRange) + { + snapToY = boundY; + snapped=true; + } + else if (abs((snapToY + tall) - (boundY + boundTall)) < m_iSnapRange) + { + snapToY = boundY + boundTall - tall; + snapped=true; + } + return snapped; + + } + + // Purpose: To calculate the windows new x,y position if it snaps + // Will snap to the OUTSIDE edges of a window (i.e. will stick peers together + // Input: left, top, position of candidate window we are seeing if we snap to + // boundWide, boundTall, width and height of window we are seeing if we snap to + // Output: snapToX, snapToY new coords for window, unchanged if we dont snap + // Returns true if we snapped, false if we did not snap. + bool getOutsideSnapPosition(int left, int top, int boundWide, int boundTall, + int &snapToX, int &snapToY) + { + Assert (boundWide >= 0); + Assert (boundTall >= 0); + + bool snapped=false; + + int right=left+boundWide; + int bottom=top+boundTall; + + int wide, tall; + _frame->GetSize(wide, tall); + Assert (wide > 0); + Assert (tall > 0); + + // we now see if we are going to be able to snap to a window side, and not + // just snap to the "open air" + // want to make it so that if any part of the window can dock to the candidate, it will + + // is this window horizontally snappable to the candidate + bool horizSnappable=( + // top of window is in range + ((snapToY > top) && (snapToY < bottom)) + // bottom of window is in range + || ((snapToY+tall > top) && (snapToY+tall < bottom)) + // window is just plain bigger than the window we wanna dock to + || ((snapToY < top) && (snapToY+tall > bottom)) ); + + + // is this window vertically snappable to the candidate + bool vertSnappable= ( + // left of window is in range + ((snapToX > left) && (snapToX < right)) + // right of window is in range + || ((snapToX+wide > left) && (snapToX+wide < right)) + // window is just plain bigger than the window we wanna dock to + || ((snapToX < left) && (snapToX+wide > right)) ); + + // if neither, might as well bail + if ( !(horizSnappable || vertSnappable) ) + return false; + + //if we're within the snap threshold then snap + if ( (snapToX <= (right+m_iSnapRange)) && + (snapToX >= (right-m_iSnapRange)) ) + { + if (horizSnappable) + { + //disallow "open air" snaps + snapped=true; + snapToX = right; + } + } + else if ((snapToX + wide) >= (left-m_iSnapRange) && + (snapToX + wide) <= (left+m_iSnapRange)) + { + if (horizSnappable) + { + snapped=true; + snapToX = left-wide; + } + } + + if ( (snapToY <= (bottom+m_iSnapRange)) && + (snapToY >= (bottom-m_iSnapRange)) ) + { + if (vertSnappable) + { + snapped=true; + snapToY = bottom; + } + } + else if ((snapToY + tall) <= (top+m_iSnapRange) && + (snapToY + tall) >= (top-m_iSnapRange)) + { + if (vertSnappable) + { + snapped=true; + snapToY = top-tall; + } + } + return snapped; + } + }; + +} + +namespace vgui +{ + //----------------------------------------------------------------------------- + // Purpose: overrides normal button drawing to use different colors & borders + //----------------------------------------------------------------------------- + class FrameButton : public Button + { + private: + IBorder *_brightBorder, *_depressedBorder, *_disabledBorder; + Color _enabledFgColor, _enabledBgColor; + Color _disabledFgColor, _disabledBgColor; + bool _disabledLook; + + public: + + static int GetButtonSide( Frame *pFrame ) + { + if ( pFrame->IsSmallCaption() ) + { + return 12; + } + + return 18; + } + + + FrameButton(Panel *parent, const char *name, const char *text) : Button(parent, name, text) + { + SetSize( FrameButton::GetButtonSide( (Frame *)parent ), FrameButton::GetButtonSide( (Frame *)parent ) ); + _brightBorder = NULL; + _depressedBorder = NULL; + _disabledBorder = NULL; + _disabledLook = true; + SetContentAlignment(Label::a_northwest); + SetTextInset(2, 1); + SetBlockDragChaining( true ); + } + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + Button::ApplySchemeSettings(pScheme); + + _enabledFgColor = GetSchemeColor("FrameTitleButton.FgColor", pScheme); + _enabledBgColor = GetSchemeColor("FrameTitleButton.BgColor", pScheme); + + _disabledFgColor = GetSchemeColor("FrameTitleButton.DisabledFgColor", pScheme); + _disabledBgColor = GetSchemeColor("FrameTitleButton.DisabledBgColor", pScheme); + + _brightBorder = pScheme->GetBorder("TitleButtonBorder"); + _depressedBorder = pScheme->GetBorder("TitleButtonDepressedBorder"); + _disabledBorder = pScheme->GetBorder("TitleButtonDisabledBorder"); + + SetDisabledLook(_disabledLook); + } + + virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) + { + if (_disabledLook) + { + return _disabledBorder; + } + + if (depressed) + { + return _depressedBorder; + } + + return _brightBorder; + } + + virtual void SetDisabledLook(bool state) + { + _disabledLook = state; + if (!_disabledLook) + { + SetDefaultColor(_enabledFgColor, _enabledBgColor); + SetArmedColor(_enabledFgColor, _enabledBgColor); + SetDepressedColor(_enabledFgColor, _enabledBgColor); + } + else + { + // setup disabled colors + SetDefaultColor(_disabledFgColor, _disabledBgColor); + SetArmedColor(_disabledFgColor, _disabledBgColor); + SetDepressedColor(_disabledFgColor, _disabledBgColor); + } + } + + virtual void PerformLayout() + { + Button::PerformLayout(); + Repaint(); + } + + // Don't request focus. + // This will keep items in the listpanel selected. + virtual void OnMousePressed(MouseCode code) + { + if (!IsEnabled()) + return; + + if (!IsMouseClickEnabled(code)) + return; + + if (IsUseCaptureMouseEnabled()) + { + { + SetSelected(true); + Repaint(); + } + + // lock mouse input to going to this button + input()->SetMouseCapture(GetVPanel()); + } + } +}; + + +//----------------------------------------------------------------------------- +// Purpose: icon button +//----------------------------------------------------------------------------- +class FrameSystemButton : public MenuButton +{ + DECLARE_CLASS_SIMPLE( FrameSystemButton, MenuButton ); + +private: + IImage *_enabled, *_disabled; + Color _enCol, _disCol; + bool _respond; + CUtlString m_EnabledImage; + CUtlString m_DisabledImage; + +public: + FrameSystemButton(Panel *parent, const char *panelName) : MenuButton(parent, panelName, "") + { + _disabled = _enabled = NULL; + _respond = true; + SetEnabled(false); + // This menu will open if we use the left or right mouse button + SetMouseClickEnabled( MOUSE_RIGHT, true ); + SetBlockDragChaining( true ); + } + + void SetImages( const char *pEnabledImage, const char *pDisabledImage = NULL ) + { + m_EnabledImage = pEnabledImage; + m_DisabledImage = pDisabledImage ? pDisabledImage : pEnabledImage; + } + + void GetImageSize( int &w, int &h ) + { + w = h = 0; + + int tw = 0, th = 0; + if ( _enabled ) + { + _enabled->GetSize( w, h ); + } + if ( _disabled ) + { + _disabled->GetSize( tw, th ); + } + if ( tw > w ) + { + w = tw; + } + if ( th > h ) + { + h = th; + } + } + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + BaseClass::ApplySchemeSettings(pScheme); + + _enCol = GetSchemeColor("FrameSystemButton.FgColor", pScheme); + _disCol = GetSchemeColor("FrameSystemButton.BgColor", pScheme); + + const char *pEnabledImage = m_EnabledImage.Length() ? m_EnabledImage.Get() : + pScheme->GetResourceString( "FrameSystemButton.Icon" ); + const char *pDisabledImage = m_DisabledImage.Length() ? m_DisabledImage.Get() : + pScheme->GetResourceString( "FrameSystemButton.DisabledIcon" ); + _enabled = scheme()->GetImage( pEnabledImage, false); + _disabled = scheme()->GetImage( pDisabledImage, false); + + SetTextInset(0, 0); + + // get our iconic image + SetEnabled(IsEnabled()); + } + + virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) + { + return NULL; + } + + virtual void SetEnabled(bool state) + { + Button::SetEnabled(state); + + if (IsEnabled()) + { + if ( _enabled ) + { + SetImageAtIndex(0, _enabled, 0); + } + SetBgColor(_enCol); + SetDefaultColor(_enCol, _enCol); + SetArmedColor(_enCol, _enCol); + SetDepressedColor(_enCol, _enCol); + } + else + { + if ( _disabled ) + { + SetImageAtIndex(0, _disabled, 0); + } + SetBgColor(_disCol); + SetDefaultColor(_disCol, _disCol); + SetArmedColor(_disCol, _disCol); + SetDepressedColor(_disCol, _disCol); + } + } + + void SetResponsive(bool state) + { + _respond = state; + } + + virtual void OnMousePressed(MouseCode code) + { + // button may look enabled but not be responsive + if (!_respond) + return; + + BaseClass::OnMousePressed(code); + } + + virtual void OnMouseDoublePressed(MouseCode code) + { + // button may look enabled but not be responsive + if (!_respond) + return; + + // only close if left is double pressed + if (code == MOUSE_LEFT) + { + // double click on the icon closes the window + // But only if the menu contains a 'close' item + vgui::Menu *pMenu = GetMenu(); + if ( pMenu && pMenu->FindChildByName("Close") ) + { + PostMessage(GetVParent(), new KeyValues("CloseFrameButtonPressed")); + } + } + } + +}; + +} // namespace vgui +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Frame::Frame(Panel *parent, const char *panelName, bool showTaskbarIcon /*=true*/, bool bPopup /*=true*/ ) : EditablePanel(parent, panelName) +{ + // frames start invisible, to avoid having window flicker in on taskbar + SetVisible(false); + if ( bPopup ) + { + MakePopup(showTaskbarIcon); + } + + m_hPreviousModal = 0; + + _title=null; + _moveable=true; + _sizeable=true; + m_bHasFocus=false; + _flashWindow=false; + _drawTitleBar = true; + m_bPreviouslyVisible = false; + m_bFadingOut = false; + m_bDisableFadeEffect = false; + m_flTransitionEffectTime = 0.0f; + m_flFocusTransitionEffectTime = 0.0f; + m_bDeleteSelfOnClose = false; + m_iClientInsetX = 5; + m_iClientInsetY = 5; + m_iClientInsetXOverridden = false; + m_iTitleTextInsetX = 28; + m_bClipToParent = false; + m_bSmallCaption = false; + m_bChainKeysToParent = false; + m_bPrimed = false; + m_hCustomTitleFont = INVALID_FONT; + + SetTitle("#Frame_Untitled", parent ? false : true); + + // add ourselves to the build group + SetBuildGroup(GetBuildGroup()); + + SetMinimumSize(128,66); + + GetFocusNavGroup().SetFocusTopLevel(true); + +#if !defined( _X360 ) + _sysMenu = NULL; + + // add dragging grips + _topGrip = new GripPanel(this, "frame_topGrip", 0, -1); + _bottomGrip = new GripPanel(this, "frame_bottomGrip", 0, 1); + _leftGrip = new GripPanel(this, "frame_leftGrip", -1, 0); + _rightGrip = new GripPanel(this, "frame_rightGrip", 1, 0); + _topLeftGrip = new GripPanel(this, "frame_tlGrip", -1, -1); + _topRightGrip = new GripPanel(this, "frame_trGrip", 1, -1); + _bottomLeftGrip = new GripPanel(this, "frame_blGrip", -1, 1); + _bottomRightGrip = new GripPanel(this, "frame_brGrip", 1, 1); + _captionGrip = new CaptionGripPanel(this, "frame_caption" ); + _captionGrip->SetCursor(dc_arrow); + + _minimizeButton = new FrameButton(this, "frame_minimize","0"); + _minimizeButton->AddActionSignalTarget(this); + _minimizeButton->SetCommand(new KeyValues("Minimize")); + + _maximizeButton = new FrameButton(this, "frame_maximize", "1"); + //!! no maximize handler implemented yet, so leave maximize button disabled + SetMaximizeButtonVisible(false); + + char str[] = { 0x6F, 0 }; + _minimizeToSysTrayButton = new FrameButton(this, "frame_mintosystray", str); + _minimizeToSysTrayButton->SetCommand("MinimizeToSysTray"); + SetMinimizeToSysTrayButtonVisible(false); + + _closeButton = new FrameButton(this, "frame_close", "r"); + _closeButton->AddActionSignalTarget(this); + _closeButton->SetCommand(new KeyValues("CloseFrameButtonPressed")); + + if (!surface()->SupportsFeature(ISurface::FRAME_MINIMIZE_MAXIMIZE)) + { + SetMinimizeButtonVisible(false); + SetMaximizeButtonVisible(false); + } + + if (parent) + { + // vgui doesn't support subwindow minimization + SetMinimizeButtonVisible(false); + SetMaximizeButtonVisible(false); + } + + _menuButton = new FrameSystemButton(this, "frame_menu"); + _menuButton->SetMenu(GetSysMenu()); +#endif + + SetupResizeCursors(); + + REGISTER_COLOR_AS_OVERRIDABLE( m_InFocusBgColor, "infocus_bgcolor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( m_OutOfFocusBgColor, "outoffocus_bgcolor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _titleBarBgColor, "titlebarbgcolor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _titleBarDisabledBgColor, "titlebardisabledbgcolor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _titleBarFgColor, "titlebarfgcolor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _titleBarDisabledFgColor, "titlebardisabledfgcolor_override" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +Frame::~Frame() +{ + if ( input()->GetAppModalSurface() == GetVPanel() ) + { + vgui::input()->ReleaseAppModalSurface(); + if ( m_hPreviousModal != 0 ) + { + vgui::input()->SetAppModalSurface( m_hPreviousModal ); + m_hPreviousModal = 0; + } + } + +#if !defined( _X360 ) + delete _topGrip; + delete _bottomGrip; + delete _leftGrip; + delete _rightGrip; + delete _topLeftGrip; + delete _topRightGrip; + delete _bottomLeftGrip; + delete _bottomRightGrip; + delete _captionGrip; + delete _minimizeButton; + delete _maximizeButton; + delete _closeButton; + delete _menuButton; + delete _minimizeToSysTrayButton; +#endif + delete _title; +} + +//----------------------------------------------------------------------------- +// Purpose: Setup the grips on the edges of the panel to resize it. +//----------------------------------------------------------------------------- +void Frame::SetupResizeCursors() +{ +#if !defined( _X360 ) + if (IsSizeable()) + { + _topGrip->SetCursor(dc_sizens); + _bottomGrip->SetCursor(dc_sizens); + _leftGrip->SetCursor(dc_sizewe); + _rightGrip->SetCursor(dc_sizewe); + _topLeftGrip->SetCursor(dc_sizenwse); + _topRightGrip->SetCursor(dc_sizenesw); + _bottomLeftGrip->SetCursor(dc_sizenesw); + _bottomRightGrip->SetCursor(dc_sizenwse); + + _bottomRightGrip->SetPaintEnabled(true); + _bottomRightGrip->SetPaintBackgroundEnabled(true); + } + else + { + // not resizable, so just use the default cursor + _topGrip->SetCursor(dc_arrow); + _bottomGrip->SetCursor(dc_arrow); + _leftGrip->SetCursor(dc_arrow); + _rightGrip->SetCursor(dc_arrow); + _topLeftGrip->SetCursor(dc_arrow); + _topRightGrip->SetCursor(dc_arrow); + _bottomLeftGrip->SetCursor(dc_arrow); + _bottomRightGrip->SetCursor(dc_arrow); + + _bottomRightGrip->SetPaintEnabled(false); + _bottomRightGrip->SetPaintBackgroundEnabled(false); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Bring the frame to the front and requests focus, ensures it's not minimized +//----------------------------------------------------------------------------- +void Frame::Activate() +{ + MoveToFront(); + if ( IsKeyBoardInputEnabled() ) + { + RequestFocus(); + } + SetVisible(true); + SetEnabled(true); + if (m_bFadingOut) + { + // we were fading out, make sure to fade back in + m_bFadingOut = false; + m_bPreviouslyVisible = false; + } + + surface()->SetMinimized(GetVPanel(), false); +} + + +//----------------------------------------------------------------------------- +// Sets up, cleans up modal dialogs +//----------------------------------------------------------------------------- +void Frame::DoModal( ) +{ + // move to the middle of the screen + MoveToCenterOfScreen(); + InvalidateLayout(); + Activate(); + m_hPreviousModal = vgui::input()->GetAppModalSurface(); + vgui::input()->SetAppModalSurface( GetVPanel() ); +} + + +//----------------------------------------------------------------------------- +// Closes a modal dialog +//----------------------------------------------------------------------------- +void Frame::CloseModal() +{ + vgui::input()->ReleaseAppModalSurface(); + if ( m_hPreviousModal != 0 ) + { + vgui::input()->SetAppModalSurface( m_hPreviousModal ); + m_hPreviousModal = 0; + } + PostMessage( this, new KeyValues("Close") ); +} + + +//----------------------------------------------------------------------------- +// Purpose: activates the dialog +// if dialog is not currently visible it starts it minimized and flashing in the taskbar +//----------------------------------------------------------------------------- +void Frame::ActivateMinimized() +{ + if ( ( IsVisible() && !IsMinimized() ) || !surface()->SupportsFeature( ISurface::FRAME_MINIMIZE_MAXIMIZE ) ) + { + Activate(); + } + else + { + ipanel()->MoveToBack(GetVPanel()); + surface()->SetMinimized(GetVPanel(), true); + SetVisible(true); + SetEnabled(true); + if (m_bFadingOut) + { + // we were fading out, make sure to fade back in + m_bFadingOut = false; + m_bPreviouslyVisible = false; + } + FlashWindow(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the dialog is currently minimized +//----------------------------------------------------------------------------- +bool Frame::IsMinimized() +{ + return surface()->IsMinimized(GetVPanel()); +} + +//----------------------------------------------------------------------------- +// Purpose: Center the dialog on the screen +//----------------------------------------------------------------------------- +void Frame::MoveToCenterOfScreen() +{ + int wx, wy, ww, wt; + surface()->GetWorkspaceBounds(wx, wy, ww, wt); + SetPos((ww - GetWide()) / 2, (wt - GetTall()) / 2); +} + + +void Frame::LayoutProportional( FrameButton *bt ) +{ + float scale = 1.0; + + if( IsProportional() ) + { + int screenW, screenH; + surface()->GetScreenSize( screenW, screenH ); + + int proW,proH; + surface()->GetProportionalBase( proW, proH ); + + scale = ( (float)( screenH ) / (float)( proH ) ); + } + + bt->SetSize( (int)( FrameButton::GetButtonSide( this ) * scale ), (int)( FrameButton::GetButtonSide( this ) * scale ) ); + bt->SetTextInset( (int)( ceil( 2 * scale ) ), (int) ( ceil(1 * scale ) ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: per-frame thinking, used for transition effects +// only gets called if the Frame is visible +//----------------------------------------------------------------------------- +void Frame::OnThink() +{ + BaseClass::OnThink(); + + // check for transition effects + if (IsVisible() && m_flTransitionEffectTime > 0 && ( !m_bDisableFadeEffect )) + { + if (m_bFadingOut) + { + // we're fading out, see if we're done so we can fully hide the window + if (GetAlpha() < ( IsX360() ? 64 : 1 )) + { + FinishClose(); + } + } + else if (!m_bPreviouslyVisible) + { + // need to fade-in + m_bPreviouslyVisible = true; + + // fade in + if (IsX360()) + { + SetAlpha(64); + } + else + { + SetAlpha(0); + } + GetAnimationController()->RunAnimationCommand(this, "alpha", 255.0f, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR); + } + } + + // check for focus changes + bool hasFocus = false; + + if (input()) + { + VPANEL focus = input()->GetFocus(); + if (focus && ipanel()->HasParent(focus, GetVPanel())) + { + if ( input()->GetAppModalSurface() == 0 || + input()->GetAppModalSurface() == GetVPanel() ) + { + hasFocus = true; + } + } + } + if (hasFocus != m_bHasFocus) + { + // Because vgui focus is message based, and focus gets reset to NULL when a focused panel is deleted, we defer the flashing/transition + // animation for an extra frame in case something is deleted, a message is sent, and then we become the focused panel again on the + // next frame + if ( !m_bPrimed ) + { + m_bPrimed = true; + return; + } + m_bPrimed = false; + m_bHasFocus = hasFocus; + OnFrameFocusChanged(m_bHasFocus); + } + else + { + m_bPrimed = false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the frame focus changes +//----------------------------------------------------------------------------- +void Frame::OnFrameFocusChanged(bool bHasFocus) +{ +#if !defined( _X360 ) + // enable/disable the frame buttons + _minimizeButton->SetDisabledLook(!bHasFocus); + _maximizeButton->SetDisabledLook(!bHasFocus); + _closeButton->SetDisabledLook(!bHasFocus); + _minimizeToSysTrayButton->SetDisabledLook(!bHasFocus); + _menuButton->SetEnabled(bHasFocus); + _minimizeButton->InvalidateLayout(); + _maximizeButton->InvalidateLayout(); + _minimizeToSysTrayButton->InvalidateLayout(); + _closeButton->InvalidateLayout(); + _menuButton->InvalidateLayout(); +#endif + + if (bHasFocus) + { + _title->SetColor(_titleBarFgColor); + } + else + { + _title->SetColor(_titleBarDisabledFgColor); + } + + // set our background color + if (bHasFocus) + { + if (m_flFocusTransitionEffectTime && ( !m_bDisableFadeEffect )) + { + GetAnimationController()->RunAnimationCommand(this, "BgColor", m_InFocusBgColor, 0.0f, m_bDisableFadeEffect ? 0.0f : m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR); + } + else + { + SetBgColor(m_InFocusBgColor); + } + } + else + { + if (m_flFocusTransitionEffectTime && ( !m_bDisableFadeEffect )) + { + GetAnimationController()->RunAnimationCommand(this, "BgColor", m_OutOfFocusBgColor, 0.0f, m_bDisableFadeEffect ? 0.0f : m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR); + } + else + { + SetBgColor(m_OutOfFocusBgColor); + } + } + + // Stop flashing when we get focus + if (bHasFocus && _flashWindow) + { + FlashWindowStop(); + } +} + +int Frame::GetDraggerSize() +{ + const int DRAGGER_SIZE = 5; + if ( m_bSmallCaption ) + { + return 3; + } + + return DRAGGER_SIZE; +} + +int Frame::GetCornerSize() +{ + const int CORNER_SIZE = 8; + if ( m_bSmallCaption ) + { + return 6; + } + + return CORNER_SIZE; +} + +int Frame::GetBottomRightSize() +{ + const int BOTTOMRIGHTSIZE = 18; + if ( m_bSmallCaption ) + { + return 12; + } + + return BOTTOMRIGHTSIZE; +} + +int Frame::GetCaptionHeight() +{ + const int CAPTIONHEIGHT = 23; + if ( m_bSmallCaption ) + { + return 12; + } + return CAPTIONHEIGHT; +} + +//----------------------------------------------------------------------------- +// Purpose: Recalculate the position of all items +//----------------------------------------------------------------------------- +void Frame::PerformLayout() +{ + // chain back + BaseClass::PerformLayout(); + + // move everything into place + int wide, tall; + GetSize(wide, tall); + +#if !defined( _X360 ) + int DRAGGER_SIZE = GetDraggerSize(); + int CORNER_SIZE = GetCornerSize(); + int CORNER_SIZE2 = CORNER_SIZE * 2; + int BOTTOMRIGHTSIZE = GetBottomRightSize(); + + _topGrip->SetBounds(CORNER_SIZE, 0, wide - CORNER_SIZE2, DRAGGER_SIZE); + _leftGrip->SetBounds(0, CORNER_SIZE, DRAGGER_SIZE, tall - CORNER_SIZE2); + _topLeftGrip->SetBounds(0, 0, CORNER_SIZE, CORNER_SIZE); + _topRightGrip->SetBounds(wide - CORNER_SIZE, 0, CORNER_SIZE, CORNER_SIZE); + _bottomLeftGrip->SetBounds(0, tall - CORNER_SIZE, CORNER_SIZE, CORNER_SIZE); + + // make the bottom-right grip larger + _bottomGrip->SetBounds(CORNER_SIZE, tall - DRAGGER_SIZE, wide - (CORNER_SIZE + BOTTOMRIGHTSIZE), DRAGGER_SIZE); + _rightGrip->SetBounds(wide - DRAGGER_SIZE, CORNER_SIZE, DRAGGER_SIZE, tall - (CORNER_SIZE + BOTTOMRIGHTSIZE)); + + _bottomRightGrip->SetBounds(wide - BOTTOMRIGHTSIZE, tall - BOTTOMRIGHTSIZE, BOTTOMRIGHTSIZE, BOTTOMRIGHTSIZE); + + _captionGrip->SetSize(wide-10,GetCaptionHeight()); + + _topGrip->MoveToFront(); + _bottomGrip->MoveToFront(); + _leftGrip->MoveToFront(); + _rightGrip->MoveToFront(); + _topLeftGrip->MoveToFront(); + _topRightGrip->MoveToFront(); + _bottomLeftGrip->MoveToFront(); + _bottomRightGrip->MoveToFront(); + + _maximizeButton->MoveToFront(); + _menuButton->MoveToFront(); + _minimizeButton->MoveToFront(); + _minimizeToSysTrayButton->MoveToFront(); + _menuButton->SetBounds(5+2, 5+3, GetCaptionHeight()-5, GetCaptionHeight()-5); +#endif + + float scale = 1; + if (IsProportional()) + { + int screenW, screenH; + surface()->GetScreenSize( screenW, screenH ); + + int proW,proH; + surface()->GetProportionalBase( proW, proH ); + + scale = ( (float)( screenH ) / (float)( proH ) ); + } + +#if !defined( _X360 ) + int offset_start = (int)( 20 * scale ); + int offset = offset_start; + + int top_border_offset = (int) ( ( 5+3 ) * scale ); + if ( m_bSmallCaption ) + { + top_border_offset = (int) ( ( 3 ) * scale ); + } + + int side_border_offset = (int) ( 5 * scale ); + // push the buttons against the east side + if (_closeButton->IsVisible()) + { + _closeButton->SetPos((wide-side_border_offset)-offset,top_border_offset); + offset += offset_start; + LayoutProportional( _closeButton ); + + } + if (_minimizeToSysTrayButton->IsVisible()) + { + _minimizeToSysTrayButton->SetPos((wide-side_border_offset)-offset,top_border_offset); + offset += offset_start; + LayoutProportional( _minimizeToSysTrayButton ); + } + if (_maximizeButton->IsVisible()) + { + _maximizeButton->SetPos((wide-side_border_offset)-offset,top_border_offset); + offset += offset_start; + LayoutProportional( _maximizeButton ); + } + if (_minimizeButton->IsVisible()) + { + _minimizeButton->SetPos((wide-side_border_offset)-offset,top_border_offset); + offset += offset_start; + LayoutProportional( _minimizeButton ); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Set the text in the title bar. +//----------------------------------------------------------------------------- +void Frame::SetTitle(const char *title, bool surfaceTitle) +{ + if (!_title) + { + _title = new TextImage( "" ); + } + + Assert(title); + _title->SetText(title); + + // see if the combobox text has changed, and if so, post a message detailing the new text + const char *newTitle = title; + + // check if the new text is a localized string, if so undo it + wchar_t unicodeText[128]; + unicodeText[0] = 0; + if (*newTitle == '#') + { + // try lookup in localization tables + StringIndex_t unlocalizedTextSymbol = g_pVGuiLocalize->FindIndex(newTitle + 1); + if (unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX) + { + // we have a new text value + wcsncpy( unicodeText, g_pVGuiLocalize->GetValueByIndex(unlocalizedTextSymbol), sizeof( unicodeText) / sizeof(wchar_t) ); + } + } + else + { + g_pVGuiLocalize->ConvertANSIToUnicode( newTitle, unicodeText, sizeof(unicodeText) ); + } + + if (surfaceTitle) + { + surface()->SetTitle(GetVPanel(), unicodeText); + } + + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the unicode text in the title bar +//----------------------------------------------------------------------------- +void Frame::SetTitle(const wchar_t *title, bool surfaceTitle) +{ + if (!_title) + { + _title = new TextImage( "" ); + } + _title->SetText(title); + if (surfaceTitle) + { + surface()->SetTitle(GetVPanel(), title); + } + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the text in the title bar. +//----------------------------------------------------------------------------- +void Frame::InternalSetTitle(const char *title) +{ + SetTitle(title, true); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the movability of the panel +//----------------------------------------------------------------------------- +void Frame::SetMoveable(bool state) +{ + _moveable=state; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the resizability of the panel +//----------------------------------------------------------------------------- +void Frame::SetSizeable(bool state) +{ + _sizeable=state; + + SetupResizeCursors(); +} + +// When moving via caption, don't let any part of window go outside parent's bounds +void Frame::SetClipToParent( bool state ) +{ + m_bClipToParent = state; +} + +bool Frame::GetClipToParent() const +{ + return m_bClipToParent; +} + +//----------------------------------------------------------------------------- +// Purpose: Check the movability of the panel +//----------------------------------------------------------------------------- +bool Frame::IsMoveable() +{ + return _moveable; +} + +//----------------------------------------------------------------------------- +// Purpose: Check the resizability of the panel +//----------------------------------------------------------------------------- +bool Frame::IsSizeable() +{ + return _sizeable; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the size of the panel inside the frame edges. +//----------------------------------------------------------------------------- +void Frame::GetClientArea(int &x, int &y, int &wide, int &tall) +{ + x = m_iClientInsetX; + + GetSize(wide, tall); + + if (_drawTitleBar) + { + int captionTall = surface()->GetFontTall(_title->GetFont()); + + int border = m_bSmallCaption ? CAPTION_TITLE_BORDER_SMALL : CAPTION_TITLE_BORDER; + int yinset = m_bSmallCaption ? 0 : m_iClientInsetY; + + yinset += m_iTitleTextInsetYOverride; + + y = yinset + captionTall + border + 1; + tall = (tall - yinset) - y; + } + + if ( m_bSmallCaption ) + { + tall -= 5; + } + + wide = (wide - m_iClientInsetX) - x; +} + +// +//----------------------------------------------------------------------------- +// Purpose: applies user configuration settings +//----------------------------------------------------------------------------- +void Frame::ApplyUserConfigSettings(KeyValues *userConfig) +{ + // calculate defaults + int wx, wy, ww, wt; + vgui::surface()->GetWorkspaceBounds(wx, wy, ww, wt); + + int x, y, wide, tall; + GetBounds(x, y, wide, tall); + bool bNoSettings = false; + if (_moveable) + { + // check to see if anything is set + if (!userConfig->FindKey("xpos", false)) + { + bNoSettings = true; + } + + // get the user config position + // default to where we're currently at + x = userConfig->GetInt("xpos", x); + y = userConfig->GetInt("ypos", y); + } + if (_sizeable) + { + wide = userConfig->GetInt("wide", wide); + tall = userConfig->GetInt("tall", tall); + + // Make sure it's no larger than the workspace + if ( wide > ww ) + { + wide = ww; + } + if ( tall > wt ) + { + tall = wt; + } + } + + // see if the dialog has a place on the screen it wants to start + if (bNoSettings && GetDefaultScreenPosition(x, y, wide, tall)) + { + bNoSettings = false; + } + + // make sure it conforms to the minimum size of the dialog + int minWide, minTall; + GetMinimumSize(minWide, minTall); + if (wide < minWide) + { + wide = minWide; + } + if (tall < minTall) + { + tall = minTall; + } + + // make sure it's on the screen + if (x + wide > ww) + { + x = wx + ww - wide; + } + if (y + tall > wt) + { + y = wy + wt - tall; + } + + if (x < wx) + { + x = wx; + } + if (y < wy) + { + y = wy; + } + + SetBounds(x, y, wide, tall); + + if (bNoSettings) + { + // since nothing was set, default our position to the middle of the screen + MoveToCenterOfScreen(); + } + + BaseClass::ApplyUserConfigSettings(userConfig); +} + +//----------------------------------------------------------------------------- +// Purpose: returns user config settings for this control +//----------------------------------------------------------------------------- +void Frame::GetUserConfigSettings(KeyValues *userConfig) +{ + if (_moveable) + { + int x, y; + GetPos(x, y); + userConfig->SetInt("xpos", x); + userConfig->SetInt("ypos", y); + } + if (_sizeable) + { + int w, t; + GetSize(w, t); + userConfig->SetInt("wide", w); + userConfig->SetInt("tall", t); + } + + BaseClass::GetUserConfigSettings(userConfig); +} + +//----------------------------------------------------------------------------- +// Purpose: optimization, return true if this control has any user config settings +//----------------------------------------------------------------------------- +bool Frame::HasUserConfigSettings() +{ + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the default position and size on the screen to appear the first time (defaults to centered) +//----------------------------------------------------------------------------- +bool Frame::GetDefaultScreenPosition(int &x, int &y, int &wide, int &tall) +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: draws title bar +//----------------------------------------------------------------------------- +void Frame::PaintBackground() +{ + // take the panel with focus and check up tree for this panel + // if you find it, than some child of you has the focus, so + // you should be focused + Color titleColor = _titleBarDisabledBgColor; + if (m_bHasFocus) + { + titleColor = _titleBarBgColor; + } + + BaseClass::PaintBackground(); + + if (_drawTitleBar) + { + int wide = GetWide(); + int tall = surface()->GetFontTall(_title->GetFont()); + + // caption + surface()->DrawSetColor(titleColor); + int inset = m_bSmallCaption ? 3 : 5; + int captionHeight = m_bSmallCaption ? 14: 28; + + surface()->DrawFilledRect(inset, inset, wide - inset, captionHeight ); + + if (_title) + { + int nTitleX = m_iTitleTextInsetXOverride ? m_iTitleTextInsetXOverride : m_iTitleTextInsetX; + int nTitleWidth = wide - 72; +#if !defined( _X360 ) + if ( _menuButton && _menuButton->IsVisible() ) + { + int mw, mh; + _menuButton->GetImageSize( mw, mh ); + nTitleX += mw; + nTitleWidth -= mw; + } +#endif + int nTitleY; + if ( m_iTitleTextInsetYOverride ) + { + nTitleY = m_iTitleTextInsetYOverride; + } + else + { + nTitleY = m_bSmallCaption ? 2 : 9; + } + _title->SetPos( nTitleX, nTitleY ); + _title->SetSize( nTitleWidth, tall); + _title->Paint(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Frame::ApplySchemeSettings(IScheme *pScheme) +{ + // always chain back + BaseClass::ApplySchemeSettings(pScheme); + + SetOverridableColor( &_titleBarFgColor, GetSchemeColor("FrameTitleBar.TextColor", pScheme) ); + SetOverridableColor( &_titleBarBgColor, GetSchemeColor("FrameTitleBar.BgColor", pScheme) ); + SetOverridableColor( &_titleBarDisabledFgColor, GetSchemeColor("FrameTitleBar.DisabledTextColor", pScheme) ); + SetOverridableColor( &_titleBarDisabledBgColor, GetSchemeColor("FrameTitleBar.DisabledBgColor", pScheme) ); + + const char *font = NULL; + if ( m_bSmallCaption ) + { + font = pScheme->GetResourceString("FrameTitleBar.SmallFont"); + } + else + { + font = pScheme->GetResourceString("FrameTitleBar.Font"); + } + + HFont titlefont; + if ( m_hCustomTitleFont ) + { + titlefont = m_hCustomTitleFont; + } + else + { + titlefont = pScheme->GetFont((font && *font) ? font : "Default", IsProportional()); + } + + _title->SetFont( titlefont ); + _title->ResizeImageToContent(); + +#if !defined( _X360 ) + HFont marfont = (HFont)0; + if ( m_bSmallCaption ) + { + marfont = pScheme->GetFont( "MarlettSmall", IsProportional() ); + } + else + { + marfont = pScheme->GetFont( "Marlett", IsProportional() ); + } + + _minimizeButton->SetFont(marfont); + _maximizeButton->SetFont(marfont); + _minimizeToSysTrayButton->SetFont(marfont); + _closeButton->SetFont(marfont); +#endif + + m_flTransitionEffectTime = atof(pScheme->GetResourceString("Frame.TransitionEffectTime")); + m_flFocusTransitionEffectTime = atof(pScheme->GetResourceString("Frame.FocusTransitionEffectTime")); + + SetOverridableColor( &m_InFocusBgColor, pScheme->GetColor("Frame.BgColor", GetBgColor()) ); + SetOverridableColor( &m_OutOfFocusBgColor, pScheme->GetColor("Frame.OutOfFocusBgColor", m_InFocusBgColor) ); + + const char *resourceString = pScheme->GetResourceString("Frame.ClientInsetX"); + if ( resourceString ) + { + m_iClientInsetX = atoi(resourceString); + } + resourceString = pScheme->GetResourceString("Frame.ClientInsetY"); + if ( resourceString ) + { + m_iClientInsetY = atoi(resourceString); + } + resourceString = pScheme->GetResourceString("Frame.TitleTextInsetX"); + if ( resourceString ) + { + m_iTitleTextInsetX = atoi(resourceString); + } + + SetBgColor(m_InFocusBgColor); + SetBorder(pScheme->GetBorder("FrameBorder")); + + OnFrameFocusChanged( m_bHasFocus ); +} + +// Disables the fade-in/out-effect even if configured in the scheme settings +void Frame::DisableFadeEffect( void ) +{ + m_flFocusTransitionEffectTime = 0.f; + m_flTransitionEffectTime = 0.f; +} + +void Frame::SetFadeEffectDisableOverride( bool disabled ) +{ + m_bDisableFadeEffect = disabled; +} + +//----------------------------------------------------------------------------- +// Purpose: Apply settings loaded from a resource file +//----------------------------------------------------------------------------- +void Frame::ApplySettings(KeyValues *inResourceData) +{ + // Don't change the frame's visibility, remove that setting from the config data + inResourceData->SetInt("visible", -1); + BaseClass::ApplySettings(inResourceData); + + SetCloseButtonVisible( inResourceData->GetBool( "setclosebuttonvisible", true ) ); + + if( !inResourceData->GetInt("settitlebarvisible", 1 ) ) // if "title" is "0" then don't draw the title bar + { + SetTitleBarVisible( false ); + } + + // set the title + const char *title = inResourceData->GetString("title", ""); + if (title && *title) + { + SetTitle(title, true); + } + + const char *titlefont = inResourceData->GetString("title_font", ""); + if ( titlefont && titlefont[0] ) + { + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + if ( pScheme ) + { + m_hCustomTitleFont = pScheme->GetFont( titlefont ); + } + } + + KeyValues *pKV = inResourceData->FindKey( "clientinsetx_override", false ); + if ( pKV ) + { + m_iClientInsetX = pKV->GetInt(); + m_iClientInsetXOverridden = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Apply settings loaded from a resource file +//----------------------------------------------------------------------------- +void Frame::GetSettings(KeyValues *outResourceData) +{ + BaseClass::GetSettings(outResourceData); + outResourceData->SetInt("settitlebarvisible", _drawTitleBar ); + + if (_title) + { + char buf[256]; + _title->GetUnlocalizedText( buf, 255 ); + if (buf[0]) + { + outResourceData->SetString("title", buf); + } + } + + if ( m_iClientInsetXOverridden ) + { + outResourceData->SetInt( "clientinsetx_override", m_iClientInsetX ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns a description of the settings possible for a frame +//----------------------------------------------------------------------------- +const char *Frame::GetDescription() +{ + static char buf[512]; + Q_snprintf(buf, sizeof(buf), "%s, string title", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: Go invisible when a close message is recieved. +//----------------------------------------------------------------------------- +void Frame::OnClose() +{ + // if we're modal, release that before we hide the window else the wrong window will get focus + if (input()->GetAppModalSurface() == GetVPanel()) + { + input()->ReleaseAppModalSurface(); + if ( m_hPreviousModal != 0 ) + { + vgui::input()->SetAppModalSurface( m_hPreviousModal ); + m_hPreviousModal = 0; + } + } + + BaseClass::OnClose(); + + if (m_flTransitionEffectTime && !m_bDisableFadeEffect) + { + // begin the hide transition effect + GetAnimationController()->RunAnimationCommand(this, "alpha", 0.0f, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR); + m_bFadingOut = true; + // move us to the back of the draw order (so that fading out over the top of other dialogs doesn't look wierd) + surface()->MovePopupToBack(GetVPanel()); + } + else + { + // hide us immediately + FinishClose(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Close button in frame pressed +//----------------------------------------------------------------------------- +void Frame::OnCloseFrameButtonPressed() +{ + OnCommand("Close"); +} + +//----------------------------------------------------------------------------- +// Purpose: Command handling +//----------------------------------------------------------------------------- +void Frame::OnCommand(const char *command) +{ + if (!stricmp(command, "Close")) + { + Close(); + } + else if (!stricmp(command, "CloseModal")) + { + CloseModal(); + } + else if (!stricmp(command, "Minimize")) + { + OnMinimize(); + } + else if (!stricmp(command, "MinimizeToSysTray")) + { + OnMinimizeToSysTray(); + } + else + { + BaseClass::OnCommand(command); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Get the system menu +//----------------------------------------------------------------------------- +Menu *Frame::GetSysMenu() +{ +#if !defined( _X360 ) + if (!_sysMenu) + { + _sysMenu = new Menu(this, NULL); + _sysMenu->SetVisible(false); + _sysMenu->AddActionSignalTarget(this); + + _sysMenu->AddMenuItem("Minimize", "#SysMenu_Minimize", "Minimize", this); + _sysMenu->AddMenuItem("Maximize", "#SysMenu_Maximize", "Maximize", this); + _sysMenu->AddMenuItem("Close", "#SysMenu_Close", "Close", this); + + // check for enabling/disabling menu items + // this might have to be done at other times as well. + Panel *menuItem = _sysMenu->FindChildByName("Minimize"); + if (menuItem) + { + menuItem->SetEnabled(_minimizeButton->IsVisible()); + } + menuItem = _sysMenu->FindChildByName("Maximize"); + if (menuItem) + { + menuItem->SetEnabled(_maximizeButton->IsVisible()); + } + menuItem = _sysMenu->FindChildByName("Close"); + if (menuItem) + { + menuItem->SetEnabled(_closeButton->IsVisible()); + } + } + + return _sysMenu; +#else + return NULL; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Set the system menu +//----------------------------------------------------------------------------- +void Frame::SetSysMenu(Menu *menu) +{ +#if !defined( _X360 ) + if (menu == _sysMenu) + return; + + _sysMenu->MarkForDeletion(); + _sysMenu = menu; + + _menuButton->SetMenu(_sysMenu); +#endif +} + + +//----------------------------------------------------------------------------- +// Set the system menu images +//----------------------------------------------------------------------------- +void Frame::SetImages( const char *pEnabledImage, const char *pDisabledImage ) +{ +#if !defined( _X360 ) + _menuButton->SetImages( pEnabledImage, pDisabledImage ); +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: Close the window +//----------------------------------------------------------------------------- +void Frame::Close() +{ + OnClose(); +} + +//----------------------------------------------------------------------------- +// Purpose: Finishes closing the dialog +//----------------------------------------------------------------------------- +void Frame::FinishClose() +{ + SetVisible(false); + m_bPreviouslyVisible = false; + m_bFadingOut = false; + + OnFinishedClose(); + + if (m_bDeleteSelfOnClose) + { + // Must be last because if vgui is not running then this will call delete this!!! + MarkForDeletion(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Frame::OnFinishedClose() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Minimize the window on the taskbar. +//----------------------------------------------------------------------------- +void Frame::OnMinimize() +{ + surface()->SetMinimized(GetVPanel(), true); +} + +//----------------------------------------------------------------------------- +// Purpose: Does nothing by default +//----------------------------------------------------------------------------- +void Frame::OnMinimizeToSysTray() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Respond to mouse presses +//----------------------------------------------------------------------------- +void Frame::OnMousePressed(MouseCode code) +{ + if (!IsBuildGroupEnabled()) + { + // if a child doesn't have focus, get it for ourselves + VPANEL focus = input()->GetFocus(); + if (!focus || !ipanel()->HasParent(focus, GetVPanel())) + { + RequestFocus(); + } + } + + BaseClass::OnMousePressed(code); +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle visibility of the system menu button +//----------------------------------------------------------------------------- +void Frame::SetMenuButtonVisible(bool state) +{ +#if !defined( _X360 ) + _menuButton->SetVisible(state); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle respond of the system menu button +// it will look enabled or disabled in response to the title bar +// but may not activate. +//----------------------------------------------------------------------------- +void Frame::SetMenuButtonResponsive(bool state) +{ +#if !defined( _X360 ) + _menuButton->SetResponsive(state); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle visibility of the minimize button +//----------------------------------------------------------------------------- +void Frame::SetMinimizeButtonVisible(bool state) +{ +#if !defined( _X360 ) + _minimizeButton->SetVisible(state); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle visibility of the maximize button +//----------------------------------------------------------------------------- +void Frame::SetMaximizeButtonVisible(bool state) +{ +#if !defined( _X360 ) + _maximizeButton->SetVisible(state); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Toggles visibility of the minimize-to-systray icon (defaults to false) +//----------------------------------------------------------------------------- +void Frame::SetMinimizeToSysTrayButtonVisible(bool state) +{ +#if !defined( _X360 ) + _minimizeToSysTrayButton->SetVisible(state); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle visibility of the close button +//----------------------------------------------------------------------------- +void Frame::SetCloseButtonVisible(bool state) +{ +#if !defined( _X360 ) + _closeButton->SetVisible(state); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: soaks up any remaining messages +//----------------------------------------------------------------------------- +void Frame::OnKeyCodeReleased(KeyCode code) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: soaks up any remaining messages +//----------------------------------------------------------------------------- +void Frame::OnKeyFocusTicked() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Toggles window flash state on a timer +//----------------------------------------------------------------------------- +void Frame::InternalFlashWindow() +{ + if (_flashWindow) + { + // toggle icon flashing + _nextFlashState = true; + surface()->FlashWindow(GetVPanel(), _nextFlashState); + _nextFlashState = !_nextFlashState; + + PostMessage(this, new KeyValues("FlashWindow"), 1.8f); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Adds the child to the focus nav group +//----------------------------------------------------------------------------- +void Frame::OnChildAdded(VPANEL child) +{ + BaseClass::OnChildAdded(child); +} + +//----------------------------------------------------------------------------- +// Purpose: Flash the window system tray button until the frame gets focus +//----------------------------------------------------------------------------- +void Frame::FlashWindow() +{ + _flashWindow = true; + _nextFlashState = true; + + InternalFlashWindow(); +} + +//----------------------------------------------------------------------------- +// Purpose: Stops any window flashing +//----------------------------------------------------------------------------- +void Frame::FlashWindowStop() +{ + surface()->FlashWindow(GetVPanel(), false); + _flashWindow = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: load the control settings - should be done after all the children are added to the dialog +//----------------------------------------------------------------------------- +void Frame::LoadControlSettings( const char *dialogResourceName, const char *pathID, KeyValues *pPreloadedKeyValues, KeyValues *pConditions ) +{ + BaseClass::LoadControlSettings( dialogResourceName, pathID, pPreloadedKeyValues, pConditions ); + + // set the focus on the default control + Panel *defaultFocus = GetFocusNavGroup().GetDefaultPanel(); + if (defaultFocus) + { + defaultFocus->RequestFocus(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Checks for ctrl+shift+b hits to enter build mode +// Activates any hotkeys / default buttons +// Swallows any unhandled input +//----------------------------------------------------------------------------- +void Frame::OnKeyCodeTyped(KeyCode code) +{ + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + + if ( IsX360() ) + { + vgui::Panel *pMap = FindChildByName( "ControllerMap" ); + if ( pMap && pMap->IsKeyBoardInputEnabled() ) + { + pMap->OnKeyCodeTyped( code ); + return; + } + } + + if ( ctrl && shift && alt && code == KEY_B) + { + // enable build mode + ActivateBuildMode(); + } + else if (ctrl && shift && alt && code == KEY_R) + { + // reload the scheme + VPANEL top = surface()->GetEmbeddedPanel(); + if (top) + { + // reload the data file + scheme()->ReloadSchemes(); + + Panel *panel = ipanel()->GetPanel(top, GetModuleName()); + if (panel) + { + // make the top-level panel reload it's scheme, it will chain down to all the child panels + panel->InvalidateLayout(false, true); + } + } + } + else if (alt && code == KEY_F4) + { + // user has hit the close + PostMessage(this, new KeyValues("CloseFrameButtonPressed")); + } + else if (code == KEY_ENTER) + { + // check for a default button + VPANEL panel = GetFocusNavGroup().GetCurrentDefaultButton(); + if (panel && ipanel()->IsVisible( panel ) && ipanel()->IsEnabled( panel )) + { + // Activate the button + PostMessage(panel, new KeyValues("Hotkey")); + } + } + else if ( code == KEY_ESCAPE && + surface()->SupportsFeature(ISurface::ESCAPE_KEY) && + input()->GetAppModalSurface() == GetVPanel() ) + { + // ESC cancels, unless we're in the engine - in the engine ESC flips between the UI and the game + CloseModal(); + } + // Usually don't chain back as Frames are the end of the line for key presses, unless + // m_bChainKeysToParent is set + else if ( m_bChainKeysToParent ) + { + BaseClass::OnKeyCodeTyped( code ); + } + else + { + input()->OnKeyCodeUnhandled( (int)code ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: If true, then OnKeyCodeTyped messages continue up past the Frame +// Input : state - +//----------------------------------------------------------------------------- +void Frame::SetChainKeysToParent( bool state ) +{ + m_bChainKeysToParent = state; +} + +//----------------------------------------------------------------------------- +// Purpose: If true, then OnKeyCodeTyped messages continue up past the Frame +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Frame::CanChainKeysToParent() const +{ + return m_bChainKeysToParent; +} + +//----------------------------------------------------------------------------- +// Purpose: Checks for ctrl+shift+b hits to enter build mode +// Activates any hotkeys / default buttons +// Swallows any unhandled input +//----------------------------------------------------------------------------- +void Frame::OnKeyTyped(wchar_t unichar) +{ + Panel *panel = GetFocusNavGroup().FindPanelByHotkey(unichar); + if (panel) + { + // tell the panel to Activate + PostMessage(panel, new KeyValues("Hotkey")); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets all title bar controls +//----------------------------------------------------------------------------- +void Frame::SetTitleBarVisible( bool state ) +{ + _drawTitleBar = state; + SetMenuButtonVisible(state); + SetMinimizeButtonVisible(state); + SetMaximizeButtonVisible(state); + SetCloseButtonVisible(state); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the frame to delete itself on close +//----------------------------------------------------------------------------- +void Frame::SetDeleteSelfOnClose( bool state ) +{ + m_bDeleteSelfOnClose = state; +} + +//----------------------------------------------------------------------------- +// Purpose: updates localized text +//----------------------------------------------------------------------------- +void Frame::OnDialogVariablesChanged( KeyValues *dialogVariables ) +{ + StringIndex_t index = _title->GetUnlocalizedTextSymbol(); + if (index != INVALID_LOCALIZE_STRING_INDEX) + { + // reconstruct the string from the variables + wchar_t buf[1024]; + g_pVGuiLocalize->ConstructString(buf, sizeof(buf), index, dialogVariables); + SetTitle(buf, true); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handles staying on screen when the screen size changes +//----------------------------------------------------------------------------- +void Frame::OnScreenSizeChanged(int iOldWide, int iOldTall) +{ + BaseClass::OnScreenSizeChanged(iOldWide, iOldTall); + + if (IsProportional()) + return; + + // make sure we're completely on screen + int iNewWide, iNewTall; + surface()->GetScreenSize(iNewWide, iNewTall); + + int x, y, wide, tall; + GetBounds(x, y, wide, tall); + + // make sure the bottom-right corner is on the screen first + if (x + wide > iNewWide) + { + x = iNewWide - wide; + } + if (y + tall > iNewTall) + { + y = iNewTall - tall; + } + + // make sure the top-left is visible + x = max( 0, x ); + y = max( 0, y ); + + // apply + SetPos(x, y); +} + +//----------------------------------------------------------------------------- +// Purpose: For supporting thin caption bars +// Input : state - +//----------------------------------------------------------------------------- +void Frame::SetSmallCaption( bool state ) +{ + m_bSmallCaption = state; + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Frame::IsSmallCaption() const +{ + return m_bSmallCaption; +} + + +//----------------------------------------------------------------------------- +// Purpose: Static method to place a frame under the cursor +//----------------------------------------------------------------------------- +void Frame::PlaceUnderCursor( ) +{ + // get cursor position, this is local to this text edit window + int cursorX, cursorY; + input()->GetCursorPos( cursorX, cursorY ); + + // relayout the menu immediately so that we know it's size + InvalidateLayout(true); + int w, h; + GetSize( w, h ); + + // work out where the cursor is and therefore the best place to put the frame + int sw, sh; + surface()->GetScreenSize( sw, sh ); + + // Try to center it first + int x, y; + x = cursorX - ( w / 2 ); + y = cursorY - ( h / 2 ); + + // Clamp to various sides + if ( x + w > sw ) + { + x = sw - w; + } + if ( y + h > sh ) + { + y = sh - h; + } + if ( x < 0 ) + { + x = 0; + } + if ( y < 0 ) + { + y = 0; + } + + SetPos( x, y ); +} diff --git a/mp/src/vgui2/vgui_controls/GraphPanel.cpp b/mp/src/vgui2/vgui_controls/GraphPanel.cpp index 1fd883ce..3e558dd7 100644 --- a/mp/src/vgui2/vgui_controls/GraphPanel.cpp +++ b/mp/src/vgui2/vgui_controls/GraphPanel.cpp @@ -1,308 +1,308 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include - -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY( GraphPanel ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -GraphPanel::GraphPanel(Panel *parent, const char *name) : BaseClass(parent, name) -{ - m_flDomainSize = 100.0f; - m_flLowRange = 0.0f; - m_flHighRange = 1.0f; - m_bUseDynamicRange = true; - m_flMinDomainSize = 0.0f; - m_flMaxDomainSize = 0.0f; - m_bMaxDomainSizeSet = false; - - // rendering, need to pull these from scheme/res file - m_iGraphBarWidth = 2; - m_iGraphBarGapWidth = 2; -} - -//----------------------------------------------------------------------------- -// Purpose: domain settings (x-axis settings) -//----------------------------------------------------------------------------- -void GraphPanel::SetDisplayDomainSize(float size) -{ - m_flDomainSize = size; - - // set the max domain size if it hasn't been set yet - if (!m_bMaxDomainSizeSet) - { - SetMaxDomainSize(size); - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets the smallest domain that will be displayed -//----------------------------------------------------------------------------- -void GraphPanel::SetMinDomainSize(float size) -{ - m_flMinDomainSize = size; -} - -//----------------------------------------------------------------------------- -// Purpose: sets the samples to keep -//----------------------------------------------------------------------------- -void GraphPanel::SetMaxDomainSize(float size) -{ - m_flMaxDomainSize = size; - m_bMaxDomainSizeSet = true; -} - -//----------------------------------------------------------------------------- -// Purpose: range settings (y-axis settings) -//----------------------------------------------------------------------------- -void GraphPanel::SetUseFixedRange(float lowRange, float highRange) -{ - m_bUseDynamicRange = false; - m_flLowRange = lowRange; - m_flHighRange = highRange; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the graph to dynamically determine the range -//----------------------------------------------------------------------------- -void GraphPanel::SetUseDynamicRange(float *rangeList, int numRanges) -{ - m_bUseDynamicRange = true; - m_RangeList.CopyArray(rangeList, numRanges); -} - -//----------------------------------------------------------------------------- -// Purpose: Gets the currently displayed range -//----------------------------------------------------------------------------- -void GraphPanel::GetDisplayedRange(float &lowRange, float &highRange) -{ - lowRange = m_flLowRange; - highRange = m_flHighRange; -} - -//----------------------------------------------------------------------------- -// Purpose: adds an item to the end of the list -//----------------------------------------------------------------------------- -void GraphPanel::AddItem(float sampleEnd, float sampleValue) -{ - if (m_Samples.Count() && m_Samples[m_Samples.Tail()].value == sampleValue) - { - // collapse identical samples - m_Samples[m_Samples.Tail()].sampleEnd = sampleEnd; - } - else - { - // add to the end of the samples list - Sample_t item; - item.value = sampleValue; - item.sampleEnd = sampleEnd; - m_Samples.AddToTail(item); - } - - // see if this frees up any samples past the end - if (m_bMaxDomainSizeSet) - { - float freePoint = sampleEnd - m_flMaxDomainSize; - while (m_Samples[m_Samples.Head()].sampleEnd < freePoint) - { - m_Samples.Remove(m_Samples.Head()); - } - } - -/* - // see the max number of samples necessary to display this information reasonably precisely - static const int MAX_LIKELY_GRAPH_WIDTH = 800; - int maxSamplesNeeded = 2 * MAX_LIKELY_GRAPH_WIDTH / (m_iGraphBarWidth + m_iGraphBarGapWidth); - if (m_Samples.Count() > 2) - { - // see if we can collapse some items - float highestSample = m_Samples[m_Samples.Tail()].sampleEnd; - - // iterate the items - // always keep the head around so we have something to go against - int sampleIndex = m_Samples.Next(m_Samples.Head()); - int nextSampleIndex = m_Samples.Next(sampleIndex); - - while (m_Samples.IsInList(nextSampleIndex)) - { - // calculate what sampling precision is actually needed to display this data - float distanceFromEnd = highestSample - m_Samples[sampleIndex].sampleEnd; - -// if (distanceFromEnd < m_flDomainSize) -// break; - - //!! this calculation is very incorrect - float minNeededSampleSize = distanceFromEnd / (m_flMinDomainSize * maxSamplesNeeded); - float sampleSize = m_Samples[nextSampleIndex].sampleEnd - m_Samples[sampleIndex].sampleEnd; - - if (sampleSize < minNeededSampleSize) - { - // collapse the item into the next index - m_Samples[nextSampleIndex].value = 0.5f * (m_Samples[nextSampleIndex].value + m_Samples[sampleIndex].value); - - // remove the item from the list - m_Samples.Remove(sampleIndex); - - // move to the next item - sampleIndex = nextSampleIndex; - nextSampleIndex = m_Samples.Next(sampleIndex); - } - else - { - // this item didn't need collapsing, so assume the next item won't - break; - } - } - } -*/ - - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: returns number of items that can be displayed -//----------------------------------------------------------------------------- -int GraphPanel::GetVisibleItemCount() -{ - return GetWide() / (m_iGraphBarWidth + m_iGraphBarGapWidth); -} - -//----------------------------------------------------------------------------- -// Purpose: lays out the graph -//----------------------------------------------------------------------------- -void GraphPanel::PerformLayout() -{ - BaseClass::PerformLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: draws the graph -//----------------------------------------------------------------------------- -void GraphPanel::Paint() -{ - if (!m_Samples.Count()) - return; - - // walk from right to left drawing the resampled data - int sampleIndex = m_Samples.Tail(); - int x = GetWide() - (m_iGraphBarWidth + m_iGraphBarGapWidth); - - // calculate how big each sample should be - float sampleSize = m_flDomainSize / GetVisibleItemCount(); - - // calculate where in the domain we start resampling - float resampleStart = m_Samples[sampleIndex].sampleEnd - sampleSize; - // always resample from a sample point that is a multiple of the sampleSize - resampleStart -= (float)fmod(resampleStart, sampleSize); - - // bar size multiplier - float barSizeMultiplier = GetTall() / (m_flHighRange - m_flLowRange); - - // set render color - surface()->DrawSetColor(GetFgColor()); - - // recalculate the sample range for dynamic resizing - float flMinValue = m_Samples[m_Samples.Head()].value; - float flMaxValue = m_Samples[m_Samples.Head()].value; - - // iterate the bars to draw - while (x > 0 && m_Samples.IsInList(sampleIndex)) - { - // move back the drawing point - x -= (m_iGraphBarWidth + m_iGraphBarGapWidth); - - // collect the samples - float value = 0.0f; - float maxValue = 0.0f; - int samplesTouched = 0; - int prevSampleIndex = m_Samples.Previous(sampleIndex); - while (m_Samples.IsInList(prevSampleIndex)) - { - // take the value - value += m_Samples[sampleIndex].value; - samplesTouched++; - - // do some work to calculate the sample range - if (m_Samples[sampleIndex].value < flMinValue) - { - flMinValue = m_Samples[sampleIndex].value; - } - if (m_Samples[sampleIndex].value > flMaxValue) - { - flMaxValue = m_Samples[sampleIndex].value; - } - if (m_Samples[sampleIndex].value > maxValue) - { - maxValue = m_Samples[sampleIndex].value; - } - - if (resampleStart < m_Samples[prevSampleIndex].sampleEnd) - { - // we're out of the sampling range, we need to move on to the next sample - sampleIndex = prevSampleIndex; - prevSampleIndex = m_Samples.Previous(sampleIndex); - } - else - { - // we're done with this resample - // move back the resample start - resampleStart -= sampleSize; - // draw the current item - break; - } - } - - // draw the item - // show the max value in the sample, not the average - int size = (int)(maxValue * barSizeMultiplier); -// int size = (int)((value * barSizeMultiplier) / samplesTouched); - surface()->DrawFilledRect(x, GetTall() - size, x + m_iGraphBarWidth, GetTall()); - } - - // calculate our final range (for use next frame) - if (m_bUseDynamicRange) - { - flMinValue = 0; - - // find the range that fits - for (int i = 0; i < m_RangeList.Count(); i++) - { - if (m_RangeList[i] > flMaxValue) - { - flMaxValue = m_RangeList[i]; - break; - } - } - - m_flLowRange = flMinValue; - m_flHighRange = flMaxValue; - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets up colors -//----------------------------------------------------------------------------- -void GraphPanel::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetFgColor(GetSchemeColor("GraphPanel.FgColor", pScheme)); - SetBgColor(GetSchemeColor("GraphPanel.BgColor", pScheme)); - SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include + +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY( GraphPanel ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +GraphPanel::GraphPanel(Panel *parent, const char *name) : BaseClass(parent, name) +{ + m_flDomainSize = 100.0f; + m_flLowRange = 0.0f; + m_flHighRange = 1.0f; + m_bUseDynamicRange = true; + m_flMinDomainSize = 0.0f; + m_flMaxDomainSize = 0.0f; + m_bMaxDomainSizeSet = false; + + // rendering, need to pull these from scheme/res file + m_iGraphBarWidth = 2; + m_iGraphBarGapWidth = 2; +} + +//----------------------------------------------------------------------------- +// Purpose: domain settings (x-axis settings) +//----------------------------------------------------------------------------- +void GraphPanel::SetDisplayDomainSize(float size) +{ + m_flDomainSize = size; + + // set the max domain size if it hasn't been set yet + if (!m_bMaxDomainSizeSet) + { + SetMaxDomainSize(size); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets the smallest domain that will be displayed +//----------------------------------------------------------------------------- +void GraphPanel::SetMinDomainSize(float size) +{ + m_flMinDomainSize = size; +} + +//----------------------------------------------------------------------------- +// Purpose: sets the samples to keep +//----------------------------------------------------------------------------- +void GraphPanel::SetMaxDomainSize(float size) +{ + m_flMaxDomainSize = size; + m_bMaxDomainSizeSet = true; +} + +//----------------------------------------------------------------------------- +// Purpose: range settings (y-axis settings) +//----------------------------------------------------------------------------- +void GraphPanel::SetUseFixedRange(float lowRange, float highRange) +{ + m_bUseDynamicRange = false; + m_flLowRange = lowRange; + m_flHighRange = highRange; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the graph to dynamically determine the range +//----------------------------------------------------------------------------- +void GraphPanel::SetUseDynamicRange(float *rangeList, int numRanges) +{ + m_bUseDynamicRange = true; + m_RangeList.CopyArray(rangeList, numRanges); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the currently displayed range +//----------------------------------------------------------------------------- +void GraphPanel::GetDisplayedRange(float &lowRange, float &highRange) +{ + lowRange = m_flLowRange; + highRange = m_flHighRange; +} + +//----------------------------------------------------------------------------- +// Purpose: adds an item to the end of the list +//----------------------------------------------------------------------------- +void GraphPanel::AddItem(float sampleEnd, float sampleValue) +{ + if (m_Samples.Count() && m_Samples[m_Samples.Tail()].value == sampleValue) + { + // collapse identical samples + m_Samples[m_Samples.Tail()].sampleEnd = sampleEnd; + } + else + { + // add to the end of the samples list + Sample_t item; + item.value = sampleValue; + item.sampleEnd = sampleEnd; + m_Samples.AddToTail(item); + } + + // see if this frees up any samples past the end + if (m_bMaxDomainSizeSet) + { + float freePoint = sampleEnd - m_flMaxDomainSize; + while (m_Samples[m_Samples.Head()].sampleEnd < freePoint) + { + m_Samples.Remove(m_Samples.Head()); + } + } + +/* + // see the max number of samples necessary to display this information reasonably precisely + static const int MAX_LIKELY_GRAPH_WIDTH = 800; + int maxSamplesNeeded = 2 * MAX_LIKELY_GRAPH_WIDTH / (m_iGraphBarWidth + m_iGraphBarGapWidth); + if (m_Samples.Count() > 2) + { + // see if we can collapse some items + float highestSample = m_Samples[m_Samples.Tail()].sampleEnd; + + // iterate the items + // always keep the head around so we have something to go against + int sampleIndex = m_Samples.Next(m_Samples.Head()); + int nextSampleIndex = m_Samples.Next(sampleIndex); + + while (m_Samples.IsInList(nextSampleIndex)) + { + // calculate what sampling precision is actually needed to display this data + float distanceFromEnd = highestSample - m_Samples[sampleIndex].sampleEnd; + +// if (distanceFromEnd < m_flDomainSize) +// break; + + //!! this calculation is very incorrect + float minNeededSampleSize = distanceFromEnd / (m_flMinDomainSize * maxSamplesNeeded); + float sampleSize = m_Samples[nextSampleIndex].sampleEnd - m_Samples[sampleIndex].sampleEnd; + + if (sampleSize < minNeededSampleSize) + { + // collapse the item into the next index + m_Samples[nextSampleIndex].value = 0.5f * (m_Samples[nextSampleIndex].value + m_Samples[sampleIndex].value); + + // remove the item from the list + m_Samples.Remove(sampleIndex); + + // move to the next item + sampleIndex = nextSampleIndex; + nextSampleIndex = m_Samples.Next(sampleIndex); + } + else + { + // this item didn't need collapsing, so assume the next item won't + break; + } + } + } +*/ + + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns number of items that can be displayed +//----------------------------------------------------------------------------- +int GraphPanel::GetVisibleItemCount() +{ + return GetWide() / (m_iGraphBarWidth + m_iGraphBarGapWidth); +} + +//----------------------------------------------------------------------------- +// Purpose: lays out the graph +//----------------------------------------------------------------------------- +void GraphPanel::PerformLayout() +{ + BaseClass::PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: draws the graph +//----------------------------------------------------------------------------- +void GraphPanel::Paint() +{ + if (!m_Samples.Count()) + return; + + // walk from right to left drawing the resampled data + int sampleIndex = m_Samples.Tail(); + int x = GetWide() - (m_iGraphBarWidth + m_iGraphBarGapWidth); + + // calculate how big each sample should be + float sampleSize = m_flDomainSize / GetVisibleItemCount(); + + // calculate where in the domain we start resampling + float resampleStart = m_Samples[sampleIndex].sampleEnd - sampleSize; + // always resample from a sample point that is a multiple of the sampleSize + resampleStart -= (float)fmod(resampleStart, sampleSize); + + // bar size multiplier + float barSizeMultiplier = GetTall() / (m_flHighRange - m_flLowRange); + + // set render color + surface()->DrawSetColor(GetFgColor()); + + // recalculate the sample range for dynamic resizing + float flMinValue = m_Samples[m_Samples.Head()].value; + float flMaxValue = m_Samples[m_Samples.Head()].value; + + // iterate the bars to draw + while (x > 0 && m_Samples.IsInList(sampleIndex)) + { + // move back the drawing point + x -= (m_iGraphBarWidth + m_iGraphBarGapWidth); + + // collect the samples + float value = 0.0f; + float maxValue = 0.0f; + int samplesTouched = 0; + int prevSampleIndex = m_Samples.Previous(sampleIndex); + while (m_Samples.IsInList(prevSampleIndex)) + { + // take the value + value += m_Samples[sampleIndex].value; + samplesTouched++; + + // do some work to calculate the sample range + if (m_Samples[sampleIndex].value < flMinValue) + { + flMinValue = m_Samples[sampleIndex].value; + } + if (m_Samples[sampleIndex].value > flMaxValue) + { + flMaxValue = m_Samples[sampleIndex].value; + } + if (m_Samples[sampleIndex].value > maxValue) + { + maxValue = m_Samples[sampleIndex].value; + } + + if (resampleStart < m_Samples[prevSampleIndex].sampleEnd) + { + // we're out of the sampling range, we need to move on to the next sample + sampleIndex = prevSampleIndex; + prevSampleIndex = m_Samples.Previous(sampleIndex); + } + else + { + // we're done with this resample + // move back the resample start + resampleStart -= sampleSize; + // draw the current item + break; + } + } + + // draw the item + // show the max value in the sample, not the average + int size = (int)(maxValue * barSizeMultiplier); +// int size = (int)((value * barSizeMultiplier) / samplesTouched); + surface()->DrawFilledRect(x, GetTall() - size, x + m_iGraphBarWidth, GetTall()); + } + + // calculate our final range (for use next frame) + if (m_bUseDynamicRange) + { + flMinValue = 0; + + // find the range that fits + for (int i = 0; i < m_RangeList.Count(); i++) + { + if (m_RangeList[i] > flMaxValue) + { + flMaxValue = m_RangeList[i]; + break; + } + } + + m_flLowRange = flMinValue; + m_flHighRange = flMaxValue; + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets up colors +//----------------------------------------------------------------------------- +void GraphPanel::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetFgColor(GetSchemeColor("GraphPanel.FgColor", pScheme)); + SetBgColor(GetSchemeColor("GraphPanel.BgColor", pScheme)); + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); +} diff --git a/mp/src/vgui2/vgui_controls/HTML.cpp b/mp/src/vgui2/vgui_controls/HTML.cpp index d7f68f10..394364e6 100644 --- a/mp/src/vgui2/vgui_controls/HTML.cpp +++ b/mp/src/vgui2/vgui_controls/HTML.cpp @@ -1,2278 +1,2278 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// This class is a message box that has two buttons, ok and cancel instead of -// just the ok button of a message box. We use a message box class for the ok button -// and implement another button here. -// -// $NoKeywords: $ -//=============================================================================// - -#include "vgui_controls/pch_vgui_controls.h" -#include -#include -#include - -#include -#include -#include "htmlmessages.pb.h" -#include -#include - -#include "html/ipainthtml.h" -#include "html/ihtmlchrome.h" -#include "html/ichromehtmlwrapper.h" -#include "html/htmlprotobuf.h" - -#include "filesystem.h" -#include "../vgui2/src/vgui_key_translation.h" - -#undef PostMessage -#undef MessageBox - -#include "OfflineMode.h" - -// memdbgon must be the last include file in a .cpp file -#include "tier0/memdbgon.h" - -using namespace vgui; - -// helper to send IPC messages to the CEF thread -#define DISPATCH_MESSAGE( eCmd ) \ - if (surface()->AccessChromeHTMLController()) \ - { \ - cmd.Body().set_browser_handle( m_iBrowser );\ - HTMLCommandBuffer_t *pBuf = surface()->AccessChromeHTMLController()->GetFreeCommandBuffer( eCmd, m_iBrowser ); \ - cmd.SerializeCrossProc( &pBuf->m_Buffer ); \ - if ( m_iBrowser == -1 ) { m_vecPendingMessages.AddToTail( pBuf ); } \ - else \ - { \ - surface()->AccessChromeHTMLController()->PushCommand( pBuf ); \ - surface()->AccessChromeHTMLController()->WakeThread(); \ - }\ - } \ - -const int k_nMaxCustomCursors = 2; // the max number of custom cursors we keep cached PER html control - -//----------------------------------------------------------------------------- -// Purpose: A simple passthrough panel to render the border onto the HTML widget -//----------------------------------------------------------------------------- -class HTMLInterior : public Panel -{ - DECLARE_CLASS_SIMPLE( HTMLInterior, Panel ); -public: - HTMLInterior( HTML *parent ) : BaseClass( parent, "HTMLInterior" ) - { - m_pHTML = parent; - SetPaintBackgroundEnabled( false ); - SetKeyBoardInputEnabled( false ); - SetMouseInputEnabled( false ); - } - -private: - HTML *m_pHTML; -}; - - -//----------------------------------------------------------------------------- -// Purpose: a vgui container for popup menus displayed by a control, only 1 menu for any control can be visible at a time -//----------------------------------------------------------------------------- -class HTMLComboBoxHost : public vgui::EditablePanel -{ - DECLARE_CLASS_SIMPLE( HTMLComboBoxHost, EditablePanel ); -public: - HTMLComboBoxHost( HTML *parent, const char *panelName ) : EditablePanel( parent, panelName ) - { - m_pParent = parent; - MakePopup(false); - } - ~HTMLComboBoxHost() {} - - virtual void PaintBackground(); - - virtual void OnMousePressed(MouseCode code); - virtual void OnMouseReleased(MouseCode code); - virtual void OnCursorMoved(int x,int y); - virtual void OnMouseDoublePressed(MouseCode code); - virtual void OnKeyTyped(wchar_t unichar); - virtual void OnKeyCodeTyped(KeyCode code); - virtual void OnKeyCodeReleased(KeyCode code); - virtual void OnMouseWheeled(int delta); - - virtual void OnKillFocus() - { - if ( vgui::input()->GetFocus() != m_pParent->GetVPanel() ) // if its not our parent trying to steal focus - { - BaseClass::OnKillFocus(); - if ( m_pParent ) - m_pParent->HidePopup(); - } - } - - virtual void PerformLayout() - { - // no op the perform layout as we just render the html controls popup texture into it - // we don't want the menu logic trying to play with its size - } - - -private: - HTML *m_pParent; - CUtlVector m_vecPendingMessages; -}; - - -//----------------------------------------------------------------------------- -// Purpose: container class for any external popup windows the browser requests -//----------------------------------------------------------------------------- -class HTMLPopup : public vgui::Frame -{ - DECLARE_CLASS_SIMPLE( HTMLPopup, vgui::Frame ); - class PopupHTML : public vgui::HTML - { - DECLARE_CLASS_SIMPLE( PopupHTML, vgui::HTML ); - public: - PopupHTML( Frame *parent, const char *pchName, bool allowJavaScript , bool bPopupWindow ) : HTML( parent, pchName, allowJavaScript, bPopupWindow ) { m_pParent = parent; } - - virtual void OnSetHTMLTitle( const char *pchTitle ) - { - BaseClass::OnSetHTMLTitle( pchTitle ); - m_pParent->SetTitle( pchTitle, true ); - } - - private: - Frame *m_pParent; - }; -public: - HTMLPopup( Panel *parent, const char *pchURL, const char *pchTitle ) : Frame( NULL, "HtmlPopup", true ) - { - m_pHTML = new PopupHTML( this, "htmlpopupchild", true, true ); - m_pHTML->OpenURL( pchURL, NULL, false ); - SetTitle( pchTitle, true ); - } - - ~HTMLPopup() - { - } - - enum - { - vert_inset = 40, - horiz_inset = 6 - }; - - void PerformLayout() - { - BaseClass::PerformLayout(); - int wide, tall; - GetSize( wide, tall ); - m_pHTML->SetPos( horiz_inset, vert_inset ); - m_pHTML->SetSize( wide - horiz_inset*2, tall - vert_inset*2 ); - } - - void SetBounds( int x, int y, int wide, int tall ) - { - BaseClass::SetBounds( x, y, wide + horiz_inset*2, tall + vert_inset*2 ); - } - - MESSAGE_FUNC( OnCloseWindow, "OnCloseWindow" ) - { - Close(); - } -private: - PopupHTML *m_pHTML; -}; - - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -HTML::HTML(Panel *parent, const char *name, bool allowJavaScript, bool bPopupWindow) : Panel(parent, name) -{ - m_iHTMLTextureID = 0; - m_iComboBoxTextureID = 0; - m_bCanGoBack = false; - m_bCanGoForward = false; - m_bInFind = false; - m_bRequestingDragURL = false; - m_bRequestingCopyLink = false; - m_flZoom = 100.0f; - m_iBrowser = -1; - m_bNeedsFullTextureUpload = false; - - m_pInteriorPanel = new HTMLInterior( this ); - SetPostChildPaintEnabled( true ); - if (surface() && surface()->AccessChromeHTMLController()) - { - surface()->AccessChromeHTMLController()->CreateBrowser( this, bPopupWindow, surface()->GetWebkitHTMLUserAgentString() ); - } - else - { - Warning("Unable to access ChromeHTMLController"); - } - m_iScrollBorderX=m_iScrollBorderY=0; - m_bScrollBarEnabled = true; - m_bContextMenuEnabled = true; - m_bNewWindowsOnly = false; - m_iMouseX = m_iMouseY = 0; - m_iDragStartX = m_iDragStartY = 0; - m_nViewSourceAllowedIndex = -1; - m_iWideLastHTMLSize = m_iTalLastHTMLSize = 0; - - _hbar = new ScrollBar(this, "HorizScrollBar", false); - _hbar->SetVisible(false); - _hbar->AddActionSignalTarget(this); - - _vbar = new ScrollBar(this, "VertScrollBar", true); - _vbar->SetVisible(false); - _vbar->AddActionSignalTarget(this); - - m_pFindBar = new HTML::CHTMLFindBar( this ); - m_pFindBar->SetZPos( 2 ); - m_pFindBar->SetVisible( false ); - - m_pComboBoxHost = new HTMLComboBoxHost( this, "ComboBoxHost" ); - m_pComboBoxHost->SetPaintBackgroundEnabled( true ); - m_pComboBoxHost->SetVisible( false ); - - m_pContextMenu = new Menu( this, "contextmenu" ); - m_pContextMenu->AddMenuItem( "#vgui_HTMLBack", new KeyValues( "Command", "command", "back" ), this ); - m_pContextMenu->AddMenuItem( "#vgui_HTMLForward", new KeyValues( "Command", "command", "forward" ), this ); - m_pContextMenu->AddMenuItem( "#vgui_HTMLReload", new KeyValues( "Command", "command", "reload" ), this ); - m_pContextMenu->AddMenuItem( "#vgui_HTMLStop", new KeyValues( "Command", "command", "stop" ), this ); - m_pContextMenu->AddSeparator(); - m_pContextMenu->AddMenuItem( "#vgui_HTMLCopyUrl", new KeyValues( "Command", "command", "copyurl" ), this ); - m_iCopyLinkMenuItemID = m_pContextMenu->AddMenuItem( "#vgui_HTMLCopyLink", new KeyValues( "Command", "command", "copylink" ), this ); - m_pContextMenu->AddMenuItem( "#TextEntry_Copy", new KeyValues( "Command", "command", "copy" ), this ); - m_pContextMenu->AddMenuItem( "#TextEntry_Paste", new KeyValues( "Command", "command", "paste" ), this ); - m_pContextMenu->AddSeparator(); - m_nViewSourceAllowedIndex = m_pContextMenu->AddMenuItem( "#vgui_HTMLViewSource", new KeyValues( "Command", "command", "viewsource" ), this ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -HTML::~HTML() -{ - m_pContextMenu->MarkForDeletion(); - - if (surface()->AccessChromeHTMLController()) - { - surface()->AccessChromeHTMLController()->RemoveBrowser( this ); - } - - FOR_EACH_VEC( m_vecHCursor, i ) - { - // BR FIXME! -// surface()->DeleteCursor( m_vecHCursor[i].m_Cursor ); - } - m_vecHCursor.RemoveAll(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Handle message to change our cursor -//----------------------------------------------------------------------------- -void HTML::OnSetCursorVGUI( int cursor ) -{ - SetCursor( (HCursor)cursor ); -} - -//----------------------------------------------------------------------------- -// Purpose: sets up colors/fonts/borders -//----------------------------------------------------------------------------- -void HTML::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - BrowserResize(); -} - - -//----------------------------------------------------------------------------- -// Purpose: overrides panel class, paints a texture of the HTML window as a background -//----------------------------------------------------------------------------- -void HTML::Paint() -{ - //VPROF_BUDGET( "HTML::Paint()", VPROF_BUDGETGROUP_OTHER_VGUI ); - BaseClass::Paint(); - - if ( m_iHTMLTextureID != 0 ) - { - surface()->DrawSetTexture( m_iHTMLTextureID ); - int tw = 0, tt = 0; - surface()->DrawSetColor( Color( 255, 255, 255, 255 ) ); - GetSize( tw, tt ); - surface()->DrawTexturedRect( 0, 0, tw, tt ); - } - - // If we have scrollbars, we need to draw the bg color under them, since the browser - // bitmap is a checkerboard under them, and they are transparent in the in-game client - if ( m_iScrollBorderX > 0 || m_iScrollBorderY > 0 ) - { - int w, h; - GetSize( w, h ); - IBorder *border = GetBorder(); - int left = 0, top = 0, right = 0, bottom = 0; - if ( border ) - { - border->GetInset( left, top, right, bottom ); - } - surface()->DrawSetColor( GetBgColor() ); - if ( m_iScrollBorderX ) - { - surface()->DrawFilledRect( w-m_iScrollBorderX - right, top, w, h - bottom ); - } - if ( m_iScrollBorderY ) - { - surface()->DrawFilledRect( left, h-m_iScrollBorderY - bottom, w-m_iScrollBorderX - right, h ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: paint the combo box texture if we have one -//----------------------------------------------------------------------------- -void HTML::PaintComboBox() -{ - BaseClass::Paint(); - if ( m_iComboBoxTextureID != 0 ) - { - surface()->DrawSetTexture( m_iComboBoxTextureID ); - surface()->DrawSetColor( Color( 255, 255, 255, 255 ) ); - int tw = m_allocedComboBoxWidth; - int tt = m_allocedComboBoxHeight; - surface()->DrawTexturedRect( 0, 0, tw, tt ); - } - -} - - -//----------------------------------------------------------------------------- -// Purpose: overrides panel class, paints a texture of the HTML window as a background -//----------------------------------------------------------------------------- -void HTMLComboBoxHost::PaintBackground() -{ - BaseClass::PaintBackground(); - - m_pParent->PaintComboBox(); -} - - -//----------------------------------------------------------------------------- -// Purpose: causes a repaint when the layout changes -//----------------------------------------------------------------------------- -void HTML::PerformLayout() -{ - BaseClass::PerformLayout(); - Repaint(); - int vbarInset = _vbar->IsVisible() ? _vbar->GetWide() : 0; - int maxw = GetWide() - vbarInset; - m_pInteriorPanel->SetBounds( 0, 0, maxw, GetTall() ); - - IScheme *pClientScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "ClientScheme" ) ); - - int iSearchInsetY = 5; - int iSearchInsetX = 5; - int iSearchTall = 24; - int iSearchWide = 150; - const char *resourceString = pClientScheme->GetResourceString( "HTML.SearchInsetY"); - if ( resourceString ) - { - iSearchInsetY = atoi(resourceString); - } - resourceString = pClientScheme->GetResourceString( "HTML.SearchInsetX"); - if ( resourceString ) - { - iSearchInsetX = atoi(resourceString); - } - resourceString = pClientScheme->GetResourceString( "HTML.SearchTall"); - if ( resourceString ) - { - iSearchTall = atoi(resourceString); - } - resourceString = pClientScheme->GetResourceString( "HTML.SearchWide"); - if ( resourceString ) - { - iSearchWide = atoi(resourceString); - } - - m_pFindBar->SetBounds( GetWide() - iSearchWide - iSearchInsetX - vbarInset, m_pFindBar->BIsHidden() ? -1*iSearchTall-5: iSearchInsetY, iSearchWide, iSearchTall ); -} - - -//----------------------------------------------------------------------------- -// Purpose: updates the underlying HTML surface widgets position -//----------------------------------------------------------------------------- -void HTML::OnMove() -{ - BaseClass::OnMove(); - - // tell cef where we are on the screen so plugins can correctly render - int nPanelAbsX, nPanelAbsY; - ipanel()->GetAbsPos( GetVPanel(), nPanelAbsX, nPanelAbsY ); - CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserPosition ); - cmd.Body().set_x( nPanelAbsX ); - cmd.Body().set_y( nPanelAbsY ); - DISPATCH_MESSAGE( eHTMLCommands_BrowserPosition ); - - if ( m_pComboBoxHost && m_pComboBoxHost->IsVisible() ) - { - m_pComboBoxHost->SetVisible( false ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: calculates the need for and position of both horizontal and vertical scroll bars -//----------------------------------------------------------------------------- -void HTML::CalcScrollBars(int w, int h) -{ - bool bScrollbarVisible = _vbar->IsVisible(); - - if ( m_bScrollBarEnabled ) - { - for ( int i = 0; i < 2; i++ ) - { - int scrollx, scrolly, scrollwide, scrolltall; - bool bVisible = false; - if ( i==0 ) - { - scrollx = m_scrollHorizontal.m_nX; - scrolly = m_scrollHorizontal.m_nY; - scrollwide = m_scrollHorizontal.m_nWide; - scrolltall = m_scrollHorizontal.m_nTall; - bVisible = m_scrollHorizontal.m_bVisible; - - // scrollbar positioning tweaks - should be moved into a resource file - scrollwide += 14; - scrolltall += 5; - } - else - { - scrollx = m_scrollVertical.m_nX; - scrolly = m_scrollVertical.m_nY; - scrollwide = m_scrollVertical.m_nWide; - scrolltall = m_scrollVertical.m_nTall; - bVisible = m_scrollVertical.m_bVisible; - - // scrollbar positioning tweaks - should be moved into a resource file - //scrollx -= 3; - if ( m_scrollHorizontal.m_bVisible ) - scrolltall += 16; - else - scrolltall -= 2; - - scrollwide += 5; - } - - if ( bVisible && scrollwide && scrolltall ) - { - int panelWide, panelTall; - GetSize( panelWide, panelTall ); - - ScrollBar *bar = _vbar; - if ( i == 0 ) - bar = _hbar; - - if (!bar->IsVisible()) - { - bar->SetVisible(true); - // displayable area has changed, need to force an update - PostMessage(this, new KeyValues("OnSliderMoved"), 0.02f); - } - - int rangeWindow = panelTall - scrollwide; - if ( i==0 ) - rangeWindow = panelWide - scrolltall; - int range = m_scrollVertical.m_nMax + m_scrollVertical.m_nTall; - if ( i == 0 ) - range = m_scrollHorizontal.m_nMax + m_scrollVertical.m_nWide; - int curValue = m_scrollVertical.m_nScroll; - if ( i == 0 ) - curValue = m_scrollHorizontal.m_nScroll; - - bar->SetEnabled(false); - bar->SetRangeWindow( rangeWindow ); - bar->SetRange( 0, range ); // we want the range [0.. (img_h - h)], but the scrollbar actually returns [0..(range-rangeWindow)] so make sure -h gets deducted from the max range value - bar->SetButtonPressedScrollValue( 5 ); - if ( curValue > ( bar->GetValue() + 5 ) || curValue < (bar->GetValue() - 5 ) ) - bar->SetValue( curValue ); - - if ( i == 0 ) - { - bar->SetPos( 0, h - scrolltall - 1 ); - bar->SetWide( scrollwide ); - bar->SetTall( scrolltall ); - } - else - { - bar->SetPos( w - scrollwide, 0 ); - bar->SetTall( scrolltall ); - bar->SetWide( scrollwide ); - } - - if ( i == 0 ) - m_iScrollBorderY=scrolltall; - else - m_iScrollBorderX=scrollwide; - } - else - { - if ( i == 0 ) - { - m_iScrollBorderY=0; - _hbar->SetVisible( false ); - } - else - { - m_iScrollBorderX=0; - _vbar->SetVisible( false ); - - } - } - } - } - else - { - m_iScrollBorderX = 0; - m_iScrollBorderY=0; - _vbar->SetVisible(false); - _hbar->SetVisible(false); - } - - if ( bScrollbarVisible != _vbar->IsVisible() ) - InvalidateLayout(); -} - - -//----------------------------------------------------------------------------- -// Purpose: opens the URL, will accept any URL that IE accepts -//----------------------------------------------------------------------------- -void HTML::OpenURL(const char *URL, const char *postData, bool force) -{ - PostURL( URL, postData, force ); -} - -//----------------------------------------------------------------------------- -// Purpose: opens the URL, will accept any URL that IE accepts -//----------------------------------------------------------------------------- -void HTML::PostURL(const char *URL, const char *pchPostData, bool force) -{ - if ( m_iBrowser < 0 ) - { - m_sPendingURLLoad = URL; - m_sPendingPostData = pchPostData; - return; - } - - if ( IsSteamInOfflineMode() && !force ) - { - const char *baseDir = getenv("HTML_OFFLINE_DIR"); - if ( baseDir ) - { - // get the app we need to run - char htmlLocation[_MAX_PATH]; - char otherName[128]; - char fileLocation[_MAX_PATH]; - - if ( ! g_pFullFileSystem->FileExists( baseDir ) ) - { - Q_snprintf( otherName, sizeof(otherName), "%senglish.html", OFFLINE_FILE ); - baseDir = otherName; - } - g_pFullFileSystem->GetLocalCopy( baseDir ); // put this file on disk for IE to load - - g_pFullFileSystem->GetLocalPath( baseDir, fileLocation, sizeof(fileLocation) ); - Q_snprintf(htmlLocation, sizeof(htmlLocation), "file://%s", fileLocation); - - CHTMLProtoBufMsg cmd( eHTMLCommands_PostURL ); - cmd.Body().set_url( htmlLocation ); - DISPATCH_MESSAGE( eHTMLCommands_PostURL ); - - } - else - { - CHTMLProtoBufMsg cmd( eHTMLCommands_PostURL ); - cmd.Body().set_url( URL ); - DISPATCH_MESSAGE( eHTMLCommands_PostURL ); - } - } - else - { - if ( pchPostData && Q_strlen(pchPostData) > 0 ) - { - CHTMLProtoBufMsg cmd( eHTMLCommands_PostURL ); - cmd.Body().set_url( URL ); - cmd.Body().set_post( pchPostData ); - DISPATCH_MESSAGE( eHTMLCommands_PostURL ); - - } - else - { - CHTMLProtoBufMsg cmd( eHTMLCommands_PostURL ); - cmd.Body().set_url( URL ); - DISPATCH_MESSAGE( eHTMLCommands_PostURL ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: opens the URL, will accept any URL that IE accepts -//----------------------------------------------------------------------------- -bool HTML::StopLoading() -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_StopLoad ); - DISPATCH_MESSAGE( eHTMLCommands_StopLoad ); - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: refreshes the current page -//----------------------------------------------------------------------------- -bool HTML::Refresh() -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_Reload ); - DISPATCH_MESSAGE( eHTMLCommands_Reload ); - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Tells the browser control to go back -//----------------------------------------------------------------------------- -void HTML::GoBack() -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_GoBack ); - DISPATCH_MESSAGE( eHTMLCommands_GoBack ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Tells the browser control to go forward -//----------------------------------------------------------------------------- -void HTML::GoForward() -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_GoForward ); - DISPATCH_MESSAGE( eHTMLCommands_GoForward ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Checks if the browser can go back further -//----------------------------------------------------------------------------- -bool HTML::BCanGoBack() -{ - return m_bCanGoBack; -} - - -//----------------------------------------------------------------------------- -// Purpose: Checks if the browser can go forward further -//----------------------------------------------------------------------------- -bool HTML::BCanGoFoward() -{ - return m_bCanGoForward; -} - - -//----------------------------------------------------------------------------- -// Purpose: handle resizing -//----------------------------------------------------------------------------- -void HTML::OnSizeChanged(int wide,int tall) -{ - BaseClass::OnSizeChanged(wide,tall); - UpdateSizeAndScrollBars(); - UpdateCachedHTMLValues(); -#ifdef WIN32 - // under windows we get stuck in the windows message loop pushing out WM_WINDOWPOSCHANGED without returning in the windproc loop - // so we need to manually pump the html dispatching of messages here - if ( surface() && surface()->AccessChromeHTMLController() ) - { - surface()->AccessChromeHTMLController()->RunFrame(); - } -#endif -} - - -//----------------------------------------------------------------------------- -// Purpose: Run javascript in the page -//----------------------------------------------------------------------------- -void HTML::RunJavascript( const char *pchScript ) -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_ExecuteJavaScript ); - cmd.Body().set_script( pchScript ); - DISPATCH_MESSAGE( eHTMLCommands_ExecuteJavaScript ); -} - - - - -//----------------------------------------------------------------------------- -// Purpose: helper to convert UI mouse codes to CEF ones -//----------------------------------------------------------------------------- -int ConvertMouseCodeToCEFCode( MouseCode code ) -{ - switch( code ) - { - case MOUSE_LEFT: - return IInputEventHTML::eButtonLeft; - break; - case MOUSE_RIGHT: - return IInputEventHTML::eButtonRight; - break; - case MOUSE_MIDDLE: - return IInputEventHTML::eButtonMiddle; - break; - default: - return IInputEventHTML::eButtonLeft; - break; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: passes mouse clicks to the control -//----------------------------------------------------------------------------- -void HTML::OnMousePressed(MouseCode code) -{ - m_sDragURL = NULL; - - // mouse4 = back button - if ( code == MOUSE_4 ) - { - PostActionSignal( new KeyValues( "HTMLBackRequested" ) ); - return; - } - if ( code == MOUSE_5 ) - { - PostActionSignal( new KeyValues( "HTMLForwardRequested" ) ); - return; - } - - - if ( code == MOUSE_RIGHT && m_bContextMenuEnabled ) - { - GetLinkAtPosition( m_iMouseX, m_iMouseY ); - Menu::PlaceContextMenu( this, m_pContextMenu ); - return; - } - - // ask for the focus to come to this window - RequestFocus(); - - // now tell the browser about the click - // ignore right clicks if context menu has been disabled - if ( code != MOUSE_RIGHT ) - { - CHTMLProtoBufMsg cmd( eHTMLCommands_MouseDown ); - cmd.Body().set_mouse_button( ConvertMouseCodeToCEFCode( code ) ); - DISPATCH_MESSAGE( eHTMLCommands_MouseDown ); - } - - if ( code == MOUSE_LEFT ) - { - input()->GetCursorPos( m_iDragStartX, m_iDragStartY ); - int htmlx, htmly; - ipanel()->GetAbsPos( GetVPanel(), htmlx, htmly ); - - GetLinkAtPosition( m_iDragStartX - htmlx, m_iDragStartY - htmly ); - - m_bRequestingDragURL = true; - // make sure we get notified when the mouse gets released - if ( !m_sDragURL.IsEmpty() ) - { - input()->SetMouseCapture( GetVPanel() ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: passes mouse up events -//----------------------------------------------------------------------------- -void HTML::OnMouseReleased(MouseCode code) -{ - if ( code == MOUSE_LEFT ) - { - input()->SetMouseCapture( NULL ); - input()->SetCursorOveride( 0 ); - - if ( !m_sDragURL.IsEmpty() && input()->GetMouseOver() != GetVPanel() && input()->GetMouseOver() != NULL ) - { - // post the text as a drag drop to the target panel - KeyValuesAD kv( "DragDrop" ); - if ( ipanel()->RequestInfo( input()->GetMouseOver(), kv ) - && kv->GetPtr( "AcceptPanel" ) != NULL ) - { - VPANEL vpanel = (VPANEL)kv->GetPtr( "AcceptPanel" ); - ivgui()->PostMessage( vpanel, new KeyValues( "DragDrop", "text", m_sDragURL.Get() ), GetVPanel() ); - } - } - m_sDragURL = NULL; - } - - CHTMLProtoBufMsg cmd( eHTMLCommands_MouseUp ); - cmd.Body().set_mouse_button( ConvertMouseCodeToCEFCode( code ) ); - DISPATCH_MESSAGE( eHTMLCommands_MouseUp ); -} - - -//----------------------------------------------------------------------------- -// Purpose: keeps track of where the cursor is -//----------------------------------------------------------------------------- -void HTML::OnCursorMoved(int x,int y) -{ - // Only do this when we are over the current panel - if ( vgui::input()->GetMouseOver() == GetVPanel() ) - { - m_iMouseX = x; - m_iMouseY = y; - - CHTMLProtoBufMsg cmd( eHTMLCommands_MouseMove ); - cmd.Body().set_x( m_iMouseX ); - cmd.Body().set_y( m_iMouseY ); - DISPATCH_MESSAGE( eHTMLCommands_MouseMove ); - } - else if ( !m_sDragURL.IsEmpty() ) - { - if ( input()->GetMouseOver() == NULL ) - { - // we're not over any vgui window, switch to the OS implementation of drag/drop - // BR FIXME -// surface()->StartDragDropText( m_sDragURL ); - m_sDragURL = NULL; - } - } - - if ( !m_sDragURL.IsEmpty() && !input()->GetCursorOveride() ) - { - // if we've dragged far enough (in global coordinates), set to use the drag cursor - int gx, gy; - input()->GetCursorPos( gx, gy ); - if ( abs(m_iDragStartX-gx) + abs(m_iDragStartY-gy) > 3 ) - { -// input()->SetCursorOveride( dc_alias ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: passes double click events to the browser -//----------------------------------------------------------------------------- -void HTML::OnMouseDoublePressed(MouseCode code) -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_MouseDblClick ); - cmd.Body().set_mouse_button( ConvertMouseCodeToCEFCode( code ) ); - DISPATCH_MESSAGE( eHTMLCommands_MouseDblClick ); -} - - -//----------------------------------------------------------------------------- -// Purpose: passes key presses to the browser (we don't current do this) -//----------------------------------------------------------------------------- -void HTML::OnKeyTyped(wchar_t unichar) -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_KeyChar ); - cmd.Body().set_unichar( unichar ); - DISPATCH_MESSAGE( eHTMLCommands_KeyChar ); -} - - -//----------------------------------------------------------------------------- -// Purpose: pop up the find dialog -//----------------------------------------------------------------------------- -void HTML::ShowFindDialog() -{ - IScheme *pClientScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "ClientScheme" ) ); - if ( !pClientScheme ) - return; - - m_pFindBar->SetVisible( true ); - m_pFindBar->RequestFocus(); - m_pFindBar->SetText( "" ); - m_pFindBar->HideCountLabel(); - m_pFindBar->SetHidden( false ); - int x = 0, y = 0, h = 0, w = 0; - m_pFindBar->GetBounds( x, y, w, h ); - m_pFindBar->SetPos( x, -1*h ); - int iSearchInsetY = 0; - const char *resourceString = pClientScheme->GetResourceString( "HTML.SearchInsetY"); - if ( resourceString ) - { - iSearchInsetY = atoi(resourceString); - } - float flAnimationTime = 0.0f; - resourceString = pClientScheme->GetResourceString( "HTML.SearchAnimationTime"); - if ( resourceString ) - { - flAnimationTime = atof(resourceString); - } - - GetAnimationController()->RunAnimationCommand( m_pFindBar, "ypos", iSearchInsetY, 0.0f, flAnimationTime, AnimationController::INTERPOLATOR_LINEAR ); -} - - -//----------------------------------------------------------------------------- -// Purpose: hide the find dialog -//----------------------------------------------------------------------------- -void HTML::HideFindDialog() -{ - IScheme *pClientScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "ClientScheme" ) ); - if ( !pClientScheme ) - return; - - int x = 0, y = 0, h = 0, w = 0; - m_pFindBar->GetBounds( x, y, w, h ); - float flAnimationTime = 0.0f; - const char *resourceString = pClientScheme->GetResourceString( "HTML.SearchAnimationTime"); - if ( resourceString ) - { - flAnimationTime = atof(resourceString); - } - - GetAnimationController()->RunAnimationCommand( m_pFindBar, "ypos", -1*h-5, 0.0f, flAnimationTime, AnimationController::INTERPOLATOR_LINEAR ); - m_pFindBar->SetHidden( true ); - StopFind(); -} - - -//----------------------------------------------------------------------------- -// Purpose: is the find dialog visible? -//----------------------------------------------------------------------------- -bool HTML::FindDialogVisible() -{ - return m_pFindBar->IsVisible() && !m_pFindBar->BIsHidden(); -} - - -//----------------------------------------------------------------------------- -// Purpose: return the bitmask of any modifier keys that are currently down -//----------------------------------------------------------------------------- -int GetKeyModifiers() -{ - // Any time a key is pressed reset modifier list as well - int nModifierCodes = 0; - if( vgui::input()->IsKeyDown( KEY_LCONTROL ) || vgui::input()->IsKeyDown( KEY_RCONTROL ) ) - nModifierCodes |= IInputEventHTML::CrtlDown; - - if( vgui::input()->IsKeyDown( KEY_LALT ) || vgui::input()->IsKeyDown( KEY_RALT ) ) - nModifierCodes |= IInputEventHTML::AltDown; - - if( vgui::input()->IsKeyDown( KEY_LSHIFT ) || vgui::input()->IsKeyDown( KEY_RSHIFT ) ) - nModifierCodes |= IInputEventHTML::ShiftDown; - -#ifdef OSX - // for now pipe through the cmd-key to be like the control key so we get copy/paste - if( vgui::input()->IsKeyDown( KEY_LWIN ) || vgui::input()->IsKeyDown( KEY_RWIN ) ) - nModifierCodes |= IInputEventHTML::CrtlDown; -#endif - - return nModifierCodes; -} - - -//----------------------------------------------------------------------------- -// Purpose: passes key presses to the browser -//----------------------------------------------------------------------------- -void HTML::OnKeyCodeTyped(KeyCode code) -{ - switch( code ) - { - case KEY_PAGEDOWN: - { - int val = _vbar->GetValue(); - val += 200; - _vbar->SetValue(val); - break; - } - case KEY_PAGEUP: - { - int val = _vbar->GetValue(); - val -= 200; - _vbar->SetValue(val); - break; - } - case KEY_F5: - { - Refresh(); - break; - } - case KEY_F: - { - if ( (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ) - || ( IsOSX() && ( input()->IsKeyDown(KEY_LWIN) || input()->IsKeyDown(KEY_RWIN) ) ) ) - { - if ( !FindDialogVisible() ) - { - ShowFindDialog(); - } - else - { - HideFindDialog(); - } - break; - } - } - case KEY_ESCAPE: - { - if ( FindDialogVisible() ) - { - HideFindDialog(); - break; - } - } - case KEY_TAB: - { - if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ) - { - // pass control-tab to parent (through baseclass) - BaseClass::OnKeyTyped( code ); - return; - } - break; - } - } - - CHTMLProtoBufMsg cmd( eHTMLCommands_KeyDown ); - cmd.Body().set_keycode( KeyCode_VGUIToVirtualKey(code) ); - cmd.Body().set_modifiers( GetKeyModifiers() ); - DISPATCH_MESSAGE( eHTMLCommands_KeyDown ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void HTML::OnKeyCodeReleased(KeyCode code) -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_KeyUp ); - cmd.Body().set_keycode( KeyCode_VGUIToVirtualKey(code) ); - cmd.Body().set_modifiers( GetKeyModifiers() ); - DISPATCH_MESSAGE( eHTMLCommands_KeyUp ); -} - - -//----------------------------------------------------------------------------- -// Purpose: scrolls the vertical scroll bar on a web page -//----------------------------------------------------------------------------- -void HTML::OnMouseWheeled(int delta) -{ - if (_vbar && ( ( m_pComboBoxHost && !m_pComboBoxHost->IsVisible() ) ) ) - { - int val = _vbar->GetValue(); - val -= (delta * 100.0/3.0 ); // 100 for every 3 lines matches chromes code - _vbar->SetValue(val); - } - - CHTMLProtoBufMsg cmd( eHTMLCommands_MouseWheel ); - cmd.Body().set_delta( delta ); - DISPATCH_MESSAGE( eHTMLCommands_MouseWheel ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Inserts a custom URL handler -//----------------------------------------------------------------------------- -void HTML::AddCustomURLHandler(const char *customProtocolName, vgui::Panel *target) -{ - int index = m_CustomURLHandlers.AddToTail(); - m_CustomURLHandlers[index].hPanel = target; - Q_strncpy(m_CustomURLHandlers[index].url, customProtocolName, sizeof(m_CustomURLHandlers[index].url)); -} - - -//----------------------------------------------------------------------------- -// Purpose: shared code for sizing the HTML surface window -//----------------------------------------------------------------------------- -void HTML::BrowserResize() -{ - int w,h; - GetSize( w, h ); - int right = 0, bottom = 0; - // TODO::STYLE - /* - IAppearance *pAppearance = GetAppearance(); - int left = 0, top = 0; - if ( pAppearance ) - { - pAppearance->GetInset( left, top, right, bottom ); - } - */ - - if ( m_iWideLastHTMLSize != ( w - m_iScrollBorderX - right ) || m_iTalLastHTMLSize != ( h - m_iScrollBorderY - bottom ) ) - { - m_iWideLastHTMLSize = w - m_iScrollBorderX - right; - m_iTalLastHTMLSize = h - m_iScrollBorderY - bottom; - if ( m_iTalLastHTMLSize <= 0 ) - { - SetTall( 64 ); - m_iTalLastHTMLSize = 64 - bottom; - } - - { - CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserSize ); - cmd.Body().set_width( m_iWideLastHTMLSize ); - cmd.Body().set_height( m_iTalLastHTMLSize ); - DISPATCH_MESSAGE( eHTMLCommands_BrowserSize ); - } - - - // webkit forgets the scroll offset when you resize (it saves the scroll in a DC and a resize throws away the DC) - // so just tell it after the resize - int scrollV = _vbar->GetValue(); - int scrollH = _hbar->GetValue(); - - { - CHTMLProtoBufMsg cmd( eHTMLCommands_SetHorizontalScroll ); - cmd.Body().set_scroll( scrollH ); - DISPATCH_MESSAGE( eHTMLCommands_SetHorizontalScroll ); - } - { - CHTMLProtoBufMsg cmd( eHTMLCommands_SetVerticalScroll ); - cmd.Body().set_scroll( scrollV ); - DISPATCH_MESSAGE( eHTMLCommands_SetVerticalScroll ); - } - } - -} - - -//----------------------------------------------------------------------------- -// Purpose: when a slider moves causes the IE images to re-render itself -//----------------------------------------------------------------------------- -void HTML::OnSliderMoved() -{ - if(_hbar->IsVisible()) - { - int scrollX =_hbar->GetValue(); - CHTMLProtoBufMsg cmd( eHTMLCommands_SetHorizontalScroll ); - cmd.Body().set_scroll( scrollX ); - DISPATCH_MESSAGE( eHTMLCommands_SetHorizontalScroll ); - } - - if(_vbar->IsVisible()) - { - int scrollY=_vbar->GetValue(); - CHTMLProtoBufMsg cmd( eHTMLCommands_SetVerticalScroll ); - cmd.Body().set_scroll( scrollY ); - DISPATCH_MESSAGE( eHTMLCommands_SetVerticalScroll ); - } - - // post a message that the slider has moved - PostActionSignal( new KeyValues( "HTMLSliderMoved" ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -bool HTML::IsScrolledToBottom() -{ - if ( !_vbar->IsVisible() ) - return true; - - return m_scrollVertical.m_nScroll >= m_scrollVertical.m_nMax; -} - - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -bool HTML::IsScrollbarVisible() -{ - return _vbar->IsVisible(); -} - - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void HTML::SetScrollbarsEnabled(bool state) -{ - m_bScrollBarEnabled = state; -} - - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void HTML::SetContextMenuEnabled(bool state) -{ - m_bContextMenuEnabled = state; -} - - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void HTML::SetViewSourceEnabled(bool state) -{ - m_pContextMenu->SetItemVisible( m_nViewSourceAllowedIndex, state ); -} - - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void HTML::NewWindowsOnly( bool state ) -{ - m_bNewWindowsOnly = state; -} - - -//----------------------------------------------------------------------------- -// Purpose: called when our children have finished painting -//----------------------------------------------------------------------------- -void HTML::PostChildPaint() -{ - BaseClass::PostChildPaint(); - // TODO::STYLE - //m_pInteriorPanel->SetPaintAppearanceEnabled( true ); // turn painting back on so the IE hwnd can render this border -} - - -//----------------------------------------------------------------------------- -// Purpose: Adds a custom header to all requests -//----------------------------------------------------------------------------- -void HTML::AddHeader( const char *pchHeader, const char *pchValue ) -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_AddHeader ); - cmd.Body().set_key( pchHeader ); - cmd.Body().set_value( pchValue ); - DISPATCH_MESSAGE( eHTMLCommands_AddHeader ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void HTML::OnSetFocus() -{ - BaseClass::OnSetFocus(); - CHTMLProtoBufMsg cmd( eHTMLCommands_SetFocus ); - cmd.Body().set_focus( true ); - DISPATCH_MESSAGE( eHTMLCommands_SetFocus ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void HTML::OnKillFocus() -{ - if ( vgui::input()->GetFocus() != m_pComboBoxHost->GetVPanel() ) // if its not the menu stealing our focus - BaseClass::OnKillFocus(); - - // Don't clear the actual html focus if a context menu is what took focus - if ( m_pContextMenu->HasFocus() ) - return; - - if ( m_pComboBoxHost->HasFocus() ) - return; - - CHTMLProtoBufMsg cmd( eHTMLCommands_SetFocus ); - cmd.Body().set_focus( false ); - DISPATCH_MESSAGE( eHTMLCommands_SetFocus ); -} - - -//----------------------------------------------------------------------------- -// Purpose: webkit is telling us to use this cursor type -//----------------------------------------------------------------------------- -void HTML::OnCommand( const char *pchCommand ) -{ - if ( !Q_stricmp( pchCommand, "back" ) ) - { - PostActionSignal( new KeyValues( "HTMLBackRequested" ) ); - } - else if ( !Q_stricmp( pchCommand, "forward" ) ) - { - PostActionSignal( new KeyValues( "HTMLForwardRequested" ) ); - } - else if ( !Q_stricmp( pchCommand, "reload" ) ) - { - Refresh(); - } - else if ( !Q_stricmp( pchCommand, "stop" ) ) - { - StopLoading(); - } - else if ( !Q_stricmp( pchCommand, "viewsource" ) ) - { - CHTMLProtoBufMsg cmd( eHTMLCommands_ViewSource ); - DISPATCH_MESSAGE( eHTMLCommands_ViewSource ); - } - else if ( !Q_stricmp( pchCommand, "copy" ) ) - { - CHTMLProtoBufMsg cmd( eHTMLCommands_Copy ); - DISPATCH_MESSAGE( eHTMLCommands_Copy ); - } - else if ( !Q_stricmp( pchCommand, "paste" ) ) - { - CHTMLProtoBufMsg cmd( eHTMLCommands_Paste ); - DISPATCH_MESSAGE( eHTMLCommands_Paste ); - } - else if ( !Q_stricmp( pchCommand, "copyurl" ) ) - { - system()->SetClipboardText( m_sCurrentURL, m_sCurrentURL.Length() ); - } - else if ( !Q_stricmp( pchCommand, "copylink" ) ) - { - int x, y; - m_pContextMenu->GetPos( x, y ); - int htmlx, htmly; - ipanel()->GetAbsPos( GetVPanel(), htmlx, htmly ); - - m_bRequestingCopyLink = true; - GetLinkAtPosition( x - htmlx, y - htmly ); - } - else - BaseClass::OnCommand( pchCommand ); - -} - - -//----------------------------------------------------------------------------- -// Purpose: the control wants us to ask the user what file to load -//----------------------------------------------------------------------------- -void HTML::OnFileSelected( const char *pchSelectedFile ) -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_FileLoadDialogResponse ); - cmd.Body().add_files( pchSelectedFile ); - DISPATCH_MESSAGE( eHTMLCommands_FileLoadDialogResponse ); - m_hFileOpenDialog->Close(); -} - -//----------------------------------------------------------------------------- -// Purpose: called when the user dismissed the file dialog with no selection -//----------------------------------------------------------------------------- -void HTML::OnFileSelectionCancelled() -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_FileLoadDialogResponse ); - DISPATCH_MESSAGE( eHTMLCommands_FileLoadDialogResponse ); - m_hFileOpenDialog->Close(); -} - -//----------------------------------------------------------------------------- -// Purpose: find any text on the html page with this sub string -//----------------------------------------------------------------------------- -void HTML::Find( const char *pchSubStr ) -{ - m_bInFind = false; - if ( m_sLastSearchString == pchSubStr ) // same string as last time, lets fine next - m_bInFind = true; - - m_sLastSearchString = pchSubStr; - - CHTMLProtoBufMsg cmd( eHTMLCommands_Find ); - cmd.Body().set_find( pchSubStr ); - cmd.Body().set_infind( m_bInFind ); - DISPATCH_MESSAGE( eHTMLCommands_Find ); -} - - -//----------------------------------------------------------------------------- -// Purpose: find any text on the html page with this sub string -//----------------------------------------------------------------------------- -void HTML::FindPrevious() -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_Find ); - cmd.Body().set_find( m_sLastSearchString ); - cmd.Body().set_infind( m_bInFind ); - cmd.Body().set_reverse( true ); - DISPATCH_MESSAGE( eHTMLCommands_Find ); -} - - -//----------------------------------------------------------------------------- -// Purpose: find any text on the html page with this sub string -//----------------------------------------------------------------------------- -void HTML::FindNext() -{ - Find( m_sLastSearchString ); -} - - -//----------------------------------------------------------------------------- -// Purpose: stop an outstanding find request -//----------------------------------------------------------------------------- -void HTML::StopFind( ) -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_StopFind ); - DISPATCH_MESSAGE( eHTMLCommands_StopFind ); - - m_bInFind = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: input handler -//----------------------------------------------------------------------------- -void HTML::OnEditNewLine( Panel *pPanel ) -{ - OnTextChanged( pPanel ); -} - - -//-----------------------h------------------------------------------------------ -// Purpose: input handler -//----------------------------------------------------------------------------- -void HTML::OnTextChanged( Panel *pPanel ) -{ - char rgchText[2048]; - m_pFindBar->GetText( rgchText, sizeof( rgchText ) ); - Find( rgchText ); -} - - -//----------------------------------------------------------------------------- -// Purpose: passes mouse clicks to the control -//----------------------------------------------------------------------------- -void HTMLComboBoxHost::OnMousePressed(MouseCode code) -{ - m_pParent->OnMousePressed(code); -} - - -//----------------------------------------------------------------------------- -// Purpose: passes mouse up events -//----------------------------------------------------------------------------- -void HTMLComboBoxHost::OnMouseReleased(MouseCode code) -{ - m_pParent->OnMouseReleased(code); -} - - -//----------------------------------------------------------------------------- -// Purpose: keeps track of where the cursor is -//----------------------------------------------------------------------------- -void HTMLComboBoxHost::OnCursorMoved(int x,int y) -{ - // Only do this when we are over the current panel - if ( vgui::input()->GetMouseOver() == GetVPanel() ) - { - int m_iBrowser = m_pParent->BrowserGetIndex(); - CHTMLProtoBufMsg cmd( eHTMLCommands_MouseMove ); - cmd.Body().set_x( x ); - cmd.Body().set_y( y ); - DISPATCH_MESSAGE( eHTMLCommands_MouseMove ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: passes double click events to the browser -//----------------------------------------------------------------------------- -void HTMLComboBoxHost::OnMouseDoublePressed(MouseCode code) -{ - m_pParent->OnMouseDoublePressed(code); -} - - -//----------------------------------------------------------------------------- -// Purpose: passes key presses to the browser (we don't current do this) -//----------------------------------------------------------------------------- -void HTMLComboBoxHost::OnKeyTyped(wchar_t unichar) -{ - m_pParent->OnKeyTyped(unichar); -} - - -//----------------------------------------------------------------------------- -// Purpose: passes key presses to the browser -//----------------------------------------------------------------------------- -void HTMLComboBoxHost::OnKeyCodeTyped(KeyCode code) -{ - m_pParent->OnKeyCodeTyped(code); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void HTMLComboBoxHost::OnKeyCodeReleased(KeyCode code) -{ - m_pParent->OnKeyCodeReleased(code); -} - - -//----------------------------------------------------------------------------- -// Purpose: scrolls the vertical scroll bar on a web page -//----------------------------------------------------------------------------- -void HTMLComboBoxHost::OnMouseWheeled(int delta) -{ - m_pParent->OnMouseWheeled( delta ); -} - - -//----------------------------------------------------------------------------- -// Purpose: helper class for the find bar -//----------------------------------------------------------------------------- -HTML::CHTMLFindBar::CHTMLFindBar( HTML *parent ) : EditablePanel( parent, "FindBar" ) -{ - m_pParent = parent; - m_bHidden = false; - m_pFindBar = new TextEntry( this, "FindEntry" ); - m_pFindBar->AddActionSignalTarget( parent ); - m_pFindBar->SendNewLine( true ); - m_pFindCountLabel = new Label( this, "FindCount", "" ); - m_pFindCountLabel->SetVisible( false ); - LoadControlSettings( "resource/layout/htmlfindbar.layout" ); -} - - -//----------------------------------------------------------------------------- -// Purpose: button input into the find bar -//----------------------------------------------------------------------------- -void HTML::CHTMLFindBar::OnCommand( const char *pchCmd ) -{ - if ( !Q_stricmp( pchCmd, "close" ) ) - { - m_pParent->HideFindDialog(); - } - else if ( !Q_stricmp( pchCmd, "previous" ) ) - { - m_pParent->FindPrevious(); - } - else if ( !Q_stricmp( pchCmd, "next" ) ) - { - m_pParent->FindNext(); - } - else - BaseClass::OnCommand( pchCmd ); - -} - -//----------------------------------------------------------------------------- -// Purpose: TEMPORARY WORKAROUND FOR VS2005 INTERFACE ISSUES -//----------------------------------------------------------------------------- -#define TMP_HTML_MSG_FUNC( eHTMLCommand, bodyType, commandFunc ) \ - case eHTMLCommand: \ - { \ - CHTMLProtoBufMsg< bodyType > cmd( pCmd->m_eCmd ); \ - if ( cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ) \ - commandFunc( &cmd.BodyConst() ); \ - } \ - break; \ - -void HTML::_DeserializeAndDispatch( HTMLCommandBuffer_t *pCmd ) -{ - switch ( pCmd->m_eCmd ) - { - default: - break; - TMP_HTML_MSG_FUNC( eHTMLCommands_BrowserReady, CMsgBrowserReady, BrowserReady ); - TMP_HTML_MSG_FUNC( eHTMLCommands_NeedsPaint, CMsgNeedsPaint, BrowserNeedsPaint ); - TMP_HTML_MSG_FUNC( eHTMLCommands_StartRequest, CMsgStartRequest, BrowserStartRequest ); - TMP_HTML_MSG_FUNC( eHTMLCommands_URLChanged, CMsgURLChanged, BrowserURLChanged ); - TMP_HTML_MSG_FUNC( eHTMLCommands_FinishedRequest, CMsgFinishedRequest, BrowserFinishedRequest ); - TMP_HTML_MSG_FUNC( eHTMLCommands_ShowPopup, CMsgShowPopup, BrowserShowPopup ); - TMP_HTML_MSG_FUNC( eHTMLCommands_HidePopup, CMsgHidePopup, BrowserHidePopup ); - TMP_HTML_MSG_FUNC( eHTMLCommands_OpenNewTab, CMsgOpenNewTab, BrowserOpenNewTab ); - TMP_HTML_MSG_FUNC( eHTMLCommands_PopupHTMLWindow, CMsgPopupHTMLWindow, BrowserPopupHTMLWindow ); - TMP_HTML_MSG_FUNC( eHTMLCommands_SetHTMLTitle, CMsgSetHTMLTitle, BrowserSetHTMLTitle ); - TMP_HTML_MSG_FUNC( eHTMLCommands_LoadingResource, CMsgLoadingResource, BrowserLoadingResource ); - TMP_HTML_MSG_FUNC( eHTMLCommands_StatusText, CMsgStatusText, BrowserStatusText ); - TMP_HTML_MSG_FUNC( eHTMLCommands_SetCursor, CMsgSetCursor, BrowserSetCursor ); - TMP_HTML_MSG_FUNC( eHTMLCommands_FileLoadDialog, CMsgFileLoadDialog, BrowserFileLoadDialog ); - TMP_HTML_MSG_FUNC( eHTMLCommands_ShowToolTip, CMsgShowToolTip, BrowserShowToolTip ); - TMP_HTML_MSG_FUNC( eHTMLCommands_UpdateToolTip, CMsgUpdateToolTip, BrowserUpdateToolTip ); - TMP_HTML_MSG_FUNC( eHTMLCommands_HideToolTip, CMsgHideToolTip, BrowserHideToolTip ); - TMP_HTML_MSG_FUNC( eHTMLCommands_SearchResults, CMsgSearchResults, BrowserSearchResults ); - TMP_HTML_MSG_FUNC( eHTMLCommands_Close, CMsgClose, BrowserClose ); - TMP_HTML_MSG_FUNC( eHTMLCommands_GetZoomResponse, CMsgGetZoomResponse, BrowserGetZoomResponse ); - TMP_HTML_MSG_FUNC( eHTMLCommands_HorizontalScrollBarSizeResponse, CMsgHorizontalScrollBarSizeResponse, BrowserHorizontalScrollBarSizeResponse ); - TMP_HTML_MSG_FUNC( eHTMLCommands_VerticalScrollBarSizeResponse, CMsgVerticalScrollBarSizeResponse, BrowserVerticalScrollBarSizeResponse ); - TMP_HTML_MSG_FUNC( eHTMLCommands_LinkAtPositionResponse, CMsgLinkAtPositionResponse, BrowserLinkAtPositionResponse ); - TMP_HTML_MSG_FUNC( eHTMLCommands_ZoomToElementAtPositionResponse, CMsgZoomToElementAtPositionResponse, BrowserZoomToElementAtPositionResponse ); - TMP_HTML_MSG_FUNC( eHTMLCommands_JSAlert, CMsgJSAlert, BrowserJSAlert ); - TMP_HTML_MSG_FUNC( eHTMLCommands_JSConfirm, CMsgJSConfirm, BrowserJSConfirm ); - TMP_HTML_MSG_FUNC( eHTMLCommands_OpenSteamURL, CMsgOpenSteamURL, BrowserOpenSteamURL ); - TMP_HTML_MSG_FUNC( eHTMLCommands_CanGoBackandForward, CMsgCanGoBackAndForward, BrowserCanGoBackandForward ); - TMP_HTML_MSG_FUNC( eHTMLCommands_SizePopup, CMsgSizePopup, BrowserSizePopup ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: browser has been constructed on the cef thread, lets use it -//----------------------------------------------------------------------------- -void HTML::BrowserReady( const CMsgBrowserReady *pCmd ) -{ - const char *pchTitle = g_pVGuiLocalize->FindAsUTF8( "#cef_error_title" ); - const char *pchHeader = g_pVGuiLocalize->FindAsUTF8( "#cef_error_header" ); - const char *pchDetailCacheMiss = g_pVGuiLocalize->FindAsUTF8( "#cef_cachemiss" ); - const char *pchDetailBadUURL = g_pVGuiLocalize->FindAsUTF8( "#cef_badurl" ); - const char *pchDetailConnectionProblem = g_pVGuiLocalize->FindAsUTF8( "#cef_connectionproblem" ); - const char *pchDetailProxyProblem = g_pVGuiLocalize->FindAsUTF8( "#cef_proxyconnectionproblem" ); - const char *pchDetailUnknown = g_pVGuiLocalize->FindAsUTF8( "#cef_unknown" ); - - // tell it utf8 loc strings to use - CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserErrorStrings ); - cmd.Body().set_title( pchTitle ); - cmd.Body().set_header( pchHeader ); - cmd.Body().set_cache_miss( pchDetailCacheMiss ); - cmd.Body().set_bad_url( pchDetailBadUURL ); - cmd.Body().set_connection_problem( pchDetailConnectionProblem ); - cmd.Body().set_proxy_problem( pchDetailProxyProblem ); - cmd.Body().set_unknown( pchDetailUnknown ); - DISPATCH_MESSAGE( eHTMLCommands_BrowserErrorStrings ); - - if ( !m_sPendingURLLoad.IsEmpty() ) - { - PostURL( m_sPendingURLLoad, m_sPendingPostData, false ); - m_sPendingURLLoad.Clear(); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: we have a new texture to update -//----------------------------------------------------------------------------- -void HTML::BrowserNeedsPaint( const CMsgNeedsPaint *pCmd ) -{ - int tw = 0, tt = 0; - if ( m_iHTMLTextureID != 0 ) - { - tw = m_allocedTextureWidth; - tt = m_allocedTextureHeight; - } - - if ( m_iHTMLTextureID != 0 && ( ( _vbar->IsVisible() && pCmd->scrolly() > 0 && abs( (int)pCmd->scrolly() - m_scrollVertical.m_nScroll) > 5 ) || ( _hbar->IsVisible() && pCmd->scrollx() > 0 && abs( (int)pCmd->scrollx() - m_scrollHorizontal.m_nScroll ) > 5 ) ) ) - { - // this isn't an update from a scroll position we expect, ignore it and ask for a refresh of our update pos2 - CHTMLProtoBufMsg cmd( eHTMLCommands_NeedsPaintResponse ); - cmd.Body().set_textureid( pCmd->textureid() ); - DISPATCH_MESSAGE( eHTMLCommands_NeedsPaintResponse ); - m_bNeedsFullTextureUpload = true; - return; - } - - // update the vgui texture - if ( m_bNeedsFullTextureUpload || m_iHTMLTextureID == 0 || tw != (int)pCmd->wide() || tt != (int)pCmd->tall() ) - { - m_bNeedsFullTextureUpload = false; - if ( m_iHTMLTextureID != 0 ) - surface()->DeleteTextureByID( m_iHTMLTextureID ); - - // if the dimensions changed we also need to re-create the texture ID to support the overlay properly (it won't resize a texture on the fly, this is the only control that needs - // to so lets have a tiny bit more code here to support that) - m_iHTMLTextureID = surface()->CreateNewTextureID( true ); - surface()->DrawSetTextureRGBAEx( m_iHTMLTextureID, (const unsigned char *)pCmd->rgba(), pCmd->wide(), pCmd->tall(), IMAGE_FORMAT_BGRA8888 );// BR FIXME - this call seems to shift by some number of pixels? - m_allocedTextureWidth = pCmd->wide(); - m_allocedTextureHeight = pCmd->tall(); - } - else if ( (int)pCmd->updatewide() > 0 && (int)pCmd->updatetall() > 0 ) - { - // same size texture, just bits changing in it, lets twiddle - surface()->DrawUpdateRegionTextureRGBA( m_iHTMLTextureID, pCmd->updatex(), pCmd->updatey(), (const unsigned char *)pCmd->rgba(), pCmd->updatewide(), pCmd->updatetall(), IMAGE_FORMAT_BGRA8888 ); - } - else - { - surface()->DrawSetTextureRGBAEx( m_iHTMLTextureID, (const unsigned char *)pCmd->rgba(), pCmd->wide(), pCmd->tall(), IMAGE_FORMAT_BGRA8888 ); - } - - if ( m_pComboBoxHost->IsVisible() ) - { - // update the combo box texture also - if ( m_iComboBoxTextureID != 0 ) - { - tw = m_allocedComboBoxWidth; - tt = m_allocedComboBoxHeight; - } - - if ( m_iComboBoxTextureID == 0 || tw != (int)pCmd->combobox_wide() || tt != (int)pCmd->combobox_tall() ) - { - if ( m_iComboBoxTextureID != 0 ) - surface()->DeleteTextureByID( m_iComboBoxTextureID ); - - // if the dimensions changed we also need to re-create the texture ID to support the overlay properly (it won't resize a texture on the fly, this is the only control that needs - // to so lets have a tiny bit more code here to support that) - m_iComboBoxTextureID = surface()->CreateNewTextureID( true ); - surface()->DrawSetTextureRGBAEx( m_iComboBoxTextureID, (const unsigned char *)pCmd->combobox_rgba(), pCmd->combobox_wide(), pCmd->combobox_tall(), IMAGE_FORMAT_BGRA8888 ); - m_allocedComboBoxWidth = (int)pCmd->combobox_wide(); - m_allocedComboBoxHeight = (int)pCmd->combobox_tall(); - } - else - { - // same size texture, just bits changing in it, lets twiddle - surface()->DrawUpdateRegionTextureRGBA( m_iComboBoxTextureID, 0, 0, (const unsigned char *)pCmd->combobox_rgba(), pCmd->combobox_wide(), pCmd->combobox_tall(), IMAGE_FORMAT_BGRA8888 ); - } - } - - // need a paint next time - Repaint(); - - CHTMLProtoBufMsg cmd( eHTMLCommands_NeedsPaintResponse ); - cmd.Body().set_textureid( pCmd->textureid() ); - DISPATCH_MESSAGE( eHTMLCommands_NeedsPaintResponse ); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser wants to start loading this url, do we let it? -//----------------------------------------------------------------------------- -bool HTML::OnStartRequest( const char *url, const char *target, const char *pchPostData, bool bIsRedirect ) -{ - if ( !url || !Q_stricmp( url, "about:blank") ) - return true ; // this is just webkit loading a new frames contents inside an existing page - - HideFindDialog(); - // see if we have a custom handler for this - bool bURLHandled = false; - for (int i = 0; i < m_CustomURLHandlers.Count(); i++) - { - if (!Q_strnicmp(m_CustomURLHandlers[i].url,url, Q_strlen(m_CustomURLHandlers[i].url))) - { - // we have a custom handler - Panel *targetPanel = m_CustomURLHandlers[i].hPanel; - if (targetPanel) - { - PostMessage(targetPanel, new KeyValues("CustomURL", "url", m_CustomURLHandlers[i].url ) ); - } - - bURLHandled = true; - } - } - - if (bURLHandled) - return false; - - if ( m_bNewWindowsOnly && bIsRedirect ) - { - if ( target && ( !Q_stricmp( target, "_blank" ) || !Q_stricmp( target, "_new" ) ) ) // only allow NEW windows (_blank ones) - { - return true; - } - else - { - return false; - } - } - - if ( target && !Q_strlen( target ) ) - { - m_sCurrentURL = url; - - KeyValues *pMessage = new KeyValues( "OnURLChanged" ); - pMessage->SetString( "url", url ); - pMessage->SetString( "postdata", pchPostData ); - pMessage->SetInt( "isredirect", bIsRedirect ? 1 : 0 ); - - PostActionSignal( pMessage ); - } - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: callback from cef thread, load a url please -//----------------------------------------------------------------------------- -void HTML::BrowserStartRequest( const CMsgStartRequest *pCmd ) -{ - bool bRes = OnStartRequest( pCmd->url().c_str(), pCmd->target().c_str(), pCmd->postdata().c_str(), pCmd->bisredirect() ); - - CHTMLProtoBufMsg cmd( eHTMLCommands_StartRequestResponse ); - cmd.Body().set_ballow( bRes ); - DISPATCH_MESSAGE( eHTMLCommands_StartRequestResponse ); - - UpdateCachedHTMLValues(); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser went to a new url -//----------------------------------------------------------------------------- -void HTML::BrowserURLChanged( const CMsgURLChanged *pCmd ) -{ - m_sCurrentURL = pCmd->url().c_str(); - - KeyValues *pMessage = new KeyValues( "OnURLChanged" ); - pMessage->SetString( "url", pCmd->url().c_str() ); - pMessage->SetString( "postdata", pCmd->postdata().c_str() ); - pMessage->SetInt( "isredirect", pCmd->bisredirect() ? 1 : 0 ); - - PostActionSignal( pMessage ); - - OnURLChanged( m_sCurrentURL, pCmd->postdata().c_str(), pCmd->bisredirect() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: finished loading this page -//----------------------------------------------------------------------------- -void HTML::BrowserFinishedRequest( const CMsgFinishedRequest *pCmd ) -{ - PostActionSignal( new KeyValues( "OnFinishRequest", "url", pCmd->url().c_str() ) ); - if ( pCmd->pagetitle().length() ) - PostActionSignal( new KeyValues( "PageTitleChange", "title", pCmd->pagetitle().c_str() ) ); - KeyValues *pKVSecure = new KeyValues( "SecurityStatus" ); - pKVSecure->SetString( "url", pCmd->url().c_str() ); - pKVSecure->SetInt( "secure", pCmd->security_info().bissecure() ); - pKVSecure->SetInt( "certerror", pCmd->security_info().bhascerterror() ); - pKVSecure->SetInt( "isevcert", pCmd->security_info().bisevcert() ); - pKVSecure->SetString( "certname", pCmd->security_info().certname().c_str() ); - PostActionSignal( pKVSecure ); - - CUtlMap < CUtlString, CUtlString > mapHeaders; - SetDefLessFunc( mapHeaders ); - for ( int i = 0; i < pCmd->headers_size(); i++ ) - { - const CHTMLHeader &header = pCmd->headers(i); - mapHeaders.Insert( header.key().c_str(), header.value().c_str() ); - } - - OnFinishRequest( pCmd->url().c_str(), pCmd->pagetitle().c_str(), mapHeaders ); - - UpdateCachedHTMLValues(); -} - - -//----------------------------------------------------------------------------- -// Purpose: show a popup dialog -//----------------------------------------------------------------------------- -void HTML::BrowserShowPopup( const CMsgShowPopup *pCmd ) -{ - m_pComboBoxHost->SetVisible( true ); -} - - -//----------------------------------------------------------------------------- -// Purpose: hide the popup -//----------------------------------------------------------------------------- -void HTML::HidePopup() -{ - m_pComboBoxHost->SetVisible( false ); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser wants us to hide a popup -//----------------------------------------------------------------------------- -void HTML::BrowserHidePopup( const CMsgHidePopup *pCmd ) -{ - HidePopup(); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser wants us to position a popup -//----------------------------------------------------------------------------- -void HTML::BrowserSizePopup( const CMsgSizePopup *pCmd ) -{ - int nAbsX, nAbsY; - ipanel()->GetAbsPos( GetVPanel(), nAbsX, nAbsY ); - m_pComboBoxHost->SetBounds( pCmd->x() + 1 + nAbsX, pCmd->y() + nAbsY, pCmd->wide(), pCmd->tall() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser wants to open a new tab -//----------------------------------------------------------------------------- -void HTML::BrowserOpenNewTab( const CMsgOpenNewTab *pCmd ) -{ - (pCmd); - // Not suppored by default, if a child class overrides us and knows how to handle tabs, then it can do this. -} - - -//----------------------------------------------------------------------------- -// Purpose: display a new html window -//----------------------------------------------------------------------------- -void HTML::BrowserPopupHTMLWindow( const CMsgPopupHTMLWindow *pCmd ) -{ - HTMLPopup *p = new HTMLPopup( this, pCmd->url().c_str(), "" ); - int wide = pCmd->wide(); - int tall = pCmd->tall(); - if ( wide == 0 || tall == 0 ) - { - wide = MAX( 640, GetWide() ); - tall = MAX( 480, GetTall() ); - } - - p->SetBounds( pCmd->x(), pCmd->y(), wide, tall ); - p->SetDeleteSelfOnClose( true ); - if ( pCmd->x() == 0 || pCmd->y() == 0 ) - p->MoveToCenterOfScreen(); - p->Activate(); - -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us the page title -//----------------------------------------------------------------------------- -void HTML::BrowserSetHTMLTitle( const CMsgSetHTMLTitle *pCmd ) -{ - PostMessage( GetParent(), new KeyValues( "OnSetHTMLTitle", "title", pCmd->title().c_str() ) ); - OnSetHTMLTitle( pCmd->title().c_str() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: still loading stuff for this page -//----------------------------------------------------------------------------- -void HTML::BrowserLoadingResource( const CMsgLoadingResource *pCmd ) -{ - UpdateCachedHTMLValues(); -} - - -//----------------------------------------------------------------------------- -// Purpose: status bar details -//----------------------------------------------------------------------------- -void HTML::BrowserStatusText( const CMsgStatusText *pCmd ) -{ - PostActionSignal( new KeyValues( "OnSetStatusText", "status", pCmd->text().c_str() ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us to use this cursor -//----------------------------------------------------------------------------- -void HTML::BrowserSetCursor( const CMsgSetCursor *pCmd ) -{ - // Mouse cursor value in CMsgSetCursor is set to one of EMouseCursor, - // by CChromePainter::OnSetCursor in html_chrome.cpp - // Code below relies on start of EMouseCursor being exactly same as vgui::CursorCode - - vgui::CursorCode cursor; - uint32 msgCursor = pCmd->cursor(); - - if ( msgCursor >= (uint32)(dc_last) ) - { - cursor = dc_arrow; - } - else - { - cursor = (CursorCode)msgCursor; - } - - SetCursor( cursor ); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling to show the file loading dialog -//----------------------------------------------------------------------------- -void HTML::BrowserFileLoadDialog( const CMsgFileLoadDialog *pCmd ) -{ - /* - // try and use the OS-specific file dialog first - char rgchFileName[MAX_UNICODE_PATH_IN_UTF8]; - if ( surface()->OpenOSFileOpenDialog( pCmd->title().c_str(), pCmd->initialfile().c_str(), NULL, rgchFileName, sizeof(rgchFileName) ) ) - { - CHTMLProtoBufMsg cmd( eHTMLCommands_FileLoadDialogResponse ); - cmd.Body().add_files( rgchFileName ); - DISPATCH_MESSAGE( eHTMLCommands_FileLoadDialogResponse ); - } - else*/ - { - // couldn't access an OS-specific dialog, use the internal one - if ( m_hFileOpenDialog.Get() ) - { - delete m_hFileOpenDialog.Get(); - m_hFileOpenDialog = NULL; - } - m_hFileOpenDialog = new FileOpenDialog( this, pCmd->title().c_str(), true ); - m_hFileOpenDialog->SetStartDirectory( pCmd->initialfile().c_str() ); - m_hFileOpenDialog->AddActionSignalTarget( this ); - m_hFileOpenDialog->SetAutoDelete( true ); - m_hFileOpenDialog->DoModal(false); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: browser asking to show a tooltip -//----------------------------------------------------------------------------- -void HTML::BrowserShowToolTip( const CMsgShowToolTip *pCmd ) -{ - /* - BR FIXME - Tooltip *tip = GetTooltip(); - tip->SetText( pCmd->text().c_str() ); - tip->SetTooltipFormatToMultiLine(); - tip->SetTooltipDelayMS( 250 ); - tip->SetMaxToolTipWidth( MAX( 200, GetWide()/2 ) ); - tip->ShowTooltip( this ); - */ -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us to update tool tip text -//----------------------------------------------------------------------------- -void HTML::BrowserUpdateToolTip( const CMsgUpdateToolTip *pCmd ) -{ -// GetTooltip()->SetText( pCmd->text().c_str() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling that it is done with the tip -//----------------------------------------------------------------------------- -void HTML::BrowserHideToolTip( const CMsgHideToolTip *pCmd ) -{ -// GetTooltip()->HideTooltip(); -// DeleteToolTip(); -} - - -//----------------------------------------------------------------------------- -// Purpose: callback when performing a search -//----------------------------------------------------------------------------- -void HTML::BrowserSearchResults( const CMsgSearchResults *pCmd ) -{ - if ( pCmd->results() == 0 ) - m_pFindBar->HideCountLabel(); - else - m_pFindBar->ShowCountLabel(); - - if ( pCmd->results() > 0 ) - m_pFindBar->SetDialogVariable( "findcount", (int)pCmd->results() ); - if ( pCmd->activematch() > 0 ) - m_pFindBar->SetDialogVariable( "findactive", (int)pCmd->activematch() ); - m_pFindBar->InvalidateLayout(); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us it had a close requested -//----------------------------------------------------------------------------- -void HTML::BrowserClose( const CMsgClose *pCmd ) -{ - PostActionSignal( new KeyValues( "OnCloseWindow" ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us the size of the horizontal scrollbars -//----------------------------------------------------------------------------- -void HTML::BrowserHorizontalScrollBarSizeResponse( const CMsgHorizontalScrollBarSizeResponse *pCmd ) -{ - ScrollData_t scrollHorizontal; - scrollHorizontal.m_nX = pCmd->x(); - scrollHorizontal.m_nY = pCmd->y(); - scrollHorizontal.m_nWide = pCmd->wide(); - scrollHorizontal.m_nTall = pCmd->tall(); - scrollHorizontal.m_nScroll = pCmd->scroll(); - scrollHorizontal.m_nMax = pCmd->scroll_max(); - scrollHorizontal.m_bVisible = ( m_scrollHorizontal.m_nTall > 0 ); - scrollHorizontal.m_flZoom = pCmd->zoom(); - - if ( scrollHorizontal != m_scrollHorizontal ) - { - m_scrollHorizontal = scrollHorizontal; - UpdateSizeAndScrollBars(); - m_bNeedsFullTextureUpload = true; - } - else - m_scrollHorizontal = scrollHorizontal; -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us the size of the vertical scrollbars -//----------------------------------------------------------------------------- -void HTML::BrowserVerticalScrollBarSizeResponse( const CMsgVerticalScrollBarSizeResponse *pCmd ) -{ - ScrollData_t scrollVertical; - scrollVertical.m_nX = pCmd->x(); - scrollVertical.m_nY = pCmd->y(); - scrollVertical.m_nWide = pCmd->wide(); - scrollVertical.m_nTall = pCmd->tall(); - scrollVertical.m_nScroll = pCmd->scroll(); - scrollVertical.m_nMax = pCmd->scroll_max(); - scrollVertical.m_bVisible = ( m_scrollVertical.m_nTall > 0 ); - scrollVertical.m_flZoom = pCmd->zoom(); - - if ( scrollVertical != m_scrollVertical ) - { - m_scrollVertical = scrollVertical; - UpdateSizeAndScrollBars(); - m_bNeedsFullTextureUpload = true; - } - else - m_scrollVertical = scrollVertical; -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us the current page zoom -//----------------------------------------------------------------------------- -void HTML::BrowserGetZoomResponse( const CMsgGetZoomResponse *pCmd ) -{ - m_flZoom = pCmd->zoom(); - if ( m_flZoom == 0.0f ) - m_flZoom = 100.0f; - m_flZoom /= 100; // scale zoom to 1.0 being 100%, webkit gives us 100 for normal scale - -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us what is at this location on the page -//----------------------------------------------------------------------------- -void HTML::BrowserLinkAtPositionResponse( const CMsgLinkAtPositionResponse *pCmd ) -{ - m_LinkAtPos.m_sURL = pCmd->url().c_str(); - m_LinkAtPos.m_nX = pCmd->x(); - m_LinkAtPos.m_nY = pCmd->y(); - - m_pContextMenu->SetItemVisible( m_iCopyLinkMenuItemID, !m_LinkAtPos.m_sURL.IsEmpty() ? true : false ); - if ( m_bRequestingDragURL ) - { - m_bRequestingDragURL = false; - m_sDragURL = m_LinkAtPos.m_sURL; - // make sure we get notified when the mouse gets released - if ( !m_sDragURL.IsEmpty() ) - { - input()->SetMouseCapture( GetVPanel() ); - } - } - - if ( m_bRequestingCopyLink ) - { - m_bRequestingCopyLink = false; - if ( !m_LinkAtPos.m_sURL.IsEmpty() ) - system()->SetClipboardText( m_LinkAtPos.m_sURL, m_LinkAtPos.m_sURL.Length() ); - else - system()->SetClipboardText( "", 1 ); - } - - OnLinkAtPosition( m_LinkAtPos.m_sURL ); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us that a zoom to element is done -//----------------------------------------------------------------------------- -void HTML::BrowserZoomToElementAtPositionResponse( const CMsgZoomToElementAtPositionResponse *pCmd ) -{ - -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us to pop a javascript alert dialog -//----------------------------------------------------------------------------- -void HTML::BrowserJSAlert( const CMsgJSAlert *pCmd ) -{ - MessageBox *pDlg = new MessageBox( m_sCurrentURL, (const char *)pCmd->message().c_str(), this ); - pDlg->AddActionSignalTarget( this ); - pDlg->SetCommand( new KeyValues( "DismissJSDialog", "result", false ) ); - pDlg->DoModal(); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us to pop a js confirm dialog -//----------------------------------------------------------------------------- -void HTML::BrowserJSConfirm( const CMsgJSConfirm *pCmd ) -{ - QueryBox *pDlg = new QueryBox( m_sCurrentURL, (const char *)pCmd->message().c_str(), this ); - pDlg->AddActionSignalTarget( this ); - pDlg->SetOKCommand( new KeyValues( "DismissJSDialog", "result", true ) ); - pDlg->SetCancelCommand( new KeyValues( "DismissJSDialog", "result", false ) ); - pDlg->DoModal(); -} - - -//----------------------------------------------------------------------------- -// Purpose: got an answer from the dialog, tell cef -//----------------------------------------------------------------------------- -void HTML::DismissJSDialog( int bResult ) -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_JSDialogResponse ); - cmd.Body().set_result( bResult==1 ); - DISPATCH_MESSAGE( eHTMLCommands_JSDialogResponse ); -}; - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us the state of back and forward buttons -//----------------------------------------------------------------------------- -void HTML::BrowserCanGoBackandForward( const CMsgCanGoBackAndForward *pCmd ) -{ - m_bCanGoBack = pCmd->bgoback(); - m_bCanGoForward = pCmd->bgoforward(); -} - - -//----------------------------------------------------------------------------- -// Purpose: browser telling us a steam url was asked to be loaded -//----------------------------------------------------------------------------- -void HTML::BrowserOpenSteamURL( const CMsgOpenSteamURL *pCmd ) -{ - vgui::ivgui()->PostMessage( surface()->GetEmbeddedPanel(), - new KeyValues( "OpenSteamDialog", "cmd", pCmd->url().c_str() ), NULL, 0.3f ); -} - - -//----------------------------------------------------------------------------- -// Purpose: update the value of the cached variables we keep -//----------------------------------------------------------------------------- -void HTML::UpdateCachedHTMLValues() -{ - // request scroll bar sizes - { - CHTMLProtoBufMsg cmd( eHTMLCommands_VerticalScrollBarSize ); - DISPATCH_MESSAGE( eHTMLCommands_VerticalScrollBarSize ); - } - { - CHTMLProtoBufMsg cmd( eHTMLCommands_HorizontalScrollBarSize ); - DISPATCH_MESSAGE( eHTMLCommands_HorizontalScrollBarSize ); - } - { - CHTMLProtoBufMsg cmd( eHTMLCommands_GetZoom ); - DISPATCH_MESSAGE( eHTMLCommands_GetZoom ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: ask the browser for what is at this x,y -//----------------------------------------------------------------------------- -void HTML::GetLinkAtPosition( int x, int y ) -{ - CHTMLProtoBufMsg cmd( eHTMLCommands_LinkAtPosition ); - cmd.Body().set_x( x ); - cmd.Body().set_y( y ); - DISPATCH_MESSAGE( eHTMLCommands_LinkAtPosition ); -} - -//----------------------------------------------------------------------------- -// Purpose: send any queued html messages we have -//----------------------------------------------------------------------------- -void HTML::SendPendingHTMLMessages() -{ - FOR_EACH_VEC( m_vecPendingMessages, i ) - { - m_vecPendingMessages[i]->m_iBrowser = m_iBrowser; - surface()->AccessChromeHTMLController()->PushCommand( m_vecPendingMessages[i] ); - surface()->AccessChromeHTMLController()->WakeThread(); - } - m_vecPendingMessages.RemoveAll(); -} - -//----------------------------------------------------------------------------- -// Purpose: update the size of the browser itself and scrollbars it shows -//----------------------------------------------------------------------------- -void HTML::UpdateSizeAndScrollBars() -{ - // Tell IE - BrowserResize(); - - // Do this after we tell IE! - int w,h; - GetSize( w, h ); - CalcScrollBars(w,h); - - InvalidateLayout(); -} - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// This class is a message box that has two buttons, ok and cancel instead of +// just the ok button of a message box. We use a message box class for the ok button +// and implement another button here. +// +// $NoKeywords: $ +//=============================================================================// + +#include "vgui_controls/pch_vgui_controls.h" +#include +#include +#include + +#include +#include +#include "htmlmessages.pb.h" +#include +#include + +#include "html/ipainthtml.h" +#include "html/ihtmlchrome.h" +#include "html/ichromehtmlwrapper.h" +#include "html/htmlprotobuf.h" + +#include "filesystem.h" +#include "../vgui2/src/vgui_key_translation.h" + +#undef PostMessage +#undef MessageBox + +#include "OfflineMode.h" + +// memdbgon must be the last include file in a .cpp file +#include "tier0/memdbgon.h" + +using namespace vgui; + +// helper to send IPC messages to the CEF thread +#define DISPATCH_MESSAGE( eCmd ) \ + if (surface()->AccessChromeHTMLController()) \ + { \ + cmd.Body().set_browser_handle( m_iBrowser );\ + HTMLCommandBuffer_t *pBuf = surface()->AccessChromeHTMLController()->GetFreeCommandBuffer( eCmd, m_iBrowser ); \ + cmd.SerializeCrossProc( &pBuf->m_Buffer ); \ + if ( m_iBrowser == -1 ) { m_vecPendingMessages.AddToTail( pBuf ); } \ + else \ + { \ + surface()->AccessChromeHTMLController()->PushCommand( pBuf ); \ + surface()->AccessChromeHTMLController()->WakeThread(); \ + }\ + } \ + +const int k_nMaxCustomCursors = 2; // the max number of custom cursors we keep cached PER html control + +//----------------------------------------------------------------------------- +// Purpose: A simple passthrough panel to render the border onto the HTML widget +//----------------------------------------------------------------------------- +class HTMLInterior : public Panel +{ + DECLARE_CLASS_SIMPLE( HTMLInterior, Panel ); +public: + HTMLInterior( HTML *parent ) : BaseClass( parent, "HTMLInterior" ) + { + m_pHTML = parent; + SetPaintBackgroundEnabled( false ); + SetKeyBoardInputEnabled( false ); + SetMouseInputEnabled( false ); + } + +private: + HTML *m_pHTML; +}; + + +//----------------------------------------------------------------------------- +// Purpose: a vgui container for popup menus displayed by a control, only 1 menu for any control can be visible at a time +//----------------------------------------------------------------------------- +class HTMLComboBoxHost : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( HTMLComboBoxHost, EditablePanel ); +public: + HTMLComboBoxHost( HTML *parent, const char *panelName ) : EditablePanel( parent, panelName ) + { + m_pParent = parent; + MakePopup(false); + } + ~HTMLComboBoxHost() {} + + virtual void PaintBackground(); + + virtual void OnMousePressed(MouseCode code); + virtual void OnMouseReleased(MouseCode code); + virtual void OnCursorMoved(int x,int y); + virtual void OnMouseDoublePressed(MouseCode code); + virtual void OnKeyTyped(wchar_t unichar); + virtual void OnKeyCodeTyped(KeyCode code); + virtual void OnKeyCodeReleased(KeyCode code); + virtual void OnMouseWheeled(int delta); + + virtual void OnKillFocus() + { + if ( vgui::input()->GetFocus() != m_pParent->GetVPanel() ) // if its not our parent trying to steal focus + { + BaseClass::OnKillFocus(); + if ( m_pParent ) + m_pParent->HidePopup(); + } + } + + virtual void PerformLayout() + { + // no op the perform layout as we just render the html controls popup texture into it + // we don't want the menu logic trying to play with its size + } + + +private: + HTML *m_pParent; + CUtlVector m_vecPendingMessages; +}; + + +//----------------------------------------------------------------------------- +// Purpose: container class for any external popup windows the browser requests +//----------------------------------------------------------------------------- +class HTMLPopup : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( HTMLPopup, vgui::Frame ); + class PopupHTML : public vgui::HTML + { + DECLARE_CLASS_SIMPLE( PopupHTML, vgui::HTML ); + public: + PopupHTML( Frame *parent, const char *pchName, bool allowJavaScript , bool bPopupWindow ) : HTML( parent, pchName, allowJavaScript, bPopupWindow ) { m_pParent = parent; } + + virtual void OnSetHTMLTitle( const char *pchTitle ) + { + BaseClass::OnSetHTMLTitle( pchTitle ); + m_pParent->SetTitle( pchTitle, true ); + } + + private: + Frame *m_pParent; + }; +public: + HTMLPopup( Panel *parent, const char *pchURL, const char *pchTitle ) : Frame( NULL, "HtmlPopup", true ) + { + m_pHTML = new PopupHTML( this, "htmlpopupchild", true, true ); + m_pHTML->OpenURL( pchURL, NULL, false ); + SetTitle( pchTitle, true ); + } + + ~HTMLPopup() + { + } + + enum + { + vert_inset = 40, + horiz_inset = 6 + }; + + void PerformLayout() + { + BaseClass::PerformLayout(); + int wide, tall; + GetSize( wide, tall ); + m_pHTML->SetPos( horiz_inset, vert_inset ); + m_pHTML->SetSize( wide - horiz_inset*2, tall - vert_inset*2 ); + } + + void SetBounds( int x, int y, int wide, int tall ) + { + BaseClass::SetBounds( x, y, wide + horiz_inset*2, tall + vert_inset*2 ); + } + + MESSAGE_FUNC( OnCloseWindow, "OnCloseWindow" ) + { + Close(); + } +private: + PopupHTML *m_pHTML; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +HTML::HTML(Panel *parent, const char *name, bool allowJavaScript, bool bPopupWindow) : Panel(parent, name) +{ + m_iHTMLTextureID = 0; + m_iComboBoxTextureID = 0; + m_bCanGoBack = false; + m_bCanGoForward = false; + m_bInFind = false; + m_bRequestingDragURL = false; + m_bRequestingCopyLink = false; + m_flZoom = 100.0f; + m_iBrowser = -1; + m_bNeedsFullTextureUpload = false; + + m_pInteriorPanel = new HTMLInterior( this ); + SetPostChildPaintEnabled( true ); + if (surface() && surface()->AccessChromeHTMLController()) + { + surface()->AccessChromeHTMLController()->CreateBrowser( this, bPopupWindow, surface()->GetWebkitHTMLUserAgentString() ); + } + else + { + Warning("Unable to access ChromeHTMLController"); + } + m_iScrollBorderX=m_iScrollBorderY=0; + m_bScrollBarEnabled = true; + m_bContextMenuEnabled = true; + m_bNewWindowsOnly = false; + m_iMouseX = m_iMouseY = 0; + m_iDragStartX = m_iDragStartY = 0; + m_nViewSourceAllowedIndex = -1; + m_iWideLastHTMLSize = m_iTalLastHTMLSize = 0; + + _hbar = new ScrollBar(this, "HorizScrollBar", false); + _hbar->SetVisible(false); + _hbar->AddActionSignalTarget(this); + + _vbar = new ScrollBar(this, "VertScrollBar", true); + _vbar->SetVisible(false); + _vbar->AddActionSignalTarget(this); + + m_pFindBar = new HTML::CHTMLFindBar( this ); + m_pFindBar->SetZPos( 2 ); + m_pFindBar->SetVisible( false ); + + m_pComboBoxHost = new HTMLComboBoxHost( this, "ComboBoxHost" ); + m_pComboBoxHost->SetPaintBackgroundEnabled( true ); + m_pComboBoxHost->SetVisible( false ); + + m_pContextMenu = new Menu( this, "contextmenu" ); + m_pContextMenu->AddMenuItem( "#vgui_HTMLBack", new KeyValues( "Command", "command", "back" ), this ); + m_pContextMenu->AddMenuItem( "#vgui_HTMLForward", new KeyValues( "Command", "command", "forward" ), this ); + m_pContextMenu->AddMenuItem( "#vgui_HTMLReload", new KeyValues( "Command", "command", "reload" ), this ); + m_pContextMenu->AddMenuItem( "#vgui_HTMLStop", new KeyValues( "Command", "command", "stop" ), this ); + m_pContextMenu->AddSeparator(); + m_pContextMenu->AddMenuItem( "#vgui_HTMLCopyUrl", new KeyValues( "Command", "command", "copyurl" ), this ); + m_iCopyLinkMenuItemID = m_pContextMenu->AddMenuItem( "#vgui_HTMLCopyLink", new KeyValues( "Command", "command", "copylink" ), this ); + m_pContextMenu->AddMenuItem( "#TextEntry_Copy", new KeyValues( "Command", "command", "copy" ), this ); + m_pContextMenu->AddMenuItem( "#TextEntry_Paste", new KeyValues( "Command", "command", "paste" ), this ); + m_pContextMenu->AddSeparator(); + m_nViewSourceAllowedIndex = m_pContextMenu->AddMenuItem( "#vgui_HTMLViewSource", new KeyValues( "Command", "command", "viewsource" ), this ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +HTML::~HTML() +{ + m_pContextMenu->MarkForDeletion(); + + if (surface()->AccessChromeHTMLController()) + { + surface()->AccessChromeHTMLController()->RemoveBrowser( this ); + } + + FOR_EACH_VEC( m_vecHCursor, i ) + { + // BR FIXME! +// surface()->DeleteCursor( m_vecHCursor[i].m_Cursor ); + } + m_vecHCursor.RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Handle message to change our cursor +//----------------------------------------------------------------------------- +void HTML::OnSetCursorVGUI( int cursor ) +{ + SetCursor( (HCursor)cursor ); +} + +//----------------------------------------------------------------------------- +// Purpose: sets up colors/fonts/borders +//----------------------------------------------------------------------------- +void HTML::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + BrowserResize(); +} + + +//----------------------------------------------------------------------------- +// Purpose: overrides panel class, paints a texture of the HTML window as a background +//----------------------------------------------------------------------------- +void HTML::Paint() +{ + //VPROF_BUDGET( "HTML::Paint()", VPROF_BUDGETGROUP_OTHER_VGUI ); + BaseClass::Paint(); + + if ( m_iHTMLTextureID != 0 ) + { + surface()->DrawSetTexture( m_iHTMLTextureID ); + int tw = 0, tt = 0; + surface()->DrawSetColor( Color( 255, 255, 255, 255 ) ); + GetSize( tw, tt ); + surface()->DrawTexturedRect( 0, 0, tw, tt ); + } + + // If we have scrollbars, we need to draw the bg color under them, since the browser + // bitmap is a checkerboard under them, and they are transparent in the in-game client + if ( m_iScrollBorderX > 0 || m_iScrollBorderY > 0 ) + { + int w, h; + GetSize( w, h ); + IBorder *border = GetBorder(); + int left = 0, top = 0, right = 0, bottom = 0; + if ( border ) + { + border->GetInset( left, top, right, bottom ); + } + surface()->DrawSetColor( GetBgColor() ); + if ( m_iScrollBorderX ) + { + surface()->DrawFilledRect( w-m_iScrollBorderX - right, top, w, h - bottom ); + } + if ( m_iScrollBorderY ) + { + surface()->DrawFilledRect( left, h-m_iScrollBorderY - bottom, w-m_iScrollBorderX - right, h ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: paint the combo box texture if we have one +//----------------------------------------------------------------------------- +void HTML::PaintComboBox() +{ + BaseClass::Paint(); + if ( m_iComboBoxTextureID != 0 ) + { + surface()->DrawSetTexture( m_iComboBoxTextureID ); + surface()->DrawSetColor( Color( 255, 255, 255, 255 ) ); + int tw = m_allocedComboBoxWidth; + int tt = m_allocedComboBoxHeight; + surface()->DrawTexturedRect( 0, 0, tw, tt ); + } + +} + + +//----------------------------------------------------------------------------- +// Purpose: overrides panel class, paints a texture of the HTML window as a background +//----------------------------------------------------------------------------- +void HTMLComboBoxHost::PaintBackground() +{ + BaseClass::PaintBackground(); + + m_pParent->PaintComboBox(); +} + + +//----------------------------------------------------------------------------- +// Purpose: causes a repaint when the layout changes +//----------------------------------------------------------------------------- +void HTML::PerformLayout() +{ + BaseClass::PerformLayout(); + Repaint(); + int vbarInset = _vbar->IsVisible() ? _vbar->GetWide() : 0; + int maxw = GetWide() - vbarInset; + m_pInteriorPanel->SetBounds( 0, 0, maxw, GetTall() ); + + IScheme *pClientScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "ClientScheme" ) ); + + int iSearchInsetY = 5; + int iSearchInsetX = 5; + int iSearchTall = 24; + int iSearchWide = 150; + const char *resourceString = pClientScheme->GetResourceString( "HTML.SearchInsetY"); + if ( resourceString ) + { + iSearchInsetY = atoi(resourceString); + } + resourceString = pClientScheme->GetResourceString( "HTML.SearchInsetX"); + if ( resourceString ) + { + iSearchInsetX = atoi(resourceString); + } + resourceString = pClientScheme->GetResourceString( "HTML.SearchTall"); + if ( resourceString ) + { + iSearchTall = atoi(resourceString); + } + resourceString = pClientScheme->GetResourceString( "HTML.SearchWide"); + if ( resourceString ) + { + iSearchWide = atoi(resourceString); + } + + m_pFindBar->SetBounds( GetWide() - iSearchWide - iSearchInsetX - vbarInset, m_pFindBar->BIsHidden() ? -1*iSearchTall-5: iSearchInsetY, iSearchWide, iSearchTall ); +} + + +//----------------------------------------------------------------------------- +// Purpose: updates the underlying HTML surface widgets position +//----------------------------------------------------------------------------- +void HTML::OnMove() +{ + BaseClass::OnMove(); + + // tell cef where we are on the screen so plugins can correctly render + int nPanelAbsX, nPanelAbsY; + ipanel()->GetAbsPos( GetVPanel(), nPanelAbsX, nPanelAbsY ); + CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserPosition ); + cmd.Body().set_x( nPanelAbsX ); + cmd.Body().set_y( nPanelAbsY ); + DISPATCH_MESSAGE( eHTMLCommands_BrowserPosition ); + + if ( m_pComboBoxHost && m_pComboBoxHost->IsVisible() ) + { + m_pComboBoxHost->SetVisible( false ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: calculates the need for and position of both horizontal and vertical scroll bars +//----------------------------------------------------------------------------- +void HTML::CalcScrollBars(int w, int h) +{ + bool bScrollbarVisible = _vbar->IsVisible(); + + if ( m_bScrollBarEnabled ) + { + for ( int i = 0; i < 2; i++ ) + { + int scrollx, scrolly, scrollwide, scrolltall; + bool bVisible = false; + if ( i==0 ) + { + scrollx = m_scrollHorizontal.m_nX; + scrolly = m_scrollHorizontal.m_nY; + scrollwide = m_scrollHorizontal.m_nWide; + scrolltall = m_scrollHorizontal.m_nTall; + bVisible = m_scrollHorizontal.m_bVisible; + + // scrollbar positioning tweaks - should be moved into a resource file + scrollwide += 14; + scrolltall += 5; + } + else + { + scrollx = m_scrollVertical.m_nX; + scrolly = m_scrollVertical.m_nY; + scrollwide = m_scrollVertical.m_nWide; + scrolltall = m_scrollVertical.m_nTall; + bVisible = m_scrollVertical.m_bVisible; + + // scrollbar positioning tweaks - should be moved into a resource file + //scrollx -= 3; + if ( m_scrollHorizontal.m_bVisible ) + scrolltall += 16; + else + scrolltall -= 2; + + scrollwide += 5; + } + + if ( bVisible && scrollwide && scrolltall ) + { + int panelWide, panelTall; + GetSize( panelWide, panelTall ); + + ScrollBar *bar = _vbar; + if ( i == 0 ) + bar = _hbar; + + if (!bar->IsVisible()) + { + bar->SetVisible(true); + // displayable area has changed, need to force an update + PostMessage(this, new KeyValues("OnSliderMoved"), 0.02f); + } + + int rangeWindow = panelTall - scrollwide; + if ( i==0 ) + rangeWindow = panelWide - scrolltall; + int range = m_scrollVertical.m_nMax + m_scrollVertical.m_nTall; + if ( i == 0 ) + range = m_scrollHorizontal.m_nMax + m_scrollVertical.m_nWide; + int curValue = m_scrollVertical.m_nScroll; + if ( i == 0 ) + curValue = m_scrollHorizontal.m_nScroll; + + bar->SetEnabled(false); + bar->SetRangeWindow( rangeWindow ); + bar->SetRange( 0, range ); // we want the range [0.. (img_h - h)], but the scrollbar actually returns [0..(range-rangeWindow)] so make sure -h gets deducted from the max range value + bar->SetButtonPressedScrollValue( 5 ); + if ( curValue > ( bar->GetValue() + 5 ) || curValue < (bar->GetValue() - 5 ) ) + bar->SetValue( curValue ); + + if ( i == 0 ) + { + bar->SetPos( 0, h - scrolltall - 1 ); + bar->SetWide( scrollwide ); + bar->SetTall( scrolltall ); + } + else + { + bar->SetPos( w - scrollwide, 0 ); + bar->SetTall( scrolltall ); + bar->SetWide( scrollwide ); + } + + if ( i == 0 ) + m_iScrollBorderY=scrolltall; + else + m_iScrollBorderX=scrollwide; + } + else + { + if ( i == 0 ) + { + m_iScrollBorderY=0; + _hbar->SetVisible( false ); + } + else + { + m_iScrollBorderX=0; + _vbar->SetVisible( false ); + + } + } + } + } + else + { + m_iScrollBorderX = 0; + m_iScrollBorderY=0; + _vbar->SetVisible(false); + _hbar->SetVisible(false); + } + + if ( bScrollbarVisible != _vbar->IsVisible() ) + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Purpose: opens the URL, will accept any URL that IE accepts +//----------------------------------------------------------------------------- +void HTML::OpenURL(const char *URL, const char *postData, bool force) +{ + PostURL( URL, postData, force ); +} + +//----------------------------------------------------------------------------- +// Purpose: opens the URL, will accept any URL that IE accepts +//----------------------------------------------------------------------------- +void HTML::PostURL(const char *URL, const char *pchPostData, bool force) +{ + if ( m_iBrowser < 0 ) + { + m_sPendingURLLoad = URL; + m_sPendingPostData = pchPostData; + return; + } + + if ( IsSteamInOfflineMode() && !force ) + { + const char *baseDir = getenv("HTML_OFFLINE_DIR"); + if ( baseDir ) + { + // get the app we need to run + char htmlLocation[_MAX_PATH]; + char otherName[128]; + char fileLocation[_MAX_PATH]; + + if ( ! g_pFullFileSystem->FileExists( baseDir ) ) + { + Q_snprintf( otherName, sizeof(otherName), "%senglish.html", OFFLINE_FILE ); + baseDir = otherName; + } + g_pFullFileSystem->GetLocalCopy( baseDir ); // put this file on disk for IE to load + + g_pFullFileSystem->GetLocalPath( baseDir, fileLocation, sizeof(fileLocation) ); + Q_snprintf(htmlLocation, sizeof(htmlLocation), "file://%s", fileLocation); + + CHTMLProtoBufMsg cmd( eHTMLCommands_PostURL ); + cmd.Body().set_url( htmlLocation ); + DISPATCH_MESSAGE( eHTMLCommands_PostURL ); + + } + else + { + CHTMLProtoBufMsg cmd( eHTMLCommands_PostURL ); + cmd.Body().set_url( URL ); + DISPATCH_MESSAGE( eHTMLCommands_PostURL ); + } + } + else + { + if ( pchPostData && Q_strlen(pchPostData) > 0 ) + { + CHTMLProtoBufMsg cmd( eHTMLCommands_PostURL ); + cmd.Body().set_url( URL ); + cmd.Body().set_post( pchPostData ); + DISPATCH_MESSAGE( eHTMLCommands_PostURL ); + + } + else + { + CHTMLProtoBufMsg cmd( eHTMLCommands_PostURL ); + cmd.Body().set_url( URL ); + DISPATCH_MESSAGE( eHTMLCommands_PostURL ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: opens the URL, will accept any URL that IE accepts +//----------------------------------------------------------------------------- +bool HTML::StopLoading() +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_StopLoad ); + DISPATCH_MESSAGE( eHTMLCommands_StopLoad ); + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes the current page +//----------------------------------------------------------------------------- +bool HTML::Refresh() +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_Reload ); + DISPATCH_MESSAGE( eHTMLCommands_Reload ); + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Tells the browser control to go back +//----------------------------------------------------------------------------- +void HTML::GoBack() +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_GoBack ); + DISPATCH_MESSAGE( eHTMLCommands_GoBack ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Tells the browser control to go forward +//----------------------------------------------------------------------------- +void HTML::GoForward() +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_GoForward ); + DISPATCH_MESSAGE( eHTMLCommands_GoForward ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Checks if the browser can go back further +//----------------------------------------------------------------------------- +bool HTML::BCanGoBack() +{ + return m_bCanGoBack; +} + + +//----------------------------------------------------------------------------- +// Purpose: Checks if the browser can go forward further +//----------------------------------------------------------------------------- +bool HTML::BCanGoFoward() +{ + return m_bCanGoForward; +} + + +//----------------------------------------------------------------------------- +// Purpose: handle resizing +//----------------------------------------------------------------------------- +void HTML::OnSizeChanged(int wide,int tall) +{ + BaseClass::OnSizeChanged(wide,tall); + UpdateSizeAndScrollBars(); + UpdateCachedHTMLValues(); +#ifdef WIN32 + // under windows we get stuck in the windows message loop pushing out WM_WINDOWPOSCHANGED without returning in the windproc loop + // so we need to manually pump the html dispatching of messages here + if ( surface() && surface()->AccessChromeHTMLController() ) + { + surface()->AccessChromeHTMLController()->RunFrame(); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: Run javascript in the page +//----------------------------------------------------------------------------- +void HTML::RunJavascript( const char *pchScript ) +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_ExecuteJavaScript ); + cmd.Body().set_script( pchScript ); + DISPATCH_MESSAGE( eHTMLCommands_ExecuteJavaScript ); +} + + + + +//----------------------------------------------------------------------------- +// Purpose: helper to convert UI mouse codes to CEF ones +//----------------------------------------------------------------------------- +int ConvertMouseCodeToCEFCode( MouseCode code ) +{ + switch( code ) + { + case MOUSE_LEFT: + return IInputEventHTML::eButtonLeft; + break; + case MOUSE_RIGHT: + return IInputEventHTML::eButtonRight; + break; + case MOUSE_MIDDLE: + return IInputEventHTML::eButtonMiddle; + break; + default: + return IInputEventHTML::eButtonLeft; + break; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: passes mouse clicks to the control +//----------------------------------------------------------------------------- +void HTML::OnMousePressed(MouseCode code) +{ + m_sDragURL = NULL; + + // mouse4 = back button + if ( code == MOUSE_4 ) + { + PostActionSignal( new KeyValues( "HTMLBackRequested" ) ); + return; + } + if ( code == MOUSE_5 ) + { + PostActionSignal( new KeyValues( "HTMLForwardRequested" ) ); + return; + } + + + if ( code == MOUSE_RIGHT && m_bContextMenuEnabled ) + { + GetLinkAtPosition( m_iMouseX, m_iMouseY ); + Menu::PlaceContextMenu( this, m_pContextMenu ); + return; + } + + // ask for the focus to come to this window + RequestFocus(); + + // now tell the browser about the click + // ignore right clicks if context menu has been disabled + if ( code != MOUSE_RIGHT ) + { + CHTMLProtoBufMsg cmd( eHTMLCommands_MouseDown ); + cmd.Body().set_mouse_button( ConvertMouseCodeToCEFCode( code ) ); + DISPATCH_MESSAGE( eHTMLCommands_MouseDown ); + } + + if ( code == MOUSE_LEFT ) + { + input()->GetCursorPos( m_iDragStartX, m_iDragStartY ); + int htmlx, htmly; + ipanel()->GetAbsPos( GetVPanel(), htmlx, htmly ); + + GetLinkAtPosition( m_iDragStartX - htmlx, m_iDragStartY - htmly ); + + m_bRequestingDragURL = true; + // make sure we get notified when the mouse gets released + if ( !m_sDragURL.IsEmpty() ) + { + input()->SetMouseCapture( GetVPanel() ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: passes mouse up events +//----------------------------------------------------------------------------- +void HTML::OnMouseReleased(MouseCode code) +{ + if ( code == MOUSE_LEFT ) + { + input()->SetMouseCapture( NULL ); + input()->SetCursorOveride( 0 ); + + if ( !m_sDragURL.IsEmpty() && input()->GetMouseOver() != GetVPanel() && input()->GetMouseOver() != NULL ) + { + // post the text as a drag drop to the target panel + KeyValuesAD kv( "DragDrop" ); + if ( ipanel()->RequestInfo( input()->GetMouseOver(), kv ) + && kv->GetPtr( "AcceptPanel" ) != NULL ) + { + VPANEL vpanel = (VPANEL)kv->GetPtr( "AcceptPanel" ); + ivgui()->PostMessage( vpanel, new KeyValues( "DragDrop", "text", m_sDragURL.Get() ), GetVPanel() ); + } + } + m_sDragURL = NULL; + } + + CHTMLProtoBufMsg cmd( eHTMLCommands_MouseUp ); + cmd.Body().set_mouse_button( ConvertMouseCodeToCEFCode( code ) ); + DISPATCH_MESSAGE( eHTMLCommands_MouseUp ); +} + + +//----------------------------------------------------------------------------- +// Purpose: keeps track of where the cursor is +//----------------------------------------------------------------------------- +void HTML::OnCursorMoved(int x,int y) +{ + // Only do this when we are over the current panel + if ( vgui::input()->GetMouseOver() == GetVPanel() ) + { + m_iMouseX = x; + m_iMouseY = y; + + CHTMLProtoBufMsg cmd( eHTMLCommands_MouseMove ); + cmd.Body().set_x( m_iMouseX ); + cmd.Body().set_y( m_iMouseY ); + DISPATCH_MESSAGE( eHTMLCommands_MouseMove ); + } + else if ( !m_sDragURL.IsEmpty() ) + { + if ( input()->GetMouseOver() == NULL ) + { + // we're not over any vgui window, switch to the OS implementation of drag/drop + // BR FIXME +// surface()->StartDragDropText( m_sDragURL ); + m_sDragURL = NULL; + } + } + + if ( !m_sDragURL.IsEmpty() && !input()->GetCursorOveride() ) + { + // if we've dragged far enough (in global coordinates), set to use the drag cursor + int gx, gy; + input()->GetCursorPos( gx, gy ); + if ( abs(m_iDragStartX-gx) + abs(m_iDragStartY-gy) > 3 ) + { +// input()->SetCursorOveride( dc_alias ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: passes double click events to the browser +//----------------------------------------------------------------------------- +void HTML::OnMouseDoublePressed(MouseCode code) +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_MouseDblClick ); + cmd.Body().set_mouse_button( ConvertMouseCodeToCEFCode( code ) ); + DISPATCH_MESSAGE( eHTMLCommands_MouseDblClick ); +} + + +//----------------------------------------------------------------------------- +// Purpose: passes key presses to the browser (we don't current do this) +//----------------------------------------------------------------------------- +void HTML::OnKeyTyped(wchar_t unichar) +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_KeyChar ); + cmd.Body().set_unichar( unichar ); + DISPATCH_MESSAGE( eHTMLCommands_KeyChar ); +} + + +//----------------------------------------------------------------------------- +// Purpose: pop up the find dialog +//----------------------------------------------------------------------------- +void HTML::ShowFindDialog() +{ + IScheme *pClientScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "ClientScheme" ) ); + if ( !pClientScheme ) + return; + + m_pFindBar->SetVisible( true ); + m_pFindBar->RequestFocus(); + m_pFindBar->SetText( "" ); + m_pFindBar->HideCountLabel(); + m_pFindBar->SetHidden( false ); + int x = 0, y = 0, h = 0, w = 0; + m_pFindBar->GetBounds( x, y, w, h ); + m_pFindBar->SetPos( x, -1*h ); + int iSearchInsetY = 0; + const char *resourceString = pClientScheme->GetResourceString( "HTML.SearchInsetY"); + if ( resourceString ) + { + iSearchInsetY = atoi(resourceString); + } + float flAnimationTime = 0.0f; + resourceString = pClientScheme->GetResourceString( "HTML.SearchAnimationTime"); + if ( resourceString ) + { + flAnimationTime = atof(resourceString); + } + + GetAnimationController()->RunAnimationCommand( m_pFindBar, "ypos", iSearchInsetY, 0.0f, flAnimationTime, AnimationController::INTERPOLATOR_LINEAR ); +} + + +//----------------------------------------------------------------------------- +// Purpose: hide the find dialog +//----------------------------------------------------------------------------- +void HTML::HideFindDialog() +{ + IScheme *pClientScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "ClientScheme" ) ); + if ( !pClientScheme ) + return; + + int x = 0, y = 0, h = 0, w = 0; + m_pFindBar->GetBounds( x, y, w, h ); + float flAnimationTime = 0.0f; + const char *resourceString = pClientScheme->GetResourceString( "HTML.SearchAnimationTime"); + if ( resourceString ) + { + flAnimationTime = atof(resourceString); + } + + GetAnimationController()->RunAnimationCommand( m_pFindBar, "ypos", -1*h-5, 0.0f, flAnimationTime, AnimationController::INTERPOLATOR_LINEAR ); + m_pFindBar->SetHidden( true ); + StopFind(); +} + + +//----------------------------------------------------------------------------- +// Purpose: is the find dialog visible? +//----------------------------------------------------------------------------- +bool HTML::FindDialogVisible() +{ + return m_pFindBar->IsVisible() && !m_pFindBar->BIsHidden(); +} + + +//----------------------------------------------------------------------------- +// Purpose: return the bitmask of any modifier keys that are currently down +//----------------------------------------------------------------------------- +int GetKeyModifiers() +{ + // Any time a key is pressed reset modifier list as well + int nModifierCodes = 0; + if( vgui::input()->IsKeyDown( KEY_LCONTROL ) || vgui::input()->IsKeyDown( KEY_RCONTROL ) ) + nModifierCodes |= IInputEventHTML::CrtlDown; + + if( vgui::input()->IsKeyDown( KEY_LALT ) || vgui::input()->IsKeyDown( KEY_RALT ) ) + nModifierCodes |= IInputEventHTML::AltDown; + + if( vgui::input()->IsKeyDown( KEY_LSHIFT ) || vgui::input()->IsKeyDown( KEY_RSHIFT ) ) + nModifierCodes |= IInputEventHTML::ShiftDown; + +#ifdef OSX + // for now pipe through the cmd-key to be like the control key so we get copy/paste + if( vgui::input()->IsKeyDown( KEY_LWIN ) || vgui::input()->IsKeyDown( KEY_RWIN ) ) + nModifierCodes |= IInputEventHTML::CrtlDown; +#endif + + return nModifierCodes; +} + + +//----------------------------------------------------------------------------- +// Purpose: passes key presses to the browser +//----------------------------------------------------------------------------- +void HTML::OnKeyCodeTyped(KeyCode code) +{ + switch( code ) + { + case KEY_PAGEDOWN: + { + int val = _vbar->GetValue(); + val += 200; + _vbar->SetValue(val); + break; + } + case KEY_PAGEUP: + { + int val = _vbar->GetValue(); + val -= 200; + _vbar->SetValue(val); + break; + } + case KEY_F5: + { + Refresh(); + break; + } + case KEY_F: + { + if ( (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ) + || ( IsOSX() && ( input()->IsKeyDown(KEY_LWIN) || input()->IsKeyDown(KEY_RWIN) ) ) ) + { + if ( !FindDialogVisible() ) + { + ShowFindDialog(); + } + else + { + HideFindDialog(); + } + break; + } + } + case KEY_ESCAPE: + { + if ( FindDialogVisible() ) + { + HideFindDialog(); + break; + } + } + case KEY_TAB: + { + if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ) + { + // pass control-tab to parent (through baseclass) + BaseClass::OnKeyTyped( code ); + return; + } + break; + } + } + + CHTMLProtoBufMsg cmd( eHTMLCommands_KeyDown ); + cmd.Body().set_keycode( KeyCode_VGUIToVirtualKey(code) ); + cmd.Body().set_modifiers( GetKeyModifiers() ); + DISPATCH_MESSAGE( eHTMLCommands_KeyDown ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void HTML::OnKeyCodeReleased(KeyCode code) +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_KeyUp ); + cmd.Body().set_keycode( KeyCode_VGUIToVirtualKey(code) ); + cmd.Body().set_modifiers( GetKeyModifiers() ); + DISPATCH_MESSAGE( eHTMLCommands_KeyUp ); +} + + +//----------------------------------------------------------------------------- +// Purpose: scrolls the vertical scroll bar on a web page +//----------------------------------------------------------------------------- +void HTML::OnMouseWheeled(int delta) +{ + if (_vbar && ( ( m_pComboBoxHost && !m_pComboBoxHost->IsVisible() ) ) ) + { + int val = _vbar->GetValue(); + val -= (delta * 100.0/3.0 ); // 100 for every 3 lines matches chromes code + _vbar->SetValue(val); + } + + CHTMLProtoBufMsg cmd( eHTMLCommands_MouseWheel ); + cmd.Body().set_delta( delta ); + DISPATCH_MESSAGE( eHTMLCommands_MouseWheel ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Inserts a custom URL handler +//----------------------------------------------------------------------------- +void HTML::AddCustomURLHandler(const char *customProtocolName, vgui::Panel *target) +{ + int index = m_CustomURLHandlers.AddToTail(); + m_CustomURLHandlers[index].hPanel = target; + Q_strncpy(m_CustomURLHandlers[index].url, customProtocolName, sizeof(m_CustomURLHandlers[index].url)); +} + + +//----------------------------------------------------------------------------- +// Purpose: shared code for sizing the HTML surface window +//----------------------------------------------------------------------------- +void HTML::BrowserResize() +{ + int w,h; + GetSize( w, h ); + int right = 0, bottom = 0; + // TODO::STYLE + /* + IAppearance *pAppearance = GetAppearance(); + int left = 0, top = 0; + if ( pAppearance ) + { + pAppearance->GetInset( left, top, right, bottom ); + } + */ + + if ( m_iWideLastHTMLSize != ( w - m_iScrollBorderX - right ) || m_iTalLastHTMLSize != ( h - m_iScrollBorderY - bottom ) ) + { + m_iWideLastHTMLSize = w - m_iScrollBorderX - right; + m_iTalLastHTMLSize = h - m_iScrollBorderY - bottom; + if ( m_iTalLastHTMLSize <= 0 ) + { + SetTall( 64 ); + m_iTalLastHTMLSize = 64 - bottom; + } + + { + CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserSize ); + cmd.Body().set_width( m_iWideLastHTMLSize ); + cmd.Body().set_height( m_iTalLastHTMLSize ); + DISPATCH_MESSAGE( eHTMLCommands_BrowserSize ); + } + + + // webkit forgets the scroll offset when you resize (it saves the scroll in a DC and a resize throws away the DC) + // so just tell it after the resize + int scrollV = _vbar->GetValue(); + int scrollH = _hbar->GetValue(); + + { + CHTMLProtoBufMsg cmd( eHTMLCommands_SetHorizontalScroll ); + cmd.Body().set_scroll( scrollH ); + DISPATCH_MESSAGE( eHTMLCommands_SetHorizontalScroll ); + } + { + CHTMLProtoBufMsg cmd( eHTMLCommands_SetVerticalScroll ); + cmd.Body().set_scroll( scrollV ); + DISPATCH_MESSAGE( eHTMLCommands_SetVerticalScroll ); + } + } + +} + + +//----------------------------------------------------------------------------- +// Purpose: when a slider moves causes the IE images to re-render itself +//----------------------------------------------------------------------------- +void HTML::OnSliderMoved() +{ + if(_hbar->IsVisible()) + { + int scrollX =_hbar->GetValue(); + CHTMLProtoBufMsg cmd( eHTMLCommands_SetHorizontalScroll ); + cmd.Body().set_scroll( scrollX ); + DISPATCH_MESSAGE( eHTMLCommands_SetHorizontalScroll ); + } + + if(_vbar->IsVisible()) + { + int scrollY=_vbar->GetValue(); + CHTMLProtoBufMsg cmd( eHTMLCommands_SetVerticalScroll ); + cmd.Body().set_scroll( scrollY ); + DISPATCH_MESSAGE( eHTMLCommands_SetVerticalScroll ); + } + + // post a message that the slider has moved + PostActionSignal( new KeyValues( "HTMLSliderMoved" ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool HTML::IsScrolledToBottom() +{ + if ( !_vbar->IsVisible() ) + return true; + + return m_scrollVertical.m_nScroll >= m_scrollVertical.m_nMax; +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool HTML::IsScrollbarVisible() +{ + return _vbar->IsVisible(); +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void HTML::SetScrollbarsEnabled(bool state) +{ + m_bScrollBarEnabled = state; +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void HTML::SetContextMenuEnabled(bool state) +{ + m_bContextMenuEnabled = state; +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void HTML::SetViewSourceEnabled(bool state) +{ + m_pContextMenu->SetItemVisible( m_nViewSourceAllowedIndex, state ); +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void HTML::NewWindowsOnly( bool state ) +{ + m_bNewWindowsOnly = state; +} + + +//----------------------------------------------------------------------------- +// Purpose: called when our children have finished painting +//----------------------------------------------------------------------------- +void HTML::PostChildPaint() +{ + BaseClass::PostChildPaint(); + // TODO::STYLE + //m_pInteriorPanel->SetPaintAppearanceEnabled( true ); // turn painting back on so the IE hwnd can render this border +} + + +//----------------------------------------------------------------------------- +// Purpose: Adds a custom header to all requests +//----------------------------------------------------------------------------- +void HTML::AddHeader( const char *pchHeader, const char *pchValue ) +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_AddHeader ); + cmd.Body().set_key( pchHeader ); + cmd.Body().set_value( pchValue ); + DISPATCH_MESSAGE( eHTMLCommands_AddHeader ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void HTML::OnSetFocus() +{ + BaseClass::OnSetFocus(); + CHTMLProtoBufMsg cmd( eHTMLCommands_SetFocus ); + cmd.Body().set_focus( true ); + DISPATCH_MESSAGE( eHTMLCommands_SetFocus ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void HTML::OnKillFocus() +{ + if ( vgui::input()->GetFocus() != m_pComboBoxHost->GetVPanel() ) // if its not the menu stealing our focus + BaseClass::OnKillFocus(); + + // Don't clear the actual html focus if a context menu is what took focus + if ( m_pContextMenu->HasFocus() ) + return; + + if ( m_pComboBoxHost->HasFocus() ) + return; + + CHTMLProtoBufMsg cmd( eHTMLCommands_SetFocus ); + cmd.Body().set_focus( false ); + DISPATCH_MESSAGE( eHTMLCommands_SetFocus ); +} + + +//----------------------------------------------------------------------------- +// Purpose: webkit is telling us to use this cursor type +//----------------------------------------------------------------------------- +void HTML::OnCommand( const char *pchCommand ) +{ + if ( !Q_stricmp( pchCommand, "back" ) ) + { + PostActionSignal( new KeyValues( "HTMLBackRequested" ) ); + } + else if ( !Q_stricmp( pchCommand, "forward" ) ) + { + PostActionSignal( new KeyValues( "HTMLForwardRequested" ) ); + } + else if ( !Q_stricmp( pchCommand, "reload" ) ) + { + Refresh(); + } + else if ( !Q_stricmp( pchCommand, "stop" ) ) + { + StopLoading(); + } + else if ( !Q_stricmp( pchCommand, "viewsource" ) ) + { + CHTMLProtoBufMsg cmd( eHTMLCommands_ViewSource ); + DISPATCH_MESSAGE( eHTMLCommands_ViewSource ); + } + else if ( !Q_stricmp( pchCommand, "copy" ) ) + { + CHTMLProtoBufMsg cmd( eHTMLCommands_Copy ); + DISPATCH_MESSAGE( eHTMLCommands_Copy ); + } + else if ( !Q_stricmp( pchCommand, "paste" ) ) + { + CHTMLProtoBufMsg cmd( eHTMLCommands_Paste ); + DISPATCH_MESSAGE( eHTMLCommands_Paste ); + } + else if ( !Q_stricmp( pchCommand, "copyurl" ) ) + { + system()->SetClipboardText( m_sCurrentURL, m_sCurrentURL.Length() ); + } + else if ( !Q_stricmp( pchCommand, "copylink" ) ) + { + int x, y; + m_pContextMenu->GetPos( x, y ); + int htmlx, htmly; + ipanel()->GetAbsPos( GetVPanel(), htmlx, htmly ); + + m_bRequestingCopyLink = true; + GetLinkAtPosition( x - htmlx, y - htmly ); + } + else + BaseClass::OnCommand( pchCommand ); + +} + + +//----------------------------------------------------------------------------- +// Purpose: the control wants us to ask the user what file to load +//----------------------------------------------------------------------------- +void HTML::OnFileSelected( const char *pchSelectedFile ) +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_FileLoadDialogResponse ); + cmd.Body().add_files( pchSelectedFile ); + DISPATCH_MESSAGE( eHTMLCommands_FileLoadDialogResponse ); + m_hFileOpenDialog->Close(); +} + +//----------------------------------------------------------------------------- +// Purpose: called when the user dismissed the file dialog with no selection +//----------------------------------------------------------------------------- +void HTML::OnFileSelectionCancelled() +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_FileLoadDialogResponse ); + DISPATCH_MESSAGE( eHTMLCommands_FileLoadDialogResponse ); + m_hFileOpenDialog->Close(); +} + +//----------------------------------------------------------------------------- +// Purpose: find any text on the html page with this sub string +//----------------------------------------------------------------------------- +void HTML::Find( const char *pchSubStr ) +{ + m_bInFind = false; + if ( m_sLastSearchString == pchSubStr ) // same string as last time, lets fine next + m_bInFind = true; + + m_sLastSearchString = pchSubStr; + + CHTMLProtoBufMsg cmd( eHTMLCommands_Find ); + cmd.Body().set_find( pchSubStr ); + cmd.Body().set_infind( m_bInFind ); + DISPATCH_MESSAGE( eHTMLCommands_Find ); +} + + +//----------------------------------------------------------------------------- +// Purpose: find any text on the html page with this sub string +//----------------------------------------------------------------------------- +void HTML::FindPrevious() +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_Find ); + cmd.Body().set_find( m_sLastSearchString ); + cmd.Body().set_infind( m_bInFind ); + cmd.Body().set_reverse( true ); + DISPATCH_MESSAGE( eHTMLCommands_Find ); +} + + +//----------------------------------------------------------------------------- +// Purpose: find any text on the html page with this sub string +//----------------------------------------------------------------------------- +void HTML::FindNext() +{ + Find( m_sLastSearchString ); +} + + +//----------------------------------------------------------------------------- +// Purpose: stop an outstanding find request +//----------------------------------------------------------------------------- +void HTML::StopFind( ) +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_StopFind ); + DISPATCH_MESSAGE( eHTMLCommands_StopFind ); + + m_bInFind = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: input handler +//----------------------------------------------------------------------------- +void HTML::OnEditNewLine( Panel *pPanel ) +{ + OnTextChanged( pPanel ); +} + + +//-----------------------h------------------------------------------------------ +// Purpose: input handler +//----------------------------------------------------------------------------- +void HTML::OnTextChanged( Panel *pPanel ) +{ + char rgchText[2048]; + m_pFindBar->GetText( rgchText, sizeof( rgchText ) ); + Find( rgchText ); +} + + +//----------------------------------------------------------------------------- +// Purpose: passes mouse clicks to the control +//----------------------------------------------------------------------------- +void HTMLComboBoxHost::OnMousePressed(MouseCode code) +{ + m_pParent->OnMousePressed(code); +} + + +//----------------------------------------------------------------------------- +// Purpose: passes mouse up events +//----------------------------------------------------------------------------- +void HTMLComboBoxHost::OnMouseReleased(MouseCode code) +{ + m_pParent->OnMouseReleased(code); +} + + +//----------------------------------------------------------------------------- +// Purpose: keeps track of where the cursor is +//----------------------------------------------------------------------------- +void HTMLComboBoxHost::OnCursorMoved(int x,int y) +{ + // Only do this when we are over the current panel + if ( vgui::input()->GetMouseOver() == GetVPanel() ) + { + int m_iBrowser = m_pParent->BrowserGetIndex(); + CHTMLProtoBufMsg cmd( eHTMLCommands_MouseMove ); + cmd.Body().set_x( x ); + cmd.Body().set_y( y ); + DISPATCH_MESSAGE( eHTMLCommands_MouseMove ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: passes double click events to the browser +//----------------------------------------------------------------------------- +void HTMLComboBoxHost::OnMouseDoublePressed(MouseCode code) +{ + m_pParent->OnMouseDoublePressed(code); +} + + +//----------------------------------------------------------------------------- +// Purpose: passes key presses to the browser (we don't current do this) +//----------------------------------------------------------------------------- +void HTMLComboBoxHost::OnKeyTyped(wchar_t unichar) +{ + m_pParent->OnKeyTyped(unichar); +} + + +//----------------------------------------------------------------------------- +// Purpose: passes key presses to the browser +//----------------------------------------------------------------------------- +void HTMLComboBoxHost::OnKeyCodeTyped(KeyCode code) +{ + m_pParent->OnKeyCodeTyped(code); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void HTMLComboBoxHost::OnKeyCodeReleased(KeyCode code) +{ + m_pParent->OnKeyCodeReleased(code); +} + + +//----------------------------------------------------------------------------- +// Purpose: scrolls the vertical scroll bar on a web page +//----------------------------------------------------------------------------- +void HTMLComboBoxHost::OnMouseWheeled(int delta) +{ + m_pParent->OnMouseWheeled( delta ); +} + + +//----------------------------------------------------------------------------- +// Purpose: helper class for the find bar +//----------------------------------------------------------------------------- +HTML::CHTMLFindBar::CHTMLFindBar( HTML *parent ) : EditablePanel( parent, "FindBar" ) +{ + m_pParent = parent; + m_bHidden = false; + m_pFindBar = new TextEntry( this, "FindEntry" ); + m_pFindBar->AddActionSignalTarget( parent ); + m_pFindBar->SendNewLine( true ); + m_pFindCountLabel = new Label( this, "FindCount", "" ); + m_pFindCountLabel->SetVisible( false ); + LoadControlSettings( "resource/layout/htmlfindbar.layout" ); +} + + +//----------------------------------------------------------------------------- +// Purpose: button input into the find bar +//----------------------------------------------------------------------------- +void HTML::CHTMLFindBar::OnCommand( const char *pchCmd ) +{ + if ( !Q_stricmp( pchCmd, "close" ) ) + { + m_pParent->HideFindDialog(); + } + else if ( !Q_stricmp( pchCmd, "previous" ) ) + { + m_pParent->FindPrevious(); + } + else if ( !Q_stricmp( pchCmd, "next" ) ) + { + m_pParent->FindNext(); + } + else + BaseClass::OnCommand( pchCmd ); + +} + +//----------------------------------------------------------------------------- +// Purpose: TEMPORARY WORKAROUND FOR VS2005 INTERFACE ISSUES +//----------------------------------------------------------------------------- +#define TMP_HTML_MSG_FUNC( eHTMLCommand, bodyType, commandFunc ) \ + case eHTMLCommand: \ + { \ + CHTMLProtoBufMsg< bodyType > cmd( pCmd->m_eCmd ); \ + if ( cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ) \ + commandFunc( &cmd.BodyConst() ); \ + } \ + break; \ + +void HTML::_DeserializeAndDispatch( HTMLCommandBuffer_t *pCmd ) +{ + switch ( pCmd->m_eCmd ) + { + default: + break; + TMP_HTML_MSG_FUNC( eHTMLCommands_BrowserReady, CMsgBrowserReady, BrowserReady ); + TMP_HTML_MSG_FUNC( eHTMLCommands_NeedsPaint, CMsgNeedsPaint, BrowserNeedsPaint ); + TMP_HTML_MSG_FUNC( eHTMLCommands_StartRequest, CMsgStartRequest, BrowserStartRequest ); + TMP_HTML_MSG_FUNC( eHTMLCommands_URLChanged, CMsgURLChanged, BrowserURLChanged ); + TMP_HTML_MSG_FUNC( eHTMLCommands_FinishedRequest, CMsgFinishedRequest, BrowserFinishedRequest ); + TMP_HTML_MSG_FUNC( eHTMLCommands_ShowPopup, CMsgShowPopup, BrowserShowPopup ); + TMP_HTML_MSG_FUNC( eHTMLCommands_HidePopup, CMsgHidePopup, BrowserHidePopup ); + TMP_HTML_MSG_FUNC( eHTMLCommands_OpenNewTab, CMsgOpenNewTab, BrowserOpenNewTab ); + TMP_HTML_MSG_FUNC( eHTMLCommands_PopupHTMLWindow, CMsgPopupHTMLWindow, BrowserPopupHTMLWindow ); + TMP_HTML_MSG_FUNC( eHTMLCommands_SetHTMLTitle, CMsgSetHTMLTitle, BrowserSetHTMLTitle ); + TMP_HTML_MSG_FUNC( eHTMLCommands_LoadingResource, CMsgLoadingResource, BrowserLoadingResource ); + TMP_HTML_MSG_FUNC( eHTMLCommands_StatusText, CMsgStatusText, BrowserStatusText ); + TMP_HTML_MSG_FUNC( eHTMLCommands_SetCursor, CMsgSetCursor, BrowserSetCursor ); + TMP_HTML_MSG_FUNC( eHTMLCommands_FileLoadDialog, CMsgFileLoadDialog, BrowserFileLoadDialog ); + TMP_HTML_MSG_FUNC( eHTMLCommands_ShowToolTip, CMsgShowToolTip, BrowserShowToolTip ); + TMP_HTML_MSG_FUNC( eHTMLCommands_UpdateToolTip, CMsgUpdateToolTip, BrowserUpdateToolTip ); + TMP_HTML_MSG_FUNC( eHTMLCommands_HideToolTip, CMsgHideToolTip, BrowserHideToolTip ); + TMP_HTML_MSG_FUNC( eHTMLCommands_SearchResults, CMsgSearchResults, BrowserSearchResults ); + TMP_HTML_MSG_FUNC( eHTMLCommands_Close, CMsgClose, BrowserClose ); + TMP_HTML_MSG_FUNC( eHTMLCommands_GetZoomResponse, CMsgGetZoomResponse, BrowserGetZoomResponse ); + TMP_HTML_MSG_FUNC( eHTMLCommands_HorizontalScrollBarSizeResponse, CMsgHorizontalScrollBarSizeResponse, BrowserHorizontalScrollBarSizeResponse ); + TMP_HTML_MSG_FUNC( eHTMLCommands_VerticalScrollBarSizeResponse, CMsgVerticalScrollBarSizeResponse, BrowserVerticalScrollBarSizeResponse ); + TMP_HTML_MSG_FUNC( eHTMLCommands_LinkAtPositionResponse, CMsgLinkAtPositionResponse, BrowserLinkAtPositionResponse ); + TMP_HTML_MSG_FUNC( eHTMLCommands_ZoomToElementAtPositionResponse, CMsgZoomToElementAtPositionResponse, BrowserZoomToElementAtPositionResponse ); + TMP_HTML_MSG_FUNC( eHTMLCommands_JSAlert, CMsgJSAlert, BrowserJSAlert ); + TMP_HTML_MSG_FUNC( eHTMLCommands_JSConfirm, CMsgJSConfirm, BrowserJSConfirm ); + TMP_HTML_MSG_FUNC( eHTMLCommands_OpenSteamURL, CMsgOpenSteamURL, BrowserOpenSteamURL ); + TMP_HTML_MSG_FUNC( eHTMLCommands_CanGoBackandForward, CMsgCanGoBackAndForward, BrowserCanGoBackandForward ); + TMP_HTML_MSG_FUNC( eHTMLCommands_SizePopup, CMsgSizePopup, BrowserSizePopup ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: browser has been constructed on the cef thread, lets use it +//----------------------------------------------------------------------------- +void HTML::BrowserReady( const CMsgBrowserReady *pCmd ) +{ + const char *pchTitle = g_pVGuiLocalize->FindAsUTF8( "#cef_error_title" ); + const char *pchHeader = g_pVGuiLocalize->FindAsUTF8( "#cef_error_header" ); + const char *pchDetailCacheMiss = g_pVGuiLocalize->FindAsUTF8( "#cef_cachemiss" ); + const char *pchDetailBadUURL = g_pVGuiLocalize->FindAsUTF8( "#cef_badurl" ); + const char *pchDetailConnectionProblem = g_pVGuiLocalize->FindAsUTF8( "#cef_connectionproblem" ); + const char *pchDetailProxyProblem = g_pVGuiLocalize->FindAsUTF8( "#cef_proxyconnectionproblem" ); + const char *pchDetailUnknown = g_pVGuiLocalize->FindAsUTF8( "#cef_unknown" ); + + // tell it utf8 loc strings to use + CHTMLProtoBufMsg cmd( eHTMLCommands_BrowserErrorStrings ); + cmd.Body().set_title( pchTitle ); + cmd.Body().set_header( pchHeader ); + cmd.Body().set_cache_miss( pchDetailCacheMiss ); + cmd.Body().set_bad_url( pchDetailBadUURL ); + cmd.Body().set_connection_problem( pchDetailConnectionProblem ); + cmd.Body().set_proxy_problem( pchDetailProxyProblem ); + cmd.Body().set_unknown( pchDetailUnknown ); + DISPATCH_MESSAGE( eHTMLCommands_BrowserErrorStrings ); + + if ( !m_sPendingURLLoad.IsEmpty() ) + { + PostURL( m_sPendingURLLoad, m_sPendingPostData, false ); + m_sPendingURLLoad.Clear(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: we have a new texture to update +//----------------------------------------------------------------------------- +void HTML::BrowserNeedsPaint( const CMsgNeedsPaint *pCmd ) +{ + int tw = 0, tt = 0; + if ( m_iHTMLTextureID != 0 ) + { + tw = m_allocedTextureWidth; + tt = m_allocedTextureHeight; + } + + if ( m_iHTMLTextureID != 0 && ( ( _vbar->IsVisible() && pCmd->scrolly() > 0 && abs( (int)pCmd->scrolly() - m_scrollVertical.m_nScroll) > 5 ) || ( _hbar->IsVisible() && pCmd->scrollx() > 0 && abs( (int)pCmd->scrollx() - m_scrollHorizontal.m_nScroll ) > 5 ) ) ) + { + // this isn't an update from a scroll position we expect, ignore it and ask for a refresh of our update pos2 + CHTMLProtoBufMsg cmd( eHTMLCommands_NeedsPaintResponse ); + cmd.Body().set_textureid( pCmd->textureid() ); + DISPATCH_MESSAGE( eHTMLCommands_NeedsPaintResponse ); + m_bNeedsFullTextureUpload = true; + return; + } + + // update the vgui texture + if ( m_bNeedsFullTextureUpload || m_iHTMLTextureID == 0 || tw != (int)pCmd->wide() || tt != (int)pCmd->tall() ) + { + m_bNeedsFullTextureUpload = false; + if ( m_iHTMLTextureID != 0 ) + surface()->DeleteTextureByID( m_iHTMLTextureID ); + + // if the dimensions changed we also need to re-create the texture ID to support the overlay properly (it won't resize a texture on the fly, this is the only control that needs + // to so lets have a tiny bit more code here to support that) + m_iHTMLTextureID = surface()->CreateNewTextureID( true ); + surface()->DrawSetTextureRGBAEx( m_iHTMLTextureID, (const unsigned char *)pCmd->rgba(), pCmd->wide(), pCmd->tall(), IMAGE_FORMAT_BGRA8888 );// BR FIXME - this call seems to shift by some number of pixels? + m_allocedTextureWidth = pCmd->wide(); + m_allocedTextureHeight = pCmd->tall(); + } + else if ( (int)pCmd->updatewide() > 0 && (int)pCmd->updatetall() > 0 ) + { + // same size texture, just bits changing in it, lets twiddle + surface()->DrawUpdateRegionTextureRGBA( m_iHTMLTextureID, pCmd->updatex(), pCmd->updatey(), (const unsigned char *)pCmd->rgba(), pCmd->updatewide(), pCmd->updatetall(), IMAGE_FORMAT_BGRA8888 ); + } + else + { + surface()->DrawSetTextureRGBAEx( m_iHTMLTextureID, (const unsigned char *)pCmd->rgba(), pCmd->wide(), pCmd->tall(), IMAGE_FORMAT_BGRA8888 ); + } + + if ( m_pComboBoxHost->IsVisible() ) + { + // update the combo box texture also + if ( m_iComboBoxTextureID != 0 ) + { + tw = m_allocedComboBoxWidth; + tt = m_allocedComboBoxHeight; + } + + if ( m_iComboBoxTextureID == 0 || tw != (int)pCmd->combobox_wide() || tt != (int)pCmd->combobox_tall() ) + { + if ( m_iComboBoxTextureID != 0 ) + surface()->DeleteTextureByID( m_iComboBoxTextureID ); + + // if the dimensions changed we also need to re-create the texture ID to support the overlay properly (it won't resize a texture on the fly, this is the only control that needs + // to so lets have a tiny bit more code here to support that) + m_iComboBoxTextureID = surface()->CreateNewTextureID( true ); + surface()->DrawSetTextureRGBAEx( m_iComboBoxTextureID, (const unsigned char *)pCmd->combobox_rgba(), pCmd->combobox_wide(), pCmd->combobox_tall(), IMAGE_FORMAT_BGRA8888 ); + m_allocedComboBoxWidth = (int)pCmd->combobox_wide(); + m_allocedComboBoxHeight = (int)pCmd->combobox_tall(); + } + else + { + // same size texture, just bits changing in it, lets twiddle + surface()->DrawUpdateRegionTextureRGBA( m_iComboBoxTextureID, 0, 0, (const unsigned char *)pCmd->combobox_rgba(), pCmd->combobox_wide(), pCmd->combobox_tall(), IMAGE_FORMAT_BGRA8888 ); + } + } + + // need a paint next time + Repaint(); + + CHTMLProtoBufMsg cmd( eHTMLCommands_NeedsPaintResponse ); + cmd.Body().set_textureid( pCmd->textureid() ); + DISPATCH_MESSAGE( eHTMLCommands_NeedsPaintResponse ); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser wants to start loading this url, do we let it? +//----------------------------------------------------------------------------- +bool HTML::OnStartRequest( const char *url, const char *target, const char *pchPostData, bool bIsRedirect ) +{ + if ( !url || !Q_stricmp( url, "about:blank") ) + return true ; // this is just webkit loading a new frames contents inside an existing page + + HideFindDialog(); + // see if we have a custom handler for this + bool bURLHandled = false; + for (int i = 0; i < m_CustomURLHandlers.Count(); i++) + { + if (!Q_strnicmp(m_CustomURLHandlers[i].url,url, Q_strlen(m_CustomURLHandlers[i].url))) + { + // we have a custom handler + Panel *targetPanel = m_CustomURLHandlers[i].hPanel; + if (targetPanel) + { + PostMessage(targetPanel, new KeyValues("CustomURL", "url", m_CustomURLHandlers[i].url ) ); + } + + bURLHandled = true; + } + } + + if (bURLHandled) + return false; + + if ( m_bNewWindowsOnly && bIsRedirect ) + { + if ( target && ( !Q_stricmp( target, "_blank" ) || !Q_stricmp( target, "_new" ) ) ) // only allow NEW windows (_blank ones) + { + return true; + } + else + { + return false; + } + } + + if ( target && !Q_strlen( target ) ) + { + m_sCurrentURL = url; + + KeyValues *pMessage = new KeyValues( "OnURLChanged" ); + pMessage->SetString( "url", url ); + pMessage->SetString( "postdata", pchPostData ); + pMessage->SetInt( "isredirect", bIsRedirect ? 1 : 0 ); + + PostActionSignal( pMessage ); + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: callback from cef thread, load a url please +//----------------------------------------------------------------------------- +void HTML::BrowserStartRequest( const CMsgStartRequest *pCmd ) +{ + bool bRes = OnStartRequest( pCmd->url().c_str(), pCmd->target().c_str(), pCmd->postdata().c_str(), pCmd->bisredirect() ); + + CHTMLProtoBufMsg cmd( eHTMLCommands_StartRequestResponse ); + cmd.Body().set_ballow( bRes ); + DISPATCH_MESSAGE( eHTMLCommands_StartRequestResponse ); + + UpdateCachedHTMLValues(); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser went to a new url +//----------------------------------------------------------------------------- +void HTML::BrowserURLChanged( const CMsgURLChanged *pCmd ) +{ + m_sCurrentURL = pCmd->url().c_str(); + + KeyValues *pMessage = new KeyValues( "OnURLChanged" ); + pMessage->SetString( "url", pCmd->url().c_str() ); + pMessage->SetString( "postdata", pCmd->postdata().c_str() ); + pMessage->SetInt( "isredirect", pCmd->bisredirect() ? 1 : 0 ); + + PostActionSignal( pMessage ); + + OnURLChanged( m_sCurrentURL, pCmd->postdata().c_str(), pCmd->bisredirect() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: finished loading this page +//----------------------------------------------------------------------------- +void HTML::BrowserFinishedRequest( const CMsgFinishedRequest *pCmd ) +{ + PostActionSignal( new KeyValues( "OnFinishRequest", "url", pCmd->url().c_str() ) ); + if ( pCmd->pagetitle().length() ) + PostActionSignal( new KeyValues( "PageTitleChange", "title", pCmd->pagetitle().c_str() ) ); + KeyValues *pKVSecure = new KeyValues( "SecurityStatus" ); + pKVSecure->SetString( "url", pCmd->url().c_str() ); + pKVSecure->SetInt( "secure", pCmd->security_info().bissecure() ); + pKVSecure->SetInt( "certerror", pCmd->security_info().bhascerterror() ); + pKVSecure->SetInt( "isevcert", pCmd->security_info().bisevcert() ); + pKVSecure->SetString( "certname", pCmd->security_info().certname().c_str() ); + PostActionSignal( pKVSecure ); + + CUtlMap < CUtlString, CUtlString > mapHeaders; + SetDefLessFunc( mapHeaders ); + for ( int i = 0; i < pCmd->headers_size(); i++ ) + { + const CHTMLHeader &header = pCmd->headers(i); + mapHeaders.Insert( header.key().c_str(), header.value().c_str() ); + } + + OnFinishRequest( pCmd->url().c_str(), pCmd->pagetitle().c_str(), mapHeaders ); + + UpdateCachedHTMLValues(); +} + + +//----------------------------------------------------------------------------- +// Purpose: show a popup dialog +//----------------------------------------------------------------------------- +void HTML::BrowserShowPopup( const CMsgShowPopup *pCmd ) +{ + m_pComboBoxHost->SetVisible( true ); +} + + +//----------------------------------------------------------------------------- +// Purpose: hide the popup +//----------------------------------------------------------------------------- +void HTML::HidePopup() +{ + m_pComboBoxHost->SetVisible( false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser wants us to hide a popup +//----------------------------------------------------------------------------- +void HTML::BrowserHidePopup( const CMsgHidePopup *pCmd ) +{ + HidePopup(); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser wants us to position a popup +//----------------------------------------------------------------------------- +void HTML::BrowserSizePopup( const CMsgSizePopup *pCmd ) +{ + int nAbsX, nAbsY; + ipanel()->GetAbsPos( GetVPanel(), nAbsX, nAbsY ); + m_pComboBoxHost->SetBounds( pCmd->x() + 1 + nAbsX, pCmd->y() + nAbsY, pCmd->wide(), pCmd->tall() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser wants to open a new tab +//----------------------------------------------------------------------------- +void HTML::BrowserOpenNewTab( const CMsgOpenNewTab *pCmd ) +{ + (pCmd); + // Not suppored by default, if a child class overrides us and knows how to handle tabs, then it can do this. +} + + +//----------------------------------------------------------------------------- +// Purpose: display a new html window +//----------------------------------------------------------------------------- +void HTML::BrowserPopupHTMLWindow( const CMsgPopupHTMLWindow *pCmd ) +{ + HTMLPopup *p = new HTMLPopup( this, pCmd->url().c_str(), "" ); + int wide = pCmd->wide(); + int tall = pCmd->tall(); + if ( wide == 0 || tall == 0 ) + { + wide = MAX( 640, GetWide() ); + tall = MAX( 480, GetTall() ); + } + + p->SetBounds( pCmd->x(), pCmd->y(), wide, tall ); + p->SetDeleteSelfOnClose( true ); + if ( pCmd->x() == 0 || pCmd->y() == 0 ) + p->MoveToCenterOfScreen(); + p->Activate(); + +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us the page title +//----------------------------------------------------------------------------- +void HTML::BrowserSetHTMLTitle( const CMsgSetHTMLTitle *pCmd ) +{ + PostMessage( GetParent(), new KeyValues( "OnSetHTMLTitle", "title", pCmd->title().c_str() ) ); + OnSetHTMLTitle( pCmd->title().c_str() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: still loading stuff for this page +//----------------------------------------------------------------------------- +void HTML::BrowserLoadingResource( const CMsgLoadingResource *pCmd ) +{ + UpdateCachedHTMLValues(); +} + + +//----------------------------------------------------------------------------- +// Purpose: status bar details +//----------------------------------------------------------------------------- +void HTML::BrowserStatusText( const CMsgStatusText *pCmd ) +{ + PostActionSignal( new KeyValues( "OnSetStatusText", "status", pCmd->text().c_str() ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us to use this cursor +//----------------------------------------------------------------------------- +void HTML::BrowserSetCursor( const CMsgSetCursor *pCmd ) +{ + // Mouse cursor value in CMsgSetCursor is set to one of EMouseCursor, + // by CChromePainter::OnSetCursor in html_chrome.cpp + // Code below relies on start of EMouseCursor being exactly same as vgui::CursorCode + + vgui::CursorCode cursor; + uint32 msgCursor = pCmd->cursor(); + + if ( msgCursor >= (uint32)(dc_last) ) + { + cursor = dc_arrow; + } + else + { + cursor = (CursorCode)msgCursor; + } + + SetCursor( cursor ); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling to show the file loading dialog +//----------------------------------------------------------------------------- +void HTML::BrowserFileLoadDialog( const CMsgFileLoadDialog *pCmd ) +{ + /* + // try and use the OS-specific file dialog first + char rgchFileName[MAX_UNICODE_PATH_IN_UTF8]; + if ( surface()->OpenOSFileOpenDialog( pCmd->title().c_str(), pCmd->initialfile().c_str(), NULL, rgchFileName, sizeof(rgchFileName) ) ) + { + CHTMLProtoBufMsg cmd( eHTMLCommands_FileLoadDialogResponse ); + cmd.Body().add_files( rgchFileName ); + DISPATCH_MESSAGE( eHTMLCommands_FileLoadDialogResponse ); + } + else*/ + { + // couldn't access an OS-specific dialog, use the internal one + if ( m_hFileOpenDialog.Get() ) + { + delete m_hFileOpenDialog.Get(); + m_hFileOpenDialog = NULL; + } + m_hFileOpenDialog = new FileOpenDialog( this, pCmd->title().c_str(), true ); + m_hFileOpenDialog->SetStartDirectory( pCmd->initialfile().c_str() ); + m_hFileOpenDialog->AddActionSignalTarget( this ); + m_hFileOpenDialog->SetAutoDelete( true ); + m_hFileOpenDialog->DoModal(false); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: browser asking to show a tooltip +//----------------------------------------------------------------------------- +void HTML::BrowserShowToolTip( const CMsgShowToolTip *pCmd ) +{ + /* + BR FIXME + Tooltip *tip = GetTooltip(); + tip->SetText( pCmd->text().c_str() ); + tip->SetTooltipFormatToMultiLine(); + tip->SetTooltipDelayMS( 250 ); + tip->SetMaxToolTipWidth( MAX( 200, GetWide()/2 ) ); + tip->ShowTooltip( this ); + */ +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us to update tool tip text +//----------------------------------------------------------------------------- +void HTML::BrowserUpdateToolTip( const CMsgUpdateToolTip *pCmd ) +{ +// GetTooltip()->SetText( pCmd->text().c_str() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling that it is done with the tip +//----------------------------------------------------------------------------- +void HTML::BrowserHideToolTip( const CMsgHideToolTip *pCmd ) +{ +// GetTooltip()->HideTooltip(); +// DeleteToolTip(); +} + + +//----------------------------------------------------------------------------- +// Purpose: callback when performing a search +//----------------------------------------------------------------------------- +void HTML::BrowserSearchResults( const CMsgSearchResults *pCmd ) +{ + if ( pCmd->results() == 0 ) + m_pFindBar->HideCountLabel(); + else + m_pFindBar->ShowCountLabel(); + + if ( pCmd->results() > 0 ) + m_pFindBar->SetDialogVariable( "findcount", (int)pCmd->results() ); + if ( pCmd->activematch() > 0 ) + m_pFindBar->SetDialogVariable( "findactive", (int)pCmd->activematch() ); + m_pFindBar->InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us it had a close requested +//----------------------------------------------------------------------------- +void HTML::BrowserClose( const CMsgClose *pCmd ) +{ + PostActionSignal( new KeyValues( "OnCloseWindow" ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us the size of the horizontal scrollbars +//----------------------------------------------------------------------------- +void HTML::BrowserHorizontalScrollBarSizeResponse( const CMsgHorizontalScrollBarSizeResponse *pCmd ) +{ + ScrollData_t scrollHorizontal; + scrollHorizontal.m_nX = pCmd->x(); + scrollHorizontal.m_nY = pCmd->y(); + scrollHorizontal.m_nWide = pCmd->wide(); + scrollHorizontal.m_nTall = pCmd->tall(); + scrollHorizontal.m_nScroll = pCmd->scroll(); + scrollHorizontal.m_nMax = pCmd->scroll_max(); + scrollHorizontal.m_bVisible = ( m_scrollHorizontal.m_nTall > 0 ); + scrollHorizontal.m_flZoom = pCmd->zoom(); + + if ( scrollHorizontal != m_scrollHorizontal ) + { + m_scrollHorizontal = scrollHorizontal; + UpdateSizeAndScrollBars(); + m_bNeedsFullTextureUpload = true; + } + else + m_scrollHorizontal = scrollHorizontal; +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us the size of the vertical scrollbars +//----------------------------------------------------------------------------- +void HTML::BrowserVerticalScrollBarSizeResponse( const CMsgVerticalScrollBarSizeResponse *pCmd ) +{ + ScrollData_t scrollVertical; + scrollVertical.m_nX = pCmd->x(); + scrollVertical.m_nY = pCmd->y(); + scrollVertical.m_nWide = pCmd->wide(); + scrollVertical.m_nTall = pCmd->tall(); + scrollVertical.m_nScroll = pCmd->scroll(); + scrollVertical.m_nMax = pCmd->scroll_max(); + scrollVertical.m_bVisible = ( m_scrollVertical.m_nTall > 0 ); + scrollVertical.m_flZoom = pCmd->zoom(); + + if ( scrollVertical != m_scrollVertical ) + { + m_scrollVertical = scrollVertical; + UpdateSizeAndScrollBars(); + m_bNeedsFullTextureUpload = true; + } + else + m_scrollVertical = scrollVertical; +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us the current page zoom +//----------------------------------------------------------------------------- +void HTML::BrowserGetZoomResponse( const CMsgGetZoomResponse *pCmd ) +{ + m_flZoom = pCmd->zoom(); + if ( m_flZoom == 0.0f ) + m_flZoom = 100.0f; + m_flZoom /= 100; // scale zoom to 1.0 being 100%, webkit gives us 100 for normal scale + +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us what is at this location on the page +//----------------------------------------------------------------------------- +void HTML::BrowserLinkAtPositionResponse( const CMsgLinkAtPositionResponse *pCmd ) +{ + m_LinkAtPos.m_sURL = pCmd->url().c_str(); + m_LinkAtPos.m_nX = pCmd->x(); + m_LinkAtPos.m_nY = pCmd->y(); + + m_pContextMenu->SetItemVisible( m_iCopyLinkMenuItemID, !m_LinkAtPos.m_sURL.IsEmpty() ? true : false ); + if ( m_bRequestingDragURL ) + { + m_bRequestingDragURL = false; + m_sDragURL = m_LinkAtPos.m_sURL; + // make sure we get notified when the mouse gets released + if ( !m_sDragURL.IsEmpty() ) + { + input()->SetMouseCapture( GetVPanel() ); + } + } + + if ( m_bRequestingCopyLink ) + { + m_bRequestingCopyLink = false; + if ( !m_LinkAtPos.m_sURL.IsEmpty() ) + system()->SetClipboardText( m_LinkAtPos.m_sURL, m_LinkAtPos.m_sURL.Length() ); + else + system()->SetClipboardText( "", 1 ); + } + + OnLinkAtPosition( m_LinkAtPos.m_sURL ); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us that a zoom to element is done +//----------------------------------------------------------------------------- +void HTML::BrowserZoomToElementAtPositionResponse( const CMsgZoomToElementAtPositionResponse *pCmd ) +{ + +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us to pop a javascript alert dialog +//----------------------------------------------------------------------------- +void HTML::BrowserJSAlert( const CMsgJSAlert *pCmd ) +{ + MessageBox *pDlg = new MessageBox( m_sCurrentURL, (const char *)pCmd->message().c_str(), this ); + pDlg->AddActionSignalTarget( this ); + pDlg->SetCommand( new KeyValues( "DismissJSDialog", "result", false ) ); + pDlg->DoModal(); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us to pop a js confirm dialog +//----------------------------------------------------------------------------- +void HTML::BrowserJSConfirm( const CMsgJSConfirm *pCmd ) +{ + QueryBox *pDlg = new QueryBox( m_sCurrentURL, (const char *)pCmd->message().c_str(), this ); + pDlg->AddActionSignalTarget( this ); + pDlg->SetOKCommand( new KeyValues( "DismissJSDialog", "result", true ) ); + pDlg->SetCancelCommand( new KeyValues( "DismissJSDialog", "result", false ) ); + pDlg->DoModal(); +} + + +//----------------------------------------------------------------------------- +// Purpose: got an answer from the dialog, tell cef +//----------------------------------------------------------------------------- +void HTML::DismissJSDialog( int bResult ) +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_JSDialogResponse ); + cmd.Body().set_result( bResult==1 ); + DISPATCH_MESSAGE( eHTMLCommands_JSDialogResponse ); +}; + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us the state of back and forward buttons +//----------------------------------------------------------------------------- +void HTML::BrowserCanGoBackandForward( const CMsgCanGoBackAndForward *pCmd ) +{ + m_bCanGoBack = pCmd->bgoback(); + m_bCanGoForward = pCmd->bgoforward(); +} + + +//----------------------------------------------------------------------------- +// Purpose: browser telling us a steam url was asked to be loaded +//----------------------------------------------------------------------------- +void HTML::BrowserOpenSteamURL( const CMsgOpenSteamURL *pCmd ) +{ + vgui::ivgui()->PostMessage( surface()->GetEmbeddedPanel(), + new KeyValues( "OpenSteamDialog", "cmd", pCmd->url().c_str() ), NULL, 0.3f ); +} + + +//----------------------------------------------------------------------------- +// Purpose: update the value of the cached variables we keep +//----------------------------------------------------------------------------- +void HTML::UpdateCachedHTMLValues() +{ + // request scroll bar sizes + { + CHTMLProtoBufMsg cmd( eHTMLCommands_VerticalScrollBarSize ); + DISPATCH_MESSAGE( eHTMLCommands_VerticalScrollBarSize ); + } + { + CHTMLProtoBufMsg cmd( eHTMLCommands_HorizontalScrollBarSize ); + DISPATCH_MESSAGE( eHTMLCommands_HorizontalScrollBarSize ); + } + { + CHTMLProtoBufMsg cmd( eHTMLCommands_GetZoom ); + DISPATCH_MESSAGE( eHTMLCommands_GetZoom ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: ask the browser for what is at this x,y +//----------------------------------------------------------------------------- +void HTML::GetLinkAtPosition( int x, int y ) +{ + CHTMLProtoBufMsg cmd( eHTMLCommands_LinkAtPosition ); + cmd.Body().set_x( x ); + cmd.Body().set_y( y ); + DISPATCH_MESSAGE( eHTMLCommands_LinkAtPosition ); +} + +//----------------------------------------------------------------------------- +// Purpose: send any queued html messages we have +//----------------------------------------------------------------------------- +void HTML::SendPendingHTMLMessages() +{ + FOR_EACH_VEC( m_vecPendingMessages, i ) + { + m_vecPendingMessages[i]->m_iBrowser = m_iBrowser; + surface()->AccessChromeHTMLController()->PushCommand( m_vecPendingMessages[i] ); + surface()->AccessChromeHTMLController()->WakeThread(); + } + m_vecPendingMessages.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: update the size of the browser itself and scrollbars it shows +//----------------------------------------------------------------------------- +void HTML::UpdateSizeAndScrollBars() +{ + // Tell IE + BrowserResize(); + + // Do this after we tell IE! + int w,h; + GetSize( w, h ); + CalcScrollBars(w,h); + + InvalidateLayout(); +} + + diff --git a/mp/src/vgui2/vgui_controls/Image.cpp b/mp/src/vgui2/vgui_controls/Image.cpp index b7ab8564..30af2964 100644 --- a/mp/src/vgui2/vgui_controls/Image.cpp +++ b/mp/src/vgui2/vgui_controls/Image.cpp @@ -1,282 +1,282 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include - -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: Conctructor. Start with default position and default color. -//----------------------------------------------------------------------------- -Image::Image() -{ - SetPos(0,0); - SetSize(0,0); - SetColor(Color(255,255,255,255)); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -Image::~Image() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Set the position of the image, you need to reset this every time you -// call Paint() -//----------------------------------------------------------------------------- -void Image::SetPos(int x,int y) -{ - _pos[0]=x; - _pos[1]=y; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the position of the image -//----------------------------------------------------------------------------- -void Image::GetPos(int& x,int& y) -{ - x=_pos[0]; - y=_pos[1]; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the size of the image -//----------------------------------------------------------------------------- -void Image::GetSize(int &wide, int &tall) -{ - wide = _size[0]; - tall = _size[1]; -} - -//----------------------------------------------------------------------------- -// Purpose: Gets the size of the image contents (by default the set size) -//----------------------------------------------------------------------------- -void Image::GetContentSize(int &wide, int &tall) -{ - GetSize(wide, tall); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the size of the image -//----------------------------------------------------------------------------- -void Image::SetSize(int wide, int tall) -{ - _size[0]=wide; - _size[1]=tall; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the draw color using a Color struct. -//----------------------------------------------------------------------------- -void Image::DrawSetColor(Color col) -{ - surface()->DrawSetColor(col[0], col[1], col[2], col[3]); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the draw color using RGBA ints -//----------------------------------------------------------------------------- -void Image::DrawSetColor(int r,int g,int b,int a) -{ - surface()->DrawSetColor(r,g,b,a); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw a filled rectangle -//----------------------------------------------------------------------------- -void Image::DrawFilledRect(int x0,int y0,int x1,int y1) -{ - x0+=_pos[0]; - y0+=_pos[1]; - x1+=_pos[0]; - y1+=_pos[1]; - surface()->DrawFilledRect(x0,y0,x1,y1); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw an outlined rectangle -//----------------------------------------------------------------------------- -void Image::DrawOutlinedRect(int x0,int y0,int x1,int y1) -{ - x0+=_pos[0]; - y0+=_pos[1]; - x1+=_pos[0]; - y1+=_pos[1]; - surface()->DrawOutlinedRect(x0,y0,x1,y1); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw a line between two points -//----------------------------------------------------------------------------- -void Image::DrawLine(int x0,int y0,int x1,int y1) -{ - x0+=_pos[0]; - y0+=_pos[1]; - x1+=_pos[0]; - y1+=_pos[1]; - surface()->DrawLine(x0,y0,x1,y1); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw a line between a list of 'numPoints' points -//----------------------------------------------------------------------------- -void Image::DrawPolyLine(int *px, int *py, int numPoints) -{ - // update the positions to be relative to this panel - for(int i=0;iDrawPolyLine(px, py, numPoints); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the font -//----------------------------------------------------------------------------- -void Image::DrawSetTextFont(HFont font) -{ - surface()->DrawSetTextFont(font); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the text color using a color struct -//----------------------------------------------------------------------------- -void Image::DrawSetTextColor(Color sc) -{ - surface()->DrawSetTextColor(sc[0], sc[1], sc[2], sc[3]); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the text color useing RGBA ints -//----------------------------------------------------------------------------- -void Image::DrawSetTextColor(int r,int g,int b,int a) -{ - surface()->DrawSetTextColor(r,g,b,a); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the text position -//----------------------------------------------------------------------------- -void Image::DrawSetTextPos(int x,int y) -{ - x+=_pos[0]; - y+=_pos[1]; - surface()->DrawSetTextPos(x,y); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw a text string -//----------------------------------------------------------------------------- -void Image::DrawPrintText(const wchar_t *str,int strlen) -{ - surface()->DrawPrintText(str, strlen); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw a text string at the given coords. -//----------------------------------------------------------------------------- -void Image::DrawPrintText(int x, int y, const wchar_t *str, int strlen) -{ - x += _pos[0]; - y += _pos[1]; - - surface()->DrawSetTextPos(x, y); - surface()->DrawPrintText(str, strlen); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw a character -//----------------------------------------------------------------------------- -void Image::DrawPrintChar(wchar_t ch) -{ - surface()->DrawUnicodeChar(ch); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw a character at the given coords -//----------------------------------------------------------------------------- -void Image::DrawPrintChar(int x, int y, wchar_t ch) -{ - x+=_pos[0]; - y+=_pos[1]; - - surface()->DrawSetTextPos(x, y); - surface()->DrawUnicodeChar(ch); -} - -//----------------------------------------------------------------------------- -// Purpose: Set a texture -//----------------------------------------------------------------------------- -void Image::DrawSetTexture(int id) -{ - surface()->DrawSetTexture(id); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw a rectangle filled with the current texture -//----------------------------------------------------------------------------- -void Image::DrawTexturedRect(int x0,int y0,int x1,int y1) -{ - surface()->DrawTexturedRect(x0,y0,x1,y1); -} - -//----------------------------------------------------------------------------- -// Purpose: Paint the contents of the image on screen. -// You must call this explicitly each frame. -//----------------------------------------------------------------------------- -void Image::Paint() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Set the current color using a color struct -//----------------------------------------------------------------------------- -void Image::SetColor(Color color) -{ - _color=color; - DrawSetTextColor(color); // now update the device context underneath us :) -} - -//----------------------------------------------------------------------------- -// Purpose: Get the current color as a color struct -//----------------------------------------------------------------------------- -Color Image::GetColor() -{ - return _color; -} - -bool Image::Evict() -{ - return false; -} - -int Image::GetNumFrames() -{ - return 0; -} - -void Image::SetFrame( int nFrame ) -{ -} - -HTexture Image::GetID() -{ - return 0; -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include + +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Conctructor. Start with default position and default color. +//----------------------------------------------------------------------------- +Image::Image() +{ + SetPos(0,0); + SetSize(0,0); + SetColor(Color(255,255,255,255)); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +Image::~Image() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Set the position of the image, you need to reset this every time you +// call Paint() +//----------------------------------------------------------------------------- +void Image::SetPos(int x,int y) +{ + _pos[0]=x; + _pos[1]=y; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the position of the image +//----------------------------------------------------------------------------- +void Image::GetPos(int& x,int& y) +{ + x=_pos[0]; + y=_pos[1]; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the size of the image +//----------------------------------------------------------------------------- +void Image::GetSize(int &wide, int &tall) +{ + wide = _size[0]; + tall = _size[1]; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the size of the image contents (by default the set size) +//----------------------------------------------------------------------------- +void Image::GetContentSize(int &wide, int &tall) +{ + GetSize(wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the size of the image +//----------------------------------------------------------------------------- +void Image::SetSize(int wide, int tall) +{ + _size[0]=wide; + _size[1]=tall; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the draw color using a Color struct. +//----------------------------------------------------------------------------- +void Image::DrawSetColor(Color col) +{ + surface()->DrawSetColor(col[0], col[1], col[2], col[3]); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the draw color using RGBA ints +//----------------------------------------------------------------------------- +void Image::DrawSetColor(int r,int g,int b,int a) +{ + surface()->DrawSetColor(r,g,b,a); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw a filled rectangle +//----------------------------------------------------------------------------- +void Image::DrawFilledRect(int x0,int y0,int x1,int y1) +{ + x0+=_pos[0]; + y0+=_pos[1]; + x1+=_pos[0]; + y1+=_pos[1]; + surface()->DrawFilledRect(x0,y0,x1,y1); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw an outlined rectangle +//----------------------------------------------------------------------------- +void Image::DrawOutlinedRect(int x0,int y0,int x1,int y1) +{ + x0+=_pos[0]; + y0+=_pos[1]; + x1+=_pos[0]; + y1+=_pos[1]; + surface()->DrawOutlinedRect(x0,y0,x1,y1); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw a line between two points +//----------------------------------------------------------------------------- +void Image::DrawLine(int x0,int y0,int x1,int y1) +{ + x0+=_pos[0]; + y0+=_pos[1]; + x1+=_pos[0]; + y1+=_pos[1]; + surface()->DrawLine(x0,y0,x1,y1); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw a line between a list of 'numPoints' points +//----------------------------------------------------------------------------- +void Image::DrawPolyLine(int *px, int *py, int numPoints) +{ + // update the positions to be relative to this panel + for(int i=0;iDrawPolyLine(px, py, numPoints); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the font +//----------------------------------------------------------------------------- +void Image::DrawSetTextFont(HFont font) +{ + surface()->DrawSetTextFont(font); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the text color using a color struct +//----------------------------------------------------------------------------- +void Image::DrawSetTextColor(Color sc) +{ + surface()->DrawSetTextColor(sc[0], sc[1], sc[2], sc[3]); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the text color useing RGBA ints +//----------------------------------------------------------------------------- +void Image::DrawSetTextColor(int r,int g,int b,int a) +{ + surface()->DrawSetTextColor(r,g,b,a); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the text position +//----------------------------------------------------------------------------- +void Image::DrawSetTextPos(int x,int y) +{ + x+=_pos[0]; + y+=_pos[1]; + surface()->DrawSetTextPos(x,y); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw a text string +//----------------------------------------------------------------------------- +void Image::DrawPrintText(const wchar_t *str,int strlen) +{ + surface()->DrawPrintText(str, strlen); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw a text string at the given coords. +//----------------------------------------------------------------------------- +void Image::DrawPrintText(int x, int y, const wchar_t *str, int strlen) +{ + x += _pos[0]; + y += _pos[1]; + + surface()->DrawSetTextPos(x, y); + surface()->DrawPrintText(str, strlen); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw a character +//----------------------------------------------------------------------------- +void Image::DrawPrintChar(wchar_t ch) +{ + surface()->DrawUnicodeChar(ch); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw a character at the given coords +//----------------------------------------------------------------------------- +void Image::DrawPrintChar(int x, int y, wchar_t ch) +{ + x+=_pos[0]; + y+=_pos[1]; + + surface()->DrawSetTextPos(x, y); + surface()->DrawUnicodeChar(ch); +} + +//----------------------------------------------------------------------------- +// Purpose: Set a texture +//----------------------------------------------------------------------------- +void Image::DrawSetTexture(int id) +{ + surface()->DrawSetTexture(id); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw a rectangle filled with the current texture +//----------------------------------------------------------------------------- +void Image::DrawTexturedRect(int x0,int y0,int x1,int y1) +{ + surface()->DrawTexturedRect(x0,y0,x1,y1); +} + +//----------------------------------------------------------------------------- +// Purpose: Paint the contents of the image on screen. +// You must call this explicitly each frame. +//----------------------------------------------------------------------------- +void Image::Paint() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Set the current color using a color struct +//----------------------------------------------------------------------------- +void Image::SetColor(Color color) +{ + _color=color; + DrawSetTextColor(color); // now update the device context underneath us :) +} + +//----------------------------------------------------------------------------- +// Purpose: Get the current color as a color struct +//----------------------------------------------------------------------------- +Color Image::GetColor() +{ + return _color; +} + +bool Image::Evict() +{ + return false; +} + +int Image::GetNumFrames() +{ + return 0; +} + +void Image::SetFrame( int nFrame ) +{ +} + +HTexture Image::GetID() +{ + return 0; +} + diff --git a/mp/src/vgui2/vgui_controls/ImageList.cpp b/mp/src/vgui2/vgui_controls/ImageList.cpp index 3778478b..dbf8e888 100644 --- a/mp/src/vgui2/vgui_controls/ImageList.cpp +++ b/mp/src/vgui2/vgui_controls/ImageList.cpp @@ -1,106 +1,106 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include - -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: blank image, intentially draws nothing -//----------------------------------------------------------------------------- -class BlankImage : public IImage -{ -public: - virtual void Paint() {} - virtual void SetPos(int x, int y) {} - virtual void GetContentSize(int &wide, int &tall) { wide = 0; tall = 0; } - virtual void GetSize(int &wide, int &tall) { wide = 0; tall = 0; } - virtual void SetSize(int wide, int tall) {} - virtual void SetColor(Color col) {} - virtual bool Evict() { return false; } - virtual int GetNumFrames() { return 0; } - virtual void SetFrame( int nFrame ) {} - virtual HTexture GetID() { return 0; } - virtual void SetRotation( int iRotation ) { return; }; -}; - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -ImageList::ImageList(bool deleteImagesWhenDone) -{ - m_bDeleteImagesWhenDone = deleteImagesWhenDone; - AddImage(new BlankImage()); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -ImageList::~ImageList() -{ - if (m_bDeleteImagesWhenDone) - { - // delete all the images, except for the first image (which is always the blank image) - for (int i = 1; i < m_Images.Count(); i++) - { - delete m_Images[i]; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: adds a new image to the list, returning the index it was placed at -//----------------------------------------------------------------------------- -int ImageList::AddImage(vgui::IImage *image) -{ - return m_Images.AddToTail(image); -} - -//----------------------------------------------------------------------------- -// Purpose: sets an image at a specified index, growing and adding NULL images if necessary -//----------------------------------------------------------------------------- -void ImageList::SetImageAtIndex(int index, vgui::IImage *image) -{ - // allocate more images if necessary - while (m_Images.Count() <= index) - { - m_Images.AddToTail(NULL); - } - - m_Images[index] = image; -} - -//----------------------------------------------------------------------------- -// Purpose: returns the number of images -//----------------------------------------------------------------------------- -int ImageList::GetImageCount() -{ - return m_Images.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: gets an image, imageIndex is of range [0, GetImageCount) -//----------------------------------------------------------------------------- -vgui::IImage *ImageList::GetImage(int imageIndex) -{ - return m_Images[imageIndex]; -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if an index is valid -//----------------------------------------------------------------------------- -bool ImageList::IsValidIndex(int imageIndex) -{ - return m_Images.IsValidIndex(imageIndex); -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include + +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: blank image, intentially draws nothing +//----------------------------------------------------------------------------- +class BlankImage : public IImage +{ +public: + virtual void Paint() {} + virtual void SetPos(int x, int y) {} + virtual void GetContentSize(int &wide, int &tall) { wide = 0; tall = 0; } + virtual void GetSize(int &wide, int &tall) { wide = 0; tall = 0; } + virtual void SetSize(int wide, int tall) {} + virtual void SetColor(Color col) {} + virtual bool Evict() { return false; } + virtual int GetNumFrames() { return 0; } + virtual void SetFrame( int nFrame ) {} + virtual HTexture GetID() { return 0; } + virtual void SetRotation( int iRotation ) { return; }; +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ImageList::ImageList(bool deleteImagesWhenDone) +{ + m_bDeleteImagesWhenDone = deleteImagesWhenDone; + AddImage(new BlankImage()); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +ImageList::~ImageList() +{ + if (m_bDeleteImagesWhenDone) + { + // delete all the images, except for the first image (which is always the blank image) + for (int i = 1; i < m_Images.Count(); i++) + { + delete m_Images[i]; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: adds a new image to the list, returning the index it was placed at +//----------------------------------------------------------------------------- +int ImageList::AddImage(vgui::IImage *image) +{ + return m_Images.AddToTail(image); +} + +//----------------------------------------------------------------------------- +// Purpose: sets an image at a specified index, growing and adding NULL images if necessary +//----------------------------------------------------------------------------- +void ImageList::SetImageAtIndex(int index, vgui::IImage *image) +{ + // allocate more images if necessary + while (m_Images.Count() <= index) + { + m_Images.AddToTail(NULL); + } + + m_Images[index] = image; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the number of images +//----------------------------------------------------------------------------- +int ImageList::GetImageCount() +{ + return m_Images.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: gets an image, imageIndex is of range [0, GetImageCount) +//----------------------------------------------------------------------------- +vgui::IImage *ImageList::GetImage(int imageIndex) +{ + return m_Images[imageIndex]; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if an index is valid +//----------------------------------------------------------------------------- +bool ImageList::IsValidIndex(int imageIndex) +{ + return m_Images.IsValidIndex(imageIndex); +} + diff --git a/mp/src/vgui2/vgui_controls/ImagePanel.cpp b/mp/src/vgui2/vgui_controls/ImagePanel.cpp index bce00908..3e31e23b 100644 --- a/mp/src/vgui2/vgui_controls/ImagePanel.cpp +++ b/mp/src/vgui2/vgui_controls/ImagePanel.cpp @@ -1,463 +1,463 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY( ImagePanel ); - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -ImagePanel::ImagePanel(Panel *parent, const char *name) : Panel(parent, name) -{ - m_pImage = NULL; - m_pszImageName = NULL; - m_pszFillColorName = NULL; - m_pszDrawColorName = NULL; // HPE addition - m_bCenterImage = false; - m_bScaleImage = false; - m_bTileImage = false; - m_bTileHorizontally = false; - m_bTileVertically = false; - m_fScaleAmount = 0.0f; - m_FillColor = Color(0, 0, 0, 0); - m_DrawColor = Color(255,255,255,255); - m_iRotation = ROTATED_UNROTATED; - - SetImage( m_pImage ); - - REGISTER_COLOR_AS_OVERRIDABLE( m_FillColor, "fillcolor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( m_DrawColor, "drawcolor_override" ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -ImagePanel::~ImagePanel() -{ - delete [] m_pszImageName; - delete [] m_pszFillColorName; - delete [] m_pszDrawColorName; // HPE addition -} - -//----------------------------------------------------------------------------- -// Purpose: handles size changing -//----------------------------------------------------------------------------- -void ImagePanel::OnSizeChanged(int newWide, int newTall) -{ - BaseClass::OnSizeChanged(newWide, newTall); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ImagePanel::SetImage(IImage *image) -{ - m_pImage = image; - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: sets an image by file name -//----------------------------------------------------------------------------- -void ImagePanel::SetImage(const char *imageName) -{ - if ( imageName && m_pszImageName && V_stricmp( imageName, m_pszImageName ) == 0 ) - return; - - int len = Q_strlen(imageName) + 1; - delete [] m_pszImageName; - m_pszImageName = new char[ len ]; - Q_strncpy(m_pszImageName, imageName, len ); - InvalidateLayout(false, true); // force applyschemesettings to run -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -IImage *ImagePanel::GetImage() -{ - return m_pImage; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Color ImagePanel::GetDrawColor( void ) -{ - return m_DrawColor; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ImagePanel::SetDrawColor( Color drawColor ) -{ - m_DrawColor = drawColor; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ImagePanel::PaintBackground() -{ - if (m_FillColor[3] > 0) - { - // draw the specified fill color - int wide, tall; - GetSize(wide, tall); - surface()->DrawSetColor(m_FillColor); - surface()->DrawFilledRect(0, 0, wide, tall); - } - if ( m_pImage ) - { - //============================================================================= - // HPE_BEGIN: - // [pfreese] Color should be always set from GetDrawColor(), not just when - // scaling is true (see previous code) - //============================================================================= - - // surface()->DrawSetColor( 255, 255, 255, GetAlpha() ); - m_pImage->SetColor( GetDrawColor() ); - - //============================================================================= - // HPE_END - //============================================================================= - - if ( m_bCenterImage ) - { - int wide, tall; - GetSize(wide, tall); - - int imageWide, imageTall; - m_pImage->GetSize( imageWide, imageTall ); - - if ( m_bScaleImage && m_fScaleAmount > 0.0f ) - { - imageWide = static_cast( static_cast(imageWide) * m_fScaleAmount ); - imageTall = static_cast( static_cast(imageTall) * m_fScaleAmount ); - } - - m_pImage->SetPos( (wide - imageWide) / 2, (tall - imageTall) / 2 ); - } - else - { - m_pImage->SetPos(0, 0); - } - - if (m_bScaleImage) - { - // Image size is stored in the bitmap, so temporarily set its size - // to our panel size and then restore after we draw it. - - int imageWide, imageTall; - m_pImage->GetSize( imageWide, imageTall ); - - if ( m_fScaleAmount > 0.0f ) - { - float wide, tall; - wide = static_cast(imageWide) * m_fScaleAmount; - tall = static_cast(imageTall) * m_fScaleAmount; - m_pImage->SetSize( static_cast(wide), static_cast(tall) ); - } - else - { - int wide, tall; - GetSize( wide, tall ); - m_pImage->SetSize( wide, tall ); - } - - m_pImage->Paint(); - - m_pImage->SetSize( imageWide, imageTall ); - } - else if ( m_bTileImage || m_bTileHorizontally || m_bTileVertically ) - { - int wide, tall; - GetSize(wide, tall); - int imageWide, imageTall; - m_pImage->GetSize( imageWide, imageTall ); - - int y = 0; - while ( y < tall ) - { - int x = 0; - while (x < wide) - { - m_pImage->SetPos(x,y); - m_pImage->Paint(); - - x += imageWide; - - if ( !m_bTileHorizontally ) - break; - } - - y += imageTall; - - if ( !m_bTileVertically ) - break; - } - m_pImage->SetPos(0, 0); - } - else - { - m_pImage->SetColor( GetDrawColor() ); - m_pImage->Paint(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Gets control settings for editing -//----------------------------------------------------------------------------- -void ImagePanel::GetSettings(KeyValues *outResourceData) -{ - BaseClass::GetSettings(outResourceData); - if (m_pszImageName) - { - outResourceData->SetString("image", m_pszImageName); - } - if (m_pszFillColorName) - { - outResourceData->SetString("fillcolor", m_pszFillColorName); - } - - //============================================================================= - // HPE_BEGIN: - // [pfreese] Added support for specifying drawcolor - //============================================================================= - if (m_pszDrawColorName) - { - outResourceData->SetString("drawcolor", m_pszDrawColorName); - } - //============================================================================= - // HPE_END - //============================================================================= - - if (GetBorder()) - { - outResourceData->SetString("border", GetBorder()->GetName()); - } - - outResourceData->SetInt("scaleImage", m_bScaleImage); - outResourceData->SetFloat("scaleAmount", m_fScaleAmount); - outResourceData->SetInt("tileImage", m_bTileImage); - outResourceData->SetInt("tileHorizontally", m_bTileHorizontally); - outResourceData->SetInt("tileVertically", m_bTileVertically); -} - -//----------------------------------------------------------------------------- -// Purpose: Applies designer settings from res file -//----------------------------------------------------------------------------- -void ImagePanel::ApplySettings(KeyValues *inResourceData) -{ - delete [] m_pszImageName; - delete [] m_pszFillColorName; - delete [] m_pszDrawColorName; // HPE addition - m_pszImageName = NULL; - m_pszFillColorName = NULL; - m_pszDrawColorName = NULL; // HPE addition - - m_bScaleImage = inResourceData->GetInt("scaleImage", 0); - m_fScaleAmount = inResourceData->GetFloat("scaleAmount", 0.0f); - m_bTileImage = inResourceData->GetInt("tileImage", 0); - m_bTileHorizontally = inResourceData->GetInt("tileHorizontally", m_bTileImage); - m_bTileVertically = inResourceData->GetInt("tileVertically", m_bTileImage); - const char *imageName = inResourceData->GetString("image", ""); - if ( *imageName ) - { - SetImage( imageName ); - } - - const char *pszFillColor = inResourceData->GetString("fillcolor", ""); - if (*pszFillColor) - { - int r = 0, g = 0, b = 0, a = 255; - int len = Q_strlen(pszFillColor) + 1; - m_pszFillColorName = new char[ len ]; - Q_strncpy( m_pszFillColorName, pszFillColor, len ); - - if (sscanf(pszFillColor, "%d %d %d %d", &r, &g, &b, &a) >= 3) - { - // it's a direct color - m_FillColor = Color(r, g, b, a); - } - else - { - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - m_FillColor = pScheme->GetColor(pszFillColor, Color(0, 0, 0, 0)); - } - } - - //============================================================================= - // HPE_BEGIN: - // [pfreese] Added support for specifying drawcolor - //============================================================================= - const char *pszDrawColor = inResourceData->GetString("drawcolor", ""); - if (*pszDrawColor) - { - int r = 255, g = 255, b = 255, a = 255; - int len = Q_strlen(pszDrawColor) + 1; - m_pszDrawColorName = new char[ len ]; - Q_strncpy( m_pszDrawColorName, pszDrawColor, len ); - - if (sscanf(pszDrawColor, "%d %d %d %d", &r, &g, &b, &a) >= 3) - { - // it's a direct color - m_DrawColor = Color(r, g, b, a); - } - else - { - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - m_DrawColor = pScheme->GetColor(pszDrawColor, Color(255, 255, 255, 255)); - } - } - //============================================================================= - // HPE_END - //============================================================================= - - const char *pszBorder = inResourceData->GetString("border", ""); - if (*pszBorder) - { - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - SetBorder(pScheme->GetBorder(pszBorder)); - } - - BaseClass::ApplySettings(inResourceData); -} - -//----------------------------------------------------------------------------- -// Purpose: load the image, this is done just before this control is displayed -//----------------------------------------------------------------------------- -void ImagePanel::ApplySchemeSettings( IScheme *pScheme ) -{ - BaseClass::ApplySchemeSettings(pScheme); - if ( m_pszImageName && strlen( m_pszImageName ) > 0 ) - { - SetImage(scheme()->GetImage(m_pszImageName, m_bScaleImage)); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Describes editing details -//----------------------------------------------------------------------------- -const char *ImagePanel::GetDescription() -{ - static char buf[1024]; - _snprintf(buf, sizeof(buf), "%s, string image, string border, string fillcolor, bool scaleImage", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: sets whether or not the image should scale to fit the size of the ImagePanel (defaults to false) -//----------------------------------------------------------------------------- -void ImagePanel::SetShouldScaleImage( bool state ) -{ - m_bScaleImage = state; -} - -//----------------------------------------------------------------------------- -// Purpose: gets whether or not the image should be scaled to fit the size of the ImagePanel -//----------------------------------------------------------------------------- -bool ImagePanel::GetShouldScaleImage() -{ - return m_bScaleImage; -} - -//----------------------------------------------------------------------------- -// Purpose: used in conjunction with setting that the image should scale and defines an absolute scale amount -//----------------------------------------------------------------------------- -void ImagePanel::SetScaleAmount( float scale ) -{ - m_fScaleAmount = scale; -} - -float ImagePanel::GetScaleAmount( void ) -{ - return m_fScaleAmount; -} - -//----------------------------------------------------------------------------- -// Purpose: set the color to fill with, if no Image is specified -//----------------------------------------------------------------------------- -void ImagePanel::SetFillColor( Color col ) -{ - m_FillColor = col; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -Color ImagePanel::GetFillColor() -{ - return m_FillColor; -} - -char *ImagePanel::GetImageName() -{ - return m_pszImageName; -} - -bool ImagePanel::EvictImage() -{ - if ( !m_pImage ) - { - // nothing to do - return false; - } - - if ( !scheme()->DeleteImage( m_pszImageName ) ) - { - // no eviction occured, could have an outstanding reference - return false; - } - - // clear out our cached concept of it - // as it may change - // the next SetImage() will re-establish - m_pImage = NULL; - delete [] m_pszImageName; - m_pszImageName = NULL; - - return true; -} - -int ImagePanel::GetNumFrames() -{ - if ( !m_pImage ) - { - return 0; - } - - return m_pImage->GetNumFrames(); -} - -void ImagePanel::SetFrame( int nFrame ) -{ - if ( !m_pImage ) - { - return; - } - - return m_pImage->SetFrame( nFrame ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY( ImagePanel ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ImagePanel::ImagePanel(Panel *parent, const char *name) : Panel(parent, name) +{ + m_pImage = NULL; + m_pszImageName = NULL; + m_pszFillColorName = NULL; + m_pszDrawColorName = NULL; // HPE addition + m_bCenterImage = false; + m_bScaleImage = false; + m_bTileImage = false; + m_bTileHorizontally = false; + m_bTileVertically = false; + m_fScaleAmount = 0.0f; + m_FillColor = Color(0, 0, 0, 0); + m_DrawColor = Color(255,255,255,255); + m_iRotation = ROTATED_UNROTATED; + + SetImage( m_pImage ); + + REGISTER_COLOR_AS_OVERRIDABLE( m_FillColor, "fillcolor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( m_DrawColor, "drawcolor_override" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ImagePanel::~ImagePanel() +{ + delete [] m_pszImageName; + delete [] m_pszFillColorName; + delete [] m_pszDrawColorName; // HPE addition +} + +//----------------------------------------------------------------------------- +// Purpose: handles size changing +//----------------------------------------------------------------------------- +void ImagePanel::OnSizeChanged(int newWide, int newTall) +{ + BaseClass::OnSizeChanged(newWide, newTall); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ImagePanel::SetImage(IImage *image) +{ + m_pImage = image; + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: sets an image by file name +//----------------------------------------------------------------------------- +void ImagePanel::SetImage(const char *imageName) +{ + if ( imageName && m_pszImageName && V_stricmp( imageName, m_pszImageName ) == 0 ) + return; + + int len = Q_strlen(imageName) + 1; + delete [] m_pszImageName; + m_pszImageName = new char[ len ]; + Q_strncpy(m_pszImageName, imageName, len ); + InvalidateLayout(false, true); // force applyschemesettings to run +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IImage *ImagePanel::GetImage() +{ + return m_pImage; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Color ImagePanel::GetDrawColor( void ) +{ + return m_DrawColor; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ImagePanel::SetDrawColor( Color drawColor ) +{ + m_DrawColor = drawColor; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ImagePanel::PaintBackground() +{ + if (m_FillColor[3] > 0) + { + // draw the specified fill color + int wide, tall; + GetSize(wide, tall); + surface()->DrawSetColor(m_FillColor); + surface()->DrawFilledRect(0, 0, wide, tall); + } + if ( m_pImage ) + { + //============================================================================= + // HPE_BEGIN: + // [pfreese] Color should be always set from GetDrawColor(), not just when + // scaling is true (see previous code) + //============================================================================= + + // surface()->DrawSetColor( 255, 255, 255, GetAlpha() ); + m_pImage->SetColor( GetDrawColor() ); + + //============================================================================= + // HPE_END + //============================================================================= + + if ( m_bCenterImage ) + { + int wide, tall; + GetSize(wide, tall); + + int imageWide, imageTall; + m_pImage->GetSize( imageWide, imageTall ); + + if ( m_bScaleImage && m_fScaleAmount > 0.0f ) + { + imageWide = static_cast( static_cast(imageWide) * m_fScaleAmount ); + imageTall = static_cast( static_cast(imageTall) * m_fScaleAmount ); + } + + m_pImage->SetPos( (wide - imageWide) / 2, (tall - imageTall) / 2 ); + } + else + { + m_pImage->SetPos(0, 0); + } + + if (m_bScaleImage) + { + // Image size is stored in the bitmap, so temporarily set its size + // to our panel size and then restore after we draw it. + + int imageWide, imageTall; + m_pImage->GetSize( imageWide, imageTall ); + + if ( m_fScaleAmount > 0.0f ) + { + float wide, tall; + wide = static_cast(imageWide) * m_fScaleAmount; + tall = static_cast(imageTall) * m_fScaleAmount; + m_pImage->SetSize( static_cast(wide), static_cast(tall) ); + } + else + { + int wide, tall; + GetSize( wide, tall ); + m_pImage->SetSize( wide, tall ); + } + + m_pImage->Paint(); + + m_pImage->SetSize( imageWide, imageTall ); + } + else if ( m_bTileImage || m_bTileHorizontally || m_bTileVertically ) + { + int wide, tall; + GetSize(wide, tall); + int imageWide, imageTall; + m_pImage->GetSize( imageWide, imageTall ); + + int y = 0; + while ( y < tall ) + { + int x = 0; + while (x < wide) + { + m_pImage->SetPos(x,y); + m_pImage->Paint(); + + x += imageWide; + + if ( !m_bTileHorizontally ) + break; + } + + y += imageTall; + + if ( !m_bTileVertically ) + break; + } + m_pImage->SetPos(0, 0); + } + else + { + m_pImage->SetColor( GetDrawColor() ); + m_pImage->Paint(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Gets control settings for editing +//----------------------------------------------------------------------------- +void ImagePanel::GetSettings(KeyValues *outResourceData) +{ + BaseClass::GetSettings(outResourceData); + if (m_pszImageName) + { + outResourceData->SetString("image", m_pszImageName); + } + if (m_pszFillColorName) + { + outResourceData->SetString("fillcolor", m_pszFillColorName); + } + + //============================================================================= + // HPE_BEGIN: + // [pfreese] Added support for specifying drawcolor + //============================================================================= + if (m_pszDrawColorName) + { + outResourceData->SetString("drawcolor", m_pszDrawColorName); + } + //============================================================================= + // HPE_END + //============================================================================= + + if (GetBorder()) + { + outResourceData->SetString("border", GetBorder()->GetName()); + } + + outResourceData->SetInt("scaleImage", m_bScaleImage); + outResourceData->SetFloat("scaleAmount", m_fScaleAmount); + outResourceData->SetInt("tileImage", m_bTileImage); + outResourceData->SetInt("tileHorizontally", m_bTileHorizontally); + outResourceData->SetInt("tileVertically", m_bTileVertically); +} + +//----------------------------------------------------------------------------- +// Purpose: Applies designer settings from res file +//----------------------------------------------------------------------------- +void ImagePanel::ApplySettings(KeyValues *inResourceData) +{ + delete [] m_pszImageName; + delete [] m_pszFillColorName; + delete [] m_pszDrawColorName; // HPE addition + m_pszImageName = NULL; + m_pszFillColorName = NULL; + m_pszDrawColorName = NULL; // HPE addition + + m_bScaleImage = inResourceData->GetInt("scaleImage", 0); + m_fScaleAmount = inResourceData->GetFloat("scaleAmount", 0.0f); + m_bTileImage = inResourceData->GetInt("tileImage", 0); + m_bTileHorizontally = inResourceData->GetInt("tileHorizontally", m_bTileImage); + m_bTileVertically = inResourceData->GetInt("tileVertically", m_bTileImage); + const char *imageName = inResourceData->GetString("image", ""); + if ( *imageName ) + { + SetImage( imageName ); + } + + const char *pszFillColor = inResourceData->GetString("fillcolor", ""); + if (*pszFillColor) + { + int r = 0, g = 0, b = 0, a = 255; + int len = Q_strlen(pszFillColor) + 1; + m_pszFillColorName = new char[ len ]; + Q_strncpy( m_pszFillColorName, pszFillColor, len ); + + if (sscanf(pszFillColor, "%d %d %d %d", &r, &g, &b, &a) >= 3) + { + // it's a direct color + m_FillColor = Color(r, g, b, a); + } + else + { + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + m_FillColor = pScheme->GetColor(pszFillColor, Color(0, 0, 0, 0)); + } + } + + //============================================================================= + // HPE_BEGIN: + // [pfreese] Added support for specifying drawcolor + //============================================================================= + const char *pszDrawColor = inResourceData->GetString("drawcolor", ""); + if (*pszDrawColor) + { + int r = 255, g = 255, b = 255, a = 255; + int len = Q_strlen(pszDrawColor) + 1; + m_pszDrawColorName = new char[ len ]; + Q_strncpy( m_pszDrawColorName, pszDrawColor, len ); + + if (sscanf(pszDrawColor, "%d %d %d %d", &r, &g, &b, &a) >= 3) + { + // it's a direct color + m_DrawColor = Color(r, g, b, a); + } + else + { + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + m_DrawColor = pScheme->GetColor(pszDrawColor, Color(255, 255, 255, 255)); + } + } + //============================================================================= + // HPE_END + //============================================================================= + + const char *pszBorder = inResourceData->GetString("border", ""); + if (*pszBorder) + { + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + SetBorder(pScheme->GetBorder(pszBorder)); + } + + BaseClass::ApplySettings(inResourceData); +} + +//----------------------------------------------------------------------------- +// Purpose: load the image, this is done just before this control is displayed +//----------------------------------------------------------------------------- +void ImagePanel::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings(pScheme); + if ( m_pszImageName && strlen( m_pszImageName ) > 0 ) + { + SetImage(scheme()->GetImage(m_pszImageName, m_bScaleImage)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Describes editing details +//----------------------------------------------------------------------------- +const char *ImagePanel::GetDescription() +{ + static char buf[1024]; + _snprintf(buf, sizeof(buf), "%s, string image, string border, string fillcolor, bool scaleImage", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: sets whether or not the image should scale to fit the size of the ImagePanel (defaults to false) +//----------------------------------------------------------------------------- +void ImagePanel::SetShouldScaleImage( bool state ) +{ + m_bScaleImage = state; +} + +//----------------------------------------------------------------------------- +// Purpose: gets whether or not the image should be scaled to fit the size of the ImagePanel +//----------------------------------------------------------------------------- +bool ImagePanel::GetShouldScaleImage() +{ + return m_bScaleImage; +} + +//----------------------------------------------------------------------------- +// Purpose: used in conjunction with setting that the image should scale and defines an absolute scale amount +//----------------------------------------------------------------------------- +void ImagePanel::SetScaleAmount( float scale ) +{ + m_fScaleAmount = scale; +} + +float ImagePanel::GetScaleAmount( void ) +{ + return m_fScaleAmount; +} + +//----------------------------------------------------------------------------- +// Purpose: set the color to fill with, if no Image is specified +//----------------------------------------------------------------------------- +void ImagePanel::SetFillColor( Color col ) +{ + m_FillColor = col; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +Color ImagePanel::GetFillColor() +{ + return m_FillColor; +} + +char *ImagePanel::GetImageName() +{ + return m_pszImageName; +} + +bool ImagePanel::EvictImage() +{ + if ( !m_pImage ) + { + // nothing to do + return false; + } + + if ( !scheme()->DeleteImage( m_pszImageName ) ) + { + // no eviction occured, could have an outstanding reference + return false; + } + + // clear out our cached concept of it + // as it may change + // the next SetImage() will re-establish + m_pImage = NULL; + delete [] m_pszImageName; + m_pszImageName = NULL; + + return true; +} + +int ImagePanel::GetNumFrames() +{ + if ( !m_pImage ) + { + return 0; + } + + return m_pImage->GetNumFrames(); +} + +void ImagePanel::SetFrame( int nFrame ) +{ + if ( !m_pImage ) + { + return; + } + + return m_pImage->SetFrame( nFrame ); +} diff --git a/mp/src/vgui2/vgui_controls/InputDialog.cpp b/mp/src/vgui2/vgui_controls/InputDialog.cpp index 2e1e0624..ca7cf704 100644 --- a/mp/src/vgui2/vgui_controls/InputDialog.cpp +++ b/mp/src/vgui2/vgui_controls/InputDialog.cpp @@ -1,236 +1,236 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include -#include -#include -#include -#include "tier1/KeyValues.h" -#include "vgui/IInput.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -BaseInputDialog::BaseInputDialog( vgui::Panel *parent, const char *title ) : - BaseClass( parent, NULL ) -{ - m_pContextKeyValues = NULL; - - SetDeleteSelfOnClose( true ); - SetTitle(title, true); - SetSize(320, 180); - SetSizeable( false ); - - m_pCancelButton = new Button(this, "CancelButton", "#VGui_Cancel"); - m_pOKButton = new Button(this, "OKButton", "#VGui_OK"); - m_pCancelButton->SetCommand("Cancel"); - m_pOKButton->SetCommand("OK"); - m_pOKButton->SetAsDefaultButton( true ); - - if ( parent ) - { - AddActionSignalTarget( parent ); - } -} - -BaseInputDialog::~BaseInputDialog() -{ - CleanUpContextKeyValues(); -} - -//----------------------------------------------------------------------------- -// Purpose: Cleans up the keyvalues -//----------------------------------------------------------------------------- -void BaseInputDialog::CleanUpContextKeyValues() -{ - if ( m_pContextKeyValues ) - { - m_pContextKeyValues->deleteThis(); - m_pContextKeyValues = NULL; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void BaseInputDialog::DoModal( KeyValues *pContextKeyValues ) -{ - CleanUpContextKeyValues(); - m_pContextKeyValues = pContextKeyValues; - BaseClass::DoModal(); -} - -//----------------------------------------------------------------------------- -// Purpose: lays out controls -//----------------------------------------------------------------------------- -void BaseInputDialog::PerformLayout() -{ - BaseClass::PerformLayout(); - - int w, h; - GetSize( w, h ); - - // lay out all the controls - int topy = IsSmallCaption() ? 15 : 30; - int halfw = w / 2; - - PerformLayout( 12, topy, w - 24, h - 100 ); - - m_pOKButton->SetBounds( halfw - 84, h - 30, 72, 24 ); - m_pCancelButton->SetBounds( halfw + 12, h - 30, 72, 24 ); -} - - -//----------------------------------------------------------------------------- -// Purpose: handles button commands -//----------------------------------------------------------------------------- -void BaseInputDialog::OnCommand(const char *command) -{ - KeyValues *kv = NULL; - if ( !stricmp( command, "OK" ) ) - { - kv = new KeyValues( "InputCompleted" ); - kv->SetPtr( "dialog", this ); - } - else if ( !stricmp( command, "Cancel" ) ) - { - kv = new KeyValues( "InputCanceled" ); - } - else - { - BaseClass::OnCommand( command ); - return; - } - - if ( m_pContextKeyValues ) - { - kv->AddSubKey( m_pContextKeyValues ); - m_pContextKeyValues = NULL; - } - PostActionSignal( kv ); - CloseModal(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Utility dialog, used to ask yes/no questions of the user -//----------------------------------------------------------------------------- -InputMessageBox::InputMessageBox( vgui::Panel *parent, const char *title, char const *prompt ) -: BaseClass( parent, title ) -{ - SetSize( 320, 120 ); - - m_pPrompt = new Label( this, "Prompt", prompt ); -} - -InputMessageBox::~InputMessageBox() -{ -} - -void InputMessageBox::PerformLayout( int x, int y, int w, int h ) -{ - m_pPrompt->SetBounds( x, y, w, 24 ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -InputDialog::InputDialog(vgui::Panel *parent, const char *title, char const *prompt, char const *defaultValue /*=""*/ ) : - BaseClass(parent, title) -{ - SetSize( 320, 120 ); - - m_pPrompt = new Label( this, "Prompt", prompt ); - - m_pInput = new TextEntry( this, "Text" ); - m_pInput->SetText( defaultValue ); - m_pInput->SelectAllText( true ); - m_pInput->RequestFocus(); -} - - -InputDialog::~InputDialog() -{ -} - - -//----------------------------------------------------------------------------- -// Sets the dialog to be multiline -//----------------------------------------------------------------------------- -void InputDialog::SetMultiline( bool state ) -{ - m_pInput->SetMultiline( state ); - m_pInput->SetCatchEnterKey( state ); -} - - -//----------------------------------------------------------------------------- -// Allow numeric input only -//----------------------------------------------------------------------------- -void InputDialog::AllowNumericInputOnly( bool bOnlyNumeric ) -{ - if ( m_pInput ) - { - m_pInput->SetAllowNumericInputOnly( bOnlyNumeric ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: lays out controls -//----------------------------------------------------------------------------- -void InputDialog::PerformLayout( int x, int y, int w, int h ) -{ - m_pPrompt->SetBounds( x, y, w, 24 ); - m_pInput ->SetBounds( x, y + 30, w, m_pInput->IsMultiline() ? h - 30 : 24 ); -} - - -//----------------------------------------------------------------------------- -// Purpose: handles button commands -//----------------------------------------------------------------------------- -void InputDialog::OnCommand(const char *command) -{ - // overriding OnCommand for backwards compatability - // it'd be nice at some point to find all uses of InputDialog and just use BaseInputDialog's OnCommand - - if (!stricmp(command, "OK")) - { - int nTextLength = m_pInput->GetTextLength() + 1; - char* txt = (char*)_alloca( nTextLength * sizeof(char) ); - m_pInput->GetText( txt, nTextLength ); - KeyValues *kv = new KeyValues( "InputCompleted", "text", txt ); - if ( m_pContextKeyValues ) - { - kv->AddSubKey( m_pContextKeyValues ); - m_pContextKeyValues = NULL; - } - PostActionSignal( kv ); - CloseModal(); - } - else if (!stricmp(command, "Cancel")) - { - KeyValues *kv = new KeyValues( "InputCanceled" ); - if ( m_pContextKeyValues ) - { - kv->AddSubKey( m_pContextKeyValues ); - m_pContextKeyValues = NULL; - } - PostActionSignal( kv ); - CloseModal(); - } - else - { - BaseClass::OnCommand(command); - } -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include +#include +#include +#include +#include "tier1/KeyValues.h" +#include "vgui/IInput.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +BaseInputDialog::BaseInputDialog( vgui::Panel *parent, const char *title ) : + BaseClass( parent, NULL ) +{ + m_pContextKeyValues = NULL; + + SetDeleteSelfOnClose( true ); + SetTitle(title, true); + SetSize(320, 180); + SetSizeable( false ); + + m_pCancelButton = new Button(this, "CancelButton", "#VGui_Cancel"); + m_pOKButton = new Button(this, "OKButton", "#VGui_OK"); + m_pCancelButton->SetCommand("Cancel"); + m_pOKButton->SetCommand("OK"); + m_pOKButton->SetAsDefaultButton( true ); + + if ( parent ) + { + AddActionSignalTarget( parent ); + } +} + +BaseInputDialog::~BaseInputDialog() +{ + CleanUpContextKeyValues(); +} + +//----------------------------------------------------------------------------- +// Purpose: Cleans up the keyvalues +//----------------------------------------------------------------------------- +void BaseInputDialog::CleanUpContextKeyValues() +{ + if ( m_pContextKeyValues ) + { + m_pContextKeyValues->deleteThis(); + m_pContextKeyValues = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void BaseInputDialog::DoModal( KeyValues *pContextKeyValues ) +{ + CleanUpContextKeyValues(); + m_pContextKeyValues = pContextKeyValues; + BaseClass::DoModal(); +} + +//----------------------------------------------------------------------------- +// Purpose: lays out controls +//----------------------------------------------------------------------------- +void BaseInputDialog::PerformLayout() +{ + BaseClass::PerformLayout(); + + int w, h; + GetSize( w, h ); + + // lay out all the controls + int topy = IsSmallCaption() ? 15 : 30; + int halfw = w / 2; + + PerformLayout( 12, topy, w - 24, h - 100 ); + + m_pOKButton->SetBounds( halfw - 84, h - 30, 72, 24 ); + m_pCancelButton->SetBounds( halfw + 12, h - 30, 72, 24 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: handles button commands +//----------------------------------------------------------------------------- +void BaseInputDialog::OnCommand(const char *command) +{ + KeyValues *kv = NULL; + if ( !stricmp( command, "OK" ) ) + { + kv = new KeyValues( "InputCompleted" ); + kv->SetPtr( "dialog", this ); + } + else if ( !stricmp( command, "Cancel" ) ) + { + kv = new KeyValues( "InputCanceled" ); + } + else + { + BaseClass::OnCommand( command ); + return; + } + + if ( m_pContextKeyValues ) + { + kv->AddSubKey( m_pContextKeyValues ); + m_pContextKeyValues = NULL; + } + PostActionSignal( kv ); + CloseModal(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Utility dialog, used to ask yes/no questions of the user +//----------------------------------------------------------------------------- +InputMessageBox::InputMessageBox( vgui::Panel *parent, const char *title, char const *prompt ) +: BaseClass( parent, title ) +{ + SetSize( 320, 120 ); + + m_pPrompt = new Label( this, "Prompt", prompt ); +} + +InputMessageBox::~InputMessageBox() +{ +} + +void InputMessageBox::PerformLayout( int x, int y, int w, int h ) +{ + m_pPrompt->SetBounds( x, y, w, 24 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +InputDialog::InputDialog(vgui::Panel *parent, const char *title, char const *prompt, char const *defaultValue /*=""*/ ) : + BaseClass(parent, title) +{ + SetSize( 320, 120 ); + + m_pPrompt = new Label( this, "Prompt", prompt ); + + m_pInput = new TextEntry( this, "Text" ); + m_pInput->SetText( defaultValue ); + m_pInput->SelectAllText( true ); + m_pInput->RequestFocus(); +} + + +InputDialog::~InputDialog() +{ +} + + +//----------------------------------------------------------------------------- +// Sets the dialog to be multiline +//----------------------------------------------------------------------------- +void InputDialog::SetMultiline( bool state ) +{ + m_pInput->SetMultiline( state ); + m_pInput->SetCatchEnterKey( state ); +} + + +//----------------------------------------------------------------------------- +// Allow numeric input only +//----------------------------------------------------------------------------- +void InputDialog::AllowNumericInputOnly( bool bOnlyNumeric ) +{ + if ( m_pInput ) + { + m_pInput->SetAllowNumericInputOnly( bOnlyNumeric ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: lays out controls +//----------------------------------------------------------------------------- +void InputDialog::PerformLayout( int x, int y, int w, int h ) +{ + m_pPrompt->SetBounds( x, y, w, 24 ); + m_pInput ->SetBounds( x, y + 30, w, m_pInput->IsMultiline() ? h - 30 : 24 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: handles button commands +//----------------------------------------------------------------------------- +void InputDialog::OnCommand(const char *command) +{ + // overriding OnCommand for backwards compatability + // it'd be nice at some point to find all uses of InputDialog and just use BaseInputDialog's OnCommand + + if (!stricmp(command, "OK")) + { + int nTextLength = m_pInput->GetTextLength() + 1; + char* txt = (char*)_alloca( nTextLength * sizeof(char) ); + m_pInput->GetText( txt, nTextLength ); + KeyValues *kv = new KeyValues( "InputCompleted", "text", txt ); + if ( m_pContextKeyValues ) + { + kv->AddSubKey( m_pContextKeyValues ); + m_pContextKeyValues = NULL; + } + PostActionSignal( kv ); + CloseModal(); + } + else if (!stricmp(command, "Cancel")) + { + KeyValues *kv = new KeyValues( "InputCanceled" ); + if ( m_pContextKeyValues ) + { + kv->AddSubKey( m_pContextKeyValues ); + m_pContextKeyValues = NULL; + } + PostActionSignal( kv ); + CloseModal(); + } + else + { + BaseClass::OnCommand(command); + } +} diff --git a/mp/src/vgui2/vgui_controls/KeyBindingHelpDialog.cpp b/mp/src/vgui2/vgui_controls/KeyBindingHelpDialog.cpp index 531ff1df..ca92c858 100644 --- a/mp/src/vgui2/vgui_controls/KeyBindingHelpDialog.cpp +++ b/mp/src/vgui2/vgui_controls/KeyBindingHelpDialog.cpp @@ -1,355 +1,355 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//============================================================================= - -#include "vgui_controls/KeyBindingHelpDialog.h" -#include "vgui_controls/ListPanel.h" -#include "vgui/ISurface.h" -#include "vgui/IVGui.h" -#include "vgui/ILocalize.h" -#include "vgui/IInput.h" -#include "vgui/ISystem.h" -#include "KeyValues.h" -#include "vgui/Cursor.h" -#include "tier1/utldict.h" -#include "vgui_controls/KeyBoardEditorDialog.h" - -// NOTE: This has to be the last file included! -#include "tier0/memdbgon.h" - - -using namespace vgui; - -// If the user holds the key bound to help down for this long, then the dialog will stay on automatically -#define KB_HELP_CONTINUE_SHOWING_TIME 1.0 - -static bool BindingLessFunc( KeyValues * const & lhs, KeyValues * const &rhs ) -{ - KeyValues *p1, *p2; - - p1 = const_cast< KeyValues * >( lhs ); - p2 = const_cast< KeyValues * >( rhs ); - return ( Q_stricmp( p1->GetString( "Action" ), p2->GetString( "Action" ) ) < 0 ) ? true : false; -} - -CKeyBindingHelpDialog::CKeyBindingHelpDialog( Panel *parent, Panel *panelToView, KeyBindingContextHandle_t handle, KeyCode code, int modifiers ) - : BaseClass( parent, "KeyBindingHelpDialog" ), - m_Handle( handle ), - m_KeyCode( code ), - m_Modifiers( modifiers ), - m_bPermanent( false ) -{ - Assert( panelToView ); - m_hPanel = panelToView; - - m_pList = new ListPanel( this, "KeyBindings" ); - m_pList->SetIgnoreDoubleClick( true ); - m_pList->AddColumnHeader(0, "Action", "#KBEditorBindingName", 175, 0); - m_pList->AddColumnHeader(1, "Binding", "#KBEditorBinding", 175, 0); - m_pList->AddColumnHeader(2, "Description", "#KBEditorDescription", 300, 0); - - LoadControlSettings( "resource/KeyBindingHelpDialog.res" ); - - if ( panelToView && panelToView->GetName() && panelToView->GetName()[0] ) - { - SetTitle( panelToView->GetName(), true ); - } - else - { - SetTitle( "#KBHelpDialogTitle", true ); - } - - SetSmallCaption( true ); - SetMinimumSize( 400, 400 ); - SetMinimizeButtonVisible( false ); - SetMaximizeButtonVisible( false ); - SetSizeable( true ); - SetMoveable( true ); - SetMenuButtonVisible( false ); - - SetVisible( true ); - - MoveToCenterOfScreen(); - - PopulateList(); - - m_flShowTime = system()->GetCurrentTime(); - ivgui()->AddTickSignal( GetVPanel(), 0 ); - - input()->SetAppModalSurface( GetVPanel() ); -} - -CKeyBindingHelpDialog::~CKeyBindingHelpDialog() -{ - if ( input()->GetAppModalSurface() == GetVPanel() ) - { - input()->SetAppModalSurface( 0 ); - } -} - -void CKeyBindingHelpDialog::OnTick() -{ - BaseClass::OnTick(); - - bool keyStillDown = IsHelpKeyStillBeingHeld(); - - double curtime = system()->GetCurrentTime(); - double elapsed = curtime - m_flShowTime; - // After a second of holding the key, releasing the key will close the dialog - if ( elapsed > KB_HELP_CONTINUE_SHOWING_TIME ) - { - if ( !keyStillDown ) - { - MarkForDeletion(); - return; - } - } - // Otherwise, if they tapped the key within a second and now have released... - else if ( !keyStillDown ) - { - // Continue showing dialog indefinitely - ivgui()->RemoveTickSignal( GetVPanel() ); - m_bPermanent = true; - } -} - -// The key originally bound to help was pressed -void CKeyBindingHelpDialog::HelpKeyPressed() -{ - // Don't kill while editor is being shown... - if ( m_hKeyBindingsEditor.Get() ) - return; - - if ( m_bPermanent ) - { - MarkForDeletion(); - } -} - -bool CKeyBindingHelpDialog::IsHelpKeyStillBeingHeld() -{ - bool keyDown = input()->IsKeyDown( m_KeyCode ); - if ( !keyDown ) - return false; - - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - - int modifiers = 0; - if ( shift ) - { - modifiers |= MODIFIER_SHIFT; - } - if ( ctrl ) - { - modifiers |= MODIFIER_CONTROL; - } - if ( alt ) - { - modifiers |= MODIFIER_ALT; - } - - if ( modifiers != m_Modifiers ) - { - return false; - } - - return true; -} - -void CKeyBindingHelpDialog::OnCommand( char const *cmd ) -{ - if ( !Q_stricmp( cmd, "OK" ) || - !Q_stricmp( cmd, "cancel" ) || - !Q_stricmp( cmd, "Close" ) ) - { - MarkForDeletion(); - } - else if ( !Q_stricmp( cmd, "edit" ) ) - { - // Show the keybindings edit dialog - if ( m_hKeyBindingsEditor.Get() ) - { - delete m_hKeyBindingsEditor.Get(); - } - - // Don't delete panel if H key is released... - - m_hKeyBindingsEditor = new CKeyBoardEditorDialog( this, m_hPanel, m_Handle ); - m_hKeyBindingsEditor->DoModal(); - - ivgui()->RemoveTickSignal( GetVPanel() ); - m_bPermanent = true; - } - else - { - BaseClass::OnCommand( cmd ); - } -} - -void CKeyBindingHelpDialog::OnKeyCodeTyped(vgui::KeyCode code) -{ - BaseClass::OnKeyCodeTyped( code ); -} - -void CKeyBindingHelpDialog::GetMappingList( Panel *panel, CUtlVector< PanelKeyBindingMap * >& maps ) -{ - PanelKeyBindingMap *map = panel->GetKBMap(); - while ( map ) - { - maps.AddToTail( map ); - map = map->baseMap; - } -} - - -void CKeyBindingHelpDialog::AnsiText( char const *token, char *out, size_t buflen ) -{ - out[ 0 ] = 0; - - wchar_t *str = g_pVGuiLocalize->Find( token ); - if ( !str ) - { - Q_strncpy( out, token, buflen ); - } - else - { - g_pVGuiLocalize->ConvertUnicodeToANSI( str, out, buflen ); - } -} - -struct ListInfo_t -{ - PanelKeyBindingMap *m_pMap; - Panel *m_pPanel; -}; - -void CKeyBindingHelpDialog::PopulateList() -{ - m_pList->DeleteAllItems(); - - int i, j; - - CUtlVector< ListInfo_t > maps; - vgui::Panel *pPanel = m_hPanel; - while ( pPanel->IsKeyBindingChainToParentAllowed() ) - { - PanelKeyBindingMap *map = pPanel->GetKBMap(); - while ( map ) - { - int k; - int c = maps.Count(); - for ( k = 0; k < c; ++k ) - { - if ( maps[k].m_pMap == map ) - break; - } - if ( k == c ) - { - int k = maps.AddToTail( ); - maps[k].m_pMap = map; - maps[k].m_pPanel = pPanel; - } - map = map->baseMap; - } - - pPanel = pPanel->GetParent(); - if ( !pPanel ) - break; - } - - CUtlRBTree< KeyValues *, int > sorted( 0, 0, BindingLessFunc ); - - // add header item - int c = maps.Count(); - for ( i = 0; i < c; ++i ) - { - PanelKeyBindingMap *m = maps[ i ].m_pMap; - Panel *pPanel = maps[i].m_pPanel; - Assert( m ); - - int bindings = m->boundkeys.Count(); - for ( j = 0; j < bindings; ++j ) - { - BoundKey_t *kbMap = &m->boundkeys[ j ]; - Assert( kbMap ); - - // Create a new: blank item - KeyValues *item = new KeyValues( "Item" ); - - // Fill in data - char loc[ 128 ]; - Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname ); - - char ansi[ 256 ]; - AnsiText( loc, ansi, sizeof( ansi ) ); - - item->SetString( "Action", ansi ); - item->SetWString( "Binding", Panel::KeyCodeModifiersToDisplayString( (KeyCode)kbMap->keycode, kbMap->modifiers ) ); - - // Find the binding - KeyBindingMap_t *bindingMap = pPanel->LookupBinding( kbMap->bindingname ); - if ( bindingMap && - bindingMap->helpstring ) - { - AnsiText( bindingMap->helpstring, ansi, sizeof( ansi ) ); - item->SetString( "Description", ansi ); - } - - item->SetPtr( "Item", kbMap ); - - sorted.Insert( item ); - } - - // Now try and find any "unbound" keys... - int mappings = m->entries.Count(); - for ( j = 0; j < mappings; ++j ) - { - KeyBindingMap_t *kbMap = &m->entries[ j ]; - - // See if it's bound - CUtlVector< BoundKey_t * > list; - pPanel->LookupBoundKeys( kbMap->bindingname, list ); - if ( list.Count() > 0 ) - continue; - - // Not bound, add a placeholder entry - // Create a new: blank item - KeyValues *item = new KeyValues( "Item" ); - - // fill in data - char loc[ 128 ]; - Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname ); - - char ansi[ 256 ]; - AnsiText( loc, ansi, sizeof( ansi ) ); - - item->SetString( "Action", ansi ); - item->SetWString( "Binding", L"" ); - if ( kbMap->helpstring ) - { - AnsiText( kbMap->helpstring, ansi, sizeof( ansi ) ); - item->SetString( "Description", ansi ); - } - - item->SetPtr( "Unbound", kbMap ); - - sorted.Insert( item ); - } - } - - for ( j = sorted.FirstInorder() ; j != sorted.InvalidIndex(); j = sorted.NextInorder( j ) ) - { - KeyValues *item = sorted[ j ]; - - // Add to list - m_pList->AddItem( item, 0, false, false ); - - item->deleteThis(); - } - - sorted.RemoveAll(); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "vgui_controls/KeyBindingHelpDialog.h" +#include "vgui_controls/ListPanel.h" +#include "vgui/ISurface.h" +#include "vgui/IVGui.h" +#include "vgui/ILocalize.h" +#include "vgui/IInput.h" +#include "vgui/ISystem.h" +#include "KeyValues.h" +#include "vgui/Cursor.h" +#include "tier1/utldict.h" +#include "vgui_controls/KeyBoardEditorDialog.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +using namespace vgui; + +// If the user holds the key bound to help down for this long, then the dialog will stay on automatically +#define KB_HELP_CONTINUE_SHOWING_TIME 1.0 + +static bool BindingLessFunc( KeyValues * const & lhs, KeyValues * const &rhs ) +{ + KeyValues *p1, *p2; + + p1 = const_cast< KeyValues * >( lhs ); + p2 = const_cast< KeyValues * >( rhs ); + return ( Q_stricmp( p1->GetString( "Action" ), p2->GetString( "Action" ) ) < 0 ) ? true : false; +} + +CKeyBindingHelpDialog::CKeyBindingHelpDialog( Panel *parent, Panel *panelToView, KeyBindingContextHandle_t handle, KeyCode code, int modifiers ) + : BaseClass( parent, "KeyBindingHelpDialog" ), + m_Handle( handle ), + m_KeyCode( code ), + m_Modifiers( modifiers ), + m_bPermanent( false ) +{ + Assert( panelToView ); + m_hPanel = panelToView; + + m_pList = new ListPanel( this, "KeyBindings" ); + m_pList->SetIgnoreDoubleClick( true ); + m_pList->AddColumnHeader(0, "Action", "#KBEditorBindingName", 175, 0); + m_pList->AddColumnHeader(1, "Binding", "#KBEditorBinding", 175, 0); + m_pList->AddColumnHeader(2, "Description", "#KBEditorDescription", 300, 0); + + LoadControlSettings( "resource/KeyBindingHelpDialog.res" ); + + if ( panelToView && panelToView->GetName() && panelToView->GetName()[0] ) + { + SetTitle( panelToView->GetName(), true ); + } + else + { + SetTitle( "#KBHelpDialogTitle", true ); + } + + SetSmallCaption( true ); + SetMinimumSize( 400, 400 ); + SetMinimizeButtonVisible( false ); + SetMaximizeButtonVisible( false ); + SetSizeable( true ); + SetMoveable( true ); + SetMenuButtonVisible( false ); + + SetVisible( true ); + + MoveToCenterOfScreen(); + + PopulateList(); + + m_flShowTime = system()->GetCurrentTime(); + ivgui()->AddTickSignal( GetVPanel(), 0 ); + + input()->SetAppModalSurface( GetVPanel() ); +} + +CKeyBindingHelpDialog::~CKeyBindingHelpDialog() +{ + if ( input()->GetAppModalSurface() == GetVPanel() ) + { + input()->SetAppModalSurface( 0 ); + } +} + +void CKeyBindingHelpDialog::OnTick() +{ + BaseClass::OnTick(); + + bool keyStillDown = IsHelpKeyStillBeingHeld(); + + double curtime = system()->GetCurrentTime(); + double elapsed = curtime - m_flShowTime; + // After a second of holding the key, releasing the key will close the dialog + if ( elapsed > KB_HELP_CONTINUE_SHOWING_TIME ) + { + if ( !keyStillDown ) + { + MarkForDeletion(); + return; + } + } + // Otherwise, if they tapped the key within a second and now have released... + else if ( !keyStillDown ) + { + // Continue showing dialog indefinitely + ivgui()->RemoveTickSignal( GetVPanel() ); + m_bPermanent = true; + } +} + +// The key originally bound to help was pressed +void CKeyBindingHelpDialog::HelpKeyPressed() +{ + // Don't kill while editor is being shown... + if ( m_hKeyBindingsEditor.Get() ) + return; + + if ( m_bPermanent ) + { + MarkForDeletion(); + } +} + +bool CKeyBindingHelpDialog::IsHelpKeyStillBeingHeld() +{ + bool keyDown = input()->IsKeyDown( m_KeyCode ); + if ( !keyDown ) + return false; + + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + + int modifiers = 0; + if ( shift ) + { + modifiers |= MODIFIER_SHIFT; + } + if ( ctrl ) + { + modifiers |= MODIFIER_CONTROL; + } + if ( alt ) + { + modifiers |= MODIFIER_ALT; + } + + if ( modifiers != m_Modifiers ) + { + return false; + } + + return true; +} + +void CKeyBindingHelpDialog::OnCommand( char const *cmd ) +{ + if ( !Q_stricmp( cmd, "OK" ) || + !Q_stricmp( cmd, "cancel" ) || + !Q_stricmp( cmd, "Close" ) ) + { + MarkForDeletion(); + } + else if ( !Q_stricmp( cmd, "edit" ) ) + { + // Show the keybindings edit dialog + if ( m_hKeyBindingsEditor.Get() ) + { + delete m_hKeyBindingsEditor.Get(); + } + + // Don't delete panel if H key is released... + + m_hKeyBindingsEditor = new CKeyBoardEditorDialog( this, m_hPanel, m_Handle ); + m_hKeyBindingsEditor->DoModal(); + + ivgui()->RemoveTickSignal( GetVPanel() ); + m_bPermanent = true; + } + else + { + BaseClass::OnCommand( cmd ); + } +} + +void CKeyBindingHelpDialog::OnKeyCodeTyped(vgui::KeyCode code) +{ + BaseClass::OnKeyCodeTyped( code ); +} + +void CKeyBindingHelpDialog::GetMappingList( Panel *panel, CUtlVector< PanelKeyBindingMap * >& maps ) +{ + PanelKeyBindingMap *map = panel->GetKBMap(); + while ( map ) + { + maps.AddToTail( map ); + map = map->baseMap; + } +} + + +void CKeyBindingHelpDialog::AnsiText( char const *token, char *out, size_t buflen ) +{ + out[ 0 ] = 0; + + wchar_t *str = g_pVGuiLocalize->Find( token ); + if ( !str ) + { + Q_strncpy( out, token, buflen ); + } + else + { + g_pVGuiLocalize->ConvertUnicodeToANSI( str, out, buflen ); + } +} + +struct ListInfo_t +{ + PanelKeyBindingMap *m_pMap; + Panel *m_pPanel; +}; + +void CKeyBindingHelpDialog::PopulateList() +{ + m_pList->DeleteAllItems(); + + int i, j; + + CUtlVector< ListInfo_t > maps; + vgui::Panel *pPanel = m_hPanel; + while ( pPanel->IsKeyBindingChainToParentAllowed() ) + { + PanelKeyBindingMap *map = pPanel->GetKBMap(); + while ( map ) + { + int k; + int c = maps.Count(); + for ( k = 0; k < c; ++k ) + { + if ( maps[k].m_pMap == map ) + break; + } + if ( k == c ) + { + int k = maps.AddToTail( ); + maps[k].m_pMap = map; + maps[k].m_pPanel = pPanel; + } + map = map->baseMap; + } + + pPanel = pPanel->GetParent(); + if ( !pPanel ) + break; + } + + CUtlRBTree< KeyValues *, int > sorted( 0, 0, BindingLessFunc ); + + // add header item + int c = maps.Count(); + for ( i = 0; i < c; ++i ) + { + PanelKeyBindingMap *m = maps[ i ].m_pMap; + Panel *pPanel = maps[i].m_pPanel; + Assert( m ); + + int bindings = m->boundkeys.Count(); + for ( j = 0; j < bindings; ++j ) + { + BoundKey_t *kbMap = &m->boundkeys[ j ]; + Assert( kbMap ); + + // Create a new: blank item + KeyValues *item = new KeyValues( "Item" ); + + // Fill in data + char loc[ 128 ]; + Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname ); + + char ansi[ 256 ]; + AnsiText( loc, ansi, sizeof( ansi ) ); + + item->SetString( "Action", ansi ); + item->SetWString( "Binding", Panel::KeyCodeModifiersToDisplayString( (KeyCode)kbMap->keycode, kbMap->modifiers ) ); + + // Find the binding + KeyBindingMap_t *bindingMap = pPanel->LookupBinding( kbMap->bindingname ); + if ( bindingMap && + bindingMap->helpstring ) + { + AnsiText( bindingMap->helpstring, ansi, sizeof( ansi ) ); + item->SetString( "Description", ansi ); + } + + item->SetPtr( "Item", kbMap ); + + sorted.Insert( item ); + } + + // Now try and find any "unbound" keys... + int mappings = m->entries.Count(); + for ( j = 0; j < mappings; ++j ) + { + KeyBindingMap_t *kbMap = &m->entries[ j ]; + + // See if it's bound + CUtlVector< BoundKey_t * > list; + pPanel->LookupBoundKeys( kbMap->bindingname, list ); + if ( list.Count() > 0 ) + continue; + + // Not bound, add a placeholder entry + // Create a new: blank item + KeyValues *item = new KeyValues( "Item" ); + + // fill in data + char loc[ 128 ]; + Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname ); + + char ansi[ 256 ]; + AnsiText( loc, ansi, sizeof( ansi ) ); + + item->SetString( "Action", ansi ); + item->SetWString( "Binding", L"" ); + if ( kbMap->helpstring ) + { + AnsiText( kbMap->helpstring, ansi, sizeof( ansi ) ); + item->SetString( "Description", ansi ); + } + + item->SetPtr( "Unbound", kbMap ); + + sorted.Insert( item ); + } + } + + for ( j = sorted.FirstInorder() ; j != sorted.InvalidIndex(); j = sorted.NextInorder( j ) ) + { + KeyValues *item = sorted[ j ]; + + // Add to list + m_pList->AddItem( item, 0, false, false ); + + item->deleteThis(); + } + + sorted.RemoveAll(); +} diff --git a/mp/src/vgui2/vgui_controls/KeyBoardEditorDialog.cpp b/mp/src/vgui2/vgui_controls/KeyBoardEditorDialog.cpp index 437f536a..c177ffaf 100644 --- a/mp/src/vgui2/vgui_controls/KeyBoardEditorDialog.cpp +++ b/mp/src/vgui2/vgui_controls/KeyBoardEditorDialog.cpp @@ -1,849 +1,849 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//============================================================================= - -#include "vgui_controls/KeyBoardEditorDialog.h" -#include "vgui_controls/ListPanel.h" -#include "vgui_controls/Button.h" -#include "vgui_controls/TextEntry.h" -#include "vgui/ISurface.h" -#include "vgui/IInput.h" -#include "vgui/IVGui.h" -#include "vgui/ILocalize.h" -#include "KeyValues.h" -#include "vgui/Cursor.h" -#include "tier1/utldict.h" - -// NOTE: This has to be the last file included! -#include "tier0/memdbgon.h" - - -using namespace vgui; - -static char *CopyString( const char *in ) -{ - if ( !in ) - return NULL; - - int len = strlen( in ); - char *n = new char[ len + 1 ]; - Q_strncpy( n, in, len + 1 ); - return n; -} - -CKeyBoardEditorPage::SaveMapping_t::SaveMapping_t() : map( 0 ) -{ -} - -CKeyBoardEditorPage::SaveMapping_t::SaveMapping_t( const SaveMapping_t& src ) -{ - map = src.map; - current = src.current; - original = src.original; -} - -//----------------------------------------------------------------------------- -// Purpose: Special list subclass to handle drawing of trap mode prompt on top of -// lists client area -//----------------------------------------------------------------------------- -class VControlsListPanel : public ListPanel -{ - DECLARE_CLASS_SIMPLE( VControlsListPanel, ListPanel ); - -public: - // Construction - VControlsListPanel( vgui::Panel *parent, const char *listName ); - virtual ~VControlsListPanel(); - - // Start/end capturing - virtual void StartCaptureMode(vgui::HCursor hCursor = NULL); - virtual void EndCaptureMode(vgui::HCursor hCursor = NULL); - virtual bool IsCapturing(); - - // Set which item should be associated with the prompt - virtual void SetItemOfInterest(int itemID); - virtual int GetItemOfInterest(); - - virtual void OnMousePressed(vgui::MouseCode code); - virtual void OnMouseDoublePressed(vgui::MouseCode code); - - KEYBINDING_FUNC( clearbinding, KEY_DELETE, 0, OnClearBinding, 0, 0 ); - -private: - void ApplySchemeSettings(vgui::IScheme *pScheme ); - - // Are we showing the prompt? - bool m_bCaptureMode; - // If so, where? - int m_nClickRow; - // Font to use for showing the prompt - vgui::HFont m_hFont; - // panel used to edit - class CInlineEditPanel *m_pInlineEditPanel; - int m_iMouseX, m_iMouseY; -}; - -//----------------------------------------------------------------------------- -// Purpose: panel used for inline editing of key bindings -//----------------------------------------------------------------------------- -class CInlineEditPanel : public vgui::Panel -{ - DECLARE_CLASS_SIMPLE( CInlineEditPanel, vgui::Panel ); - -public: - CInlineEditPanel() : vgui::Panel(NULL, "InlineEditPanel") - { - } - - virtual void Paint() - { - int wide, tall; - GetSize(wide, tall); - - // Draw a white rectangle around that cell - vgui::surface()->DrawSetColor( 63, 63, 63, 255 ); - vgui::surface()->DrawFilledRect( 0, 0, wide, tall ); - - vgui::surface()->DrawSetColor( 0, 255, 0, 255 ); - vgui::surface()->DrawOutlinedRect( 0, 0, wide, tall ); - } - - virtual void OnKeyCodeTyped(KeyCode code) - { - // forward up - if (GetParent()) - { - GetParent()->OnKeyCodeTyped(code); - } - } - - virtual void ApplySchemeSettings(IScheme *pScheme) - { - Panel::ApplySchemeSettings(pScheme); - SetBorder(pScheme->GetBorder("DepressedButtonBorder")); - } - - void OnMousePressed(vgui::MouseCode code) - { - // forward up mouse pressed messages to be handled by the key options - if (GetParent()) - { - GetParent()->OnMousePressed(code); - } - } -}; - -//----------------------------------------------------------------------------- -// Purpose: Construction -//----------------------------------------------------------------------------- -VControlsListPanel::VControlsListPanel( vgui::Panel *parent, const char *listName ) : BaseClass( parent, listName ) -{ - m_bCaptureMode = false; - m_nClickRow = 0; - m_pInlineEditPanel = new CInlineEditPanel(); - m_hFont = INVALID_FONT; -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -VControlsListPanel::~VControlsListPanel() -{ - m_pInlineEditPanel->MarkForDeletion(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void VControlsListPanel::ApplySchemeSettings(IScheme *pScheme ) -{ - BaseClass::ApplySchemeSettings( pScheme ); - m_hFont = pScheme->GetFont("DefaultVerySmall", IsProportional() ); -} - -//----------------------------------------------------------------------------- -// Purpose: Start capture prompt display -//----------------------------------------------------------------------------- -void VControlsListPanel::StartCaptureMode( HCursor hCursor ) -{ - m_bCaptureMode = true; - EnterEditMode(m_nClickRow, 1, m_pInlineEditPanel); - input()->SetMouseFocus(m_pInlineEditPanel->GetVPanel()); - input()->SetMouseCapture(m_pInlineEditPanel->GetVPanel()); - - if (hCursor) - { - m_pInlineEditPanel->SetCursor(hCursor); - - // save off the cursor position so we can restore it - vgui::input()->GetCursorPos( m_iMouseX, m_iMouseY ); - } -} - -void VControlsListPanel::OnClearBinding() -{ - if ( m_bCaptureMode ) - return; - - if ( GetItemOfInterest() < 0 ) - return; - - PostMessage( GetParent()->GetVPanel(), new KeyValues( "ClearBinding", "item", GetItemOfInterest() ) ); -} - -//----------------------------------------------------------------------------- -// Purpose: Finish capture prompt display -//----------------------------------------------------------------------------- -void VControlsListPanel::EndCaptureMode( HCursor hCursor ) -{ - m_bCaptureMode = false; - input()->SetMouseCapture(NULL); - LeaveEditMode(); - RequestFocus(); - input()->SetMouseFocus(GetVPanel()); - if (hCursor) - { - m_pInlineEditPanel->SetCursor(hCursor); - surface()->SetCursor(hCursor); - if ( hCursor != dc_none ) - { - vgui::input()->SetCursorPos ( m_iMouseX, m_iMouseY ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set active row column -//----------------------------------------------------------------------------- -void VControlsListPanel::SetItemOfInterest(int itemID) -{ - m_nClickRow = itemID; -} - -//----------------------------------------------------------------------------- -// Purpose: Retrieve row, column of interest -//----------------------------------------------------------------------------- -int VControlsListPanel::GetItemOfInterest() -{ - return m_nClickRow; -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if we're currently waiting to capture a key -//----------------------------------------------------------------------------- -bool VControlsListPanel::IsCapturing( void ) -{ - return m_bCaptureMode; -} - -//----------------------------------------------------------------------------- -// Purpose: Forwards mouse pressed message up to keyboard page when in capture -//----------------------------------------------------------------------------- -void VControlsListPanel::OnMousePressed(vgui::MouseCode code) -{ - if (IsCapturing()) - { - // forward up mouse pressed messages to be handled by the key options - if (GetParent()) - { - GetParent()->OnMousePressed(code); - } - } - else - { - BaseClass::OnMousePressed(code); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: input handler -//----------------------------------------------------------------------------- -void VControlsListPanel::OnMouseDoublePressed( vgui::MouseCode code ) -{ - int c = GetSelectedItemsCount(); - if ( c > 0 ) - { - // enter capture mode - OnKeyCodeTyped(KEY_ENTER); - } - else - { - BaseClass::OnMouseDoublePressed(code); - } -} - -CKeyBoardEditorPage::CKeyBoardEditorPage( Panel *parent, Panel *panelToEdit, KeyBindingContextHandle_t handle ) - : BaseClass( parent, "KeyBoardEditorPage" ), - m_pPanel( panelToEdit ), - m_Handle( handle ) -{ - Assert( m_pPanel ); - - m_pList = new VControlsListPanel( this, "KeyBindings" ); - m_pList->SetIgnoreDoubleClick( true ); - m_pList->AddColumnHeader(0, "Action", "#KBEditorBindingName", 175, 0); - m_pList->AddColumnHeader(1, "Binding", "#KBEditorBinding", 175, 0); - m_pList->AddColumnHeader(2, "Description", "#KBEditorDescription", 300, 0); - - LoadControlSettings( "resource/KeyBoardEditorPage.res" ); - - SaveMappings(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -CKeyBoardEditorPage::~CKeyBoardEditorPage() -{ - int c = m_Save.Count(); - for ( int i = 0 ; i < c; ++i ) - { - delete m_Save[ i ]; - } - m_Save.RemoveAll(); -} - -void CKeyBoardEditorPage::ApplySchemeSettings( IScheme *scheme ) -{ - BaseClass::ApplySchemeSettings( scheme ); - PopulateList(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -void CKeyBoardEditorPage::SaveMappings() -{ - Assert( m_Save.Count() == 0 ); - - CUtlVector< PanelKeyBindingMap * > maps; - GetMappingList( m_pPanel, maps ); - - // add header item - int c = maps.Count(); - for ( int i = 0; i < c; ++i ) - { - PanelKeyBindingMap *m = maps[ i ]; - SaveMapping_t *sm = new SaveMapping_t; - sm->map = m; - sm->current = m->boundkeys; - sm->original = m->boundkeys; - m_Save.AddToTail( sm ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -void CKeyBoardEditorPage::UpdateCurrentMappings() -{ - int c = m_Save.Count(); - for ( int i = 0 ; i < c; ++i ) - { - PanelKeyBindingMap *m = m_Save[ i ]->map; - Assert( m ); - m_Save[ i ]->current = m->boundkeys; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -void CKeyBoardEditorPage::RestoreMappings() -{ - int c = m_Save.Count(); - for ( int i = 0; i < c; ++i ) - { - SaveMapping_t *sm = m_Save[ i ]; - sm->current = sm->original; - } -} - -void CKeyBoardEditorPage::ApplyMappings() -{ - int c = m_Save.Count(); - for ( int i = 0; i < c; ++i ) - { - SaveMapping_t *sm = m_Save[ i ]; - sm->map->boundkeys = sm->current; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: User clicked on item: remember where last active row/column was -//----------------------------------------------------------------------------- -void CKeyBoardEditorPage::ItemSelected() -{ - int c = m_pList->GetSelectedItemsCount(); - if ( c > 0 ) - { - m_pList->SetItemOfInterest( m_pList->GetSelectedItem( 0 ) ); - } -} - -void CKeyBoardEditorPage::BindKey( KeyCode code ) -{ - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - - int modifiers = 0; - if ( shift ) - { - modifiers |= MODIFIER_SHIFT; - } - if ( ctrl ) - { - modifiers |= MODIFIER_CONTROL; - } - if ( alt ) - { - modifiers |= MODIFIER_ALT; - } - - int r = m_pList->GetItemOfInterest(); - - // Retrieve clicked row and column - m_pList->EndCaptureMode(dc_arrow); - - // Find item for this row - KeyValues *item = m_pList->GetItem(r); - if ( item ) - { - BoundKey_t *kbMap = reinterpret_cast< BoundKey_t * >( item->GetPtr( "Item", 0 ) ); - if ( kbMap ) - { - KeyBindingMap_t *binding = m_pPanel->LookupBindingByKeyCode( code, modifiers ); - if ( binding && Q_stricmp( kbMap->bindingname, binding->bindingname ) ) - { - // Key is already rebound!!! - Warning( "Can't bind to '%S', key is already bound to '%s'\n", - Panel::KeyCodeToDisplayString( code ), binding->bindingname ); - return; - } - - kbMap->keycode = code; - kbMap->modifiers = modifiers; - - PopulateList(); - } - - KeyBindingMap_t *bindingMap = reinterpret_cast< KeyBindingMap_t * >( item->GetPtr( "Unbound", 0 ) ); - if ( bindingMap ) - { - KeyBindingMap_t *binding = m_pPanel->LookupBindingByKeyCode( code, modifiers ); - if ( binding && Q_stricmp( bindingMap->bindingname, binding->bindingname ) ) - { - // Key is already rebound!!! - Warning( "Can't bind to '%S', key is already bound to '%s'\n", - Panel::KeyCodeToDisplayString( code ), binding->bindingname ); - return; - } - - // Need to add to current entries - m_pPanel->AddKeyBinding( bindingMap->bindingname, code, modifiers ); - UpdateCurrentMappings(); - PopulateList(); - } - } -} - -void CKeyBoardEditorPage::OnPageHide() -{ - if ( m_pList->IsCapturing() ) - { - // Cancel capturing - m_pList->EndCaptureMode(dc_arrow); - } -} - -//----------------------------------------------------------------------------- -// Purpose: binds double-clicking or hitting enter in the keybind list to changing the key -//----------------------------------------------------------------------------- -void CKeyBoardEditorPage::OnKeyCodeTyped(vgui::KeyCode code) -{ - switch ( code ) - { - case KEY_ENTER: - { - if ( !m_pList->IsCapturing() ) - { - OnCommand( "ChangeKey" ); - } - else - { - BindKey( code ); - } - } - break; - case KEY_LSHIFT: - case KEY_RSHIFT: - case KEY_LALT: - case KEY_RALT: - case KEY_LCONTROL: - case KEY_RCONTROL: - { - // Swallow these - break; - } - break; - default: - { - if ( m_pList->IsCapturing() ) - { - BindKey( code ); - } - else - { - BaseClass::OnKeyCodeTyped(code); - } - } - } -} - -void CKeyBoardEditorPage::OnCommand( char const *cmd ) -{ - if ( !m_pList->IsCapturing() && !Q_stricmp( cmd, "ChangeKey" ) ) - { - m_pList->StartCaptureMode(dc_blank); - } - else - { - BaseClass::OnCommand( cmd ); - } -} - -void CKeyBoardEditorPage::OnSaveChanges() -{ - ApplyMappings(); -} - -void CKeyBoardEditorPage::OnRevert() -{ - RestoreMappings(); - PopulateList(); -} - -void CKeyBoardEditorPage::OnUseDefaults() -{ - m_pPanel->RevertKeyBindingsToDefault(); - UpdateCurrentMappings(); - PopulateList(); -} - -void CKeyBoardEditorPage::GetMappingList( Panel *panel, CUtlVector< PanelKeyBindingMap * >& maps ) -{ - PanelKeyBindingMap *map = panel->GetKBMap(); - while ( map ) - { - maps.AddToTail( map ); - map = map->baseMap; - } -} - -static bool BindingLessFunc( KeyValues * const & lhs, KeyValues * const &rhs ) -{ - KeyValues *p1, *p2; - - p1 = const_cast< KeyValues * >( lhs ); - p2 = const_cast< KeyValues * >( rhs ); - return ( Q_stricmp( p1->GetString( "Action" ), p2->GetString( "Action" ) ) < 0 ) ? true : false; -} - -void CKeyBoardEditorPage::AnsiText( char const *token, char *out, size_t buflen ) -{ - out[ 0 ] = 0; - - wchar_t *str = g_pVGuiLocalize->Find( token ); - if ( !str ) - { - Q_strncpy( out, token, buflen ); - } - else - { - g_pVGuiLocalize->ConvertUnicodeToANSI( str, out, buflen ); - } -} - -void CKeyBoardEditorPage::PopulateList() -{ - m_pList->DeleteAllItems(); - - int i, j; - - CUtlRBTree< KeyValues *, int > sorted( 0, 0, BindingLessFunc ); - - // add header item - int c = m_Save.Count(); - for ( i = 0; i < c; ++i ) - { - SaveMapping_t* sm = m_Save[ i ]; - - PanelKeyBindingMap *m = sm->map; - Assert( m ); - - int bindings = sm->current.Count(); - for ( j = 0; j < bindings; ++j ) - { - BoundKey_t *kbMap = &sm->current[ j ]; - Assert( kbMap ); - - // Create a new: blank item - KeyValues *item = new KeyValues( "Item" ); - - // Fill in data - char loc[ 128 ]; - Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname ); - - char ansi[ 256 ]; - AnsiText( loc, ansi, sizeof( ansi ) ); - - item->SetString( "Action", ansi ); - item->SetWString( "Binding", Panel::KeyCodeModifiersToDisplayString( (KeyCode)kbMap->keycode, kbMap->modifiers ) ); - - // Find the binding - KeyBindingMap_t *bindingMap = m_pPanel->LookupBinding( kbMap->bindingname ); - if ( bindingMap && - bindingMap->helpstring ) - { - AnsiText( bindingMap->helpstring, ansi, sizeof( ansi ) ); - item->SetString( "Description", ansi); - } - - item->SetPtr( "Item", kbMap ); - - sorted.Insert( item ); - } - - // Now try and find any "unbound" keys... - int mappings = m->entries.Count(); - for ( j = 0; j < mappings; ++j ) - { - KeyBindingMap_t *kbMap = &m->entries[ j ]; - - // See if it's bound - CUtlVector< BoundKey_t * > list; - m_pPanel->LookupBoundKeys( kbMap->bindingname, list ); - if ( list.Count() > 0 ) - continue; - - // Not bound, add a placeholder entry - // Create a new: blank item - KeyValues *item = new KeyValues( "Item" ); - - // fill in data - char loc[ 128 ]; - Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname ); - - char ansi[ 256 ]; - AnsiText( loc, ansi, sizeof( ansi ) ); - - item->SetString( "Action", ansi ); - item->SetWString( "Binding", L"" ); - if ( kbMap->helpstring ) - { - AnsiText( kbMap->helpstring, ansi, sizeof( ansi ) ); - item->SetString( "Description", ansi ); - } - - item->SetPtr( "Unbound", kbMap ); - - sorted.Insert( item ); - } - } - - for ( j = sorted.FirstInorder() ; j != sorted.InvalidIndex(); j = sorted.NextInorder( j ) ) - { - KeyValues *item = sorted[ j ]; - - // Add to list - m_pList->AddItem( item, 0, false, false ); - - item->deleteThis(); - } - - sorted.RemoveAll(); -} - -void CKeyBoardEditorPage::OnClearBinding( int item ) -{ - // Find item for this row - KeyValues *kv = m_pList->GetItem(item ); - if ( !kv ) - { - return; - } - - BoundKey_t *kbMap = reinterpret_cast< BoundKey_t * >( kv->GetPtr( "Item", 0 ) ); - if ( !kbMap ) - { - return; - } - - kbMap->keycode = KEY_NONE; - kbMap->modifiers = 0; - - PopulateList(); -} - -CKeyBoardEditorSheet::CKeyBoardEditorSheet( Panel *parent, Panel *panelToEdit, KeyBindingContextHandle_t handle ) - : BaseClass( parent, "KeyBoardEditorSheet" ), - m_bSaveToExternalFile( false ), - m_Handle( handle ), - m_SaveFileName( UTL_INVAL_SYMBOL ), - m_SaveFilePathID( UTL_INVAL_SYMBOL ) -{ - m_hPanel = panelToEdit; - - SetSmallTabs( true ); - - // Create this sheet and add the subcontrols - CKeyBoardEditorPage *active = NULL; - - int subCount = Panel::GetPanelsWithKeyBindingsCount( handle ); - for ( int i = 0; i < subCount; ++i ) - { - Panel *p = Panel::GetPanelWithKeyBindings( handle, i ); - if ( !p ) - continue; - - // Don't display panels with no keymappings - if ( p->GetKeyMappingCount() == 0 ) - continue; - - CKeyBoardEditorPage *newPage = new CKeyBoardEditorPage( this, p, handle ); - AddPage( newPage, p->GetName() ); - if ( p == panelToEdit ) - { - active = newPage; - } - } - - if ( active ) - { - SetActivePage( active ); - } - - LoadControlSettings( "resource/KeyBoardEditorSheet.res" ); -} - -void CKeyBoardEditorSheet::SetKeybindingsSaveFile( char const *filename, char const *pathID /*= 0*/ ) -{ - Assert( filename ); - m_bSaveToExternalFile = true; - m_SaveFileName = filename; - if ( pathID != NULL ) - { - m_SaveFilePathID = pathID; - } - else - { - m_SaveFilePathID = UTL_INVAL_SYMBOL; - } -} - -void CKeyBoardEditorSheet::OnSaveChanges() -{ - int c = GetNumPages(); - for ( int i = 0 ; i < c; ++i ) - { - CKeyBoardEditorPage *page = static_cast< CKeyBoardEditorPage * >( GetPage( i ) ); - page->OnSaveChanges(); - } - - if ( m_bSaveToExternalFile ) - { - m_hPanel->SaveKeyBindingsToFile( m_Handle, m_SaveFileName.String(), m_SaveFilePathID.IsValid() ? m_SaveFilePathID.String() : NULL ); - } - else - { - m_hPanel->SaveKeyBindings( m_Handle ); - } -} - -void CKeyBoardEditorSheet::OnRevert() -{ - int c = GetNumPages(); - for ( int i = 0 ; i < c; ++i ) - { - CKeyBoardEditorPage *page = static_cast< CKeyBoardEditorPage * >( GetPage( i ) ); - page->OnRevert(); - } -} - -void CKeyBoardEditorSheet::OnUseDefaults() -{ - int c = GetNumPages(); - for ( int i = 0 ; i < c; ++i ) - { - CKeyBoardEditorPage *page = static_cast< CKeyBoardEditorPage * >( GetPage( i ) ); - page->OnUseDefaults(); - } -} - -CKeyBoardEditorDialog::CKeyBoardEditorDialog( Panel *parent, Panel *panelToEdit, KeyBindingContextHandle_t handle ) - : BaseClass( parent, "KeyBoardEditorDialog" ) -{ - m_pSave = new Button( this, "Save", "#KBEditorSave", this, "save" ); - m_pCancel = new Button( this, "Cancel", "#KBEditorCancel", this, "cancel" ); - m_pRevert = new Button( this, "Revert", "#KBEditorRevert", this, "revert" ); - m_pUseDefaults = new Button( this, "Defaults", "#KBEditorUseDefaults", this, "defaults" ); - - m_pKBEditor = new CKeyBoardEditorSheet( this, panelToEdit, handle ); - - LoadControlSettings( "resource/KeyBoardEditorDialog.res" ); - - SetTitle( "#KBEditorTitle", true ); - - SetSmallCaption( true ); - SetMinimumSize( 640, 200 ); - SetMinimizeButtonVisible( false ); - SetMaximizeButtonVisible( false ); - SetSizeable( true ); - SetMoveable( true ); - SetMenuButtonVisible( false ); - - SetVisible( true ); - - MoveToCenterOfScreen(); -} - -void CKeyBoardEditorDialog::OnCommand( char const *cmd ) -{ - if ( !Q_stricmp( cmd, "save" ) ) - { - m_pKBEditor->OnSaveChanges(); - MarkForDeletion(); - } - else if ( !Q_stricmp( cmd, "cancel" ) || - !Q_stricmp( cmd, "Close" ) ) - { - m_pKBEditor->OnRevert(); - MarkForDeletion(); - } - else if ( !Q_stricmp( cmd, "revert" ) ) - { - m_pKBEditor->OnRevert(); - } - else if ( !Q_stricmp( cmd, "defaults" ) ) - { - m_pKBEditor->OnUseDefaults(); - } - else - { - BaseClass::OnCommand( cmd ); - } -} - -void CKeyBoardEditorDialog::SetKeybindingsSaveFile( char const *filename, char const *pathID /*= 0*/ ) -{ - m_pKBEditor->SetKeybindingsSaveFile( filename, pathID ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "vgui_controls/KeyBoardEditorDialog.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/TextEntry.h" +#include "vgui/ISurface.h" +#include "vgui/IInput.h" +#include "vgui/IVGui.h" +#include "vgui/ILocalize.h" +#include "KeyValues.h" +#include "vgui/Cursor.h" +#include "tier1/utldict.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +using namespace vgui; + +static char *CopyString( const char *in ) +{ + if ( !in ) + return NULL; + + int len = strlen( in ); + char *n = new char[ len + 1 ]; + Q_strncpy( n, in, len + 1 ); + return n; +} + +CKeyBoardEditorPage::SaveMapping_t::SaveMapping_t() : map( 0 ) +{ +} + +CKeyBoardEditorPage::SaveMapping_t::SaveMapping_t( const SaveMapping_t& src ) +{ + map = src.map; + current = src.current; + original = src.original; +} + +//----------------------------------------------------------------------------- +// Purpose: Special list subclass to handle drawing of trap mode prompt on top of +// lists client area +//----------------------------------------------------------------------------- +class VControlsListPanel : public ListPanel +{ + DECLARE_CLASS_SIMPLE( VControlsListPanel, ListPanel ); + +public: + // Construction + VControlsListPanel( vgui::Panel *parent, const char *listName ); + virtual ~VControlsListPanel(); + + // Start/end capturing + virtual void StartCaptureMode(vgui::HCursor hCursor = NULL); + virtual void EndCaptureMode(vgui::HCursor hCursor = NULL); + virtual bool IsCapturing(); + + // Set which item should be associated with the prompt + virtual void SetItemOfInterest(int itemID); + virtual int GetItemOfInterest(); + + virtual void OnMousePressed(vgui::MouseCode code); + virtual void OnMouseDoublePressed(vgui::MouseCode code); + + KEYBINDING_FUNC( clearbinding, KEY_DELETE, 0, OnClearBinding, 0, 0 ); + +private: + void ApplySchemeSettings(vgui::IScheme *pScheme ); + + // Are we showing the prompt? + bool m_bCaptureMode; + // If so, where? + int m_nClickRow; + // Font to use for showing the prompt + vgui::HFont m_hFont; + // panel used to edit + class CInlineEditPanel *m_pInlineEditPanel; + int m_iMouseX, m_iMouseY; +}; + +//----------------------------------------------------------------------------- +// Purpose: panel used for inline editing of key bindings +//----------------------------------------------------------------------------- +class CInlineEditPanel : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CInlineEditPanel, vgui::Panel ); + +public: + CInlineEditPanel() : vgui::Panel(NULL, "InlineEditPanel") + { + } + + virtual void Paint() + { + int wide, tall; + GetSize(wide, tall); + + // Draw a white rectangle around that cell + vgui::surface()->DrawSetColor( 63, 63, 63, 255 ); + vgui::surface()->DrawFilledRect( 0, 0, wide, tall ); + + vgui::surface()->DrawSetColor( 0, 255, 0, 255 ); + vgui::surface()->DrawOutlinedRect( 0, 0, wide, tall ); + } + + virtual void OnKeyCodeTyped(KeyCode code) + { + // forward up + if (GetParent()) + { + GetParent()->OnKeyCodeTyped(code); + } + } + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + Panel::ApplySchemeSettings(pScheme); + SetBorder(pScheme->GetBorder("DepressedButtonBorder")); + } + + void OnMousePressed(vgui::MouseCode code) + { + // forward up mouse pressed messages to be handled by the key options + if (GetParent()) + { + GetParent()->OnMousePressed(code); + } + } +}; + +//----------------------------------------------------------------------------- +// Purpose: Construction +//----------------------------------------------------------------------------- +VControlsListPanel::VControlsListPanel( vgui::Panel *parent, const char *listName ) : BaseClass( parent, listName ) +{ + m_bCaptureMode = false; + m_nClickRow = 0; + m_pInlineEditPanel = new CInlineEditPanel(); + m_hFont = INVALID_FONT; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +VControlsListPanel::~VControlsListPanel() +{ + m_pInlineEditPanel->MarkForDeletion(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VControlsListPanel::ApplySchemeSettings(IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + m_hFont = pScheme->GetFont("DefaultVerySmall", IsProportional() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Start capture prompt display +//----------------------------------------------------------------------------- +void VControlsListPanel::StartCaptureMode( HCursor hCursor ) +{ + m_bCaptureMode = true; + EnterEditMode(m_nClickRow, 1, m_pInlineEditPanel); + input()->SetMouseFocus(m_pInlineEditPanel->GetVPanel()); + input()->SetMouseCapture(m_pInlineEditPanel->GetVPanel()); + + if (hCursor) + { + m_pInlineEditPanel->SetCursor(hCursor); + + // save off the cursor position so we can restore it + vgui::input()->GetCursorPos( m_iMouseX, m_iMouseY ); + } +} + +void VControlsListPanel::OnClearBinding() +{ + if ( m_bCaptureMode ) + return; + + if ( GetItemOfInterest() < 0 ) + return; + + PostMessage( GetParent()->GetVPanel(), new KeyValues( "ClearBinding", "item", GetItemOfInterest() ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Finish capture prompt display +//----------------------------------------------------------------------------- +void VControlsListPanel::EndCaptureMode( HCursor hCursor ) +{ + m_bCaptureMode = false; + input()->SetMouseCapture(NULL); + LeaveEditMode(); + RequestFocus(); + input()->SetMouseFocus(GetVPanel()); + if (hCursor) + { + m_pInlineEditPanel->SetCursor(hCursor); + surface()->SetCursor(hCursor); + if ( hCursor != dc_none ) + { + vgui::input()->SetCursorPos ( m_iMouseX, m_iMouseY ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set active row column +//----------------------------------------------------------------------------- +void VControlsListPanel::SetItemOfInterest(int itemID) +{ + m_nClickRow = itemID; +} + +//----------------------------------------------------------------------------- +// Purpose: Retrieve row, column of interest +//----------------------------------------------------------------------------- +int VControlsListPanel::GetItemOfInterest() +{ + return m_nClickRow; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if we're currently waiting to capture a key +//----------------------------------------------------------------------------- +bool VControlsListPanel::IsCapturing( void ) +{ + return m_bCaptureMode; +} + +//----------------------------------------------------------------------------- +// Purpose: Forwards mouse pressed message up to keyboard page when in capture +//----------------------------------------------------------------------------- +void VControlsListPanel::OnMousePressed(vgui::MouseCode code) +{ + if (IsCapturing()) + { + // forward up mouse pressed messages to be handled by the key options + if (GetParent()) + { + GetParent()->OnMousePressed(code); + } + } + else + { + BaseClass::OnMousePressed(code); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: input handler +//----------------------------------------------------------------------------- +void VControlsListPanel::OnMouseDoublePressed( vgui::MouseCode code ) +{ + int c = GetSelectedItemsCount(); + if ( c > 0 ) + { + // enter capture mode + OnKeyCodeTyped(KEY_ENTER); + } + else + { + BaseClass::OnMouseDoublePressed(code); + } +} + +CKeyBoardEditorPage::CKeyBoardEditorPage( Panel *parent, Panel *panelToEdit, KeyBindingContextHandle_t handle ) + : BaseClass( parent, "KeyBoardEditorPage" ), + m_pPanel( panelToEdit ), + m_Handle( handle ) +{ + Assert( m_pPanel ); + + m_pList = new VControlsListPanel( this, "KeyBindings" ); + m_pList->SetIgnoreDoubleClick( true ); + m_pList->AddColumnHeader(0, "Action", "#KBEditorBindingName", 175, 0); + m_pList->AddColumnHeader(1, "Binding", "#KBEditorBinding", 175, 0); + m_pList->AddColumnHeader(2, "Description", "#KBEditorDescription", 300, 0); + + LoadControlSettings( "resource/KeyBoardEditorPage.res" ); + + SaveMappings(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +CKeyBoardEditorPage::~CKeyBoardEditorPage() +{ + int c = m_Save.Count(); + for ( int i = 0 ; i < c; ++i ) + { + delete m_Save[ i ]; + } + m_Save.RemoveAll(); +} + +void CKeyBoardEditorPage::ApplySchemeSettings( IScheme *scheme ) +{ + BaseClass::ApplySchemeSettings( scheme ); + PopulateList(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void CKeyBoardEditorPage::SaveMappings() +{ + Assert( m_Save.Count() == 0 ); + + CUtlVector< PanelKeyBindingMap * > maps; + GetMappingList( m_pPanel, maps ); + + // add header item + int c = maps.Count(); + for ( int i = 0; i < c; ++i ) + { + PanelKeyBindingMap *m = maps[ i ]; + SaveMapping_t *sm = new SaveMapping_t; + sm->map = m; + sm->current = m->boundkeys; + sm->original = m->boundkeys; + m_Save.AddToTail( sm ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void CKeyBoardEditorPage::UpdateCurrentMappings() +{ + int c = m_Save.Count(); + for ( int i = 0 ; i < c; ++i ) + { + PanelKeyBindingMap *m = m_Save[ i ]->map; + Assert( m ); + m_Save[ i ]->current = m->boundkeys; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void CKeyBoardEditorPage::RestoreMappings() +{ + int c = m_Save.Count(); + for ( int i = 0; i < c; ++i ) + { + SaveMapping_t *sm = m_Save[ i ]; + sm->current = sm->original; + } +} + +void CKeyBoardEditorPage::ApplyMappings() +{ + int c = m_Save.Count(); + for ( int i = 0; i < c; ++i ) + { + SaveMapping_t *sm = m_Save[ i ]; + sm->map->boundkeys = sm->current; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: User clicked on item: remember where last active row/column was +//----------------------------------------------------------------------------- +void CKeyBoardEditorPage::ItemSelected() +{ + int c = m_pList->GetSelectedItemsCount(); + if ( c > 0 ) + { + m_pList->SetItemOfInterest( m_pList->GetSelectedItem( 0 ) ); + } +} + +void CKeyBoardEditorPage::BindKey( KeyCode code ) +{ + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + + int modifiers = 0; + if ( shift ) + { + modifiers |= MODIFIER_SHIFT; + } + if ( ctrl ) + { + modifiers |= MODIFIER_CONTROL; + } + if ( alt ) + { + modifiers |= MODIFIER_ALT; + } + + int r = m_pList->GetItemOfInterest(); + + // Retrieve clicked row and column + m_pList->EndCaptureMode(dc_arrow); + + // Find item for this row + KeyValues *item = m_pList->GetItem(r); + if ( item ) + { + BoundKey_t *kbMap = reinterpret_cast< BoundKey_t * >( item->GetPtr( "Item", 0 ) ); + if ( kbMap ) + { + KeyBindingMap_t *binding = m_pPanel->LookupBindingByKeyCode( code, modifiers ); + if ( binding && Q_stricmp( kbMap->bindingname, binding->bindingname ) ) + { + // Key is already rebound!!! + Warning( "Can't bind to '%S', key is already bound to '%s'\n", + Panel::KeyCodeToDisplayString( code ), binding->bindingname ); + return; + } + + kbMap->keycode = code; + kbMap->modifiers = modifiers; + + PopulateList(); + } + + KeyBindingMap_t *bindingMap = reinterpret_cast< KeyBindingMap_t * >( item->GetPtr( "Unbound", 0 ) ); + if ( bindingMap ) + { + KeyBindingMap_t *binding = m_pPanel->LookupBindingByKeyCode( code, modifiers ); + if ( binding && Q_stricmp( bindingMap->bindingname, binding->bindingname ) ) + { + // Key is already rebound!!! + Warning( "Can't bind to '%S', key is already bound to '%s'\n", + Panel::KeyCodeToDisplayString( code ), binding->bindingname ); + return; + } + + // Need to add to current entries + m_pPanel->AddKeyBinding( bindingMap->bindingname, code, modifiers ); + UpdateCurrentMappings(); + PopulateList(); + } + } +} + +void CKeyBoardEditorPage::OnPageHide() +{ + if ( m_pList->IsCapturing() ) + { + // Cancel capturing + m_pList->EndCaptureMode(dc_arrow); + } +} + +//----------------------------------------------------------------------------- +// Purpose: binds double-clicking or hitting enter in the keybind list to changing the key +//----------------------------------------------------------------------------- +void CKeyBoardEditorPage::OnKeyCodeTyped(vgui::KeyCode code) +{ + switch ( code ) + { + case KEY_ENTER: + { + if ( !m_pList->IsCapturing() ) + { + OnCommand( "ChangeKey" ); + } + else + { + BindKey( code ); + } + } + break; + case KEY_LSHIFT: + case KEY_RSHIFT: + case KEY_LALT: + case KEY_RALT: + case KEY_LCONTROL: + case KEY_RCONTROL: + { + // Swallow these + break; + } + break; + default: + { + if ( m_pList->IsCapturing() ) + { + BindKey( code ); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } + } + } +} + +void CKeyBoardEditorPage::OnCommand( char const *cmd ) +{ + if ( !m_pList->IsCapturing() && !Q_stricmp( cmd, "ChangeKey" ) ) + { + m_pList->StartCaptureMode(dc_blank); + } + else + { + BaseClass::OnCommand( cmd ); + } +} + +void CKeyBoardEditorPage::OnSaveChanges() +{ + ApplyMappings(); +} + +void CKeyBoardEditorPage::OnRevert() +{ + RestoreMappings(); + PopulateList(); +} + +void CKeyBoardEditorPage::OnUseDefaults() +{ + m_pPanel->RevertKeyBindingsToDefault(); + UpdateCurrentMappings(); + PopulateList(); +} + +void CKeyBoardEditorPage::GetMappingList( Panel *panel, CUtlVector< PanelKeyBindingMap * >& maps ) +{ + PanelKeyBindingMap *map = panel->GetKBMap(); + while ( map ) + { + maps.AddToTail( map ); + map = map->baseMap; + } +} + +static bool BindingLessFunc( KeyValues * const & lhs, KeyValues * const &rhs ) +{ + KeyValues *p1, *p2; + + p1 = const_cast< KeyValues * >( lhs ); + p2 = const_cast< KeyValues * >( rhs ); + return ( Q_stricmp( p1->GetString( "Action" ), p2->GetString( "Action" ) ) < 0 ) ? true : false; +} + +void CKeyBoardEditorPage::AnsiText( char const *token, char *out, size_t buflen ) +{ + out[ 0 ] = 0; + + wchar_t *str = g_pVGuiLocalize->Find( token ); + if ( !str ) + { + Q_strncpy( out, token, buflen ); + } + else + { + g_pVGuiLocalize->ConvertUnicodeToANSI( str, out, buflen ); + } +} + +void CKeyBoardEditorPage::PopulateList() +{ + m_pList->DeleteAllItems(); + + int i, j; + + CUtlRBTree< KeyValues *, int > sorted( 0, 0, BindingLessFunc ); + + // add header item + int c = m_Save.Count(); + for ( i = 0; i < c; ++i ) + { + SaveMapping_t* sm = m_Save[ i ]; + + PanelKeyBindingMap *m = sm->map; + Assert( m ); + + int bindings = sm->current.Count(); + for ( j = 0; j < bindings; ++j ) + { + BoundKey_t *kbMap = &sm->current[ j ]; + Assert( kbMap ); + + // Create a new: blank item + KeyValues *item = new KeyValues( "Item" ); + + // Fill in data + char loc[ 128 ]; + Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname ); + + char ansi[ 256 ]; + AnsiText( loc, ansi, sizeof( ansi ) ); + + item->SetString( "Action", ansi ); + item->SetWString( "Binding", Panel::KeyCodeModifiersToDisplayString( (KeyCode)kbMap->keycode, kbMap->modifiers ) ); + + // Find the binding + KeyBindingMap_t *bindingMap = m_pPanel->LookupBinding( kbMap->bindingname ); + if ( bindingMap && + bindingMap->helpstring ) + { + AnsiText( bindingMap->helpstring, ansi, sizeof( ansi ) ); + item->SetString( "Description", ansi); + } + + item->SetPtr( "Item", kbMap ); + + sorted.Insert( item ); + } + + // Now try and find any "unbound" keys... + int mappings = m->entries.Count(); + for ( j = 0; j < mappings; ++j ) + { + KeyBindingMap_t *kbMap = &m->entries[ j ]; + + // See if it's bound + CUtlVector< BoundKey_t * > list; + m_pPanel->LookupBoundKeys( kbMap->bindingname, list ); + if ( list.Count() > 0 ) + continue; + + // Not bound, add a placeholder entry + // Create a new: blank item + KeyValues *item = new KeyValues( "Item" ); + + // fill in data + char loc[ 128 ]; + Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname ); + + char ansi[ 256 ]; + AnsiText( loc, ansi, sizeof( ansi ) ); + + item->SetString( "Action", ansi ); + item->SetWString( "Binding", L"" ); + if ( kbMap->helpstring ) + { + AnsiText( kbMap->helpstring, ansi, sizeof( ansi ) ); + item->SetString( "Description", ansi ); + } + + item->SetPtr( "Unbound", kbMap ); + + sorted.Insert( item ); + } + } + + for ( j = sorted.FirstInorder() ; j != sorted.InvalidIndex(); j = sorted.NextInorder( j ) ) + { + KeyValues *item = sorted[ j ]; + + // Add to list + m_pList->AddItem( item, 0, false, false ); + + item->deleteThis(); + } + + sorted.RemoveAll(); +} + +void CKeyBoardEditorPage::OnClearBinding( int item ) +{ + // Find item for this row + KeyValues *kv = m_pList->GetItem(item ); + if ( !kv ) + { + return; + } + + BoundKey_t *kbMap = reinterpret_cast< BoundKey_t * >( kv->GetPtr( "Item", 0 ) ); + if ( !kbMap ) + { + return; + } + + kbMap->keycode = KEY_NONE; + kbMap->modifiers = 0; + + PopulateList(); +} + +CKeyBoardEditorSheet::CKeyBoardEditorSheet( Panel *parent, Panel *panelToEdit, KeyBindingContextHandle_t handle ) + : BaseClass( parent, "KeyBoardEditorSheet" ), + m_bSaveToExternalFile( false ), + m_Handle( handle ), + m_SaveFileName( UTL_INVAL_SYMBOL ), + m_SaveFilePathID( UTL_INVAL_SYMBOL ) +{ + m_hPanel = panelToEdit; + + SetSmallTabs( true ); + + // Create this sheet and add the subcontrols + CKeyBoardEditorPage *active = NULL; + + int subCount = Panel::GetPanelsWithKeyBindingsCount( handle ); + for ( int i = 0; i < subCount; ++i ) + { + Panel *p = Panel::GetPanelWithKeyBindings( handle, i ); + if ( !p ) + continue; + + // Don't display panels with no keymappings + if ( p->GetKeyMappingCount() == 0 ) + continue; + + CKeyBoardEditorPage *newPage = new CKeyBoardEditorPage( this, p, handle ); + AddPage( newPage, p->GetName() ); + if ( p == panelToEdit ) + { + active = newPage; + } + } + + if ( active ) + { + SetActivePage( active ); + } + + LoadControlSettings( "resource/KeyBoardEditorSheet.res" ); +} + +void CKeyBoardEditorSheet::SetKeybindingsSaveFile( char const *filename, char const *pathID /*= 0*/ ) +{ + Assert( filename ); + m_bSaveToExternalFile = true; + m_SaveFileName = filename; + if ( pathID != NULL ) + { + m_SaveFilePathID = pathID; + } + else + { + m_SaveFilePathID = UTL_INVAL_SYMBOL; + } +} + +void CKeyBoardEditorSheet::OnSaveChanges() +{ + int c = GetNumPages(); + for ( int i = 0 ; i < c; ++i ) + { + CKeyBoardEditorPage *page = static_cast< CKeyBoardEditorPage * >( GetPage( i ) ); + page->OnSaveChanges(); + } + + if ( m_bSaveToExternalFile ) + { + m_hPanel->SaveKeyBindingsToFile( m_Handle, m_SaveFileName.String(), m_SaveFilePathID.IsValid() ? m_SaveFilePathID.String() : NULL ); + } + else + { + m_hPanel->SaveKeyBindings( m_Handle ); + } +} + +void CKeyBoardEditorSheet::OnRevert() +{ + int c = GetNumPages(); + for ( int i = 0 ; i < c; ++i ) + { + CKeyBoardEditorPage *page = static_cast< CKeyBoardEditorPage * >( GetPage( i ) ); + page->OnRevert(); + } +} + +void CKeyBoardEditorSheet::OnUseDefaults() +{ + int c = GetNumPages(); + for ( int i = 0 ; i < c; ++i ) + { + CKeyBoardEditorPage *page = static_cast< CKeyBoardEditorPage * >( GetPage( i ) ); + page->OnUseDefaults(); + } +} + +CKeyBoardEditorDialog::CKeyBoardEditorDialog( Panel *parent, Panel *panelToEdit, KeyBindingContextHandle_t handle ) + : BaseClass( parent, "KeyBoardEditorDialog" ) +{ + m_pSave = new Button( this, "Save", "#KBEditorSave", this, "save" ); + m_pCancel = new Button( this, "Cancel", "#KBEditorCancel", this, "cancel" ); + m_pRevert = new Button( this, "Revert", "#KBEditorRevert", this, "revert" ); + m_pUseDefaults = new Button( this, "Defaults", "#KBEditorUseDefaults", this, "defaults" ); + + m_pKBEditor = new CKeyBoardEditorSheet( this, panelToEdit, handle ); + + LoadControlSettings( "resource/KeyBoardEditorDialog.res" ); + + SetTitle( "#KBEditorTitle", true ); + + SetSmallCaption( true ); + SetMinimumSize( 640, 200 ); + SetMinimizeButtonVisible( false ); + SetMaximizeButtonVisible( false ); + SetSizeable( true ); + SetMoveable( true ); + SetMenuButtonVisible( false ); + + SetVisible( true ); + + MoveToCenterOfScreen(); +} + +void CKeyBoardEditorDialog::OnCommand( char const *cmd ) +{ + if ( !Q_stricmp( cmd, "save" ) ) + { + m_pKBEditor->OnSaveChanges(); + MarkForDeletion(); + } + else if ( !Q_stricmp( cmd, "cancel" ) || + !Q_stricmp( cmd, "Close" ) ) + { + m_pKBEditor->OnRevert(); + MarkForDeletion(); + } + else if ( !Q_stricmp( cmd, "revert" ) ) + { + m_pKBEditor->OnRevert(); + } + else if ( !Q_stricmp( cmd, "defaults" ) ) + { + m_pKBEditor->OnUseDefaults(); + } + else + { + BaseClass::OnCommand( cmd ); + } +} + +void CKeyBoardEditorDialog::SetKeybindingsSaveFile( char const *filename, char const *pathID /*= 0*/ ) +{ + m_pKBEditor->SetKeybindingsSaveFile( filename, pathID ); +} diff --git a/mp/src/vgui2/vgui_controls/KeyRepeat.cpp b/mp/src/vgui2/vgui_controls/KeyRepeat.cpp index 665fb02c..1c5fcd01 100644 --- a/mp/src/vgui2/vgui_controls/KeyRepeat.cpp +++ b/mp/src/vgui2/vgui_controls/KeyRepeat.cpp @@ -1,98 +1,98 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "vgui_controls/pch_vgui_controls.h" -#include - -// memdbgon must be the last include file in a .cpp file -#include "tier0/memdbgon.h" - -using namespace vgui; - -vgui::KeyCode g_iCodesForAliases[FM_NUM_KEYREPEAT_ALIASES] = -{ - KEY_XBUTTON_UP, - KEY_XBUTTON_DOWN, - KEY_XBUTTON_LEFT, - KEY_XBUTTON_RIGHT, -}; - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CKeyRepeatHandler::KeyDown( vgui::KeyCode code ) -{ - int iIndex = GetIndexForCode(code); - if ( iIndex == -1 ) - return; - - if ( m_bAliasDown[ iIndex ] ) - return; - - Reset(); - m_bAliasDown[ iIndex ] = true; - m_flNextKeyRepeat = system()->GetCurrentTime() + 0.4; - m_bHaveKeyDown = true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CKeyRepeatHandler::KeyUp( vgui::KeyCode code ) -{ - int iIndex = GetIndexForCode(code); - if ( iIndex == -1 ) - return; - - m_bAliasDown[ GetIndexForCode(code) ] = false; - - m_bHaveKeyDown = false; - for ( int i = 0; i < FM_NUM_KEYREPEAT_ALIASES; i++ ) - { - if ( m_bAliasDown[i] ) - { - m_bHaveKeyDown = true; - break; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -vgui::KeyCode CKeyRepeatHandler::KeyRepeated( void ) -{ - if ( IsPC() ) - return BUTTON_CODE_NONE; - - if ( !m_bHaveKeyDown ) - return BUTTON_CODE_NONE; - - if ( m_flNextKeyRepeat < system()->GetCurrentTime() ) - { - for ( int i = 0; i < FM_NUM_KEYREPEAT_ALIASES; i++ ) - { - if ( m_bAliasDown[i] ) - { - m_flNextKeyRepeat = system()->GetCurrentTime() + m_flRepeatTimes[i]; - return g_iCodesForAliases[i]; - } - } - } - - return BUTTON_CODE_NONE; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CKeyRepeatHandler::SetKeyRepeatTime( vgui::KeyCode code, float flRepeat ) -{ - int iIndex = GetIndexForCode(code); - Assert( iIndex != -1 ); - m_flRepeatTimes[ iIndex ] = flRepeat; +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "vgui_controls/pch_vgui_controls.h" +#include + +// memdbgon must be the last include file in a .cpp file +#include "tier0/memdbgon.h" + +using namespace vgui; + +vgui::KeyCode g_iCodesForAliases[FM_NUM_KEYREPEAT_ALIASES] = +{ + KEY_XBUTTON_UP, + KEY_XBUTTON_DOWN, + KEY_XBUTTON_LEFT, + KEY_XBUTTON_RIGHT, +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyRepeatHandler::KeyDown( vgui::KeyCode code ) +{ + int iIndex = GetIndexForCode(code); + if ( iIndex == -1 ) + return; + + if ( m_bAliasDown[ iIndex ] ) + return; + + Reset(); + m_bAliasDown[ iIndex ] = true; + m_flNextKeyRepeat = system()->GetCurrentTime() + 0.4; + m_bHaveKeyDown = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyRepeatHandler::KeyUp( vgui::KeyCode code ) +{ + int iIndex = GetIndexForCode(code); + if ( iIndex == -1 ) + return; + + m_bAliasDown[ GetIndexForCode(code) ] = false; + + m_bHaveKeyDown = false; + for ( int i = 0; i < FM_NUM_KEYREPEAT_ALIASES; i++ ) + { + if ( m_bAliasDown[i] ) + { + m_bHaveKeyDown = true; + break; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +vgui::KeyCode CKeyRepeatHandler::KeyRepeated( void ) +{ + if ( IsPC() ) + return BUTTON_CODE_NONE; + + if ( !m_bHaveKeyDown ) + return BUTTON_CODE_NONE; + + if ( m_flNextKeyRepeat < system()->GetCurrentTime() ) + { + for ( int i = 0; i < FM_NUM_KEYREPEAT_ALIASES; i++ ) + { + if ( m_bAliasDown[i] ) + { + m_flNextKeyRepeat = system()->GetCurrentTime() + m_flRepeatTimes[i]; + return g_iCodesForAliases[i]; + } + } + } + + return BUTTON_CODE_NONE; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyRepeatHandler::SetKeyRepeatTime( vgui::KeyCode code, float flRepeat ) +{ + int iIndex = GetIndexForCode(code); + Assert( iIndex != -1 ); + m_flRepeatTimes[ iIndex ] = flRepeat; } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/Label.cpp b/mp/src/vgui2/vgui_controls/Label.cpp index 26199308..776c8c6e 100644 --- a/mp/src/vgui2/vgui_controls/Label.cpp +++ b/mp/src/vgui2/vgui_controls/Label.cpp @@ -1,1386 +1,1386 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -DECLARE_BUILD_FACTORY_DEFAULT_TEXT( Label, Label ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -Label::Label(Panel *parent, const char *panelName, const char *text) : BaseClass(parent, panelName) -{ - Init(); - - _textImage = new TextImage(text); - _textImage->SetColor(Color(0, 0, 0, 0)); - SetText(text); - _textImageIndex = AddImage(_textImage, 0); - - REGISTER_COLOR_AS_OVERRIDABLE( _disabledFgColor2, "disabledfgcolor2_override" ); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -Label::Label(Panel *parent, const char *panelName, const wchar_t *wszText) : BaseClass(parent, panelName) -{ - Init(); - - _textImage = new TextImage(wszText); - _textImage->SetColor(Color(0, 0, 0, 0)); - SetText(wszText); - _textImageIndex = AddImage(_textImage, 0); - - REGISTER_COLOR_AS_OVERRIDABLE( _disabledFgColor2, "disabledfgcolor2_override" ); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -Label::~Label() -{ - delete _textImage; - delete [] _associateName; - delete [] _fontOverrideName; -} - -//----------------------------------------------------------------------------- -// Purpose: Construct the label -//----------------------------------------------------------------------------- -void Label::Init() -{ - _contentAlignment = a_west; - _textColorState = CS_NORMAL; - _textInset[0] = 0; - _textInset[1] = 0; - _hotkey = 0; - _associate = NULL; - _associateName = NULL; - _fontOverrideName = NULL; - m_bWrap = false; - m_bCenterWrap = false; - m_bAutoWideToContents = false; - m_bUseProportionalInsets = false; - m_bAutoWideDirty = false; - -// SetPaintBackgroundEnabled(false); -} - -//----------------------------------------------------------------------------- -// Purpose: Set whether the text is displayed bright or dull -//----------------------------------------------------------------------------- -void Label::SetTextColorState(EColorState state) -{ - if (_textColorState != state) - { - _textColorState = state; - InvalidateLayout(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Return the full size of the contained content -//----------------------------------------------------------------------------- -void Label::GetContentSize(int &wide, int &tall) -{ - if( GetFont() == INVALID_FONT ) // we haven't loaded our font yet, so load it now - { - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - if ( pScheme ) - { - SetFont( pScheme->GetFont( "Default", IsProportional() ) ); - } - } - - - int tx0, ty0, tx1, ty1; - ComputeAlignment(tx0, ty0, tx1, ty1); - - // the +8 is padding to the content size - // the code which uses it should really set that itself; - // however a lot of existing code relies on this - wide = (tx1 - tx0) + _textInset[0]; - - // get the size of the text image and remove it - int iWide, iTall; - _textImage->GetSize(iWide, iTall); - wide -= iWide; - // get the full, untruncated (no elipsis) size of the text image. - _textImage->GetContentSize(iWide, iTall); - wide += iWide; - - // addin the image offsets as well - for (int i=0; i < _imageDar.Size(); i++) - wide += _imageDar[i].offset; - - tall = max((ty1 - ty0) + _textInset[1], iTall); -} - -//----------------------------------------------------------------------------- -// Purpose: Calculate the keyboard key that is a hotkey -//----------------------------------------------------------------------------- -wchar_t Label::CalculateHotkey(const char *text) -{ - for (const char *ch = text; *ch != 0; ch++) - { - if (*ch == '&') - { - // get the next character - ch++; - - if (*ch == '&') - { - // just an & - continue; - } - else if (*ch == 0) - { - break; - } - else if (V_isalnum(*ch)) - { - // found the hotkey - return (wchar_t)tolower(*ch); - } - } - } - - return '\0'; -} - -wchar_t Label::CalculateHotkey(const wchar_t *text) -{ - if( text ) - { - for (const wchar_t *ch = text; *ch != 0; ch++) - { - if (*ch == '&') - { - // get the next character - ch++; - - if (*ch == '&') - { - // just an & - continue; - } - else if (*ch == 0) - { - break; - } - else if (iswalnum(*ch)) - { - // found the hotkey - return (wchar_t)towlower(*ch); - } - } - } - } - - return '\0'; -} - -//----------------------------------------------------------------------------- -// Purpose: Check if this label has a hotkey that is the key passed in. -//----------------------------------------------------------------------------- -Panel *Label::HasHotkey(wchar_t key) -{ -#ifdef VGUI_HOTKEYS_ENABLED - if ( iswalnum( key ) ) - key = towlower( key ); - - if (_hotkey == key) - return this; -#endif - - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the hotkey for this label -//----------------------------------------------------------------------------- -void Label::SetHotkey(wchar_t ch) -{ - _hotkey = ch; -} - -wchar_t Label::GetHotKey() -{ - return _hotkey; -} - -//----------------------------------------------------------------------------- -// Purpose: Handle a hotkey by passing on focus to associate -//----------------------------------------------------------------------------- -void Label::OnHotkeyPressed() -{ - // we can't accept focus, but if we are associated to a control give it to that - if (_associate.Get() && !IsBuildModeActive()) - { - _associate->RequestFocus(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Redirect mouse pressed to giving focus to associate -//----------------------------------------------------------------------------- -void Label::OnMousePressed(MouseCode code) -{ - if (_associate.Get() && !IsBuildModeActive()) - { - _associate->RequestFocus(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Return the text in the label -//----------------------------------------------------------------------------- -void Label::GetText(char *textOut, int bufferLen) -{ - _textImage->GetText(textOut, bufferLen); -} - -//----------------------------------------------------------------------------- -// Purpose: Return the text in the label -//----------------------------------------------------------------------------- -void Label::GetText(wchar_t *textOut, int bufLenInBytes) -{ - _textImage->GetText(textOut, bufLenInBytes); -} - -//----------------------------------------------------------------------------- -// Purpose: Take the string and looks it up in the localization file -// to convert it to unicode -// Setting the text will not set the size of the label. -// Set the size explicitly or use setToContent() -//----------------------------------------------------------------------------- -void Label::SetText(const char *text) -{ - // if set to null, just make blank - if (!text) - { - text = ""; - } - - // let the text image do the translation itself - _textImage->SetText(text); - - if( text[0] == '#' ) - { - SetHotkey(CalculateHotkey(g_pVGuiLocalize->Find(text))); - } - else - { - SetHotkey(CalculateHotkey(text)); - } - - m_bAutoWideDirty = m_bAutoWideToContents; - - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set unicode text directly -//----------------------------------------------------------------------------- -void Label::SetText(const wchar_t *unicodeString, bool bClearUnlocalizedSymbol) -{ - m_bAutoWideDirty = m_bAutoWideToContents; - - if ( unicodeString && _textImage->GetUText() && !Q_wcscmp(unicodeString,_textImage->GetUText()) ) - return; - - _textImage->SetText(unicodeString, bClearUnlocalizedSymbol); - -//!! need to calculate hotkey from translated string - SetHotkey(CalculateHotkey(unicodeString)); - - InvalidateLayout(); // possible that the textimage needs to expand - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: updates localized text -//----------------------------------------------------------------------------- -void Label::OnDialogVariablesChanged(KeyValues *dialogVariables ) -{ - StringIndex_t index = _textImage->GetUnlocalizedTextSymbol(); - if (index != INVALID_LOCALIZE_STRING_INDEX) - { - // reconstruct the string from the variables - wchar_t buf[1024]; - g_pVGuiLocalize->ConstructString(buf, sizeof(buf), index, dialogVariables); - SetText(buf); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Additional offset at the Start of the text (from whichever side it is aligned) -//----------------------------------------------------------------------------- -void Label::SetTextInset(int xInset, int yInset) -{ - _textInset[0] = xInset; - _textInset[1] = yInset; - - int wide, tall; - GetSize( wide, tall); - _textImage->SetDrawWidth(wide - _textInset[0]); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Label::GetTextInset(int *xInset, int *yInset ) -{ - *xInset = _textInset[0]; - *yInset = _textInset[1]; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the enabled state -//----------------------------------------------------------------------------- -void Label::SetEnabled(bool state) -{ - Panel::SetEnabled(state); -} - -//----------------------------------------------------------------------------- -// Purpose: Calculates where in the panel the content resides -// Input : &tx0 - [out] position of the content -// &ty0 - -// &tx1 - -// &ty1 - -// Note: horizontal alignment is west if the image dar has -// more than one image in it, this is because we use image sizes -// to determine layout in classes for example, Menu. -//----------------------------------------------------------------------------- -void Label::ComputeAlignment(int &tx0, int &ty0, int &tx1, int &ty1) -{ - int wide, tall; - GetPaintSize(wide, tall); - int tWide,tTall; - - // text bounding box - tx0 = 0; - ty0 = 0; - - // loop through all the images and calculate the complete bounds - int maxX = 0, maxY = 0; - - int actualXAlignment = _contentAlignment; - for (int i = 0; i < _imageDar.Count(); i++) - { - TImageInfo &imageInfo = _imageDar[i]; - IImage *image = imageInfo.image; - if (!image) - continue; // skip over null images - - // add up the bounds - int iWide, iTall; - image->GetSize(iWide, iTall); - if (iWide > wide) // if the image is larger than the label just do a west alignment - actualXAlignment = Label::a_west; - - // get the max height - maxY = max(maxY, iTall); - maxX += iWide; - - // add the offset to x - maxX += imageInfo.offset; - } - - tWide = maxX; - tTall = maxY; - - // x align text - switch (actualXAlignment) - { - // left - case Label::a_northwest: - case Label::a_west: - case Label::a_southwest: - { - tx0 = 0; - break; - } - // center - case Label::a_north: - case Label::a_center: - case Label::a_south: - { - tx0 = (wide - tWide) / 2; - break; - } - // right - case Label::a_northeast: - case Label::a_east: - case Label::a_southeast: - { - tx0 = wide - tWide; - break; - } - } - - // y align text - switch (_contentAlignment) - { - //top - case Label::a_northwest: - case Label::a_north: - case Label::a_northeast: - { - ty0 = 0; - break; - } - // center - case Label::a_west: - case Label::a_center: - case Label::a_east: - { - ty0 = (tall - tTall) / 2; - break; - } - // south - case Label::a_southwest: - case Label::a_south: - case Label::a_southeast: - { - ty0 = tall - tTall; - break; - } - } - - tx1 = tx0 + tWide; - ty1 = ty0 + tTall; -} - -//----------------------------------------------------------------------------- -// Purpose: overridden main drawing function for the panel -//----------------------------------------------------------------------------- -void Label::Paint() -{ - int tx0, ty0, tx1, ty1; - ComputeAlignment(tx0, ty0, tx1, ty1); - - // calculate who our associate is if we haven't already - if (_associateName) - { - SetAssociatedControl(FindSiblingByName(_associateName)); - delete [] _associateName; - _associateName = NULL; - } - - int labelWide, labelTall; - GetSize(labelWide, labelTall); - int x = tx0, y = _textInset[1] + ty0; - int imageYPos = 0; // a place to save the y offset for when we draw the disable version of the image - - // draw the set of images - for (int i = 0; i < _imageDar.Count(); i++) - { - TImageInfo &imageInfo = _imageDar[i]; - IImage *image = imageInfo.image; - if (!image) - continue; // skip over null images - - // add the offset to x - x += imageInfo.offset; - - // if this is the text image then add its inset to the left or from the right - if (i == _textImageIndex) - { - switch ( _contentAlignment ) - { - // left - case Label::a_northwest: - case Label::a_west: - case Label::a_southwest: - { - x += _textInset[0]; - break; - } - // right - case Label::a_northeast: - case Label::a_east: - case Label::a_southeast: - { - x -= _textInset[0]; - break; - } - } - } - - // see if the image is in a fixed position - if (imageInfo.xpos >= 0) - { - x = imageInfo.xpos; - } - - // draw - imageYPos = y; - image->SetPos(x, y); - - // fix up y for center-aligned text - if (_contentAlignment == Label::a_west || _contentAlignment == Label::a_center || _contentAlignment == Label::a_east) - { - int iw, it; - image->GetSize(iw, it); - if (it < (ty1 - ty0)) - { - imageYPos = ((ty1 - ty0) - it) / 2 + y; - image->SetPos(x, ((ty1 - ty0) - it) / 2 + y); - } - } - - // don't resize the image unless its too big - if (imageInfo.width >= 0) - { - int w, t; - image->GetSize(w, t); - if (w > imageInfo.width) - { - image->SetSize(imageInfo.width, t); - } - } - - // if it's the basic text image then draw specially - if (image == _textImage) - { - if (IsEnabled()) - { - if (_associate.Get() && ipanel()->HasParent(input()->GetFocus(), _associate->GetVPanel())) - { - _textImage->SetColor(_associateColor); - } - else - { - _textImage->SetColor(GetFgColor()); - } - - _textImage->Paint(); - } - else - { - // draw disabled version, with embossed look - // offset image - _textImage->SetPos(x + 1, imageYPos + 1); - _textImage->SetColor(_disabledFgColor1); - _textImage->Paint(); - - surface()->DrawFlushText(); - - // overlayed image - _textImage->SetPos(x, imageYPos); - _textImage->SetColor(_disabledFgColor2); - _textImage->Paint(); - } - } - else - { - image->Paint(); - } - - // add the image size to x - int wide, tall; - image->GetSize(wide, tall); - x += wide; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Helper function, draws a simple line with dashing parameters -//----------------------------------------------------------------------------- -void Label::DrawDashedLine(int x0, int y0, int x1, int y1, int dashLen, int gapLen) -{ - // work out which way the line goes - if ((x1 - x0) > (y1 - y0)) - { - // x direction line - while (1) - { - if (x0 + dashLen > x1) - { - // draw partial - surface()->DrawFilledRect(x0, y0, x1, y1); - } - else - { - surface()->DrawFilledRect(x0, y0, x0 + dashLen, y1); - } - - x0 += dashLen; - - if (x0 + gapLen > x1) - break; - - x0 += gapLen; - } - } - else - { - // y direction - while (1) - { - if (y0 + dashLen > y1) - { - // draw partial - surface()->DrawFilledRect(x0, y0, x1, y1); - } - else - { - surface()->DrawFilledRect(x0, y0, x1, y0 + dashLen); - } - - y0 += dashLen; - - if (y0 + gapLen > y1) - break; - - y0 += gapLen; - } - } -} - -void Label::SetContentAlignment(Alignment alignment) -{ - _contentAlignment=alignment; - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Size the width of the label to its contents - only works from in ApplySchemeSettings or PerformLayout() -//----------------------------------------------------------------------------- -void Label::SizeToContents() -{ - int wide, tall; - GetContentSize(wide, tall); - - SetSize(wide, tall); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the font the text is drawn in -//----------------------------------------------------------------------------- -void Label::SetFont(HFont font) -{ - _textImage->SetFont(font); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Resond to resizing of the panel -//----------------------------------------------------------------------------- -void Label::OnSizeChanged(int wide, int tall) -{ - InvalidateLayout(); - Panel::OnSizeChanged(wide, tall); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the font the textImage is drawn in. -//----------------------------------------------------------------------------- -HFont Label::GetFont() -{ - return _textImage->GetFont(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the foreground color of the Label -//----------------------------------------------------------------------------- -void Label::SetFgColor(Color color) -{ - if (!(GetFgColor() == color)) - { - BaseClass::SetFgColor(color); - _textImage->SetColor(color); - Repaint(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Get the foreground color of the Label -//----------------------------------------------------------------------------- -Color Label::GetFgColor() -{ - Color clr = Panel::GetFgColor(); - return clr; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the foreground color 1 color of the Label -//----------------------------------------------------------------------------- -void Label::SetDisabledFgColor1(Color color) -{ - _disabledFgColor1 = color; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Label::SetDisabledFgColor2(Color color) -{ - _disabledFgColor2 = color; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Color Label::GetDisabledFgColor1() -{ - return _disabledFgColor1; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Color Label::GetDisabledFgColor2() -{ - return _disabledFgColor2; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -TextImage *Label::GetTextImage() -{ - return _textImage; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool Label::RequestInfo(KeyValues *outputData) -{ - if (!stricmp(outputData->GetName(), "GetText")) - { - wchar_t wbuf[256]; - _textImage->GetText(wbuf, 255); - outputData->SetWString("text", wbuf); - return true; - } - - return Panel::RequestInfo(outputData); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the text from the message -//----------------------------------------------------------------------------- -void Label::OnSetText(KeyValues *params) -{ - KeyValues *pkvText = params->FindKey("text", false); - if (!pkvText) - return; - - if (pkvText->GetDataType() == KeyValues::TYPE_STRING) - { - SetText(pkvText->GetString()); - } - else if (pkvText->GetDataType() == KeyValues::TYPE_WSTRING) - { - SetText(pkvText->GetWString()); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Add an image to the list -// returns the index the image was placed in -//----------------------------------------------------------------------------- -int Label::AddImage(IImage *image, int offset) -{ - int newImage = _imageDar.AddToTail(); - _imageDar[newImage].image = image; - _imageDar[newImage].offset = (short)offset; - _imageDar[newImage].xpos = -1; - _imageDar[newImage].width = -1; - InvalidateLayout(); - return newImage; -} - -//----------------------------------------------------------------------------- -// Purpose: removes all images from the list -// user is responsible for the memory -//----------------------------------------------------------------------------- -void Label::ClearImages() -{ - _imageDar.RemoveAll(); - _textImageIndex = -1; -} - -void Label::ResetToSimpleTextImage() -{ - ClearImages(); - _textImageIndex = AddImage(_textImage, 0); -} - - -//----------------------------------------------------------------------------- -// Purpose: Multiple image handling -// Images are drawn from left to right across the label, ordered by index -// By default there is a TextImage in position 0 -// Set the contents of an IImage in the IImage array. -//----------------------------------------------------------------------------- -void Label::SetImageAtIndex(int index, IImage *image, int offset) -{ - EnsureImageCapacity(index); -// Assert( image ); - - if ( _imageDar[index].image != image || _imageDar[index].offset != offset) - { - _imageDar[index].image = image; - _imageDar[index].offset = (short)offset; - InvalidateLayout(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Get an IImage in the IImage array. -//----------------------------------------------------------------------------- -IImage *Label::GetImageAtIndex(int index) -{ - if ( _imageDar.IsValidIndex( index ) ) - return _imageDar[index].image; - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the number of images in the array. -//----------------------------------------------------------------------------- -int Label::GetImageCount() -{ - return _imageDar.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: Move where the default text image is within the image array -// (it starts in position 0) -// Input : newIndex - -// Output : int - the index the default text image was previously in -//----------------------------------------------------------------------------- -int Label::SetTextImageIndex(int newIndex) -{ - if (newIndex == _textImageIndex) - return _textImageIndex; - - EnsureImageCapacity(newIndex); - - int oldIndex = _textImageIndex; - if ( _textImageIndex >= 0 ) - { - _imageDar[_textImageIndex].image = NULL; - } - if (newIndex > -1) - { - _imageDar[newIndex].image = _textImage; - } - _textImageIndex = newIndex; - return oldIndex; -} - -//----------------------------------------------------------------------------- -// Purpose: Ensure that the maxIndex will be a valid index -//----------------------------------------------------------------------------- -void Label::EnsureImageCapacity(int maxIndex) -{ - while (_imageDar.Size() <= maxIndex) - { - AddImage(NULL, 0); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set the offset in pixels before the image -//----------------------------------------------------------------------------- -void Label::SetImagePreOffset(int index, int preOffset) -{ - if (_imageDar.IsValidIndex(index) && _imageDar[index].offset != preOffset) - { - _imageDar[index].offset = (short)preOffset; - InvalidateLayout(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: fixes the layout bounds of the image within the label -//----------------------------------------------------------------------------- -void Label::SetImageBounds(int index, int x, int width) -{ - _imageDar[index].xpos = (short)x; - _imageDar[index].width = (short)width; -} - -//----------------------------------------------------------------------------- -// Purpose: Labels can be associated with controls, and alter behaviour based on the associates behaviour -// If the associate is disabled, so are we -// If the associate has focus, we may alter how we draw -// If we get a hotkey press or focus message, we forward the focus to the associate -//----------------------------------------------------------------------------- -void Label::SetAssociatedControl(Panel *control) -{ - if (control != this) - { - _associate = control; - } - else - { - // don't let the associate ever be set to be ourself - _associate = NULL; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Called after a panel requests focus to fix up the whole chain -//----------------------------------------------------------------------------- -void Label::OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel) -{ - if (_associate.Get() && subFocus == GetVPanel() && !IsBuildModeActive()) - { - // we've received focus; pass the focus onto the associate instead - _associate->RequestFocus(); - } - else - { - BaseClass::OnRequestFocus(subFocus, defaultPanel); - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets custom settings from the scheme file -//----------------------------------------------------------------------------- -void Label::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - if (_fontOverrideName) - { - // use the custom specified font since we have one set - SetFont(pScheme->GetFont(_fontOverrideName, IsProportional())); - } - if ( GetFont() == INVALID_FONT ) - { - SetFont( pScheme->GetFont( "Default", IsProportional() ) ); - } - - if ( m_bWrap || m_bCenterWrap ) - { - //tell it how big it is - int wide, tall; - Panel::GetSize(wide, tall); - wide -= _textInset[0]; // take inset into account - _textImage->SetSize(wide, tall); - - _textImage->RecalculateNewLinePositions(); - } - else - { - // if you don't set the size of the image, many, many buttons will break - we might want to look into fixing this all over the place later - int wide, tall; - _textImage->GetContentSize(wide, tall); - _textImage->SetSize(wide, tall); - } - - if ( m_bAutoWideToContents ) - { - m_bAutoWideDirty = true; - HandleAutoSizing(); - } - - // clear out any the images, since they will have been invalidated - for (int i = 0; i < _imageDar.Count(); i++) - { - IImage *image = _imageDar[i].image; - if (!image) - continue; // skip over null images - - if (i == _textImageIndex) - continue; - - _imageDar[i].image = NULL; - } - - SetDisabledFgColor1(GetSchemeColor("Label.DisabledFgColor1", pScheme)); - SetDisabledFgColor2(GetSchemeColor("Label.DisabledFgColor2", pScheme)); - SetBgColor(GetSchemeColor("Label.BgColor", pScheme)); - - switch (_textColorState) - { - case CS_DULL: - SetFgColor(GetSchemeColor("Label.TextDullColor", pScheme)); - break; - case CS_BRIGHT: - SetFgColor(GetSchemeColor("Label.TextBrightColor", pScheme)); - break; - case CS_NORMAL: - default: - SetFgColor(GetSchemeColor("Label.TextColor", pScheme)); - break; - } - - _associateColor = GetSchemeColor("Label.SelectedTextColor", pScheme); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Label::GetSettings( KeyValues *outResourceData ) -{ - // panel settings - Panel::GetSettings( outResourceData ); - - // label settings - char buf[256]; - _textImage->GetUnlocalizedText( buf, 255 ); - if (!strnicmp(buf, "#var_", 5)) - { - // strip off the variable prepender on save - outResourceData->SetString( "labelText", buf + 5 ); - } - else - { - outResourceData->SetString( "labelText", buf ); - } - - const char *alignmentString = ""; - switch ( _contentAlignment ) - { - case a_northwest: alignmentString = "north-west"; break; - case a_north: alignmentString = "north"; break; - case a_northeast: alignmentString = "north-east"; break; - case a_center: alignmentString = "center"; break; - case a_east: alignmentString = "east"; break; - case a_southwest: alignmentString = "south-west"; break; - case a_south: alignmentString = "south"; break; - case a_southeast: alignmentString = "south-east"; break; - case a_west: - default: alignmentString = "west"; break; - } - - outResourceData->SetString( "textAlignment", alignmentString ); - - if (_associate) - { - outResourceData->SetString("associate", _associate->GetName()); - } - - outResourceData->SetInt("dulltext", (int)(_textColorState == CS_DULL)); - outResourceData->SetInt("brighttext", (int)(_textColorState == CS_BRIGHT)); - - if (_fontOverrideName) - { - outResourceData->SetString("font", _fontOverrideName); - } - - outResourceData->SetInt("wrap", ( m_bWrap ? 1 : 0 )); - outResourceData->SetInt("centerwrap", ( m_bCenterWrap ? 1 : 0 )); - - if ( m_bUseProportionalInsets ) - { - outResourceData->SetInt("textinsetx", scheme()->GetProportionalNormalizedValueEx( GetScheme(), _textInset[0] ) ); - outResourceData->SetInt("textinsety", _textInset[1]); - } - else - { - outResourceData->SetInt("textinsetx", _textInset[0]); - outResourceData->SetInt("textinsety", _textInset[1]); - } - outResourceData->SetInt("auto_wide_tocontents", ( m_bAutoWideToContents ? 1 : 0 )); - outResourceData->SetInt("use_proportional_insets", ( m_bUseProportionalInsets ? 1 : 0 )); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Label::ApplySettings( KeyValues *inResourceData ) -{ - BaseClass::ApplySettings( inResourceData ); - - // label settings - const char *labelText = inResourceData->GetString( "labelText", NULL ); - if ( labelText ) - { - if (labelText[0] == '%' && labelText[strlen(labelText) - 1] == '%') - { - // it's a variable, set it to be a special variable localized string - wchar_t unicodeVar[256]; - g_pVGuiLocalize->ConvertANSIToUnicode(labelText, unicodeVar, sizeof(unicodeVar)); - - char var[256]; - _snprintf(var, sizeof(var), "#var_%s", labelText); - g_pVGuiLocalize->AddString(var + 1, unicodeVar, ""); - SetText(var); - } - else - { - SetText(labelText); - } - } - - // text alignment - const char *alignmentString = inResourceData->GetString( "textAlignment", "" ); - int align = -1; - - if ( !stricmp(alignmentString, "north-west") ) - { - align = a_northwest; - } - else if ( !stricmp(alignmentString, "north") ) - { - align = a_north; - } - else if ( !stricmp(alignmentString, "north-east") ) - { - align = a_northeast; - } - else if ( !stricmp(alignmentString, "west") ) - { - align = a_west; - } - else if ( !stricmp(alignmentString, "center") ) - { - align = a_center; - } - else if ( !stricmp(alignmentString, "east") ) - { - align = a_east; - } - else if ( !stricmp(alignmentString, "south-west") ) - { - align = a_southwest; - } - else if ( !stricmp(alignmentString, "south") ) - { - align = a_south; - } - else if ( !stricmp(alignmentString, "south-east") ) - { - align = a_southeast; - } - - if ( align != -1 ) - { - SetContentAlignment( (Alignment)align ); - } - - // the control we are to be associated with may not have been created yet, - // so keep a pointer to it's name and calculate it when we can - const char *associateName = inResourceData->GetString("associate", ""); - if (associateName[0] != 0) - { - int len = Q_strlen(associateName) + 1; - _associateName = new char[ len ]; - Q_strncpy( _associateName, associateName, len ); - } - - if (inResourceData->GetInt("dulltext", 0) == 1) - { - SetTextColorState(CS_DULL); - } - else if (inResourceData->GetInt("brighttext", 0) == 1) - { - SetTextColorState(CS_BRIGHT); - } - else - { - SetTextColorState(CS_NORMAL); - } - - // font settings - const char *overrideFont = inResourceData->GetString("font", ""); - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - - if (*overrideFont) - { - delete [] _fontOverrideName; - int len = Q_strlen(overrideFont) + 1; - _fontOverrideName = new char[ len ]; - Q_strncpy(_fontOverrideName, overrideFont, len ); - SetFont(pScheme->GetFont(_fontOverrideName, IsProportional())); - } - else if (_fontOverrideName) - { - delete [] _fontOverrideName; - _fontOverrideName = NULL; - SetFont(pScheme->GetFont("Default", IsProportional())); - } - - bool bWrapText = inResourceData->GetInt("centerwrap", 0) > 0; - SetCenterWrap( bWrapText ); - - m_bAutoWideToContents = inResourceData->GetInt("auto_wide_tocontents", 0) > 0; - - bWrapText = inResourceData->GetInt("wrap", 0) > 0; - SetWrap( bWrapText ); - - int inset_x = inResourceData->GetInt("textinsetx", _textInset[0]); - int inset_y = inResourceData->GetInt("textinsety", _textInset[1]); - // Had to play it safe and add a new key for backwards compatibility - m_bUseProportionalInsets = inResourceData->GetInt("use_proportional_insets", 0) > 0; - if ( m_bUseProportionalInsets ) - { - inset_x = scheme()->GetProportionalScaledValueEx( GetScheme(), inset_x ); - } - - SetTextInset( inset_x, inset_y ); - - bool bAllCaps = inResourceData->GetInt("allcaps", 0) > 0; - SetAllCaps( bAllCaps ); - - InvalidateLayout(true); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns a description of the label string -//----------------------------------------------------------------------------- -const char *Label::GetDescription( void ) -{ - static char buf[1024]; - Q_snprintf(buf, sizeof(buf), "%s, string labelText, string associate, alignment textAlignment, int wrap, int dulltext, int brighttext, string font", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: If a label has images in _imageDar, the size -// must take those into account as well as the textImage part -// Textimage part will shrink ONLY if there is not enough room. -//----------------------------------------------------------------------------- -void Label::PerformLayout() -{ - int wide, tall; - Panel::GetSize(wide, tall); - wide -= _textInset[0]; // take inset into account - - // if we just have a textImage, this is trivial. - if (_imageDar.Count() == 1 && _textImageIndex == 0) - { - if ( m_bWrap || m_bCenterWrap ) - { - int twide, ttall; - _textImage->GetContentSize(twide, ttall); - _textImage->SetSize(wide, ttall); - } - else - { - int twide, ttall; - _textImage->GetContentSize(twide, ttall); - - // tell the textImage how much space we have to draw in - if ( wide < twide) - _textImage->SetSize(wide, ttall); - else - _textImage->SetSize(twide, ttall); - } - - HandleAutoSizing(); - - HandleAutoSizing(); - - return; - } - - // assume the images in the dar cannot be resized, and if - // the images + the textimage are too wide we shring the textimage part - if (_textImageIndex < 0) - return; - - // get the size of the images - int widthOfImages = 0; - for (int i = 0; i < _imageDar.Count(); i++) - { - TImageInfo &imageInfo = _imageDar[i]; - IImage *image = imageInfo.image; - if (!image) - continue; // skip over null images - - if (i == _textImageIndex) - continue; - - // add up the bounds - int iWide, iTall; - image->GetSize(iWide, iTall); - widthOfImages += iWide; - } - - // so this is how much room we have to draw the textimage part - int spaceAvail = wide - widthOfImages; - - // if we have no space at all just leave everything as is. - if (spaceAvail < 0) - return; - - int twide, ttall; - _textImage->GetSize (twide, ttall); - // tell the textImage how much space we have to draw in - _textImage->SetSize(spaceAvail, ttall); - - HandleAutoSizing(); -} - -void Label::SetWrap( bool bWrap ) -{ - m_bWrap = bWrap; - _textImage->SetWrap( m_bWrap ); - - InvalidateLayout(); -} - -void Label::SetCenterWrap( bool bWrap ) -{ - m_bCenterWrap = bWrap; - _textImage->SetCenterWrap( m_bCenterWrap ); - - InvalidateLayout(); -} - -void Label::SetAllCaps( bool bAllCaps ) -{ - m_bAllCaps = bAllCaps; - _textImage->SetAllCaps( m_bAllCaps ); - - InvalidateLayout(); -} - -void Label::HandleAutoSizing( void ) -{ - if ( m_bAutoWideDirty ) - { - m_bAutoWideDirty = false; - - // Only change our width to match our content - int wide, tall; - GetContentSize(wide, tall); - SetSize(wide, GetTall()); - } -} - - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +DECLARE_BUILD_FACTORY_DEFAULT_TEXT( Label, Label ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Label::Label(Panel *parent, const char *panelName, const char *text) : BaseClass(parent, panelName) +{ + Init(); + + _textImage = new TextImage(text); + _textImage->SetColor(Color(0, 0, 0, 0)); + SetText(text); + _textImageIndex = AddImage(_textImage, 0); + + REGISTER_COLOR_AS_OVERRIDABLE( _disabledFgColor2, "disabledfgcolor2_override" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Label::Label(Panel *parent, const char *panelName, const wchar_t *wszText) : BaseClass(parent, panelName) +{ + Init(); + + _textImage = new TextImage(wszText); + _textImage->SetColor(Color(0, 0, 0, 0)); + SetText(wszText); + _textImageIndex = AddImage(_textImage, 0); + + REGISTER_COLOR_AS_OVERRIDABLE( _disabledFgColor2, "disabledfgcolor2_override" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +Label::~Label() +{ + delete _textImage; + delete [] _associateName; + delete [] _fontOverrideName; +} + +//----------------------------------------------------------------------------- +// Purpose: Construct the label +//----------------------------------------------------------------------------- +void Label::Init() +{ + _contentAlignment = a_west; + _textColorState = CS_NORMAL; + _textInset[0] = 0; + _textInset[1] = 0; + _hotkey = 0; + _associate = NULL; + _associateName = NULL; + _fontOverrideName = NULL; + m_bWrap = false; + m_bCenterWrap = false; + m_bAutoWideToContents = false; + m_bUseProportionalInsets = false; + m_bAutoWideDirty = false; + +// SetPaintBackgroundEnabled(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Set whether the text is displayed bright or dull +//----------------------------------------------------------------------------- +void Label::SetTextColorState(EColorState state) +{ + if (_textColorState != state) + { + _textColorState = state; + InvalidateLayout(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Return the full size of the contained content +//----------------------------------------------------------------------------- +void Label::GetContentSize(int &wide, int &tall) +{ + if( GetFont() == INVALID_FONT ) // we haven't loaded our font yet, so load it now + { + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + if ( pScheme ) + { + SetFont( pScheme->GetFont( "Default", IsProportional() ) ); + } + } + + + int tx0, ty0, tx1, ty1; + ComputeAlignment(tx0, ty0, tx1, ty1); + + // the +8 is padding to the content size + // the code which uses it should really set that itself; + // however a lot of existing code relies on this + wide = (tx1 - tx0) + _textInset[0]; + + // get the size of the text image and remove it + int iWide, iTall; + _textImage->GetSize(iWide, iTall); + wide -= iWide; + // get the full, untruncated (no elipsis) size of the text image. + _textImage->GetContentSize(iWide, iTall); + wide += iWide; + + // addin the image offsets as well + for (int i=0; i < _imageDar.Size(); i++) + wide += _imageDar[i].offset; + + tall = max((ty1 - ty0) + _textInset[1], iTall); +} + +//----------------------------------------------------------------------------- +// Purpose: Calculate the keyboard key that is a hotkey +//----------------------------------------------------------------------------- +wchar_t Label::CalculateHotkey(const char *text) +{ + for (const char *ch = text; *ch != 0; ch++) + { + if (*ch == '&') + { + // get the next character + ch++; + + if (*ch == '&') + { + // just an & + continue; + } + else if (*ch == 0) + { + break; + } + else if (V_isalnum(*ch)) + { + // found the hotkey + return (wchar_t)tolower(*ch); + } + } + } + + return '\0'; +} + +wchar_t Label::CalculateHotkey(const wchar_t *text) +{ + if( text ) + { + for (const wchar_t *ch = text; *ch != 0; ch++) + { + if (*ch == '&') + { + // get the next character + ch++; + + if (*ch == '&') + { + // just an & + continue; + } + else if (*ch == 0) + { + break; + } + else if (iswalnum(*ch)) + { + // found the hotkey + return (wchar_t)towlower(*ch); + } + } + } + } + + return '\0'; +} + +//----------------------------------------------------------------------------- +// Purpose: Check if this label has a hotkey that is the key passed in. +//----------------------------------------------------------------------------- +Panel *Label::HasHotkey(wchar_t key) +{ +#ifdef VGUI_HOTKEYS_ENABLED + if ( iswalnum( key ) ) + key = towlower( key ); + + if (_hotkey == key) + return this; +#endif + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the hotkey for this label +//----------------------------------------------------------------------------- +void Label::SetHotkey(wchar_t ch) +{ + _hotkey = ch; +} + +wchar_t Label::GetHotKey() +{ + return _hotkey; +} + +//----------------------------------------------------------------------------- +// Purpose: Handle a hotkey by passing on focus to associate +//----------------------------------------------------------------------------- +void Label::OnHotkeyPressed() +{ + // we can't accept focus, but if we are associated to a control give it to that + if (_associate.Get() && !IsBuildModeActive()) + { + _associate->RequestFocus(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Redirect mouse pressed to giving focus to associate +//----------------------------------------------------------------------------- +void Label::OnMousePressed(MouseCode code) +{ + if (_associate.Get() && !IsBuildModeActive()) + { + _associate->RequestFocus(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Return the text in the label +//----------------------------------------------------------------------------- +void Label::GetText(char *textOut, int bufferLen) +{ + _textImage->GetText(textOut, bufferLen); +} + +//----------------------------------------------------------------------------- +// Purpose: Return the text in the label +//----------------------------------------------------------------------------- +void Label::GetText(wchar_t *textOut, int bufLenInBytes) +{ + _textImage->GetText(textOut, bufLenInBytes); +} + +//----------------------------------------------------------------------------- +// Purpose: Take the string and looks it up in the localization file +// to convert it to unicode +// Setting the text will not set the size of the label. +// Set the size explicitly or use setToContent() +//----------------------------------------------------------------------------- +void Label::SetText(const char *text) +{ + // if set to null, just make blank + if (!text) + { + text = ""; + } + + // let the text image do the translation itself + _textImage->SetText(text); + + if( text[0] == '#' ) + { + SetHotkey(CalculateHotkey(g_pVGuiLocalize->Find(text))); + } + else + { + SetHotkey(CalculateHotkey(text)); + } + + m_bAutoWideDirty = m_bAutoWideToContents; + + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set unicode text directly +//----------------------------------------------------------------------------- +void Label::SetText(const wchar_t *unicodeString, bool bClearUnlocalizedSymbol) +{ + m_bAutoWideDirty = m_bAutoWideToContents; + + if ( unicodeString && _textImage->GetUText() && !Q_wcscmp(unicodeString,_textImage->GetUText()) ) + return; + + _textImage->SetText(unicodeString, bClearUnlocalizedSymbol); + +//!! need to calculate hotkey from translated string + SetHotkey(CalculateHotkey(unicodeString)); + + InvalidateLayout(); // possible that the textimage needs to expand + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: updates localized text +//----------------------------------------------------------------------------- +void Label::OnDialogVariablesChanged(KeyValues *dialogVariables ) +{ + StringIndex_t index = _textImage->GetUnlocalizedTextSymbol(); + if (index != INVALID_LOCALIZE_STRING_INDEX) + { + // reconstruct the string from the variables + wchar_t buf[1024]; + g_pVGuiLocalize->ConstructString(buf, sizeof(buf), index, dialogVariables); + SetText(buf); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Additional offset at the Start of the text (from whichever side it is aligned) +//----------------------------------------------------------------------------- +void Label::SetTextInset(int xInset, int yInset) +{ + _textInset[0] = xInset; + _textInset[1] = yInset; + + int wide, tall; + GetSize( wide, tall); + _textImage->SetDrawWidth(wide - _textInset[0]); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Label::GetTextInset(int *xInset, int *yInset ) +{ + *xInset = _textInset[0]; + *yInset = _textInset[1]; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the enabled state +//----------------------------------------------------------------------------- +void Label::SetEnabled(bool state) +{ + Panel::SetEnabled(state); +} + +//----------------------------------------------------------------------------- +// Purpose: Calculates where in the panel the content resides +// Input : &tx0 - [out] position of the content +// &ty0 - +// &tx1 - +// &ty1 - +// Note: horizontal alignment is west if the image dar has +// more than one image in it, this is because we use image sizes +// to determine layout in classes for example, Menu. +//----------------------------------------------------------------------------- +void Label::ComputeAlignment(int &tx0, int &ty0, int &tx1, int &ty1) +{ + int wide, tall; + GetPaintSize(wide, tall); + int tWide,tTall; + + // text bounding box + tx0 = 0; + ty0 = 0; + + // loop through all the images and calculate the complete bounds + int maxX = 0, maxY = 0; + + int actualXAlignment = _contentAlignment; + for (int i = 0; i < _imageDar.Count(); i++) + { + TImageInfo &imageInfo = _imageDar[i]; + IImage *image = imageInfo.image; + if (!image) + continue; // skip over null images + + // add up the bounds + int iWide, iTall; + image->GetSize(iWide, iTall); + if (iWide > wide) // if the image is larger than the label just do a west alignment + actualXAlignment = Label::a_west; + + // get the max height + maxY = max(maxY, iTall); + maxX += iWide; + + // add the offset to x + maxX += imageInfo.offset; + } + + tWide = maxX; + tTall = maxY; + + // x align text + switch (actualXAlignment) + { + // left + case Label::a_northwest: + case Label::a_west: + case Label::a_southwest: + { + tx0 = 0; + break; + } + // center + case Label::a_north: + case Label::a_center: + case Label::a_south: + { + tx0 = (wide - tWide) / 2; + break; + } + // right + case Label::a_northeast: + case Label::a_east: + case Label::a_southeast: + { + tx0 = wide - tWide; + break; + } + } + + // y align text + switch (_contentAlignment) + { + //top + case Label::a_northwest: + case Label::a_north: + case Label::a_northeast: + { + ty0 = 0; + break; + } + // center + case Label::a_west: + case Label::a_center: + case Label::a_east: + { + ty0 = (tall - tTall) / 2; + break; + } + // south + case Label::a_southwest: + case Label::a_south: + case Label::a_southeast: + { + ty0 = tall - tTall; + break; + } + } + + tx1 = tx0 + tWide; + ty1 = ty0 + tTall; +} + +//----------------------------------------------------------------------------- +// Purpose: overridden main drawing function for the panel +//----------------------------------------------------------------------------- +void Label::Paint() +{ + int tx0, ty0, tx1, ty1; + ComputeAlignment(tx0, ty0, tx1, ty1); + + // calculate who our associate is if we haven't already + if (_associateName) + { + SetAssociatedControl(FindSiblingByName(_associateName)); + delete [] _associateName; + _associateName = NULL; + } + + int labelWide, labelTall; + GetSize(labelWide, labelTall); + int x = tx0, y = _textInset[1] + ty0; + int imageYPos = 0; // a place to save the y offset for when we draw the disable version of the image + + // draw the set of images + for (int i = 0; i < _imageDar.Count(); i++) + { + TImageInfo &imageInfo = _imageDar[i]; + IImage *image = imageInfo.image; + if (!image) + continue; // skip over null images + + // add the offset to x + x += imageInfo.offset; + + // if this is the text image then add its inset to the left or from the right + if (i == _textImageIndex) + { + switch ( _contentAlignment ) + { + // left + case Label::a_northwest: + case Label::a_west: + case Label::a_southwest: + { + x += _textInset[0]; + break; + } + // right + case Label::a_northeast: + case Label::a_east: + case Label::a_southeast: + { + x -= _textInset[0]; + break; + } + } + } + + // see if the image is in a fixed position + if (imageInfo.xpos >= 0) + { + x = imageInfo.xpos; + } + + // draw + imageYPos = y; + image->SetPos(x, y); + + // fix up y for center-aligned text + if (_contentAlignment == Label::a_west || _contentAlignment == Label::a_center || _contentAlignment == Label::a_east) + { + int iw, it; + image->GetSize(iw, it); + if (it < (ty1 - ty0)) + { + imageYPos = ((ty1 - ty0) - it) / 2 + y; + image->SetPos(x, ((ty1 - ty0) - it) / 2 + y); + } + } + + // don't resize the image unless its too big + if (imageInfo.width >= 0) + { + int w, t; + image->GetSize(w, t); + if (w > imageInfo.width) + { + image->SetSize(imageInfo.width, t); + } + } + + // if it's the basic text image then draw specially + if (image == _textImage) + { + if (IsEnabled()) + { + if (_associate.Get() && ipanel()->HasParent(input()->GetFocus(), _associate->GetVPanel())) + { + _textImage->SetColor(_associateColor); + } + else + { + _textImage->SetColor(GetFgColor()); + } + + _textImage->Paint(); + } + else + { + // draw disabled version, with embossed look + // offset image + _textImage->SetPos(x + 1, imageYPos + 1); + _textImage->SetColor(_disabledFgColor1); + _textImage->Paint(); + + surface()->DrawFlushText(); + + // overlayed image + _textImage->SetPos(x, imageYPos); + _textImage->SetColor(_disabledFgColor2); + _textImage->Paint(); + } + } + else + { + image->Paint(); + } + + // add the image size to x + int wide, tall; + image->GetSize(wide, tall); + x += wide; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Helper function, draws a simple line with dashing parameters +//----------------------------------------------------------------------------- +void Label::DrawDashedLine(int x0, int y0, int x1, int y1, int dashLen, int gapLen) +{ + // work out which way the line goes + if ((x1 - x0) > (y1 - y0)) + { + // x direction line + while (1) + { + if (x0 + dashLen > x1) + { + // draw partial + surface()->DrawFilledRect(x0, y0, x1, y1); + } + else + { + surface()->DrawFilledRect(x0, y0, x0 + dashLen, y1); + } + + x0 += dashLen; + + if (x0 + gapLen > x1) + break; + + x0 += gapLen; + } + } + else + { + // y direction + while (1) + { + if (y0 + dashLen > y1) + { + // draw partial + surface()->DrawFilledRect(x0, y0, x1, y1); + } + else + { + surface()->DrawFilledRect(x0, y0, x1, y0 + dashLen); + } + + y0 += dashLen; + + if (y0 + gapLen > y1) + break; + + y0 += gapLen; + } + } +} + +void Label::SetContentAlignment(Alignment alignment) +{ + _contentAlignment=alignment; + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Size the width of the label to its contents - only works from in ApplySchemeSettings or PerformLayout() +//----------------------------------------------------------------------------- +void Label::SizeToContents() +{ + int wide, tall; + GetContentSize(wide, tall); + + SetSize(wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the font the text is drawn in +//----------------------------------------------------------------------------- +void Label::SetFont(HFont font) +{ + _textImage->SetFont(font); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Resond to resizing of the panel +//----------------------------------------------------------------------------- +void Label::OnSizeChanged(int wide, int tall) +{ + InvalidateLayout(); + Panel::OnSizeChanged(wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the font the textImage is drawn in. +//----------------------------------------------------------------------------- +HFont Label::GetFont() +{ + return _textImage->GetFont(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the foreground color of the Label +//----------------------------------------------------------------------------- +void Label::SetFgColor(Color color) +{ + if (!(GetFgColor() == color)) + { + BaseClass::SetFgColor(color); + _textImage->SetColor(color); + Repaint(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get the foreground color of the Label +//----------------------------------------------------------------------------- +Color Label::GetFgColor() +{ + Color clr = Panel::GetFgColor(); + return clr; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the foreground color 1 color of the Label +//----------------------------------------------------------------------------- +void Label::SetDisabledFgColor1(Color color) +{ + _disabledFgColor1 = color; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Label::SetDisabledFgColor2(Color color) +{ + _disabledFgColor2 = color; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Color Label::GetDisabledFgColor1() +{ + return _disabledFgColor1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Color Label::GetDisabledFgColor2() +{ + return _disabledFgColor2; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +TextImage *Label::GetTextImage() +{ + return _textImage; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool Label::RequestInfo(KeyValues *outputData) +{ + if (!stricmp(outputData->GetName(), "GetText")) + { + wchar_t wbuf[256]; + _textImage->GetText(wbuf, 255); + outputData->SetWString("text", wbuf); + return true; + } + + return Panel::RequestInfo(outputData); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the text from the message +//----------------------------------------------------------------------------- +void Label::OnSetText(KeyValues *params) +{ + KeyValues *pkvText = params->FindKey("text", false); + if (!pkvText) + return; + + if (pkvText->GetDataType() == KeyValues::TYPE_STRING) + { + SetText(pkvText->GetString()); + } + else if (pkvText->GetDataType() == KeyValues::TYPE_WSTRING) + { + SetText(pkvText->GetWString()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Add an image to the list +// returns the index the image was placed in +//----------------------------------------------------------------------------- +int Label::AddImage(IImage *image, int offset) +{ + int newImage = _imageDar.AddToTail(); + _imageDar[newImage].image = image; + _imageDar[newImage].offset = (short)offset; + _imageDar[newImage].xpos = -1; + _imageDar[newImage].width = -1; + InvalidateLayout(); + return newImage; +} + +//----------------------------------------------------------------------------- +// Purpose: removes all images from the list +// user is responsible for the memory +//----------------------------------------------------------------------------- +void Label::ClearImages() +{ + _imageDar.RemoveAll(); + _textImageIndex = -1; +} + +void Label::ResetToSimpleTextImage() +{ + ClearImages(); + _textImageIndex = AddImage(_textImage, 0); +} + + +//----------------------------------------------------------------------------- +// Purpose: Multiple image handling +// Images are drawn from left to right across the label, ordered by index +// By default there is a TextImage in position 0 +// Set the contents of an IImage in the IImage array. +//----------------------------------------------------------------------------- +void Label::SetImageAtIndex(int index, IImage *image, int offset) +{ + EnsureImageCapacity(index); +// Assert( image ); + + if ( _imageDar[index].image != image || _imageDar[index].offset != offset) + { + _imageDar[index].image = image; + _imageDar[index].offset = (short)offset; + InvalidateLayout(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get an IImage in the IImage array. +//----------------------------------------------------------------------------- +IImage *Label::GetImageAtIndex(int index) +{ + if ( _imageDar.IsValidIndex( index ) ) + return _imageDar[index].image; + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the number of images in the array. +//----------------------------------------------------------------------------- +int Label::GetImageCount() +{ + return _imageDar.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: Move where the default text image is within the image array +// (it starts in position 0) +// Input : newIndex - +// Output : int - the index the default text image was previously in +//----------------------------------------------------------------------------- +int Label::SetTextImageIndex(int newIndex) +{ + if (newIndex == _textImageIndex) + return _textImageIndex; + + EnsureImageCapacity(newIndex); + + int oldIndex = _textImageIndex; + if ( _textImageIndex >= 0 ) + { + _imageDar[_textImageIndex].image = NULL; + } + if (newIndex > -1) + { + _imageDar[newIndex].image = _textImage; + } + _textImageIndex = newIndex; + return oldIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: Ensure that the maxIndex will be a valid index +//----------------------------------------------------------------------------- +void Label::EnsureImageCapacity(int maxIndex) +{ + while (_imageDar.Size() <= maxIndex) + { + AddImage(NULL, 0); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the offset in pixels before the image +//----------------------------------------------------------------------------- +void Label::SetImagePreOffset(int index, int preOffset) +{ + if (_imageDar.IsValidIndex(index) && _imageDar[index].offset != preOffset) + { + _imageDar[index].offset = (short)preOffset; + InvalidateLayout(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: fixes the layout bounds of the image within the label +//----------------------------------------------------------------------------- +void Label::SetImageBounds(int index, int x, int width) +{ + _imageDar[index].xpos = (short)x; + _imageDar[index].width = (short)width; +} + +//----------------------------------------------------------------------------- +// Purpose: Labels can be associated with controls, and alter behaviour based on the associates behaviour +// If the associate is disabled, so are we +// If the associate has focus, we may alter how we draw +// If we get a hotkey press or focus message, we forward the focus to the associate +//----------------------------------------------------------------------------- +void Label::SetAssociatedControl(Panel *control) +{ + if (control != this) + { + _associate = control; + } + else + { + // don't let the associate ever be set to be ourself + _associate = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called after a panel requests focus to fix up the whole chain +//----------------------------------------------------------------------------- +void Label::OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel) +{ + if (_associate.Get() && subFocus == GetVPanel() && !IsBuildModeActive()) + { + // we've received focus; pass the focus onto the associate instead + _associate->RequestFocus(); + } + else + { + BaseClass::OnRequestFocus(subFocus, defaultPanel); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets custom settings from the scheme file +//----------------------------------------------------------------------------- +void Label::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + if (_fontOverrideName) + { + // use the custom specified font since we have one set + SetFont(pScheme->GetFont(_fontOverrideName, IsProportional())); + } + if ( GetFont() == INVALID_FONT ) + { + SetFont( pScheme->GetFont( "Default", IsProportional() ) ); + } + + if ( m_bWrap || m_bCenterWrap ) + { + //tell it how big it is + int wide, tall; + Panel::GetSize(wide, tall); + wide -= _textInset[0]; // take inset into account + _textImage->SetSize(wide, tall); + + _textImage->RecalculateNewLinePositions(); + } + else + { + // if you don't set the size of the image, many, many buttons will break - we might want to look into fixing this all over the place later + int wide, tall; + _textImage->GetContentSize(wide, tall); + _textImage->SetSize(wide, tall); + } + + if ( m_bAutoWideToContents ) + { + m_bAutoWideDirty = true; + HandleAutoSizing(); + } + + // clear out any the images, since they will have been invalidated + for (int i = 0; i < _imageDar.Count(); i++) + { + IImage *image = _imageDar[i].image; + if (!image) + continue; // skip over null images + + if (i == _textImageIndex) + continue; + + _imageDar[i].image = NULL; + } + + SetDisabledFgColor1(GetSchemeColor("Label.DisabledFgColor1", pScheme)); + SetDisabledFgColor2(GetSchemeColor("Label.DisabledFgColor2", pScheme)); + SetBgColor(GetSchemeColor("Label.BgColor", pScheme)); + + switch (_textColorState) + { + case CS_DULL: + SetFgColor(GetSchemeColor("Label.TextDullColor", pScheme)); + break; + case CS_BRIGHT: + SetFgColor(GetSchemeColor("Label.TextBrightColor", pScheme)); + break; + case CS_NORMAL: + default: + SetFgColor(GetSchemeColor("Label.TextColor", pScheme)); + break; + } + + _associateColor = GetSchemeColor("Label.SelectedTextColor", pScheme); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Label::GetSettings( KeyValues *outResourceData ) +{ + // panel settings + Panel::GetSettings( outResourceData ); + + // label settings + char buf[256]; + _textImage->GetUnlocalizedText( buf, 255 ); + if (!strnicmp(buf, "#var_", 5)) + { + // strip off the variable prepender on save + outResourceData->SetString( "labelText", buf + 5 ); + } + else + { + outResourceData->SetString( "labelText", buf ); + } + + const char *alignmentString = ""; + switch ( _contentAlignment ) + { + case a_northwest: alignmentString = "north-west"; break; + case a_north: alignmentString = "north"; break; + case a_northeast: alignmentString = "north-east"; break; + case a_center: alignmentString = "center"; break; + case a_east: alignmentString = "east"; break; + case a_southwest: alignmentString = "south-west"; break; + case a_south: alignmentString = "south"; break; + case a_southeast: alignmentString = "south-east"; break; + case a_west: + default: alignmentString = "west"; break; + } + + outResourceData->SetString( "textAlignment", alignmentString ); + + if (_associate) + { + outResourceData->SetString("associate", _associate->GetName()); + } + + outResourceData->SetInt("dulltext", (int)(_textColorState == CS_DULL)); + outResourceData->SetInt("brighttext", (int)(_textColorState == CS_BRIGHT)); + + if (_fontOverrideName) + { + outResourceData->SetString("font", _fontOverrideName); + } + + outResourceData->SetInt("wrap", ( m_bWrap ? 1 : 0 )); + outResourceData->SetInt("centerwrap", ( m_bCenterWrap ? 1 : 0 )); + + if ( m_bUseProportionalInsets ) + { + outResourceData->SetInt("textinsetx", scheme()->GetProportionalNormalizedValueEx( GetScheme(), _textInset[0] ) ); + outResourceData->SetInt("textinsety", _textInset[1]); + } + else + { + outResourceData->SetInt("textinsetx", _textInset[0]); + outResourceData->SetInt("textinsety", _textInset[1]); + } + outResourceData->SetInt("auto_wide_tocontents", ( m_bAutoWideToContents ? 1 : 0 )); + outResourceData->SetInt("use_proportional_insets", ( m_bUseProportionalInsets ? 1 : 0 )); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Label::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + // label settings + const char *labelText = inResourceData->GetString( "labelText", NULL ); + if ( labelText ) + { + if (labelText[0] == '%' && labelText[strlen(labelText) - 1] == '%') + { + // it's a variable, set it to be a special variable localized string + wchar_t unicodeVar[256]; + g_pVGuiLocalize->ConvertANSIToUnicode(labelText, unicodeVar, sizeof(unicodeVar)); + + char var[256]; + _snprintf(var, sizeof(var), "#var_%s", labelText); + g_pVGuiLocalize->AddString(var + 1, unicodeVar, ""); + SetText(var); + } + else + { + SetText(labelText); + } + } + + // text alignment + const char *alignmentString = inResourceData->GetString( "textAlignment", "" ); + int align = -1; + + if ( !stricmp(alignmentString, "north-west") ) + { + align = a_northwest; + } + else if ( !stricmp(alignmentString, "north") ) + { + align = a_north; + } + else if ( !stricmp(alignmentString, "north-east") ) + { + align = a_northeast; + } + else if ( !stricmp(alignmentString, "west") ) + { + align = a_west; + } + else if ( !stricmp(alignmentString, "center") ) + { + align = a_center; + } + else if ( !stricmp(alignmentString, "east") ) + { + align = a_east; + } + else if ( !stricmp(alignmentString, "south-west") ) + { + align = a_southwest; + } + else if ( !stricmp(alignmentString, "south") ) + { + align = a_south; + } + else if ( !stricmp(alignmentString, "south-east") ) + { + align = a_southeast; + } + + if ( align != -1 ) + { + SetContentAlignment( (Alignment)align ); + } + + // the control we are to be associated with may not have been created yet, + // so keep a pointer to it's name and calculate it when we can + const char *associateName = inResourceData->GetString("associate", ""); + if (associateName[0] != 0) + { + int len = Q_strlen(associateName) + 1; + _associateName = new char[ len ]; + Q_strncpy( _associateName, associateName, len ); + } + + if (inResourceData->GetInt("dulltext", 0) == 1) + { + SetTextColorState(CS_DULL); + } + else if (inResourceData->GetInt("brighttext", 0) == 1) + { + SetTextColorState(CS_BRIGHT); + } + else + { + SetTextColorState(CS_NORMAL); + } + + // font settings + const char *overrideFont = inResourceData->GetString("font", ""); + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + + if (*overrideFont) + { + delete [] _fontOverrideName; + int len = Q_strlen(overrideFont) + 1; + _fontOverrideName = new char[ len ]; + Q_strncpy(_fontOverrideName, overrideFont, len ); + SetFont(pScheme->GetFont(_fontOverrideName, IsProportional())); + } + else if (_fontOverrideName) + { + delete [] _fontOverrideName; + _fontOverrideName = NULL; + SetFont(pScheme->GetFont("Default", IsProportional())); + } + + bool bWrapText = inResourceData->GetInt("centerwrap", 0) > 0; + SetCenterWrap( bWrapText ); + + m_bAutoWideToContents = inResourceData->GetInt("auto_wide_tocontents", 0) > 0; + + bWrapText = inResourceData->GetInt("wrap", 0) > 0; + SetWrap( bWrapText ); + + int inset_x = inResourceData->GetInt("textinsetx", _textInset[0]); + int inset_y = inResourceData->GetInt("textinsety", _textInset[1]); + // Had to play it safe and add a new key for backwards compatibility + m_bUseProportionalInsets = inResourceData->GetInt("use_proportional_insets", 0) > 0; + if ( m_bUseProportionalInsets ) + { + inset_x = scheme()->GetProportionalScaledValueEx( GetScheme(), inset_x ); + } + + SetTextInset( inset_x, inset_y ); + + bool bAllCaps = inResourceData->GetInt("allcaps", 0) > 0; + SetAllCaps( bAllCaps ); + + InvalidateLayout(true); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a description of the label string +//----------------------------------------------------------------------------- +const char *Label::GetDescription( void ) +{ + static char buf[1024]; + Q_snprintf(buf, sizeof(buf), "%s, string labelText, string associate, alignment textAlignment, int wrap, int dulltext, int brighttext, string font", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: If a label has images in _imageDar, the size +// must take those into account as well as the textImage part +// Textimage part will shrink ONLY if there is not enough room. +//----------------------------------------------------------------------------- +void Label::PerformLayout() +{ + int wide, tall; + Panel::GetSize(wide, tall); + wide -= _textInset[0]; // take inset into account + + // if we just have a textImage, this is trivial. + if (_imageDar.Count() == 1 && _textImageIndex == 0) + { + if ( m_bWrap || m_bCenterWrap ) + { + int twide, ttall; + _textImage->GetContentSize(twide, ttall); + _textImage->SetSize(wide, ttall); + } + else + { + int twide, ttall; + _textImage->GetContentSize(twide, ttall); + + // tell the textImage how much space we have to draw in + if ( wide < twide) + _textImage->SetSize(wide, ttall); + else + _textImage->SetSize(twide, ttall); + } + + HandleAutoSizing(); + + HandleAutoSizing(); + + return; + } + + // assume the images in the dar cannot be resized, and if + // the images + the textimage are too wide we shring the textimage part + if (_textImageIndex < 0) + return; + + // get the size of the images + int widthOfImages = 0; + for (int i = 0; i < _imageDar.Count(); i++) + { + TImageInfo &imageInfo = _imageDar[i]; + IImage *image = imageInfo.image; + if (!image) + continue; // skip over null images + + if (i == _textImageIndex) + continue; + + // add up the bounds + int iWide, iTall; + image->GetSize(iWide, iTall); + widthOfImages += iWide; + } + + // so this is how much room we have to draw the textimage part + int spaceAvail = wide - widthOfImages; + + // if we have no space at all just leave everything as is. + if (spaceAvail < 0) + return; + + int twide, ttall; + _textImage->GetSize (twide, ttall); + // tell the textImage how much space we have to draw in + _textImage->SetSize(spaceAvail, ttall); + + HandleAutoSizing(); +} + +void Label::SetWrap( bool bWrap ) +{ + m_bWrap = bWrap; + _textImage->SetWrap( m_bWrap ); + + InvalidateLayout(); +} + +void Label::SetCenterWrap( bool bWrap ) +{ + m_bCenterWrap = bWrap; + _textImage->SetCenterWrap( m_bCenterWrap ); + + InvalidateLayout(); +} + +void Label::SetAllCaps( bool bAllCaps ) +{ + m_bAllCaps = bAllCaps; + _textImage->SetAllCaps( m_bAllCaps ); + + InvalidateLayout(); +} + +void Label::HandleAutoSizing( void ) +{ + if ( m_bAutoWideDirty ) + { + m_bAutoWideDirty = false; + + // Only change our width to match our content + int wide, tall; + GetContentSize(wide, tall); + SetSize(wide, GetTall()); + } +} + + + diff --git a/mp/src/vgui2/vgui_controls/ListPanel.cpp b/mp/src/vgui2/vgui_controls/ListPanel.cpp index f91230dc..73c8a984 100644 --- a/mp/src/vgui2/vgui_controls/ListPanel.cpp +++ b/mp/src/vgui2/vgui_controls/ListPanel.cpp @@ -1,3283 +1,3283 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#define PROTECTED_THINGS_DISABLE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file -#include "tier0/memdbgon.h" - -using namespace vgui; - -enum -{ - WINDOW_BORDER_WIDTH=2 // the width of the window's border -}; - - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef clamp -#define clamp( val, min, max ) ( ((val) > (max)) ? (max) : ( ((val) < (min)) ? (min) : (val) ) ) -#endif - -//----------------------------------------------------------------------------- -// -// Button at the top of columns used to re-sort -// -//----------------------------------------------------------------------------- -class ColumnButton : public Button -{ -public: - ColumnButton(vgui::Panel *parent, const char *name, const char *text); - - // Inherited from Button - virtual void ApplySchemeSettings(IScheme *pScheme); - virtual void OnMousePressed(MouseCode code); - - void OpenColumnChoiceMenu(); -}; - -ColumnButton::ColumnButton(vgui::Panel *parent, const char *name, const char *text) : Button(parent, name, text) -{ - SetBlockDragChaining( true ); -} - -void ColumnButton::ApplySchemeSettings(IScheme *pScheme) -{ - Button::ApplySchemeSettings(pScheme); - - SetContentAlignment(Label::a_west); - SetFont(pScheme->GetFont("DefaultSmall", IsProportional())); -} - -// Don't request focus. -// This will keep items in the listpanel selected. -void ColumnButton::OnMousePressed(MouseCode code) -{ - if (!IsEnabled()) - return; - - if (code == MOUSE_RIGHT) - { - OpenColumnChoiceMenu(); - return; - } - - if (!IsMouseClickEnabled(code)) - return; - - if (IsUseCaptureMouseEnabled()) - { - { - SetSelected(true); - Repaint(); - } - - // lock mouse input to going to this button - input()->SetMouseCapture(GetVPanel()); - } -} - -void ColumnButton::OpenColumnChoiceMenu() -{ - CallParentFunction(new KeyValues("OpenColumnChoiceMenu")); -} - - -//----------------------------------------------------------------------------- -// -// Purpose: Handles resizing of columns -// -//----------------------------------------------------------------------------- -class Dragger : public Panel -{ -public: - Dragger(int column); - - // Inherited from Panel - virtual void OnMousePressed(MouseCode code); - virtual void OnMouseDoublePressed(MouseCode code); - virtual void OnMouseReleased(MouseCode code); - virtual void OnCursorMoved(int x, int y); - virtual void SetMovable(bool state); - -private: - int m_iDragger; - bool m_bDragging; - int m_iDragPos; - bool m_bMovable; // whether this dragger is movable using mouse or not -}; - - -Dragger::Dragger(int column) -{ - m_iDragger = column; - SetPaintBackgroundEnabled(false); - SetPaintEnabled(false); - SetPaintBorderEnabled(false); - SetCursor(dc_sizewe); - m_bDragging = false; - m_bMovable = true; // movable by default - m_iDragPos = 0; - SetBlockDragChaining( true ); -} - -void Dragger::OnMousePressed(MouseCode code) -{ - if (m_bMovable) - { - input()->SetMouseCapture(GetVPanel()); - - int x, y; - input()->GetCursorPos(x, y); - m_iDragPos = x; - m_bDragging = true; - } -} - -void Dragger::OnMouseDoublePressed(MouseCode code) -{ - if (m_bMovable) - { - // resize the column to the size of it's contents - PostMessage(GetParent(), new KeyValues("ResizeColumnToContents", "column", m_iDragger)); - } -} - -void Dragger::OnMouseReleased(MouseCode code) -{ - if (m_bMovable) - { - input()->SetMouseCapture(NULL); - m_bDragging = false; - } -} - -void Dragger::OnCursorMoved(int x, int y) -{ - if (m_bDragging) - { - input()->GetCursorPos(x, y); - KeyValues *msg = new KeyValues("ColumnResized"); - msg->SetInt("column", m_iDragger); - msg->SetInt("delta", x - m_iDragPos); - m_iDragPos = x; - if (GetVParent()) - { - ivgui()->PostMessage(GetVParent(), msg, GetVPanel()); - } - } -} - -void Dragger::SetMovable(bool state) -{ - m_bMovable = state; - // disable cursor change if the dragger is not movable - if( IsVisible() ) - { - if (state) - { - // if its not movable we stick with the default arrow - // if parent windows Start getting fancy cursors we should probably retrive a parent - // cursor and set it to that - SetCursor(dc_sizewe); - } - else - { - SetCursor(dc_arrow); - } - } -} - - - -namespace vgui -{ -// optimized for sorting -class FastSortListPanelItem : public ListPanelItem -{ -public: - // index into accessing item to sort - CUtlVector m_SortedTreeIndexes; - - // visibility flag (for quick hide/filter) - bool visible; - - // precalculated sort orders - int primarySortIndexValue; - int secondarySortIndexValue; -}; -} - -static ListPanel *s_pCurrentSortingListPanel = NULL; -static const char *s_pCurrentSortingColumn = NULL; -static bool s_currentSortingColumnTypeIsText = false; - -static SortFunc *s_pSortFunc = NULL; -static bool s_bSortAscending = true; -static SortFunc *s_pSortFuncSecondary = NULL; -static bool s_bSortAscendingSecondary = true; - - -//----------------------------------------------------------------------------- -// Purpose: Basic sort function, for use in qsort -//----------------------------------------------------------------------------- -static int __cdecl AscendingSortFunc(const void *elem1, const void *elem2) -{ - int itemID1 = *((int *) elem1); - int itemID2 = *((int *) elem2); - - // convert the item index into the ListPanelItem pointers - vgui::ListPanelItem *p1, *p2; - p1 = s_pCurrentSortingListPanel->GetItemData(itemID1); - p2 = s_pCurrentSortingListPanel->GetItemData(itemID2); - - int result = s_pSortFunc( s_pCurrentSortingListPanel, *p1, *p2 ); - if (result == 0) - { - // use the secondary sort functino - result = s_pSortFuncSecondary( s_pCurrentSortingListPanel, *p1, *p2 ); - - if (!s_bSortAscendingSecondary) - { - result = -result; - } - - if (result == 0) - { - // sort by the pointers to make sure we get consistent results - if (p1 > p2) - { - result = 1; - } - else - { - result = -1; - } - } - } - else - { - // flip result if not doing an ascending sort - if (!s_bSortAscending) - { - result = -result; - } - } - - return result; -} - - -//----------------------------------------------------------------------------- -// Purpose: Default column sorting function, puts things in alpabetical order -// If images are the same returns 1, else 0 -//----------------------------------------------------------------------------- -static int __cdecl DefaultSortFunc( - ListPanel *pPanel, - const ListPanelItem &item1, - const ListPanelItem &item2 ) -{ - const vgui::ListPanelItem *p1 = &item1; - const vgui::ListPanelItem *p2 = &item2; - - if ( !p1 || !p2 ) // No meaningful comparison - { - return 0; - } - - const char *col = s_pCurrentSortingColumn; - if (s_currentSortingColumnTypeIsText) // textImage column - { - if (p1->kv->FindKey(col, true)->GetDataType() == KeyValues::TYPE_INT) - { - // compare ints - int s1 = p1->kv->GetInt(col, 0); - int s2 = p2->kv->GetInt(col, 0); - - if (s1 < s2) - { - return -1; - } - else if (s1 > s2) - { - return 1; - } - return 0; - } - else - { - // compare as string - const char *s1 = p1->kv->GetString(col, ""); - const char *s2 = p2->kv->GetString(col, ""); - - return Q_stricmp(s1, s2); - } - } - else // its an imagePanel column - { - const ImagePanel *s1 = (const ImagePanel *)p1->kv->GetPtr(col, NULL); - const ImagePanel *s2 = (const ImagePanel *)p2->kv->GetPtr(col, NULL); - - if (s1 < s2) - { - return -1; - } - else if (s1 > s2) - { - return 1; - } - return 0; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Sorts items by comparing precalculated list values -//----------------------------------------------------------------------------- -static int __cdecl FastSortFunc( - ListPanel *pPanel, - const ListPanelItem &item1, - const ListPanelItem &item2 ) -{ - const vgui::FastSortListPanelItem *p1 = (vgui::FastSortListPanelItem *)&item1; - const vgui::FastSortListPanelItem *p2 = (vgui::FastSortListPanelItem *)&item2; - - Assert(p1 && p2); - - // compare the precalculated indices - if (p1->primarySortIndexValue < p2->primarySortIndexValue) - { - return 1; - } - else if (p1->primarySortIndexValue > p2->primarySortIndexValue) - { - return -1; - - } - - // they're equal, compare the secondary indices - if (p1->secondarySortIndexValue < p2->secondarySortIndexValue) - { - return 1; - } - else if (p1->secondarySortIndexValue > p2->secondarySortIndexValue) - { - return -1; - - } - - // still equal; just compare the pointers (so we get deterministic results) - return (p1 < p2) ? 1 : -1; -} - -static int s_iDuplicateIndex = 1; - -//----------------------------------------------------------------------------- -// Purpose: sorting function used in the column index redblack tree -//----------------------------------------------------------------------------- -bool ListPanel::RBTreeLessFunc(vgui::ListPanel::IndexItem_t &item1, vgui::ListPanel::IndexItem_t &item2) -{ - int result = s_pSortFunc( s_pCurrentSortingListPanel, *item1.dataItem, *item2.dataItem); - if (result == 0) - { - // they're the same value, set their duplicate index to reflect that - if (item1.duplicateIndex) - { - item2.duplicateIndex = item1.duplicateIndex; - } - else if (item2.duplicateIndex) - { - item1.duplicateIndex = item2.duplicateIndex; - } - else - { - item1.duplicateIndex = item2.duplicateIndex = s_iDuplicateIndex++; - } - } - return (result > 0); -} - - -DECLARE_BUILD_FACTORY( ListPanel ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -ListPanel::ListPanel(Panel *parent, const char *panelName) : BaseClass(parent, panelName) -{ - m_bIgnoreDoubleClick = false; - m_bMultiselectEnabled = true; - m_iEditModeItemID = 0; - m_iEditModeColumn = 0; - - m_iHeaderHeight = 20; - m_iRowHeight = 20; - m_bCanSelectIndividualCells = false; - m_iSelectedColumn = -1; - m_bAllowUserAddDeleteColumns = false; - - m_hbar = new ScrollBar(this, "HorizScrollBar", false); - m_hbar->AddActionSignalTarget(this); - m_hbar->SetVisible(false); - m_vbar = new ScrollBar(this, "VertScrollBar", true); - m_vbar->SetVisible(false); - m_vbar->AddActionSignalTarget(this); - - m_pLabel = new Label(this, NULL, ""); - m_pLabel->SetVisible(false); - m_pLabel->SetPaintBackgroundEnabled(false); - m_pLabel->SetContentAlignment(Label::a_west); - - m_pTextImage = new TextImage( "" ); - m_pImagePanel = new ImagePanel(NULL, "ListImage"); - m_pImagePanel->SetAutoDelete(false); - - m_iSortColumn = -1; - m_iSortColumnSecondary = -1; - m_bSortAscending = true; - m_bSortAscendingSecondary = true; - - m_lastBarWidth = 0; - m_iColumnDraggerMoved = -1; - m_bNeedsSort = false; - m_LastItemSelected = -1; - - m_pImageList = NULL; - m_bDeleteImageListWhenDone = false; - m_pEmptyListText = new TextImage(""); - - m_nUserConfigFileVersion = 1; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -ListPanel::~ListPanel() -{ - // free data from table - RemoveAll(); - - // free column headers - unsigned char i; - for ( i = m_ColumnsData.Head(); i != m_ColumnsData.InvalidIndex(); i= m_ColumnsData.Next( i ) ) - { - m_ColumnsData[i].m_pHeader->MarkForDeletion(); - m_ColumnsData[i].m_pResizer->MarkForDeletion(); - } - m_ColumnsData.RemoveAll(); - - delete m_pTextImage; - delete m_pImagePanel; - delete m_vbar; - - if ( m_bDeleteImageListWhenDone ) - { - delete m_pImageList; - } - - delete m_pEmptyListText; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone) -{ - // get rid of existing list image if there's one and we're supposed to get rid of it - if ( m_pImageList && m_bDeleteImageListWhenDone ) - { - delete m_pImageList; - } - - m_bDeleteImageListWhenDone = deleteImageListWhenDone; - m_pImageList = imageList; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::SetColumnHeaderHeight( int height ) -{ - m_iHeaderHeight = height; -} - -//----------------------------------------------------------------------------- -// Purpose: adds a column header. -// this->FindChildByName(columnHeaderName) can be used to retrieve a pointer to a header panel by name -// -// if minWidth and maxWidth are BOTH NOTRESIZABLE or RESIZABLE -// the min and max size will be calculated automatically for you with that attribute -// columns are resizable by default -// if min and max size are specified column is resizable -// -// A small note on passing numbers for minWidth and maxWidth, -// If the initial window size is larger than the sum of the original widths of the columns, -// you can wind up with the columns "snapping" to size after the first window focus -// This is because the dxPerBar being calculated in PerformLayout() -// is making resizable bounded headers exceed thier maxWidths at the Start. -// Solution is to either put in support for redistributing the extra dx being truncated and -// therefore added to the last column on window opening, which is what causes the snapping. -// OR to -// ensure the difference between the starting sum of widths is not too much smaller/bigger -// than the starting window size so the starting dx doesn't cause snapping to occur. -// The easiest thing is to simply set it so your column widths add up to the starting size of the window on opening. -// -// Another note: Always give bounds for the last column you add or make it not resizable. -// -// Columns can have text headers or images for headers (e.g. password icon) -//----------------------------------------------------------------------------- -void ListPanel::AddColumnHeader(int index, const char *columnName, const char *columnText, int width, int columnFlags) -{ - if (columnFlags & COLUMN_FIXEDSIZE && !(columnFlags & COLUMN_RESIZEWITHWINDOW)) - { - // for fixed size columns, set the min & max widths to be the same as the initial width - AddColumnHeader( index, columnName, columnText, width, width, width, columnFlags); - } - else - { - AddColumnHeader( index, columnName, columnText, width, 20, 10000, columnFlags); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Adds a new column -//----------------------------------------------------------------------------- -void ListPanel::AddColumnHeader(int index, const char *columnName, const char *columnText, int width, int minWidth, int maxWidth, int columnFlags) -{ - Assert (minWidth <= width); - Assert (maxWidth >= width); - - // get our permanent index - unsigned char columnDataIndex = m_ColumnsData.AddToTail(); - - // put this index on the tail, so all item's m_SortedTreeIndexes have a consistent mapping - m_ColumnsHistory.AddToTail(columnDataIndex); - - // put this column in the right place visually - m_CurrentColumns.InsertBefore(index, columnDataIndex); - - // create the actual column object - column_t &column = m_ColumnsData[columnDataIndex]; - - // create the column header button - Button *pButton = SETUP_PANEL(new ColumnButton(this, columnName, columnText)); // the cell rendering mucks with the button visibility during the solvetraverse loop, - //so force applyschemesettings to make sure its run - pButton->SetSize(width, 24); - pButton->AddActionSignalTarget(this); - pButton->SetContentAlignment(Label::a_west); - pButton->SetTextInset(5, 0); - - column.m_pHeader = pButton; - column.m_iMinWidth = minWidth; - column.m_iMaxWidth = maxWidth; - column.m_bResizesWithWindow = columnFlags & COLUMN_RESIZEWITHWINDOW; - column.m_bTypeIsText = !(columnFlags & COLUMN_IMAGE); - column.m_bHidden = false; - column.m_bUnhidable = (columnFlags & COLUMN_UNHIDABLE); - column.m_nContentAlignment = Label::a_west; - - Dragger *dragger = new Dragger(index); - dragger->SetParent(this); - dragger->AddActionSignalTarget(this); - dragger->MoveToFront(); - if (minWidth == maxWidth || (columnFlags & COLUMN_FIXEDSIZE)) - { - // not resizable so disable the slider - dragger->SetMovable(false); - } - column.m_pResizer = dragger; - - // add default sort function - column.m_pSortFunc = NULL; - - // Set the SortedTree less than func to the generic RBTreeLessThanFunc - m_ColumnsData[columnDataIndex].m_SortedTree.SetLessFunc((IndexRBTree_t::LessFunc_t)RBTreeLessFunc); - - // go through all the headers and make sure their Command has the right column ID - ResetColumnHeaderCommands(); - - // create the new data index - ResortColumnRBTree(index); - - // ensure scroll bar is topmost compared to column headers - m_vbar->MoveToFront(); - - // fix up our visibility - SetColumnVisible(index, !(columnFlags & COLUMN_HIDDEN)); - - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Recreates a column's RB Sorted Tree -//----------------------------------------------------------------------------- -void ListPanel::ResortColumnRBTree(int col) -{ - Assert(m_CurrentColumns.IsValidIndex(col)); - - unsigned char dataColumnIndex = m_CurrentColumns[col]; - int columnHistoryIndex = m_ColumnsHistory.Find(dataColumnIndex); - column_t &column = m_ColumnsData[dataColumnIndex]; - - IndexRBTree_t &rbtree = column.m_SortedTree; - - // remove all elements - we're going to create from scratch - rbtree.RemoveAll(); - - s_pCurrentSortingListPanel = this; - s_currentSortingColumnTypeIsText = column.m_bTypeIsText; // type of data in the column - SortFunc *sortFunc = column.m_pSortFunc; - if ( !sortFunc ) - { - sortFunc = DefaultSortFunc; - } - s_pSortFunc = sortFunc; - s_bSortAscending = true; - s_pSortFuncSecondary = NULL; - - // sort all current data items for this column - FOR_EACH_LL( m_DataItems, i ) - { - IndexItem_t item; - item.dataItem = m_DataItems[i]; - item.duplicateIndex = 0; - - FastSortListPanelItem *dataItem = (FastSortListPanelItem*) m_DataItems[i]; - - // if this item doesn't already have a SortedTreeIndex for this column, - // if can only be because this is the brand new column, so add it to the SortedTreeIndexes - if (dataItem->m_SortedTreeIndexes.Count() == m_ColumnsHistory.Count() - 1 && - columnHistoryIndex == m_ColumnsHistory.Count() - 1) - { - dataItem->m_SortedTreeIndexes.AddToTail(); - } - - Assert( dataItem->m_SortedTreeIndexes.IsValidIndex(columnHistoryIndex) ); - - dataItem->m_SortedTreeIndexes[columnHistoryIndex] = rbtree.Insert(item); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Resets the "SetSortColumn" command for each column - in case columns were added or removed -//----------------------------------------------------------------------------- -void ListPanel::ResetColumnHeaderCommands() -{ - int i; - for ( i = 0 ; i < m_CurrentColumns.Count() ; i++ ) - { - Button *pButton = m_ColumnsData[m_CurrentColumns[i]].m_pHeader; - pButton->SetCommand(new KeyValues("SetSortColumn", "column", i)); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the header text for a particular column. -//----------------------------------------------------------------------------- -void ListPanel::SetColumnHeaderText(int col, const char *text) -{ - m_ColumnsData[m_CurrentColumns[col]].m_pHeader->SetText(text); -} -void ListPanel::SetColumnHeaderText(int col, wchar_t *text) -{ - m_ColumnsData[m_CurrentColumns[col]].m_pHeader->SetText(text); -} - -void ListPanel::SetColumnTextAlignment( int col, int align ) -{ - m_ColumnsData[m_CurrentColumns[col]].m_nContentAlignment = align; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the column header to have an image instead of text -//----------------------------------------------------------------------------- -void ListPanel::SetColumnHeaderImage(int column, int imageListIndex) -{ - Assert(m_pImageList); - m_ColumnsData[m_CurrentColumns[column]].m_pHeader->SetTextImageIndex(-1); - m_ColumnsData[m_CurrentColumns[column]].m_pHeader->SetImageAtIndex(0, m_pImageList->GetImage(imageListIndex), 0); -} - -//----------------------------------------------------------------------------- -// Purpose: associates a tooltip with the column header -//----------------------------------------------------------------------------- -void ListPanel::SetColumnHeaderTooltip(int column, const char *tooltipText) -{ - m_ColumnsData[m_CurrentColumns[column]].m_pHeader->GetTooltip()->SetText(tooltipText); - m_ColumnsData[m_CurrentColumns[column]].m_pHeader->GetTooltip()->SetTooltipFormatToSingleLine(); - m_ColumnsData[m_CurrentColumns[column]].m_pHeader->GetTooltip()->SetTooltipDelay(0); -} - -int ListPanel::GetNumColumnHeaders() const -{ - return m_CurrentColumns.Count(); -} - -bool ListPanel::GetColumnHeaderText( int index, char *pOut, int maxLen ) -{ - if ( index < m_CurrentColumns.Count() ) - { - m_ColumnsData[m_CurrentColumns[index]].m_pHeader->GetText( pOut, maxLen ); - return true; - } - else - { - return false; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::SetColumnSortable(int col, bool sortable) -{ - if (sortable) - { - m_ColumnsData[m_CurrentColumns[col]].m_pHeader->SetCommand(new KeyValues("SetSortColumn", "column", col)); - } - else - { - m_ColumnsData[m_CurrentColumns[col]].m_pHeader->SetCommand((const char *)NULL); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Changes the visibility of a column -//----------------------------------------------------------------------------- -void ListPanel::SetColumnVisible(int col, bool visible) -{ - column_t &column = m_ColumnsData[m_CurrentColumns[col]]; - bool bHidden = !visible; - if (column.m_bHidden == bHidden) - return; - - if (column.m_bUnhidable) - return; - - column.m_bHidden = bHidden; - if (bHidden) - { - column.m_pHeader->SetVisible(false); - column.m_pResizer->SetVisible(false); - } - else - { - column.m_pHeader->SetVisible(true); - column.m_pResizer->SetVisible(true); - } - - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::RemoveColumn(int col) -{ - if ( !m_CurrentColumns.IsValidIndex( col ) ) - return; - - // find the appropriate column data - unsigned char columnDataIndex = m_CurrentColumns[col]; - - // remove it from the current columns - m_CurrentColumns.Remove(col); - - // zero out this entry in m_ColumnsHistory - unsigned char i; - for ( i = 0 ; i < m_ColumnsHistory.Count() ; i++ ) - { - if ( m_ColumnsHistory[i] == columnDataIndex ) - { - m_ColumnsHistory[i] = m_ColumnsData.InvalidIndex(); - break; - } - } - Assert( i != m_ColumnsHistory.Count() ); - - // delete and remove the column data - m_ColumnsData[columnDataIndex].m_SortedTree.RemoveAll(); - m_ColumnsData[columnDataIndex].m_pHeader->MarkForDeletion(); - m_ColumnsData[columnDataIndex].m_pResizer->MarkForDeletion(); - m_ColumnsData.Remove(columnDataIndex); - - ResetColumnHeaderCommands(); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the index of a column by column->GetName() -//----------------------------------------------------------------------------- -int ListPanel::FindColumn(const char *columnName) -{ - for (int i = 0; i < m_CurrentColumns.Count(); i++) - { - if (!stricmp(columnName, m_ColumnsData[m_CurrentColumns[i]].m_pHeader->GetName())) - { - return i; - } - } - return -1; -} - - -//----------------------------------------------------------------------------- -// Purpose: adds an item to the view -// data->GetName() is used to uniquely identify an item -// data sub items are matched against column header name to be used in the table -//----------------------------------------------------------------------------- -int ListPanel::AddItem( const KeyValues *item, unsigned int userData, bool bScrollToItem, bool bSortOnAdd) -{ - FastSortListPanelItem *newitem = new FastSortListPanelItem; - newitem->kv = item->MakeCopy(); - newitem->userData = userData; - newitem->m_pDragData = NULL; - newitem->m_bImage = newitem->kv->GetInt( "image" ) != 0 ? true : false; - newitem->m_nImageIndex = newitem->kv->GetInt( "image" ); - newitem->m_nImageIndexSelected = newitem->kv->GetInt( "imageSelected" ); - newitem->m_pIcon = reinterpret_cast< IImage * >( newitem->kv->GetPtr( "iconImage" ) ); - - int itemID = m_DataItems.AddToTail(newitem); - int displayRow = m_VisibleItems.AddToTail(itemID); - newitem->visible = true; - - // put the item in each column's sorted Tree Index - IndexItem(itemID); - - if ( bSortOnAdd ) - { - m_bNeedsSort = true; - } - - InvalidateLayout(); - - if ( bScrollToItem ) - { - // scroll to last item - m_vbar->SetValue(displayRow); - } - return itemID; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::SetUserData( int itemID, unsigned int userData ) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return; - - m_DataItems[itemID]->userData = userData; -} - -//----------------------------------------------------------------------------- -// Purpose: Finds the first itemID with a matching userData -//----------------------------------------------------------------------------- -int ListPanel::GetItemIDFromUserData( unsigned int userData ) -{ - FOR_EACH_LL( m_DataItems, itemID ) - { - if (m_DataItems[itemID]->userData == userData) - return itemID; - } - // not found - return InvalidItemID(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ListPanel::GetItemCount( void ) -{ - return m_VisibleItems.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: gets the item ID of an item by name (data->GetName()) -//----------------------------------------------------------------------------- -int ListPanel::GetItem(const char *itemName) -{ - FOR_EACH_LL( m_DataItems, i ) - { - if (!stricmp(m_DataItems[i]->kv->GetName(), itemName)) - { - return i; - } - } - - // failure - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: returns pointer to data the itemID holds -//----------------------------------------------------------------------------- -KeyValues *ListPanel::GetItem(int itemID) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return NULL; - - return m_DataItems[itemID]->kv; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ListPanel::GetItemCurrentRow(int itemID) -{ - return m_VisibleItems.Find(itemID); -} - - -//----------------------------------------------------------------------------- -// Attaches drag data to a particular item -//----------------------------------------------------------------------------- -void ListPanel::SetItemDragData( int itemID, const KeyValues *data ) -{ - ListPanelItem *pItem = m_DataItems[ itemID ]; - if ( pItem->m_pDragData ) - { - pItem->m_pDragData->deleteThis(); - } - pItem->m_pDragData = data->MakeCopy(); -} - - -//----------------------------------------------------------------------------- -// Attaches drag data to a particular item -//----------------------------------------------------------------------------- -void ListPanel::OnCreateDragData( KeyValues *msg ) -{ - int nCount = GetSelectedItemsCount(); - if ( nCount == 0 ) - return; - - for ( int i = 0; i < nCount; ++i ) - { - int nItemID = GetSelectedItem( i ); - - KeyValues *pDragData = m_DataItems[ nItemID ]->m_pDragData; - if ( pDragData ) - { - KeyValues *pDragDataCopy = pDragData->MakeCopy(); - msg->AddSubKey( pDragDataCopy ); - } - } - - // Add the keys of the last item directly into the root also - int nLastItemID = GetSelectedItem( nCount - 1 ); - KeyValues *pLastItemDrag = m_DataItems[ nLastItemID ]->m_pDragData; - if ( pLastItemDrag ) - { - pLastItemDrag->CopySubkeys( msg ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ListPanel::GetItemIDFromRow(int currentRow) -{ - if (!m_VisibleItems.IsValidIndex(currentRow)) - return -1; - - return m_VisibleItems[currentRow]; -} - - -int ListPanel::FirstItem() const -{ - return m_DataItems.Head(); -} - - -int ListPanel::NextItem( int iItem ) const -{ - return m_DataItems.Next( iItem ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ListPanel::InvalidItemID() const -{ - return m_DataItems.InvalidIndex(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool ListPanel::IsValidItemID(int itemID) -{ - return m_DataItems.IsValidIndex(itemID); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -ListPanelItem *ListPanel::GetItemData( int itemID ) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return NULL; - - return m_DataItems[ itemID ]; -} - -//----------------------------------------------------------------------------- -// Purpose: returns user data for itemID -//----------------------------------------------------------------------------- -unsigned int ListPanel::GetItemUserData(int itemID) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return 0; - - return m_DataItems[itemID]->userData; -} - - -//----------------------------------------------------------------------------- -// Purpose: updates the view with any changes to the data -// Input : itemID - index to update -//----------------------------------------------------------------------------- -void ListPanel::ApplyItemChanges(int itemID) -{ - // reindex the item and then redraw - IndexItem(itemID); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Adds the item into the column indexes -//----------------------------------------------------------------------------- -void ListPanel::IndexItem(int itemID) -{ - FastSortListPanelItem *newitem = (FastSortListPanelItem*) m_DataItems[itemID]; - - // remove the item from the indexes and re-add - int maxCount = min(m_ColumnsHistory.Count(), newitem->m_SortedTreeIndexes.Count()); - for (int i = 0; i < maxCount; i++) - { - IndexRBTree_t &rbtree = m_ColumnsData[m_ColumnsHistory[i]].m_SortedTree; - rbtree.RemoveAt(newitem->m_SortedTreeIndexes[i]); - } - - // make sure it's all free - newitem->m_SortedTreeIndexes.RemoveAll(); - - // reserve one index per historical column - pad it out - newitem->m_SortedTreeIndexes.AddMultipleToTail(m_ColumnsHistory.Count()); - - // set the current sorting list (since the insert will need to sort) - s_pCurrentSortingListPanel = this; - - // add the item into the RB tree for each column - for (int i = 0; i < m_ColumnsHistory.Count(); i++) - { - // skip over any removed columns - if ( m_ColumnsHistory[i] == m_ColumnsData.InvalidIndex() ) - continue; - - column_t &column = m_ColumnsData[m_ColumnsHistory[i]]; - - IndexItem_t item; - item.dataItem = newitem; - item.duplicateIndex = 0; - - IndexRBTree_t &rbtree = column.m_SortedTree; - - // setup sort state - s_pCurrentSortingListPanel = this; - s_pCurrentSortingColumn = column.m_pHeader->GetName(); // name of current column for sorting - s_currentSortingColumnTypeIsText = column.m_bTypeIsText; // type of data in the column - - SortFunc *sortFunc = column.m_pSortFunc; - if (!sortFunc) - { - sortFunc = DefaultSortFunc; - } - s_pSortFunc = sortFunc; - s_bSortAscending = true; - s_pSortFuncSecondary = NULL; - - // insert index - newitem->m_SortedTreeIndexes[i] = rbtree.Insert(item); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::RereadAllItems() -{ - //!! need to make this more efficient - InvalidateLayout(); -} - - -//----------------------------------------------------------------------------- -// Cleans up allocations associated with a particular item -//----------------------------------------------------------------------------- -void ListPanel::CleanupItem( FastSortListPanelItem *data ) -{ - if ( data ) - { - if (data->kv) - { - data->kv->deleteThis(); - } - if (data->m_pDragData) - { - data->m_pDragData->deleteThis(); - } - delete data; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Removes an item at the specified item -//----------------------------------------------------------------------------- -void ListPanel::RemoveItem(int itemID) -{ -#ifdef _X360 - bool renavigate = false; - if(HasFocus()) - { - for(int i = 0; i < GetSelectedItemsCount(); ++i) - { - if(itemID == GetSelectedItem(i)) - { - renavigate = true; - break; - } - } - } -#endif - - FastSortListPanelItem *data = (FastSortListPanelItem*) m_DataItems[itemID]; - if (!data) - return; - - // remove from column sorted indexes - int i; - for ( i = 0; i < m_ColumnsHistory.Count(); i++ ) - { - if ( m_ColumnsHistory[i] == m_ColumnsData.InvalidIndex()) - continue; - - IndexRBTree_t &rbtree = m_ColumnsData[m_ColumnsHistory[i]].m_SortedTree; - rbtree.RemoveAt(data->m_SortedTreeIndexes[i]); - } - - // remove from selection - m_SelectedItems.FindAndRemove(itemID); - PostActionSignal( new KeyValues("ItemDeselected") ); - - // remove from visible items - m_VisibleItems.FindAndRemove(itemID); - - // remove from data - m_DataItems.Remove(itemID); - CleanupItem( data ); - InvalidateLayout(); - -#ifdef _X360 - if(renavigate) - { - NavigateTo(); - } -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: clears and deletes all the memory used by the data items -//----------------------------------------------------------------------------- -void ListPanel::RemoveAll() -{ - // remove all sort indexes - for (int i = 0; i < m_ColumnsHistory.Count(); i++) - { - m_ColumnsData[m_ColumnsHistory[i]].m_SortedTree.RemoveAll(); - } - - FOR_EACH_LL( m_DataItems, index ) - { - FastSortListPanelItem *pItem = m_DataItems[index]; - CleanupItem( pItem ); - } - - m_DataItems.RemoveAll(); - m_VisibleItems.RemoveAll(); - ClearSelectedItems(); - - InvalidateLayout(); - -#ifdef _X360 - if(HasFocus()) - { - NavigateTo(); - } -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: obselete, use RemoveAll(); -//----------------------------------------------------------------------------- -void ListPanel::DeleteAllItems() -{ - RemoveAll(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::ResetScrollBar() -{ - // delete and reallocate to besure the scroll bar's - // information is correct. - delete m_vbar; - m_vbar = new ScrollBar(this, "VertScrollBar", true); - m_vbar->SetVisible(false); - m_vbar->AddActionSignalTarget(this); -} - -//----------------------------------------------------------------------------- -// Purpose: returns the count of selected rows -//----------------------------------------------------------------------------- -int ListPanel::GetSelectedItemsCount() -{ - return m_SelectedItems.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: returns the selected item by selection index -// Input : selectionIndex - valid in range [0, GetNumSelectedRows) -// Output : int - itemID -//----------------------------------------------------------------------------- -int ListPanel::GetSelectedItem(int selectionIndex) -{ - if ( m_SelectedItems.IsValidIndex(selectionIndex)) - return m_SelectedItems[selectionIndex]; - - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ListPanel::GetSelectedColumn() -{ - return m_iSelectedColumn; -} - - -//----------------------------------------------------------------------------- -// Purpose: Clears all selected rows -//----------------------------------------------------------------------------- -void ListPanel::ClearSelectedItems() -{ - int nPrevCount = m_SelectedItems.Count(); - m_SelectedItems.RemoveAll(); - if ( nPrevCount > 0 ) - { - PostActionSignal( new KeyValues("ItemDeselected") ); - } - m_LastItemSelected = -1; - m_iSelectedColumn = -1; -} - - -//----------------------------------------------------------------------------- -bool ListPanel::IsItemSelected( int itemID ) -{ - return m_DataItems.IsValidIndex( itemID ) && m_SelectedItems.HasElement( itemID ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::AddSelectedItem( int itemID ) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return; - - Assert( !m_SelectedItems.HasElement( itemID ) ); - - m_LastItemSelected = itemID; - m_SelectedItems.AddToTail( itemID ); - PostActionSignal( new KeyValues("ItemSelected") ); - Repaint(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::SetSingleSelectedItem( int itemID ) -{ - ClearSelectedItems(); - AddSelectedItem(itemID); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::SetSelectedCell(int itemID, int col) -{ - if ( !m_bCanSelectIndividualCells ) - { - SetSingleSelectedItem(itemID); - return; - } - - // make sure it's a valid cell - if ( !m_DataItems.IsValidIndex(itemID) ) - return; - - if ( !m_CurrentColumns.IsValidIndex(col) ) - return; - - SetSingleSelectedItem( itemID ); - m_iSelectedColumn = col; -} - - -//----------------------------------------------------------------------------- -// Purpose: returns the data held by a specific cell -//----------------------------------------------------------------------------- -void ListPanel::GetCellText(int itemID, int col, wchar_t *wbuffer, int bufferSizeInBytes) -{ - if ( !wbuffer || !bufferSizeInBytes ) - return; - - wcscpy( wbuffer, L"" ); - - KeyValues *itemData = GetItem( itemID ); - if ( !itemData ) - { - return; - } - - // Look up column header - if ( col < 0 || col >= m_CurrentColumns.Count() ) - { - return; - } - - const char *key = m_ColumnsData[m_CurrentColumns[col]].m_pHeader->GetName(); - if ( !key || !key[ 0 ] ) - { - return; - } - - char const *val = itemData->GetString( key, "" ); - if ( !val || !key[ 0 ] ) - return; - - const wchar_t *wval = NULL; - - if ( val[ 0 ] == '#' ) - { - StringIndex_t si = g_pVGuiLocalize->FindIndex( val + 1 ); - if ( si != INVALID_LOCALIZE_STRING_INDEX ) - { - wval = g_pVGuiLocalize->GetValueByIndex( si ); - } - } - - if ( !wval ) - { - wval = itemData->GetWString( key, L"" ); - } - - wcsncpy( wbuffer, wval, bufferSizeInBytes/sizeof(wchar_t) ); - wbuffer[ (bufferSizeInBytes/sizeof(wchar_t)) - 1 ] = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: returns the data held by a specific cell -//----------------------------------------------------------------------------- -IImage *ListPanel::GetCellImage(int itemID, int col) //, ImagePanel *&buffer) -{ -// if ( !buffer ) -// return; - - KeyValues *itemData = GetItem( itemID ); - if ( !itemData ) - { - return NULL; - } - - // Look up column header - if ( col < 0 || col >= m_CurrentColumns.Count() ) - { - return NULL; - } - - const char *key = m_ColumnsData[m_CurrentColumns[col]].m_pHeader->GetName(); - if ( !key || !key[ 0 ] ) - { - return NULL; - } - - if ( !m_pImageList ) - { - return NULL; - } - - int imageIndex = itemData->GetInt( key, 0 ); - if ( m_pImageList->IsValidIndex(imageIndex) ) - { - if ( imageIndex > 0 ) - { - return m_pImageList->GetImage(imageIndex); - } - } - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the panel to use to render a cell -//----------------------------------------------------------------------------- -Panel *ListPanel::GetCellRenderer(int itemID, int col) -{ - Assert( m_pTextImage ); - Assert( m_pImagePanel ); - - column_t &column = m_ColumnsData[ m_CurrentColumns[col] ]; - - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - - m_pLabel->SetContentAlignment( (Label::Alignment)column.m_nContentAlignment ); - - if ( column.m_bTypeIsText ) - { - wchar_t tempText[ 256 ]; - - // Grab cell text - GetCellText( itemID, col, tempText, 256 ); - KeyValues *item = GetItem( itemID ); - m_pTextImage->SetText(tempText); - int cw, tall; - m_pTextImage->GetContentSize(cw, tall); - - // set cell size - Panel *header = column.m_pHeader; - int wide = header->GetWide(); - m_pTextImage->SetSize( min( cw, wide - 5 ), tall); - - m_pLabel->SetTextImageIndex( 0 ); - m_pLabel->SetImageAtIndex(0, m_pTextImage, 3); - - bool selected = false; - if ( m_SelectedItems.HasElement(itemID) && ( !m_bCanSelectIndividualCells || col == m_iSelectedColumn ) ) - { - selected = true; - VPANEL focus = input()->GetFocus(); - // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected - if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))) - { - m_pLabel->SetBgColor(GetSchemeColor("ListPanel.SelectedBgColor", pScheme)); - // selection - } - else - { - m_pLabel->SetBgColor(GetSchemeColor("ListPanel.SelectedOutOfFocusBgColor", pScheme)); - } - - if ( item->IsEmpty("cellcolor") == false ) - { - m_pTextImage->SetColor( item->GetColor( "cellcolor" ) ); - } - else if ( item->GetInt("disabled", 0) == 0 ) - { - m_pTextImage->SetColor(m_SelectionFgColor); - } - else - { - m_pTextImage->SetColor(m_DisabledSelectionFgColor); - } - - m_pLabel->SetPaintBackgroundEnabled(true); - } - else - { - if ( item->IsEmpty("cellcolor") == false ) - { - m_pTextImage->SetColor( item->GetColor( "cellcolor" ) ); - } - else if ( item->GetInt("disabled", 0) == 0 ) - { - m_pTextImage->SetColor(m_LabelFgColor); - } - else - { - m_pTextImage->SetColor(m_DisabledColor); - } - m_pLabel->SetPaintBackgroundEnabled(false); - } - - FastSortListPanelItem *listItem = m_DataItems[ itemID ]; - if ( col == 0 && - listItem->m_bImage && m_pImageList ) - { - IImage *pImage = NULL; - if ( listItem->m_pIcon ) - { - pImage = listItem->m_pIcon; - } - else - { - int imageIndex = selected ? listItem->m_nImageIndexSelected : listItem->m_nImageIndex; - if ( m_pImageList->IsValidIndex(imageIndex) ) - { - pImage = m_pImageList->GetImage(imageIndex); - } - } - - if ( pImage ) - { - m_pLabel->SetTextImageIndex( 1 ); - m_pLabel->SetImageAtIndex(0, pImage, 0); - m_pLabel->SetImageAtIndex(1, m_pTextImage, 3); - } - } - - return m_pLabel; - } - else // if its an Image Panel - { - if ( m_SelectedItems.HasElement(itemID) && ( !m_bCanSelectIndividualCells || col == m_iSelectedColumn ) ) - { - VPANEL focus = input()->GetFocus(); - // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected - if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))) - { - m_pLabel->SetBgColor(GetSchemeColor("ListPanel.SelectedBgColor", pScheme)); - // selection - } - else - { - m_pLabel->SetBgColor(GetSchemeColor("ListPanel.SelectedOutOfFocusBgColor", pScheme)); - } - // selection - m_pLabel->SetPaintBackgroundEnabled(true); - } - else - { - m_pLabel->SetPaintBackgroundEnabled(false); - } - - IImage *pIImage = GetCellImage(itemID, col); - m_pLabel->SetImageAtIndex(0, pIImage, 0); - - return m_pLabel; - } -} - -//----------------------------------------------------------------------------- -// Purpose: relayouts out the panel after any internal changes -//----------------------------------------------------------------------------- -void ListPanel::PerformLayout() -{ - if ( m_CurrentColumns.Count() == 0 ) - return; - - if (m_bNeedsSort) - { - SortList(); - } - - int rowsperpage = (int) GetRowsPerPage(); - - // count the number of visible items - int visibleItemCount = m_VisibleItems.Count(); - - //!! need to make it recalculate scroll positions - m_vbar->SetVisible(true); - m_vbar->SetEnabled(false); - m_vbar->SetRangeWindow( rowsperpage ); - m_vbar->SetRange( 0, visibleItemCount); - m_vbar->SetButtonPressedScrollValue( 1 ); - - int wide, tall; - GetSize( wide, tall ); - m_vbar->SetPos(wide - (m_vbar->GetWide()+WINDOW_BORDER_WIDTH), 0); - m_vbar->SetSize(m_vbar->GetWide(), tall - 2); - m_vbar->InvalidateLayout(); - - int buttonMaxXPos = wide - (m_vbar->GetWide()+WINDOW_BORDER_WIDTH); - - int nColumns = m_CurrentColumns.Count(); - // number of bars that can be resized - int numToResize=0; - if (m_iColumnDraggerMoved != -1) // we're resizing in response to a column dragger - { - numToResize = 1; // only one column will change size, the one we dragged - } - else // we're resizing in response to a window resize - { - for (int i = 0; i < nColumns; i++) - { - if ( m_ColumnsData[m_CurrentColumns[i]].m_bResizesWithWindow // column is resizable in response to window - && !m_ColumnsData[m_CurrentColumns[i]].m_bHidden) - { - numToResize++; - } - } - } - - int dxPerBar; // zero on window first opening - - // location of the last column resizer - int oldSizeX = 0, oldSizeY = 0; - int lastColumnIndex = nColumns-1; - for (int i = nColumns-1; i >= 0; --i) - { - if (!m_ColumnsData[m_CurrentColumns[i]].m_bHidden) - { - m_ColumnsData[m_CurrentColumns[i]].m_pHeader->GetPos(oldSizeX, oldSizeY); - lastColumnIndex = i; - break; - } - } - - bool bForceShrink = false; - if ( numToResize == 0 ) - { - // make sure we've got enough to be within minwidth - int minWidth=0; - for (int i = 0; i < nColumns; i++) - { - if (!m_ColumnsData[m_CurrentColumns[i]].m_bHidden) - { - minWidth += m_ColumnsData[m_CurrentColumns[i]].m_iMinWidth; - } - } - - // if all the minimum widths cannot fit in the space given, then we will shrink ALL columns an equal amount - if (minWidth > buttonMaxXPos) - { - int dx = buttonMaxXPos - minWidth; - dxPerBar=(int)((float)dx/(float)nColumns); - bForceShrink = true; - } - else - { - dxPerBar = 0; - } - m_lastBarWidth = buttonMaxXPos; - - } - else if ( oldSizeX != 0 ) // make sure this isnt the first time we opened the window - { - int dx = buttonMaxXPos - m_lastBarWidth; // this is how much we grew or shrank. - - // see how many bars we have and now much each should grow/shrink - dxPerBar=(int)((float)dx/(float)numToResize); - m_lastBarWidth = buttonMaxXPos; - } - else // this is the first time we've opened the window, make sure all our colums fit! resize if needed - { - int startingBarWidth=0; - for (int i = 0; i < nColumns; i++) - { - if (!m_ColumnsData[m_CurrentColumns[i]].m_bHidden) - { - startingBarWidth += m_ColumnsData[m_CurrentColumns[i]].m_pHeader->GetWide(); - } - } - int dx = buttonMaxXPos - startingBarWidth; // this is how much we grew or shrank. - // see how many bars we have and now much each should grow/shrink - dxPerBar=(int)((float)dx/(float)numToResize); - m_lastBarWidth = buttonMaxXPos; - } - - // Make sure nothing is smaller than minwidth to start with or else we'll get into trouble below. - for ( int i=0; i < nColumns; i++ ) - { - column_t &column = m_ColumnsData[m_CurrentColumns[i]]; - Panel *header = column.m_pHeader; - if ( header->GetWide() < column.m_iMinWidth ) - header->SetWide( column.m_iMinWidth ); - } - - // This was a while(1) loop and we hit an infinite loop case, so now we max out the # of times it can loop. - for ( int iLoopSanityCheck=0; iLoopSanityCheck < 1000; iLoopSanityCheck++ ) - { - // try and place headers as is - before we have to force items to be minimum width - int x = -1; - int i; - for ( i = 0; i < nColumns; i++) - { - column_t &column = m_ColumnsData[m_CurrentColumns[i]]; - Panel *header = column.m_pHeader; - if (column.m_bHidden) - { - header->SetVisible(false); - continue; - } - - header->SetPos(x, 0); - header->SetVisible(true); - - // if we couldn't fit this column - then we need to force items to be minimum width - if ( x+column.m_iMinWidth >= buttonMaxXPos && !bForceShrink ) - { - break; - } - - int hWide = header->GetWide(); - - // calculate the column's width - // make it so the last column always attaches to the scroll bar - if ( i == lastColumnIndex ) - { - hWide = buttonMaxXPos-x; - } - else if (i == m_iColumnDraggerMoved ) // column resizing using dragger - { - hWide += dxPerBar; // adjust width of column - } - else if ( m_iColumnDraggerMoved == -1 ) // window is resizing - { - // either this column is allowed to resize OR we are forcing it because we're shrinking all columns - if ( column.m_bResizesWithWindow || bForceShrink ) - { - Assert ( column.m_iMinWidth <= column.m_iMaxWidth ); - hWide += dxPerBar; // adjust width of column - } - } - - // enforce column mins and max's - unless we're FORCING it to shrink - if ( hWide < column.m_iMinWidth && !bForceShrink ) - { - hWide = column.m_iMinWidth; // adjust width of column - } - else if ( hWide > column.m_iMaxWidth ) - { - hWide = column.m_iMaxWidth; - } - - header->SetSize(hWide, m_vbar->GetWide()); - x += hWide; - - // set the resizers - Panel *sizer = column.m_pResizer; - if ( i == lastColumnIndex ) - { - sizer->SetVisible(false); - } - else - { - sizer->SetVisible(true); - } - sizer->MoveToFront(); - sizer->SetPos(x - 4, 0); - sizer->SetSize(8, m_vbar->GetWide()); - } - - // we made it all the way through - if ( i == nColumns ) - break; - - // we do this AFTER trying first, to let as many columns as possible try and get to their - // desired width before we forcing the minimum width on them - - // get the total desired width of all the columns - int totalDesiredWidth = 0; - for ( i = 0 ; i < nColumns ; i++ ) - { - if (!m_ColumnsData[m_CurrentColumns[i]].m_bHidden) - { - Panel *pHeader = m_ColumnsData[m_CurrentColumns[i]].m_pHeader; - totalDesiredWidth += pHeader->GetWide(); - } - } - - // shrink from the most right column to minimum width until we can fit them all - Assert(totalDesiredWidth > buttonMaxXPos); - for ( i = nColumns-1; i >= 0 ; i--) - { - column_t &column = m_ColumnsData[m_CurrentColumns[i]]; - if (!column.m_bHidden) - { - Panel *pHeader = column.m_pHeader; - - totalDesiredWidth -= pHeader->GetWide(); - if ( totalDesiredWidth + column.m_iMinWidth <= buttonMaxXPos ) - { - int newWidth = buttonMaxXPos - totalDesiredWidth; - pHeader->SetSize( newWidth, m_vbar->GetWide() ); - break; - } - - totalDesiredWidth += column.m_iMinWidth; - pHeader->SetSize(column.m_iMinWidth, m_vbar->GetWide()); - } - } - // If we don't allow this to shrink, then as we resize, it can get stuck in an infinite loop. - dxPerBar -= 5; - if ( dxPerBar < 0 ) - dxPerBar = 0; - - if ( i == -1 ) - { - break; - } - } - - // setup edit mode - if ( m_hEditModePanel.Get() ) - { - m_iTableStartX = 0; - m_iTableStartY = m_iHeaderHeight + 1; - - int nTotalRows = m_VisibleItems.Count(); - int nRowsPerPage = GetRowsPerPage(); - - // find the first visible item to display - int nStartItem = 0; - if (nRowsPerPage <= nTotalRows) - { - nStartItem = m_vbar->GetValue(); - } - - bool bDone = false; - int drawcount = 0; - for (int i = nStartItem; i < nTotalRows && !bDone; i++) - { - int x = 0; - if (!m_VisibleItems.IsValidIndex(i)) - continue; - - int itemID = m_VisibleItems[i]; - - // iterate the columns - for (int j = 0; j < m_CurrentColumns.Count(); j++) - { - Panel *header = m_ColumnsData[m_CurrentColumns[j]].m_pHeader; - - if (!header->IsVisible()) - continue; - - int wide = header->GetWide(); - - if ( itemID == m_iEditModeItemID && - j == m_iEditModeColumn ) - { - - m_hEditModePanel->SetPos( x + m_iTableStartX + 2, (drawcount * m_iRowHeight) + m_iTableStartY); - m_hEditModePanel->SetSize( wide, m_iRowHeight - 1 ); - - bDone = true; - } - - x += wide; - } - - drawcount++; - } - } - - Repaint(); - m_iColumnDraggerMoved = -1; // reset to invalid column -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::OnSizeChanged(int wide, int tall) -{ - BaseClass::OnSizeChanged(wide, tall); - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Renders the cells -//----------------------------------------------------------------------------- -void ListPanel::Paint() -{ - if (m_bNeedsSort) - { - SortList(); - } - - // draw selection areas if any - int wide, tall; - GetSize( wide, tall ); - - m_iTableStartX = 0; - m_iTableStartY = m_iHeaderHeight + 1; - - int nTotalRows = m_VisibleItems.Count(); - int nRowsPerPage = GetRowsPerPage(); - - // find the first visible item to display - int nStartItem = 0; - if (nRowsPerPage <= nTotalRows) - { - nStartItem = m_vbar->GetValue(); - } - - int vbarInset = m_vbar->IsVisible() ? m_vbar->GetWide() : 0; - int maxw = wide - vbarInset - 8; - -// debug timing functions -// double startTime, endTime; -// startTime = system()->GetCurrentTime(); - - // iterate through and draw each cell - bool bDone = false; - int drawcount = 0; - for (int i = nStartItem; i < nTotalRows && !bDone; i++) - { - int x = 0; - if (!m_VisibleItems.IsValidIndex(i)) - continue; - - int itemID = m_VisibleItems[i]; - - // iterate the columns - for (int j = 0; j < m_CurrentColumns.Count(); j++) - { - Panel *header = m_ColumnsData[m_CurrentColumns[j]].m_pHeader; - Panel *render = GetCellRenderer(itemID, j); - - if (!header->IsVisible()) - continue; - - int wide = header->GetWide(); - - if (render) - { - // setup render panel - if (render->GetVParent() != GetVPanel()) - { - render->SetParent(GetVPanel()); - } - if (!render->IsVisible()) - { - render->SetVisible(true); - } - int xpos = x + m_iTableStartX + 2; - - render->SetPos( xpos, (drawcount * m_iRowHeight) + m_iTableStartY); - - int right = min( xpos + wide, maxw ); - int usew = right - xpos; - render->SetSize( usew, m_iRowHeight - 1 ); - - // mark the panel to draw immediately (since it will probably be recycled to draw other cells) - render->Repaint(); - surface()->SolveTraverse(render->GetVPanel()); - int x0, y0, x1, y1; - render->GetClipRect(x0, y0, x1, y1); - if ((y1 - y0) < (m_iRowHeight - 3)) - { - bDone = true; - break; - } - surface()->PaintTraverse(render->GetVPanel()); - } - /* - // work in progress, optimized paint for text - else - { - // just paint it ourselves - char tempText[256]; - // Grab cell text - GetCellText(i, j, tempText, sizeof(tempText)); - surface()->DrawSetTextPos(x + m_iTableStartX + 2, (drawcount * m_iRowHeight) + m_iTableStartY); - - for (const char *pText = tempText; *pText != 0; pText++) - { - surface()->DrawUnicodeChar((wchar_t)*pText); - } - } - */ - - x += wide; - } - - drawcount++; - } - - m_pLabel->SetVisible(false); - - // if the list is empty, draw some help text - if (m_VisibleItems.Count() < 1 && m_pEmptyListText) - { - m_pEmptyListText->SetPos(m_iTableStartX + 8, m_iTableStartY + 4); - m_pEmptyListText->SetSize(wide - 8, m_iRowHeight); - m_pEmptyListText->Paint(); - } - -// endTime = system()->GetCurrentTime(); -// ivgui()->DPrintf2("ListPanel::Paint() (%.3f sec)\n", (float)(endTime - startTime)); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::PaintBackground() -{ - BaseClass::PaintBackground(); -} - - -//----------------------------------------------------------------------------- -// Handles multiselect -//----------------------------------------------------------------------------- -void ListPanel::HandleMultiSelection( int itemID, int row, int column ) -{ - // deal with 'multiple' row selection - - // convert the last item selected to a row so we can multiply select by rows NOT items - int lastSelectedRow = (m_LastItemSelected != -1) ? m_VisibleItems.Find( m_LastItemSelected ) : row; - int startRow, endRow; - if ( row < lastSelectedRow ) - { - startRow = row; - endRow = lastSelectedRow; - } - else - { - startRow = lastSelectedRow; - endRow = row; - } - - // clear the selection if neither control key was down - we are going to readd ALL selected items - // in case the user changed the 'direction' of the shift add - if ( !input()->IsKeyDown(KEY_LCONTROL) && !input()->IsKeyDown(KEY_RCONTROL) ) - { - ClearSelectedItems(); - } - - // add any items that we haven't added - for (int i = startRow; i <= endRow; i++) - { - // get the item indexes for these rows - int selectedItemID = m_VisibleItems[i]; - if ( !m_SelectedItems.HasElement(selectedItemID) ) - { - AddSelectedItem( selectedItemID ); - } - } -} - - -//----------------------------------------------------------------------------- -// Handles multiselect -//----------------------------------------------------------------------------- -void ListPanel::HandleAddSelection( int itemID, int row, int column ) -{ - // dealing with row selection - if ( m_SelectedItems.HasElement( itemID ) ) - { - // this row is already selected, remove - m_SelectedItems.FindAndRemove( itemID ); - PostActionSignal( new KeyValues("ItemDeselected") ); - m_LastItemSelected = itemID; - } - else - { - // add the row to the selection - AddSelectedItem( itemID ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::UpdateSelection( MouseCode code, int x, int y, int row, int column ) -{ - // make sure we're clicking on a real item - if ( row < 0 || row >= m_VisibleItems.Count() ) - { - ClearSelectedItems(); - return; - } - - int itemID = m_VisibleItems[ row ]; - - // if we've right-clicked on a selection, don't change the selection - if ( code == MOUSE_RIGHT && m_SelectedItems.HasElement( itemID ) ) - return; - - if ( m_bCanSelectIndividualCells ) - { - if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ) - { - // we're ctrl selecting the same cell, clear it - if ( ( m_LastItemSelected == itemID ) && ( m_iSelectedColumn == column ) && ( m_SelectedItems.Count() == 1 ) ) - { - ClearSelectedItems(); - } - else - { - SetSelectedCell( itemID, column ); - } - } - else - { - SetSelectedCell( itemID, column ); - } - return; - } - - if ( !m_bMultiselectEnabled ) - { - SetSingleSelectedItem( itemID ); - return; - } - - if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ) - { - // check for multi-select - HandleMultiSelection( itemID, row, column ); - } - else if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ) - { - // check for row-add select - HandleAddSelection( itemID, row, column ); - } - else - { - // no CTRL or SHIFT keys - // reset the selection Start point -// if ( ( m_LastItemSelected != itemID ) || ( m_SelectedItems.Count() > 1 ) ) - { - SetSingleSelectedItem( itemID ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::OnMousePressed( MouseCode code ) -{ - if (code == MOUSE_LEFT || code == MOUSE_RIGHT) - { - if ( m_VisibleItems.Count() > 0 ) - { - // determine where we were pressed - int x, y, row, column; - input()->GetCursorPos(x, y); - GetCellAtPos(x, y, row, column); - - UpdateSelection( code, x, y, row, column ); - } - - // get the key focus - RequestFocus(); - } - - // check for context menu open - if (code == MOUSE_RIGHT) - { - if ( m_SelectedItems.Count() > 0 ) - { - PostActionSignal( new KeyValues("OpenContextMenu", "itemID", m_SelectedItems[0] )); - } - else - { - // post it, but with the invalid row - PostActionSignal( new KeyValues("OpenContextMenu", "itemID", -1 )); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Scrolls the list according to the mouse wheel movement -//----------------------------------------------------------------------------- -void ListPanel::OnMouseWheeled(int delta) -{ - if (m_hEditModePanel.Get()) - { - // ignore mouse wheel in edit mode, forward right up to parent - CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); - return; - } - - int val = m_vbar->GetValue(); - val -= (delta * 3); - m_vbar->SetValue(val); -} - -//----------------------------------------------------------------------------- -// Purpose: Double-click act like the the item under the mouse was selected -// and then the enter key hit -//----------------------------------------------------------------------------- -void ListPanel::OnMouseDoublePressed(MouseCode code) -{ - if (code == MOUSE_LEFT) - { - // select the item - OnMousePressed(code); - - // post up an enter key being hit if anything was selected - if (GetSelectedItemsCount() > 0 && !m_bIgnoreDoubleClick ) - { - OnKeyCodeTyped(KEY_ENTER); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -#ifdef _X360 -void ListPanel::OnKeyCodePressed(KeyCode code) -{ - int nTotalRows = m_VisibleItems.Count(); - int nTotalColumns = m_CurrentColumns.Count(); - if ( nTotalRows == 0 ) - return; - - // calculate info for adjusting scrolling - int nStartItem = GetStartItem(); - int nRowsPerPage = (int)GetRowsPerPage(); - - int nSelectedRow = 0; - if ( m_DataItems.IsValidIndex( m_LastItemSelected ) ) - { - nSelectedRow = m_VisibleItems.Find( m_LastItemSelected ); - } - int nSelectedColumn = m_iSelectedColumn; - - switch(code) - { - case KEY_XBUTTON_UP: - case KEY_XSTICK1_UP: - case KEY_XSTICK2_UP: - if(GetItemCount() < 1 || nSelectedRow == nStartItem) - { - ClearSelectedItems(); - BaseClass::OnKeyCodePressed(code); - return; - } - else - { - nSelectedRow -= 1; - } - break; - case KEY_XBUTTON_DOWN: - case KEY_XSTICK1_DOWN: - case KEY_XSTICK2_DOWN: - { - int itemId = GetSelectedItem(0); - if(itemId != -1 && GetItemCurrentRow(itemId) == (nTotalRows - 1)) - { - ClearSelectedItems(); - BaseClass::OnKeyCodePressed(code); - return; - } - else - { - nSelectedRow += 1; - } - } - break; - case KEY_XBUTTON_LEFT: - case KEY_XSTICK1_LEFT: - case KEY_XSTICK2_LEFT: - if (m_bCanSelectIndividualCells && (GetSelectedItemsCount() == 1) && (nSelectedColumn >= 0) ) - { - nSelectedColumn--; - if (nSelectedColumn < 0) - { - nSelectedColumn = 0; - } - break; - } - break; - case KEY_XBUTTON_RIGHT: - case KEY_XSTICK1_RIGHT: - case KEY_XSTICK2_RIGHT: - if (m_bCanSelectIndividualCells && (GetSelectedItemsCount() == 1) && (nSelectedColumn >= 0) ) - { - nSelectedColumn++; - if (nSelectedColumn >= nTotalColumns) - { - nSelectedColumn = nTotalColumns - 1; - } - break; - } - break; - case KEY_XBUTTON_A: - PostActionSignal( new KeyValues("ListPanelItemChosen", "itemID", m_SelectedItems[0] )); - break; - default: - BaseClass::OnKeyCodePressed(code); - break; - } - - // make sure newly selected item is a valid range - nSelectedRow = clamp(nSelectedRow, 0, nTotalRows - 1); - - int row = m_VisibleItems[ nSelectedRow ]; - - // This will select the cell if in single select mode, or the row in multiselect mode - if ( ( row != m_LastItemSelected ) || ( nSelectedColumn != m_iSelectedColumn ) || ( m_SelectedItems.Count() > 1 ) ) - { - SetSelectedCell( row, nSelectedColumn ); - } - - // move the newly selected item to within the visible range - if ( nRowsPerPage < nTotalRows ) - { - int nStartItem = m_vbar->GetValue(); - if ( nSelectedRow < nStartItem ) - { - // move the list back to match - m_vbar->SetValue( nSelectedRow ); - } - else if ( nSelectedRow >= nStartItem + nRowsPerPage ) - { - // move list forward to match - m_vbar->SetValue( nSelectedRow - nRowsPerPage + 1); - } - } - - // redraw - InvalidateLayout(); -} - -#else - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::OnKeyCodePressed(KeyCode code) -{ - if (m_hEditModePanel.Get()) - { - // ignore arrow keys in edit mode - // forward right up to parent so that tab focus change doesn't occur - CallParentFunction(new KeyValues("KeyCodePressed", "code", code)); - return; - } - - int nTotalRows = m_VisibleItems.Count(); - int nTotalColumns = m_CurrentColumns.Count(); - if ( nTotalRows == 0 ) - { - BaseClass::OnKeyCodePressed(code); - return; - } - - // calculate info for adjusting scrolling - int nStartItem = GetStartItem(); - int nRowsPerPage = (int)GetRowsPerPage(); - - int nSelectedRow = 0; - if ( m_DataItems.IsValidIndex( m_LastItemSelected ) ) - { - nSelectedRow = m_VisibleItems.Find( m_LastItemSelected ); - } - int nSelectedColumn = m_iSelectedColumn; - - switch (code) - { - case KEY_HOME: - nSelectedRow = 0; - break; - - case KEY_END: - nSelectedRow = nTotalRows - 1; - break; - - case KEY_PAGEUP: - if (nSelectedRow <= nStartItem) - { - // move up a page - nSelectedRow -= (nRowsPerPage - 1); - } - else - { - // move to the top of the current page - nSelectedRow = nStartItem; - } - break; - - case KEY_PAGEDOWN: - if (nSelectedRow >= (nStartItem + nRowsPerPage-1)) - { - // move down a page - nSelectedRow += (nRowsPerPage - 1); - } - else - { - // move to the bottom of the current page - nSelectedRow = nStartItem + (nRowsPerPage - 1); - } - break; - - case KEY_UP: - case KEY_XBUTTON_UP: - case KEY_XSTICK1_UP: - case KEY_XSTICK2_UP: - if ( nTotalRows > 0 ) - { - nSelectedRow--; - break; - } - // fall through - - case KEY_DOWN: - case KEY_XBUTTON_DOWN: - case KEY_XSTICK1_DOWN: - case KEY_XSTICK2_DOWN: - if ( nTotalRows > 0 ) - { - nSelectedRow++; - break; - } - // fall through - - case KEY_LEFT: - case KEY_XBUTTON_LEFT: - case KEY_XSTICK1_LEFT: - case KEY_XSTICK2_LEFT: - if (m_bCanSelectIndividualCells && (GetSelectedItemsCount() == 1) && (nSelectedColumn >= 0) ) - { - nSelectedColumn--; - if (nSelectedColumn < 0) - { - nSelectedColumn = 0; - } - break; - } - // fall through - - case KEY_RIGHT: - case KEY_XBUTTON_RIGHT: - case KEY_XSTICK1_RIGHT: - case KEY_XSTICK2_RIGHT: - if (m_bCanSelectIndividualCells && (GetSelectedItemsCount() == 1) && (nSelectedColumn >= 0) ) - { - nSelectedColumn++; - if (nSelectedColumn >= nTotalColumns) - { - nSelectedColumn = nTotalColumns - 1; - } - break; - } - // fall through - - default: - // chain back - BaseClass::OnKeyCodePressed(code); - return; - }; - - // make sure newly selected item is a valid range - nSelectedRow = clamp(nSelectedRow, 0, nTotalRows - 1); - - int row = m_VisibleItems[ nSelectedRow ]; - - // This will select the cell if in single select mode, or the row in multiselect mode - if ( ( row != m_LastItemSelected ) || ( nSelectedColumn != m_iSelectedColumn ) || ( m_SelectedItems.Count() > 1 ) ) - { - SetSelectedCell( row, nSelectedColumn ); - } - - // move the newly selected item to within the visible range - if ( nRowsPerPage < nTotalRows ) - { - int nStartItem = m_vbar->GetValue(); - if ( nSelectedRow < nStartItem ) - { - // move the list back to match - m_vbar->SetValue( nSelectedRow ); - } - else if ( nSelectedRow >= nStartItem + nRowsPerPage ) - { - // move list forward to match - m_vbar->SetValue( nSelectedRow - nRowsPerPage + 1); - } - } - - // redraw - InvalidateLayout(); -} - -#endif - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool ListPanel::GetCellBounds( int row, int col, int& x, int& y, int& wide, int& tall ) -{ - if ( col < 0 || col >= m_CurrentColumns.Count() ) - return false; - - if ( row < 0 || row >= m_VisibleItems.Count() ) - return false; - - // Is row on screen? - int startitem = GetStartItem(); - if ( row < startitem || row >= ( startitem + GetRowsPerPage() ) ) - return false; - - y = m_iTableStartY; - y += ( row - startitem ) * m_iRowHeight; - tall = m_iRowHeight; - - // Compute column cell - x = m_iTableStartX; - // walk columns - int c = 0; - while ( c < col) - { - x += m_ColumnsData[m_CurrentColumns[c]].m_pHeader->GetWide(); - c++; - } - wide = m_ColumnsData[m_CurrentColumns[c]].m_pHeader->GetWide(); - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if any found, row and column are filled out -//----------------------------------------------------------------------------- -bool ListPanel::GetCellAtPos(int x, int y, int &row, int &col) -{ - // convert to local - ScreenToLocal(x, y); - - // move to Start of table - x -= m_iTableStartX; - y -= m_iTableStartY; - - int startitem = GetStartItem(); - // make sure it's still in valid area - if ( x >= 0 && y >= 0 ) - { - // walk the rows (for when row height is independant each row) - // NOTE: if we do height independent rows, we will need to change GetCellBounds as well - for ( row = startitem ; row < m_VisibleItems.Count() ; row++ ) - { - if ( y < ( ( ( row - startitem ) + 1 ) * m_iRowHeight ) ) - break; - } - - // walk columns - int startx = 0; - for ( col = 0 ; col < m_CurrentColumns.Count() ; col++ ) - { - startx += m_ColumnsData[m_CurrentColumns[col]].m_pHeader->GetWide(); - - if ( x < startx ) - break; - } - - // make sure we're not out of range - if ( ! ( row == m_VisibleItems.Count() || col == m_CurrentColumns.Count() ) ) - { - return true; - } - } - - // out-of-bounds - row = col = -1; - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::ApplySchemeSettings(IScheme *pScheme) -{ - // force label to apply scheme settings now so we can override it - m_pLabel->InvalidateLayout(true); - - BaseClass::ApplySchemeSettings(pScheme); - - SetBgColor(GetSchemeColor("ListPanel.BgColor", pScheme)); - SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); - - m_pLabel->SetBgColor(GetSchemeColor("ListPanel.BgColor", pScheme)); - - m_LabelFgColor = GetSchemeColor("ListPanel.TextColor", pScheme); - m_DisabledColor = GetSchemeColor("ListPanel.DisabledTextColor", m_LabelFgColor, pScheme); - m_SelectionFgColor = GetSchemeColor("ListPanel.SelectedTextColor", m_LabelFgColor, pScheme); - m_DisabledSelectionFgColor = GetSchemeColor("ListPanel.DisabledSelectedTextColor", m_LabelFgColor, pScheme); - - m_pEmptyListText->SetColor(GetSchemeColor("ListPanel.EmptyListInfoTextColor", pScheme)); - - SetFont( pScheme->GetFont("Default", IsProportional() ) ); - m_pEmptyListText->SetFont( pScheme->GetFont( "Default", IsProportional() ) ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::SetSortFunc(int col, SortFunc *func) -{ - Assert(col < m_CurrentColumns.Count()); - unsigned char dataColumnIndex = m_CurrentColumns[col]; - - if ( !m_ColumnsData[dataColumnIndex].m_bTypeIsText && func != NULL) - { - m_ColumnsData[dataColumnIndex].m_pHeader->SetMouseClickEnabled(MOUSE_LEFT, 1); - } - - m_ColumnsData[dataColumnIndex].m_pSortFunc = func; - - // resort this column according to new sort func - ResortColumnRBTree(col); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::SetSortColumn(int column) -{ - m_iSortColumn = column; -} - -int ListPanel::GetSortColumn() const -{ - return m_iSortColumn; -} - -void ListPanel::SetSortColumnEx( int iPrimarySortColumn, int iSecondarySortColumn, bool bSortAscending ) -{ - m_iSortColumn = iPrimarySortColumn; - m_iSortColumnSecondary = iSecondarySortColumn; - m_bSortAscending = bSortAscending; -} - -void ListPanel::GetSortColumnEx( int &iPrimarySortColumn, int &iSecondarySortColumn, bool &bSortAscending ) const -{ - iPrimarySortColumn = m_iSortColumn; - iSecondarySortColumn = m_iSortColumnSecondary; - bSortAscending = m_bSortAscending; -} - - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::SortList( void ) -{ - m_bNeedsSort = false; - - if ( m_VisibleItems.Count() <= 1 ) - { - return; - } - - // check if the last selected item is on the screen - if so, we should try to maintain it on screen - int startItem = GetStartItem(); - int rowsperpage = (int) GetRowsPerPage(); - int screenPosition = -1; - if ( m_LastItemSelected != -1 && m_SelectedItems.Count() > 0 ) - { - int selectedItemRow = m_VisibleItems.Find(m_LastItemSelected); - if ( selectedItemRow >= startItem && selectedItemRow <= ( startItem + rowsperpage ) ) - { - screenPosition = selectedItemRow - startItem; - } - } - - // get the required sorting functions - s_pCurrentSortingListPanel = this; - - // setup globals for use in qsort - s_pSortFunc = FastSortFunc; - s_bSortAscending = m_bSortAscending; - s_pSortFuncSecondary = FastSortFunc; - s_bSortAscendingSecondary = m_bSortAscendingSecondary; - - // walk the tree and set up the current indices - if (m_CurrentColumns.IsValidIndex(m_iSortColumn)) - { - IndexRBTree_t &rbtree = m_ColumnsData[m_CurrentColumns[m_iSortColumn]].m_SortedTree; - unsigned int index = rbtree.FirstInorder(); - unsigned int lastIndex = rbtree.LastInorder(); - int prevDuplicateIndex = 0; - int sortValue = 1; - while (1) - { - FastSortListPanelItem *dataItem = (FastSortListPanelItem*) rbtree[index].dataItem; - if (dataItem->visible) - { - // only increment the sort value if we're a different token from the previous - if (!prevDuplicateIndex || prevDuplicateIndex != rbtree[index].duplicateIndex) - { - sortValue++; - } - dataItem->primarySortIndexValue = sortValue; - prevDuplicateIndex = rbtree[index].duplicateIndex; - } - - if (index == lastIndex) - break; - - index = rbtree.NextInorder(index); - } - } - - // setup secondary indices - if (m_CurrentColumns.IsValidIndex(m_iSortColumnSecondary)) - { - IndexRBTree_t &rbtree = m_ColumnsData[m_CurrentColumns[m_iSortColumnSecondary]].m_SortedTree; - unsigned int index = rbtree.FirstInorder(); - unsigned int lastIndex = rbtree.LastInorder(); - int sortValue = 1; - int prevDuplicateIndex = 0; - while (1) - { - FastSortListPanelItem *dataItem = (FastSortListPanelItem*) rbtree[index].dataItem; - if (dataItem->visible) - { - // only increment the sort value if we're a different token from the previous - if (!prevDuplicateIndex || prevDuplicateIndex != rbtree[index].duplicateIndex) - { - sortValue++; - } - dataItem->secondarySortIndexValue = sortValue; - - prevDuplicateIndex = rbtree[index].duplicateIndex; - } - - if (index == lastIndex) - break; - - index = rbtree.NextInorder(index); - } - } - - // quick sort the list - qsort(m_VisibleItems.Base(), (size_t) m_VisibleItems.Count(), (size_t) sizeof(int), AscendingSortFunc); - - if ( screenPosition != -1 ) - { - int selectedItemRow = m_VisibleItems.Find(m_LastItemSelected); - - // if we can put the last selected item in exactly the same spot, put it there, otherwise - // we need to be at the top of the list - if (selectedItemRow > screenPosition) - { - m_vbar->SetValue(selectedItemRow - screenPosition); - } - else - { - m_vbar->SetValue(0); - } - } - - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::SetFont(HFont font) -{ - Assert( font ); - if ( !font ) - return; - - m_pTextImage->SetFont(font); - m_iRowHeight = surface()->GetFontTall(font) + 2; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListPanel::OnSliderMoved() -{ - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : deltax - deltas from current position -//----------------------------------------------------------------------------- -void ListPanel::OnColumnResized(int col, int delta) -{ - m_iColumnDraggerMoved = col; - - column_t& column = m_ColumnsData[m_CurrentColumns[col]]; - - Panel *header = column.m_pHeader; - int wide, tall; - header->GetSize(wide, tall); - - - wide += delta; - - // enforce minimum sizes for the header - if ( wide < column.m_iMinWidth ) - { - wide = column.m_iMinWidth; - } - // enforce maximum sizes for the header - if ( wide > column.m_iMaxWidth ) - { - wide = column.m_iMaxWidth; - } - - // make sure we have enough space for the columns to our right - int panelWide, panelTall; - GetSize( panelWide, panelTall ); - int x, y; - header->GetPos(x, y); - int restColumnsMinWidth = 0; - int i; - for ( i = col+1 ; i < m_CurrentColumns.Count() ; i++ ) - { - column_t& nextCol = m_ColumnsData[m_CurrentColumns[i]]; - restColumnsMinWidth += nextCol.m_iMinWidth; - } - panelWide -= ( x + restColumnsMinWidth + m_vbar->GetWide() + WINDOW_BORDER_WIDTH ); - if ( wide > panelWide ) - { - wide = panelWide; - } - - header->SetSize(wide, tall); - - // the adjacent header will be moved automatically in PerformLayout() - header->InvalidateLayout(); - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: sets which column we should sort with -//----------------------------------------------------------------------------- -void ListPanel::OnSetSortColumn(int column) -{ - // if it's the primary column already, flip the sort direction - if (m_iSortColumn == column) - { - m_bSortAscending = !m_bSortAscending; - } - else - { - // switching sort columns, keep the old one as the secondary sort - m_iSortColumnSecondary = m_iSortColumn; - m_bSortAscendingSecondary = m_bSortAscending; - } - - SetSortColumn(column); - - SortList(); -} - -//----------------------------------------------------------------------------- -// Purpose: sets whether the item is visible or not -//----------------------------------------------------------------------------- -void ListPanel::SetItemVisible(int itemID, bool state) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return; - - FastSortListPanelItem *data = (FastSortListPanelItem*) m_DataItems[itemID]; - if (data->visible == state) - return; - - m_bNeedsSort = true; - - data->visible = state; - if (data->visible) - { - // add back to end of list - m_VisibleItems.AddToTail(itemID); - } - else - { - // remove from selection if it is there. - if (m_SelectedItems.HasElement(itemID)) - { - m_SelectedItems.FindAndRemove(itemID); - PostActionSignal( new KeyValues("ItemDeselected") ); - } - - // remove from data - m_VisibleItems.FindAndRemove(itemID); - - InvalidateLayout(); - } -} - - -//----------------------------------------------------------------------------- -// Is the item visible? -//----------------------------------------------------------------------------- -bool ListPanel::IsItemVisible( int itemID ) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return false; - - FastSortListPanelItem *data = (FastSortListPanelItem*) m_DataItems[itemID]; - return data->visible; -} - - -//----------------------------------------------------------------------------- -// Purpose: sets whether the item is disabled or not (effects item color) -//----------------------------------------------------------------------------- -void ListPanel::SetItemDisabled(int itemID, bool state) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return; - - m_DataItems[itemID]->kv->SetInt( "disabled", state ); -} - -//----------------------------------------------------------------------------- -// Purpose: Calculate number of rows per page -//----------------------------------------------------------------------------- -float ListPanel::GetRowsPerPage() -{ - float rowsperpage = (float)( GetTall() - m_iHeaderHeight ) / (float)m_iRowHeight; - return rowsperpage; -} - -//----------------------------------------------------------------------------- -// Purpose: Calculate the item we should Start on -//----------------------------------------------------------------------------- -int ListPanel::GetStartItem() -{ - // if rowsperpage < total number of rows - if ( GetRowsPerPage() < (float) m_VisibleItems.Count() ) - { - return m_vbar->GetValue(); - } - return 0; // otherwise Start at top -} - -//----------------------------------------------------------------------------- -// Purpose: whether or not to select specific cells (off by default) -//----------------------------------------------------------------------------- -void ListPanel::SetSelectIndividualCells(bool state) -{ - m_bCanSelectIndividualCells = state; -} - - -//----------------------------------------------------------------------------- -// whether or not multiple cells/rows can be selected -//----------------------------------------------------------------------------- -void ListPanel::SetMultiselectEnabled( bool bState ) -{ - m_bMultiselectEnabled = bState; -} - -bool ListPanel::IsMultiselectEnabled() const -{ - return m_bMultiselectEnabled; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the text which is displayed when the list is empty -//----------------------------------------------------------------------------- -void ListPanel::SetEmptyListText(const char *text) -{ - m_pEmptyListText->SetText(text); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the text which is displayed when the list is empty -//----------------------------------------------------------------------------- -void ListPanel::SetEmptyListText(const wchar_t *text) -{ - m_pEmptyListText->SetText(text); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: opens the content menu -//----------------------------------------------------------------------------- -void ListPanel::OpenColumnChoiceMenu() -{ - if (!m_bAllowUserAddDeleteColumns) - return; - - Menu *menu = new Menu(this, "ContextMenu"); - - int x, y; - input()->GetCursorPos(x, y); - menu->SetPos(x, y); - - // add all the column choices to the menu - for ( int i = 0 ; i < m_CurrentColumns.Count() ; i++ ) - { - column_t &column = m_ColumnsData[m_CurrentColumns[i]]; - - char name[128]; - column.m_pHeader->GetText(name, sizeof(name)); - int itemID = menu->AddCheckableMenuItem(name, new KeyValues("ToggleColumnVisible", "col", m_CurrentColumns[i]), this); - menu->SetMenuItemChecked(itemID, !column.m_bHidden); - - if (column.m_bUnhidable) - { - menu->SetItemEnabled(itemID, false); - } - } - - menu->SetVisible(true); -} - -//----------------------------------------------------------------------------- -// Purpose: Resizes a column -//----------------------------------------------------------------------------- -void ListPanel::ResizeColumnToContents(int column) -{ - // iterate all the items in the column, getting the size of each - column_t &col = m_ColumnsData[m_CurrentColumns[column]]; - - if (!col.m_bTypeIsText) - return; - - // start with the size of the column text - int wide = 0, minRequiredWidth = 0, tall = 0; - col.m_pHeader->GetContentSize( minRequiredWidth, tall ); - - // iterate every item - for (int i = 0; i < m_VisibleItems.Count(); i++) - { - if (!m_VisibleItems.IsValidIndex(i)) - continue; - - // get the cell - int itemID = m_VisibleItems[i]; - - // get the text - wchar_t tempText[ 256 ]; - GetCellText( itemID, column, tempText, 256 ); - m_pTextImage->SetText(tempText); - - m_pTextImage->GetContentSize(wide, tall); - - if ( wide > minRequiredWidth ) - { - minRequiredWidth = wide; - } - } - - // Introduce a slight buffer between columns - minRequiredWidth += 4; - - // call the resize - col.m_pHeader->GetSize(wide, tall); - OnColumnResized(column, minRequiredWidth - wide); -} - -//----------------------------------------------------------------------------- -// Purpose: Changes the visibilty of a column -//----------------------------------------------------------------------------- -void ListPanel::OnToggleColumnVisible(int col) -{ - if (!m_CurrentColumns.IsValidIndex(col)) - return; - - // toggle the state of the column - column_t &column = m_ColumnsData[m_CurrentColumns[col]]; - SetColumnVisible(col, column.m_bHidden); -} - -//----------------------------------------------------------------------------- -// Purpose: sets user settings -//----------------------------------------------------------------------------- -void ListPanel::ApplyUserConfigSettings(KeyValues *userConfig) -{ - // Check for version mismatch, then don't load settings. (Just revert to the defaults.) - int version = userConfig->GetInt( "configVersion", 1 ); - if ( version != m_nUserConfigFileVersion ) - { - return; - } - - // We save/restore m_lastBarWidth because all of the column widths are saved relative to that size. - // If we don't save it, you can run into this case: - // - Window width is 500, load sizes setup relative to a 1000-width window - // - Set window size to 1000 - // - In PerformLayout, it thinks the window has grown by 500 (since m_lastBarWidth is 500 and new window width is 1000) - // so it pushes out any COLUMN_RESIZEWITHWINDOW columns to their max extent and shrinks everything else to its min extent. - m_lastBarWidth = userConfig->GetInt( "lastBarWidth", 0 ); - - // read which columns are hidden - for ( int i = 0; i < m_CurrentColumns.Count(); i++ ) - { - char name[64]; - _snprintf(name, sizeof(name), "%d_hidden", i); - - int hidden = userConfig->GetInt(name, -1); - if (hidden == 0) - { - SetColumnVisible(i, true); - } - else if (hidden == 1) - { - SetColumnVisible(i, false); - } - - _snprintf(name, sizeof(name), "%d_width", i); - int nWidth = userConfig->GetInt( name, -1 ); - if ( nWidth >= 0 ) - { - column_t &column = m_ColumnsData[m_CurrentColumns[i]]; - column.m_pHeader->SetWide( nWidth ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: returns user config settings for this control -//----------------------------------------------------------------------------- -void ListPanel::GetUserConfigSettings(KeyValues *userConfig) -{ - if ( m_nUserConfigFileVersion != 1 ) - { - userConfig->SetInt( "configVersion", m_nUserConfigFileVersion ); - } - - userConfig->SetInt( "lastBarWidth", m_lastBarWidth ); - - // save which columns are hidden - for ( int i = 0 ; i < m_CurrentColumns.Count() ; i++ ) - { - column_t &column = m_ColumnsData[m_CurrentColumns[i]]; - - char name[64]; - _snprintf(name, sizeof(name), "%d_hidden", i); - userConfig->SetInt(name, column.m_bHidden ? 1 : 0); - - _snprintf(name, sizeof(name), "%d_width", i); - userConfig->SetInt( name, column.m_pHeader->GetWide() ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: optimization, return true if this control has any user config settings -//----------------------------------------------------------------------------- -bool ListPanel::HasUserConfigSettings() -{ - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void ListPanel::SetAllowUserModificationOfColumns(bool allowed) -{ - m_bAllowUserAddDeleteColumns = allowed; -} - -void ListPanel::SetIgnoreDoubleClick( bool state ) -{ - m_bIgnoreDoubleClick = state; -} - -//----------------------------------------------------------------------------- -// Purpose: set up a field for editing -//----------------------------------------------------------------------------- -void ListPanel::EnterEditMode(int itemID, int column, vgui::Panel *editPanel) -{ - m_hEditModePanel = editPanel; - m_iEditModeItemID = itemID; - m_iEditModeColumn = column; - editPanel->SetParent(this); - editPanel->SetVisible(true); - editPanel->RequestFocus(); - editPanel->MoveToFront(); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: leaves editing mode -//----------------------------------------------------------------------------- -void ListPanel::LeaveEditMode() -{ - if (m_hEditModePanel.Get()) - { - m_hEditModePanel->SetVisible(false); - m_hEditModePanel->SetParent((Panel *)NULL); - m_hEditModePanel = NULL; - } -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if we are currently in inline editing mode -//----------------------------------------------------------------------------- -bool ListPanel::IsInEditMode() -{ - return (m_hEditModePanel.Get() != NULL); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -#ifdef _X360 -void ListPanel::NavigateTo() -{ - BaseClass::NavigateTo(); - // attempt to select the first item in the list when we get focus - if(GetItemCount()) - { - SetSingleSelectedItem(FirstItem()); - } - else // if we have no items, change focus - { - if(!NavigateDown()) - { - NavigateUp(); - } - } -} -#endif +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#define PROTECTED_THINGS_DISABLE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file +#include "tier0/memdbgon.h" + +using namespace vgui; + +enum +{ + WINDOW_BORDER_WIDTH=2 // the width of the window's border +}; + + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef clamp +#define clamp( val, min, max ) ( ((val) > (max)) ? (max) : ( ((val) < (min)) ? (min) : (val) ) ) +#endif + +//----------------------------------------------------------------------------- +// +// Button at the top of columns used to re-sort +// +//----------------------------------------------------------------------------- +class ColumnButton : public Button +{ +public: + ColumnButton(vgui::Panel *parent, const char *name, const char *text); + + // Inherited from Button + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void OnMousePressed(MouseCode code); + + void OpenColumnChoiceMenu(); +}; + +ColumnButton::ColumnButton(vgui::Panel *parent, const char *name, const char *text) : Button(parent, name, text) +{ + SetBlockDragChaining( true ); +} + +void ColumnButton::ApplySchemeSettings(IScheme *pScheme) +{ + Button::ApplySchemeSettings(pScheme); + + SetContentAlignment(Label::a_west); + SetFont(pScheme->GetFont("DefaultSmall", IsProportional())); +} + +// Don't request focus. +// This will keep items in the listpanel selected. +void ColumnButton::OnMousePressed(MouseCode code) +{ + if (!IsEnabled()) + return; + + if (code == MOUSE_RIGHT) + { + OpenColumnChoiceMenu(); + return; + } + + if (!IsMouseClickEnabled(code)) + return; + + if (IsUseCaptureMouseEnabled()) + { + { + SetSelected(true); + Repaint(); + } + + // lock mouse input to going to this button + input()->SetMouseCapture(GetVPanel()); + } +} + +void ColumnButton::OpenColumnChoiceMenu() +{ + CallParentFunction(new KeyValues("OpenColumnChoiceMenu")); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Handles resizing of columns +// +//----------------------------------------------------------------------------- +class Dragger : public Panel +{ +public: + Dragger(int column); + + // Inherited from Panel + virtual void OnMousePressed(MouseCode code); + virtual void OnMouseDoublePressed(MouseCode code); + virtual void OnMouseReleased(MouseCode code); + virtual void OnCursorMoved(int x, int y); + virtual void SetMovable(bool state); + +private: + int m_iDragger; + bool m_bDragging; + int m_iDragPos; + bool m_bMovable; // whether this dragger is movable using mouse or not +}; + + +Dragger::Dragger(int column) +{ + m_iDragger = column; + SetPaintBackgroundEnabled(false); + SetPaintEnabled(false); + SetPaintBorderEnabled(false); + SetCursor(dc_sizewe); + m_bDragging = false; + m_bMovable = true; // movable by default + m_iDragPos = 0; + SetBlockDragChaining( true ); +} + +void Dragger::OnMousePressed(MouseCode code) +{ + if (m_bMovable) + { + input()->SetMouseCapture(GetVPanel()); + + int x, y; + input()->GetCursorPos(x, y); + m_iDragPos = x; + m_bDragging = true; + } +} + +void Dragger::OnMouseDoublePressed(MouseCode code) +{ + if (m_bMovable) + { + // resize the column to the size of it's contents + PostMessage(GetParent(), new KeyValues("ResizeColumnToContents", "column", m_iDragger)); + } +} + +void Dragger::OnMouseReleased(MouseCode code) +{ + if (m_bMovable) + { + input()->SetMouseCapture(NULL); + m_bDragging = false; + } +} + +void Dragger::OnCursorMoved(int x, int y) +{ + if (m_bDragging) + { + input()->GetCursorPos(x, y); + KeyValues *msg = new KeyValues("ColumnResized"); + msg->SetInt("column", m_iDragger); + msg->SetInt("delta", x - m_iDragPos); + m_iDragPos = x; + if (GetVParent()) + { + ivgui()->PostMessage(GetVParent(), msg, GetVPanel()); + } + } +} + +void Dragger::SetMovable(bool state) +{ + m_bMovable = state; + // disable cursor change if the dragger is not movable + if( IsVisible() ) + { + if (state) + { + // if its not movable we stick with the default arrow + // if parent windows Start getting fancy cursors we should probably retrive a parent + // cursor and set it to that + SetCursor(dc_sizewe); + } + else + { + SetCursor(dc_arrow); + } + } +} + + + +namespace vgui +{ +// optimized for sorting +class FastSortListPanelItem : public ListPanelItem +{ +public: + // index into accessing item to sort + CUtlVector m_SortedTreeIndexes; + + // visibility flag (for quick hide/filter) + bool visible; + + // precalculated sort orders + int primarySortIndexValue; + int secondarySortIndexValue; +}; +} + +static ListPanel *s_pCurrentSortingListPanel = NULL; +static const char *s_pCurrentSortingColumn = NULL; +static bool s_currentSortingColumnTypeIsText = false; + +static SortFunc *s_pSortFunc = NULL; +static bool s_bSortAscending = true; +static SortFunc *s_pSortFuncSecondary = NULL; +static bool s_bSortAscendingSecondary = true; + + +//----------------------------------------------------------------------------- +// Purpose: Basic sort function, for use in qsort +//----------------------------------------------------------------------------- +static int __cdecl AscendingSortFunc(const void *elem1, const void *elem2) +{ + int itemID1 = *((int *) elem1); + int itemID2 = *((int *) elem2); + + // convert the item index into the ListPanelItem pointers + vgui::ListPanelItem *p1, *p2; + p1 = s_pCurrentSortingListPanel->GetItemData(itemID1); + p2 = s_pCurrentSortingListPanel->GetItemData(itemID2); + + int result = s_pSortFunc( s_pCurrentSortingListPanel, *p1, *p2 ); + if (result == 0) + { + // use the secondary sort functino + result = s_pSortFuncSecondary( s_pCurrentSortingListPanel, *p1, *p2 ); + + if (!s_bSortAscendingSecondary) + { + result = -result; + } + + if (result == 0) + { + // sort by the pointers to make sure we get consistent results + if (p1 > p2) + { + result = 1; + } + else + { + result = -1; + } + } + } + else + { + // flip result if not doing an ascending sort + if (!s_bSortAscending) + { + result = -result; + } + } + + return result; +} + + +//----------------------------------------------------------------------------- +// Purpose: Default column sorting function, puts things in alpabetical order +// If images are the same returns 1, else 0 +//----------------------------------------------------------------------------- +static int __cdecl DefaultSortFunc( + ListPanel *pPanel, + const ListPanelItem &item1, + const ListPanelItem &item2 ) +{ + const vgui::ListPanelItem *p1 = &item1; + const vgui::ListPanelItem *p2 = &item2; + + if ( !p1 || !p2 ) // No meaningful comparison + { + return 0; + } + + const char *col = s_pCurrentSortingColumn; + if (s_currentSortingColumnTypeIsText) // textImage column + { + if (p1->kv->FindKey(col, true)->GetDataType() == KeyValues::TYPE_INT) + { + // compare ints + int s1 = p1->kv->GetInt(col, 0); + int s2 = p2->kv->GetInt(col, 0); + + if (s1 < s2) + { + return -1; + } + else if (s1 > s2) + { + return 1; + } + return 0; + } + else + { + // compare as string + const char *s1 = p1->kv->GetString(col, ""); + const char *s2 = p2->kv->GetString(col, ""); + + return Q_stricmp(s1, s2); + } + } + else // its an imagePanel column + { + const ImagePanel *s1 = (const ImagePanel *)p1->kv->GetPtr(col, NULL); + const ImagePanel *s2 = (const ImagePanel *)p2->kv->GetPtr(col, NULL); + + if (s1 < s2) + { + return -1; + } + else if (s1 > s2) + { + return 1; + } + return 0; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Sorts items by comparing precalculated list values +//----------------------------------------------------------------------------- +static int __cdecl FastSortFunc( + ListPanel *pPanel, + const ListPanelItem &item1, + const ListPanelItem &item2 ) +{ + const vgui::FastSortListPanelItem *p1 = (vgui::FastSortListPanelItem *)&item1; + const vgui::FastSortListPanelItem *p2 = (vgui::FastSortListPanelItem *)&item2; + + Assert(p1 && p2); + + // compare the precalculated indices + if (p1->primarySortIndexValue < p2->primarySortIndexValue) + { + return 1; + } + else if (p1->primarySortIndexValue > p2->primarySortIndexValue) + { + return -1; + + } + + // they're equal, compare the secondary indices + if (p1->secondarySortIndexValue < p2->secondarySortIndexValue) + { + return 1; + } + else if (p1->secondarySortIndexValue > p2->secondarySortIndexValue) + { + return -1; + + } + + // still equal; just compare the pointers (so we get deterministic results) + return (p1 < p2) ? 1 : -1; +} + +static int s_iDuplicateIndex = 1; + +//----------------------------------------------------------------------------- +// Purpose: sorting function used in the column index redblack tree +//----------------------------------------------------------------------------- +bool ListPanel::RBTreeLessFunc(vgui::ListPanel::IndexItem_t &item1, vgui::ListPanel::IndexItem_t &item2) +{ + int result = s_pSortFunc( s_pCurrentSortingListPanel, *item1.dataItem, *item2.dataItem); + if (result == 0) + { + // they're the same value, set their duplicate index to reflect that + if (item1.duplicateIndex) + { + item2.duplicateIndex = item1.duplicateIndex; + } + else if (item2.duplicateIndex) + { + item1.duplicateIndex = item2.duplicateIndex; + } + else + { + item1.duplicateIndex = item2.duplicateIndex = s_iDuplicateIndex++; + } + } + return (result > 0); +} + + +DECLARE_BUILD_FACTORY( ListPanel ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ListPanel::ListPanel(Panel *parent, const char *panelName) : BaseClass(parent, panelName) +{ + m_bIgnoreDoubleClick = false; + m_bMultiselectEnabled = true; + m_iEditModeItemID = 0; + m_iEditModeColumn = 0; + + m_iHeaderHeight = 20; + m_iRowHeight = 20; + m_bCanSelectIndividualCells = false; + m_iSelectedColumn = -1; + m_bAllowUserAddDeleteColumns = false; + + m_hbar = new ScrollBar(this, "HorizScrollBar", false); + m_hbar->AddActionSignalTarget(this); + m_hbar->SetVisible(false); + m_vbar = new ScrollBar(this, "VertScrollBar", true); + m_vbar->SetVisible(false); + m_vbar->AddActionSignalTarget(this); + + m_pLabel = new Label(this, NULL, ""); + m_pLabel->SetVisible(false); + m_pLabel->SetPaintBackgroundEnabled(false); + m_pLabel->SetContentAlignment(Label::a_west); + + m_pTextImage = new TextImage( "" ); + m_pImagePanel = new ImagePanel(NULL, "ListImage"); + m_pImagePanel->SetAutoDelete(false); + + m_iSortColumn = -1; + m_iSortColumnSecondary = -1; + m_bSortAscending = true; + m_bSortAscendingSecondary = true; + + m_lastBarWidth = 0; + m_iColumnDraggerMoved = -1; + m_bNeedsSort = false; + m_LastItemSelected = -1; + + m_pImageList = NULL; + m_bDeleteImageListWhenDone = false; + m_pEmptyListText = new TextImage(""); + + m_nUserConfigFileVersion = 1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ListPanel::~ListPanel() +{ + // free data from table + RemoveAll(); + + // free column headers + unsigned char i; + for ( i = m_ColumnsData.Head(); i != m_ColumnsData.InvalidIndex(); i= m_ColumnsData.Next( i ) ) + { + m_ColumnsData[i].m_pHeader->MarkForDeletion(); + m_ColumnsData[i].m_pResizer->MarkForDeletion(); + } + m_ColumnsData.RemoveAll(); + + delete m_pTextImage; + delete m_pImagePanel; + delete m_vbar; + + if ( m_bDeleteImageListWhenDone ) + { + delete m_pImageList; + } + + delete m_pEmptyListText; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone) +{ + // get rid of existing list image if there's one and we're supposed to get rid of it + if ( m_pImageList && m_bDeleteImageListWhenDone ) + { + delete m_pImageList; + } + + m_bDeleteImageListWhenDone = deleteImageListWhenDone; + m_pImageList = imageList; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::SetColumnHeaderHeight( int height ) +{ + m_iHeaderHeight = height; +} + +//----------------------------------------------------------------------------- +// Purpose: adds a column header. +// this->FindChildByName(columnHeaderName) can be used to retrieve a pointer to a header panel by name +// +// if minWidth and maxWidth are BOTH NOTRESIZABLE or RESIZABLE +// the min and max size will be calculated automatically for you with that attribute +// columns are resizable by default +// if min and max size are specified column is resizable +// +// A small note on passing numbers for minWidth and maxWidth, +// If the initial window size is larger than the sum of the original widths of the columns, +// you can wind up with the columns "snapping" to size after the first window focus +// This is because the dxPerBar being calculated in PerformLayout() +// is making resizable bounded headers exceed thier maxWidths at the Start. +// Solution is to either put in support for redistributing the extra dx being truncated and +// therefore added to the last column on window opening, which is what causes the snapping. +// OR to +// ensure the difference between the starting sum of widths is not too much smaller/bigger +// than the starting window size so the starting dx doesn't cause snapping to occur. +// The easiest thing is to simply set it so your column widths add up to the starting size of the window on opening. +// +// Another note: Always give bounds for the last column you add or make it not resizable. +// +// Columns can have text headers or images for headers (e.g. password icon) +//----------------------------------------------------------------------------- +void ListPanel::AddColumnHeader(int index, const char *columnName, const char *columnText, int width, int columnFlags) +{ + if (columnFlags & COLUMN_FIXEDSIZE && !(columnFlags & COLUMN_RESIZEWITHWINDOW)) + { + // for fixed size columns, set the min & max widths to be the same as the initial width + AddColumnHeader( index, columnName, columnText, width, width, width, columnFlags); + } + else + { + AddColumnHeader( index, columnName, columnText, width, 20, 10000, columnFlags); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Adds a new column +//----------------------------------------------------------------------------- +void ListPanel::AddColumnHeader(int index, const char *columnName, const char *columnText, int width, int minWidth, int maxWidth, int columnFlags) +{ + Assert (minWidth <= width); + Assert (maxWidth >= width); + + // get our permanent index + unsigned char columnDataIndex = m_ColumnsData.AddToTail(); + + // put this index on the tail, so all item's m_SortedTreeIndexes have a consistent mapping + m_ColumnsHistory.AddToTail(columnDataIndex); + + // put this column in the right place visually + m_CurrentColumns.InsertBefore(index, columnDataIndex); + + // create the actual column object + column_t &column = m_ColumnsData[columnDataIndex]; + + // create the column header button + Button *pButton = SETUP_PANEL(new ColumnButton(this, columnName, columnText)); // the cell rendering mucks with the button visibility during the solvetraverse loop, + //so force applyschemesettings to make sure its run + pButton->SetSize(width, 24); + pButton->AddActionSignalTarget(this); + pButton->SetContentAlignment(Label::a_west); + pButton->SetTextInset(5, 0); + + column.m_pHeader = pButton; + column.m_iMinWidth = minWidth; + column.m_iMaxWidth = maxWidth; + column.m_bResizesWithWindow = columnFlags & COLUMN_RESIZEWITHWINDOW; + column.m_bTypeIsText = !(columnFlags & COLUMN_IMAGE); + column.m_bHidden = false; + column.m_bUnhidable = (columnFlags & COLUMN_UNHIDABLE); + column.m_nContentAlignment = Label::a_west; + + Dragger *dragger = new Dragger(index); + dragger->SetParent(this); + dragger->AddActionSignalTarget(this); + dragger->MoveToFront(); + if (minWidth == maxWidth || (columnFlags & COLUMN_FIXEDSIZE)) + { + // not resizable so disable the slider + dragger->SetMovable(false); + } + column.m_pResizer = dragger; + + // add default sort function + column.m_pSortFunc = NULL; + + // Set the SortedTree less than func to the generic RBTreeLessThanFunc + m_ColumnsData[columnDataIndex].m_SortedTree.SetLessFunc((IndexRBTree_t::LessFunc_t)RBTreeLessFunc); + + // go through all the headers and make sure their Command has the right column ID + ResetColumnHeaderCommands(); + + // create the new data index + ResortColumnRBTree(index); + + // ensure scroll bar is topmost compared to column headers + m_vbar->MoveToFront(); + + // fix up our visibility + SetColumnVisible(index, !(columnFlags & COLUMN_HIDDEN)); + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Recreates a column's RB Sorted Tree +//----------------------------------------------------------------------------- +void ListPanel::ResortColumnRBTree(int col) +{ + Assert(m_CurrentColumns.IsValidIndex(col)); + + unsigned char dataColumnIndex = m_CurrentColumns[col]; + int columnHistoryIndex = m_ColumnsHistory.Find(dataColumnIndex); + column_t &column = m_ColumnsData[dataColumnIndex]; + + IndexRBTree_t &rbtree = column.m_SortedTree; + + // remove all elements - we're going to create from scratch + rbtree.RemoveAll(); + + s_pCurrentSortingListPanel = this; + s_currentSortingColumnTypeIsText = column.m_bTypeIsText; // type of data in the column + SortFunc *sortFunc = column.m_pSortFunc; + if ( !sortFunc ) + { + sortFunc = DefaultSortFunc; + } + s_pSortFunc = sortFunc; + s_bSortAscending = true; + s_pSortFuncSecondary = NULL; + + // sort all current data items for this column + FOR_EACH_LL( m_DataItems, i ) + { + IndexItem_t item; + item.dataItem = m_DataItems[i]; + item.duplicateIndex = 0; + + FastSortListPanelItem *dataItem = (FastSortListPanelItem*) m_DataItems[i]; + + // if this item doesn't already have a SortedTreeIndex for this column, + // if can only be because this is the brand new column, so add it to the SortedTreeIndexes + if (dataItem->m_SortedTreeIndexes.Count() == m_ColumnsHistory.Count() - 1 && + columnHistoryIndex == m_ColumnsHistory.Count() - 1) + { + dataItem->m_SortedTreeIndexes.AddToTail(); + } + + Assert( dataItem->m_SortedTreeIndexes.IsValidIndex(columnHistoryIndex) ); + + dataItem->m_SortedTreeIndexes[columnHistoryIndex] = rbtree.Insert(item); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Resets the "SetSortColumn" command for each column - in case columns were added or removed +//----------------------------------------------------------------------------- +void ListPanel::ResetColumnHeaderCommands() +{ + int i; + for ( i = 0 ; i < m_CurrentColumns.Count() ; i++ ) + { + Button *pButton = m_ColumnsData[m_CurrentColumns[i]].m_pHeader; + pButton->SetCommand(new KeyValues("SetSortColumn", "column", i)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the header text for a particular column. +//----------------------------------------------------------------------------- +void ListPanel::SetColumnHeaderText(int col, const char *text) +{ + m_ColumnsData[m_CurrentColumns[col]].m_pHeader->SetText(text); +} +void ListPanel::SetColumnHeaderText(int col, wchar_t *text) +{ + m_ColumnsData[m_CurrentColumns[col]].m_pHeader->SetText(text); +} + +void ListPanel::SetColumnTextAlignment( int col, int align ) +{ + m_ColumnsData[m_CurrentColumns[col]].m_nContentAlignment = align; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the column header to have an image instead of text +//----------------------------------------------------------------------------- +void ListPanel::SetColumnHeaderImage(int column, int imageListIndex) +{ + Assert(m_pImageList); + m_ColumnsData[m_CurrentColumns[column]].m_pHeader->SetTextImageIndex(-1); + m_ColumnsData[m_CurrentColumns[column]].m_pHeader->SetImageAtIndex(0, m_pImageList->GetImage(imageListIndex), 0); +} + +//----------------------------------------------------------------------------- +// Purpose: associates a tooltip with the column header +//----------------------------------------------------------------------------- +void ListPanel::SetColumnHeaderTooltip(int column, const char *tooltipText) +{ + m_ColumnsData[m_CurrentColumns[column]].m_pHeader->GetTooltip()->SetText(tooltipText); + m_ColumnsData[m_CurrentColumns[column]].m_pHeader->GetTooltip()->SetTooltipFormatToSingleLine(); + m_ColumnsData[m_CurrentColumns[column]].m_pHeader->GetTooltip()->SetTooltipDelay(0); +} + +int ListPanel::GetNumColumnHeaders() const +{ + return m_CurrentColumns.Count(); +} + +bool ListPanel::GetColumnHeaderText( int index, char *pOut, int maxLen ) +{ + if ( index < m_CurrentColumns.Count() ) + { + m_ColumnsData[m_CurrentColumns[index]].m_pHeader->GetText( pOut, maxLen ); + return true; + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::SetColumnSortable(int col, bool sortable) +{ + if (sortable) + { + m_ColumnsData[m_CurrentColumns[col]].m_pHeader->SetCommand(new KeyValues("SetSortColumn", "column", col)); + } + else + { + m_ColumnsData[m_CurrentColumns[col]].m_pHeader->SetCommand((const char *)NULL); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Changes the visibility of a column +//----------------------------------------------------------------------------- +void ListPanel::SetColumnVisible(int col, bool visible) +{ + column_t &column = m_ColumnsData[m_CurrentColumns[col]]; + bool bHidden = !visible; + if (column.m_bHidden == bHidden) + return; + + if (column.m_bUnhidable) + return; + + column.m_bHidden = bHidden; + if (bHidden) + { + column.m_pHeader->SetVisible(false); + column.m_pResizer->SetVisible(false); + } + else + { + column.m_pHeader->SetVisible(true); + column.m_pResizer->SetVisible(true); + } + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::RemoveColumn(int col) +{ + if ( !m_CurrentColumns.IsValidIndex( col ) ) + return; + + // find the appropriate column data + unsigned char columnDataIndex = m_CurrentColumns[col]; + + // remove it from the current columns + m_CurrentColumns.Remove(col); + + // zero out this entry in m_ColumnsHistory + unsigned char i; + for ( i = 0 ; i < m_ColumnsHistory.Count() ; i++ ) + { + if ( m_ColumnsHistory[i] == columnDataIndex ) + { + m_ColumnsHistory[i] = m_ColumnsData.InvalidIndex(); + break; + } + } + Assert( i != m_ColumnsHistory.Count() ); + + // delete and remove the column data + m_ColumnsData[columnDataIndex].m_SortedTree.RemoveAll(); + m_ColumnsData[columnDataIndex].m_pHeader->MarkForDeletion(); + m_ColumnsData[columnDataIndex].m_pResizer->MarkForDeletion(); + m_ColumnsData.Remove(columnDataIndex); + + ResetColumnHeaderCommands(); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the index of a column by column->GetName() +//----------------------------------------------------------------------------- +int ListPanel::FindColumn(const char *columnName) +{ + for (int i = 0; i < m_CurrentColumns.Count(); i++) + { + if (!stricmp(columnName, m_ColumnsData[m_CurrentColumns[i]].m_pHeader->GetName())) + { + return i; + } + } + return -1; +} + + +//----------------------------------------------------------------------------- +// Purpose: adds an item to the view +// data->GetName() is used to uniquely identify an item +// data sub items are matched against column header name to be used in the table +//----------------------------------------------------------------------------- +int ListPanel::AddItem( const KeyValues *item, unsigned int userData, bool bScrollToItem, bool bSortOnAdd) +{ + FastSortListPanelItem *newitem = new FastSortListPanelItem; + newitem->kv = item->MakeCopy(); + newitem->userData = userData; + newitem->m_pDragData = NULL; + newitem->m_bImage = newitem->kv->GetInt( "image" ) != 0 ? true : false; + newitem->m_nImageIndex = newitem->kv->GetInt( "image" ); + newitem->m_nImageIndexSelected = newitem->kv->GetInt( "imageSelected" ); + newitem->m_pIcon = reinterpret_cast< IImage * >( newitem->kv->GetPtr( "iconImage" ) ); + + int itemID = m_DataItems.AddToTail(newitem); + int displayRow = m_VisibleItems.AddToTail(itemID); + newitem->visible = true; + + // put the item in each column's sorted Tree Index + IndexItem(itemID); + + if ( bSortOnAdd ) + { + m_bNeedsSort = true; + } + + InvalidateLayout(); + + if ( bScrollToItem ) + { + // scroll to last item + m_vbar->SetValue(displayRow); + } + return itemID; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::SetUserData( int itemID, unsigned int userData ) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return; + + m_DataItems[itemID]->userData = userData; +} + +//----------------------------------------------------------------------------- +// Purpose: Finds the first itemID with a matching userData +//----------------------------------------------------------------------------- +int ListPanel::GetItemIDFromUserData( unsigned int userData ) +{ + FOR_EACH_LL( m_DataItems, itemID ) + { + if (m_DataItems[itemID]->userData == userData) + return itemID; + } + // not found + return InvalidItemID(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ListPanel::GetItemCount( void ) +{ + return m_VisibleItems.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: gets the item ID of an item by name (data->GetName()) +//----------------------------------------------------------------------------- +int ListPanel::GetItem(const char *itemName) +{ + FOR_EACH_LL( m_DataItems, i ) + { + if (!stricmp(m_DataItems[i]->kv->GetName(), itemName)) + { + return i; + } + } + + // failure + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: returns pointer to data the itemID holds +//----------------------------------------------------------------------------- +KeyValues *ListPanel::GetItem(int itemID) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return NULL; + + return m_DataItems[itemID]->kv; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ListPanel::GetItemCurrentRow(int itemID) +{ + return m_VisibleItems.Find(itemID); +} + + +//----------------------------------------------------------------------------- +// Attaches drag data to a particular item +//----------------------------------------------------------------------------- +void ListPanel::SetItemDragData( int itemID, const KeyValues *data ) +{ + ListPanelItem *pItem = m_DataItems[ itemID ]; + if ( pItem->m_pDragData ) + { + pItem->m_pDragData->deleteThis(); + } + pItem->m_pDragData = data->MakeCopy(); +} + + +//----------------------------------------------------------------------------- +// Attaches drag data to a particular item +//----------------------------------------------------------------------------- +void ListPanel::OnCreateDragData( KeyValues *msg ) +{ + int nCount = GetSelectedItemsCount(); + if ( nCount == 0 ) + return; + + for ( int i = 0; i < nCount; ++i ) + { + int nItemID = GetSelectedItem( i ); + + KeyValues *pDragData = m_DataItems[ nItemID ]->m_pDragData; + if ( pDragData ) + { + KeyValues *pDragDataCopy = pDragData->MakeCopy(); + msg->AddSubKey( pDragDataCopy ); + } + } + + // Add the keys of the last item directly into the root also + int nLastItemID = GetSelectedItem( nCount - 1 ); + KeyValues *pLastItemDrag = m_DataItems[ nLastItemID ]->m_pDragData; + if ( pLastItemDrag ) + { + pLastItemDrag->CopySubkeys( msg ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ListPanel::GetItemIDFromRow(int currentRow) +{ + if (!m_VisibleItems.IsValidIndex(currentRow)) + return -1; + + return m_VisibleItems[currentRow]; +} + + +int ListPanel::FirstItem() const +{ + return m_DataItems.Head(); +} + + +int ListPanel::NextItem( int iItem ) const +{ + return m_DataItems.Next( iItem ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ListPanel::InvalidItemID() const +{ + return m_DataItems.InvalidIndex(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool ListPanel::IsValidItemID(int itemID) +{ + return m_DataItems.IsValidIndex(itemID); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ListPanelItem *ListPanel::GetItemData( int itemID ) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return NULL; + + return m_DataItems[ itemID ]; +} + +//----------------------------------------------------------------------------- +// Purpose: returns user data for itemID +//----------------------------------------------------------------------------- +unsigned int ListPanel::GetItemUserData(int itemID) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return 0; + + return m_DataItems[itemID]->userData; +} + + +//----------------------------------------------------------------------------- +// Purpose: updates the view with any changes to the data +// Input : itemID - index to update +//----------------------------------------------------------------------------- +void ListPanel::ApplyItemChanges(int itemID) +{ + // reindex the item and then redraw + IndexItem(itemID); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds the item into the column indexes +//----------------------------------------------------------------------------- +void ListPanel::IndexItem(int itemID) +{ + FastSortListPanelItem *newitem = (FastSortListPanelItem*) m_DataItems[itemID]; + + // remove the item from the indexes and re-add + int maxCount = min(m_ColumnsHistory.Count(), newitem->m_SortedTreeIndexes.Count()); + for (int i = 0; i < maxCount; i++) + { + IndexRBTree_t &rbtree = m_ColumnsData[m_ColumnsHistory[i]].m_SortedTree; + rbtree.RemoveAt(newitem->m_SortedTreeIndexes[i]); + } + + // make sure it's all free + newitem->m_SortedTreeIndexes.RemoveAll(); + + // reserve one index per historical column - pad it out + newitem->m_SortedTreeIndexes.AddMultipleToTail(m_ColumnsHistory.Count()); + + // set the current sorting list (since the insert will need to sort) + s_pCurrentSortingListPanel = this; + + // add the item into the RB tree for each column + for (int i = 0; i < m_ColumnsHistory.Count(); i++) + { + // skip over any removed columns + if ( m_ColumnsHistory[i] == m_ColumnsData.InvalidIndex() ) + continue; + + column_t &column = m_ColumnsData[m_ColumnsHistory[i]]; + + IndexItem_t item; + item.dataItem = newitem; + item.duplicateIndex = 0; + + IndexRBTree_t &rbtree = column.m_SortedTree; + + // setup sort state + s_pCurrentSortingListPanel = this; + s_pCurrentSortingColumn = column.m_pHeader->GetName(); // name of current column for sorting + s_currentSortingColumnTypeIsText = column.m_bTypeIsText; // type of data in the column + + SortFunc *sortFunc = column.m_pSortFunc; + if (!sortFunc) + { + sortFunc = DefaultSortFunc; + } + s_pSortFunc = sortFunc; + s_bSortAscending = true; + s_pSortFuncSecondary = NULL; + + // insert index + newitem->m_SortedTreeIndexes[i] = rbtree.Insert(item); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::RereadAllItems() +{ + //!! need to make this more efficient + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Cleans up allocations associated with a particular item +//----------------------------------------------------------------------------- +void ListPanel::CleanupItem( FastSortListPanelItem *data ) +{ + if ( data ) + { + if (data->kv) + { + data->kv->deleteThis(); + } + if (data->m_pDragData) + { + data->m_pDragData->deleteThis(); + } + delete data; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Removes an item at the specified item +//----------------------------------------------------------------------------- +void ListPanel::RemoveItem(int itemID) +{ +#ifdef _X360 + bool renavigate = false; + if(HasFocus()) + { + for(int i = 0; i < GetSelectedItemsCount(); ++i) + { + if(itemID == GetSelectedItem(i)) + { + renavigate = true; + break; + } + } + } +#endif + + FastSortListPanelItem *data = (FastSortListPanelItem*) m_DataItems[itemID]; + if (!data) + return; + + // remove from column sorted indexes + int i; + for ( i = 0; i < m_ColumnsHistory.Count(); i++ ) + { + if ( m_ColumnsHistory[i] == m_ColumnsData.InvalidIndex()) + continue; + + IndexRBTree_t &rbtree = m_ColumnsData[m_ColumnsHistory[i]].m_SortedTree; + rbtree.RemoveAt(data->m_SortedTreeIndexes[i]); + } + + // remove from selection + m_SelectedItems.FindAndRemove(itemID); + PostActionSignal( new KeyValues("ItemDeselected") ); + + // remove from visible items + m_VisibleItems.FindAndRemove(itemID); + + // remove from data + m_DataItems.Remove(itemID); + CleanupItem( data ); + InvalidateLayout(); + +#ifdef _X360 + if(renavigate) + { + NavigateTo(); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: clears and deletes all the memory used by the data items +//----------------------------------------------------------------------------- +void ListPanel::RemoveAll() +{ + // remove all sort indexes + for (int i = 0; i < m_ColumnsHistory.Count(); i++) + { + m_ColumnsData[m_ColumnsHistory[i]].m_SortedTree.RemoveAll(); + } + + FOR_EACH_LL( m_DataItems, index ) + { + FastSortListPanelItem *pItem = m_DataItems[index]; + CleanupItem( pItem ); + } + + m_DataItems.RemoveAll(); + m_VisibleItems.RemoveAll(); + ClearSelectedItems(); + + InvalidateLayout(); + +#ifdef _X360 + if(HasFocus()) + { + NavigateTo(); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: obselete, use RemoveAll(); +//----------------------------------------------------------------------------- +void ListPanel::DeleteAllItems() +{ + RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::ResetScrollBar() +{ + // delete and reallocate to besure the scroll bar's + // information is correct. + delete m_vbar; + m_vbar = new ScrollBar(this, "VertScrollBar", true); + m_vbar->SetVisible(false); + m_vbar->AddActionSignalTarget(this); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the count of selected rows +//----------------------------------------------------------------------------- +int ListPanel::GetSelectedItemsCount() +{ + return m_SelectedItems.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the selected item by selection index +// Input : selectionIndex - valid in range [0, GetNumSelectedRows) +// Output : int - itemID +//----------------------------------------------------------------------------- +int ListPanel::GetSelectedItem(int selectionIndex) +{ + if ( m_SelectedItems.IsValidIndex(selectionIndex)) + return m_SelectedItems[selectionIndex]; + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ListPanel::GetSelectedColumn() +{ + return m_iSelectedColumn; +} + + +//----------------------------------------------------------------------------- +// Purpose: Clears all selected rows +//----------------------------------------------------------------------------- +void ListPanel::ClearSelectedItems() +{ + int nPrevCount = m_SelectedItems.Count(); + m_SelectedItems.RemoveAll(); + if ( nPrevCount > 0 ) + { + PostActionSignal( new KeyValues("ItemDeselected") ); + } + m_LastItemSelected = -1; + m_iSelectedColumn = -1; +} + + +//----------------------------------------------------------------------------- +bool ListPanel::IsItemSelected( int itemID ) +{ + return m_DataItems.IsValidIndex( itemID ) && m_SelectedItems.HasElement( itemID ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::AddSelectedItem( int itemID ) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return; + + Assert( !m_SelectedItems.HasElement( itemID ) ); + + m_LastItemSelected = itemID; + m_SelectedItems.AddToTail( itemID ); + PostActionSignal( new KeyValues("ItemSelected") ); + Repaint(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::SetSingleSelectedItem( int itemID ) +{ + ClearSelectedItems(); + AddSelectedItem(itemID); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::SetSelectedCell(int itemID, int col) +{ + if ( !m_bCanSelectIndividualCells ) + { + SetSingleSelectedItem(itemID); + return; + } + + // make sure it's a valid cell + if ( !m_DataItems.IsValidIndex(itemID) ) + return; + + if ( !m_CurrentColumns.IsValidIndex(col) ) + return; + + SetSingleSelectedItem( itemID ); + m_iSelectedColumn = col; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the data held by a specific cell +//----------------------------------------------------------------------------- +void ListPanel::GetCellText(int itemID, int col, wchar_t *wbuffer, int bufferSizeInBytes) +{ + if ( !wbuffer || !bufferSizeInBytes ) + return; + + wcscpy( wbuffer, L"" ); + + KeyValues *itemData = GetItem( itemID ); + if ( !itemData ) + { + return; + } + + // Look up column header + if ( col < 0 || col >= m_CurrentColumns.Count() ) + { + return; + } + + const char *key = m_ColumnsData[m_CurrentColumns[col]].m_pHeader->GetName(); + if ( !key || !key[ 0 ] ) + { + return; + } + + char const *val = itemData->GetString( key, "" ); + if ( !val || !key[ 0 ] ) + return; + + const wchar_t *wval = NULL; + + if ( val[ 0 ] == '#' ) + { + StringIndex_t si = g_pVGuiLocalize->FindIndex( val + 1 ); + if ( si != INVALID_LOCALIZE_STRING_INDEX ) + { + wval = g_pVGuiLocalize->GetValueByIndex( si ); + } + } + + if ( !wval ) + { + wval = itemData->GetWString( key, L"" ); + } + + wcsncpy( wbuffer, wval, bufferSizeInBytes/sizeof(wchar_t) ); + wbuffer[ (bufferSizeInBytes/sizeof(wchar_t)) - 1 ] = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the data held by a specific cell +//----------------------------------------------------------------------------- +IImage *ListPanel::GetCellImage(int itemID, int col) //, ImagePanel *&buffer) +{ +// if ( !buffer ) +// return; + + KeyValues *itemData = GetItem( itemID ); + if ( !itemData ) + { + return NULL; + } + + // Look up column header + if ( col < 0 || col >= m_CurrentColumns.Count() ) + { + return NULL; + } + + const char *key = m_ColumnsData[m_CurrentColumns[col]].m_pHeader->GetName(); + if ( !key || !key[ 0 ] ) + { + return NULL; + } + + if ( !m_pImageList ) + { + return NULL; + } + + int imageIndex = itemData->GetInt( key, 0 ); + if ( m_pImageList->IsValidIndex(imageIndex) ) + { + if ( imageIndex > 0 ) + { + return m_pImageList->GetImage(imageIndex); + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the panel to use to render a cell +//----------------------------------------------------------------------------- +Panel *ListPanel::GetCellRenderer(int itemID, int col) +{ + Assert( m_pTextImage ); + Assert( m_pImagePanel ); + + column_t &column = m_ColumnsData[ m_CurrentColumns[col] ]; + + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + + m_pLabel->SetContentAlignment( (Label::Alignment)column.m_nContentAlignment ); + + if ( column.m_bTypeIsText ) + { + wchar_t tempText[ 256 ]; + + // Grab cell text + GetCellText( itemID, col, tempText, 256 ); + KeyValues *item = GetItem( itemID ); + m_pTextImage->SetText(tempText); + int cw, tall; + m_pTextImage->GetContentSize(cw, tall); + + // set cell size + Panel *header = column.m_pHeader; + int wide = header->GetWide(); + m_pTextImage->SetSize( min( cw, wide - 5 ), tall); + + m_pLabel->SetTextImageIndex( 0 ); + m_pLabel->SetImageAtIndex(0, m_pTextImage, 3); + + bool selected = false; + if ( m_SelectedItems.HasElement(itemID) && ( !m_bCanSelectIndividualCells || col == m_iSelectedColumn ) ) + { + selected = true; + VPANEL focus = input()->GetFocus(); + // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected + if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))) + { + m_pLabel->SetBgColor(GetSchemeColor("ListPanel.SelectedBgColor", pScheme)); + // selection + } + else + { + m_pLabel->SetBgColor(GetSchemeColor("ListPanel.SelectedOutOfFocusBgColor", pScheme)); + } + + if ( item->IsEmpty("cellcolor") == false ) + { + m_pTextImage->SetColor( item->GetColor( "cellcolor" ) ); + } + else if ( item->GetInt("disabled", 0) == 0 ) + { + m_pTextImage->SetColor(m_SelectionFgColor); + } + else + { + m_pTextImage->SetColor(m_DisabledSelectionFgColor); + } + + m_pLabel->SetPaintBackgroundEnabled(true); + } + else + { + if ( item->IsEmpty("cellcolor") == false ) + { + m_pTextImage->SetColor( item->GetColor( "cellcolor" ) ); + } + else if ( item->GetInt("disabled", 0) == 0 ) + { + m_pTextImage->SetColor(m_LabelFgColor); + } + else + { + m_pTextImage->SetColor(m_DisabledColor); + } + m_pLabel->SetPaintBackgroundEnabled(false); + } + + FastSortListPanelItem *listItem = m_DataItems[ itemID ]; + if ( col == 0 && + listItem->m_bImage && m_pImageList ) + { + IImage *pImage = NULL; + if ( listItem->m_pIcon ) + { + pImage = listItem->m_pIcon; + } + else + { + int imageIndex = selected ? listItem->m_nImageIndexSelected : listItem->m_nImageIndex; + if ( m_pImageList->IsValidIndex(imageIndex) ) + { + pImage = m_pImageList->GetImage(imageIndex); + } + } + + if ( pImage ) + { + m_pLabel->SetTextImageIndex( 1 ); + m_pLabel->SetImageAtIndex(0, pImage, 0); + m_pLabel->SetImageAtIndex(1, m_pTextImage, 3); + } + } + + return m_pLabel; + } + else // if its an Image Panel + { + if ( m_SelectedItems.HasElement(itemID) && ( !m_bCanSelectIndividualCells || col == m_iSelectedColumn ) ) + { + VPANEL focus = input()->GetFocus(); + // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected + if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))) + { + m_pLabel->SetBgColor(GetSchemeColor("ListPanel.SelectedBgColor", pScheme)); + // selection + } + else + { + m_pLabel->SetBgColor(GetSchemeColor("ListPanel.SelectedOutOfFocusBgColor", pScheme)); + } + // selection + m_pLabel->SetPaintBackgroundEnabled(true); + } + else + { + m_pLabel->SetPaintBackgroundEnabled(false); + } + + IImage *pIImage = GetCellImage(itemID, col); + m_pLabel->SetImageAtIndex(0, pIImage, 0); + + return m_pLabel; + } +} + +//----------------------------------------------------------------------------- +// Purpose: relayouts out the panel after any internal changes +//----------------------------------------------------------------------------- +void ListPanel::PerformLayout() +{ + if ( m_CurrentColumns.Count() == 0 ) + return; + + if (m_bNeedsSort) + { + SortList(); + } + + int rowsperpage = (int) GetRowsPerPage(); + + // count the number of visible items + int visibleItemCount = m_VisibleItems.Count(); + + //!! need to make it recalculate scroll positions + m_vbar->SetVisible(true); + m_vbar->SetEnabled(false); + m_vbar->SetRangeWindow( rowsperpage ); + m_vbar->SetRange( 0, visibleItemCount); + m_vbar->SetButtonPressedScrollValue( 1 ); + + int wide, tall; + GetSize( wide, tall ); + m_vbar->SetPos(wide - (m_vbar->GetWide()+WINDOW_BORDER_WIDTH), 0); + m_vbar->SetSize(m_vbar->GetWide(), tall - 2); + m_vbar->InvalidateLayout(); + + int buttonMaxXPos = wide - (m_vbar->GetWide()+WINDOW_BORDER_WIDTH); + + int nColumns = m_CurrentColumns.Count(); + // number of bars that can be resized + int numToResize=0; + if (m_iColumnDraggerMoved != -1) // we're resizing in response to a column dragger + { + numToResize = 1; // only one column will change size, the one we dragged + } + else // we're resizing in response to a window resize + { + for (int i = 0; i < nColumns; i++) + { + if ( m_ColumnsData[m_CurrentColumns[i]].m_bResizesWithWindow // column is resizable in response to window + && !m_ColumnsData[m_CurrentColumns[i]].m_bHidden) + { + numToResize++; + } + } + } + + int dxPerBar; // zero on window first opening + + // location of the last column resizer + int oldSizeX = 0, oldSizeY = 0; + int lastColumnIndex = nColumns-1; + for (int i = nColumns-1; i >= 0; --i) + { + if (!m_ColumnsData[m_CurrentColumns[i]].m_bHidden) + { + m_ColumnsData[m_CurrentColumns[i]].m_pHeader->GetPos(oldSizeX, oldSizeY); + lastColumnIndex = i; + break; + } + } + + bool bForceShrink = false; + if ( numToResize == 0 ) + { + // make sure we've got enough to be within minwidth + int minWidth=0; + for (int i = 0; i < nColumns; i++) + { + if (!m_ColumnsData[m_CurrentColumns[i]].m_bHidden) + { + minWidth += m_ColumnsData[m_CurrentColumns[i]].m_iMinWidth; + } + } + + // if all the minimum widths cannot fit in the space given, then we will shrink ALL columns an equal amount + if (minWidth > buttonMaxXPos) + { + int dx = buttonMaxXPos - minWidth; + dxPerBar=(int)((float)dx/(float)nColumns); + bForceShrink = true; + } + else + { + dxPerBar = 0; + } + m_lastBarWidth = buttonMaxXPos; + + } + else if ( oldSizeX != 0 ) // make sure this isnt the first time we opened the window + { + int dx = buttonMaxXPos - m_lastBarWidth; // this is how much we grew or shrank. + + // see how many bars we have and now much each should grow/shrink + dxPerBar=(int)((float)dx/(float)numToResize); + m_lastBarWidth = buttonMaxXPos; + } + else // this is the first time we've opened the window, make sure all our colums fit! resize if needed + { + int startingBarWidth=0; + for (int i = 0; i < nColumns; i++) + { + if (!m_ColumnsData[m_CurrentColumns[i]].m_bHidden) + { + startingBarWidth += m_ColumnsData[m_CurrentColumns[i]].m_pHeader->GetWide(); + } + } + int dx = buttonMaxXPos - startingBarWidth; // this is how much we grew or shrank. + // see how many bars we have and now much each should grow/shrink + dxPerBar=(int)((float)dx/(float)numToResize); + m_lastBarWidth = buttonMaxXPos; + } + + // Make sure nothing is smaller than minwidth to start with or else we'll get into trouble below. + for ( int i=0; i < nColumns; i++ ) + { + column_t &column = m_ColumnsData[m_CurrentColumns[i]]; + Panel *header = column.m_pHeader; + if ( header->GetWide() < column.m_iMinWidth ) + header->SetWide( column.m_iMinWidth ); + } + + // This was a while(1) loop and we hit an infinite loop case, so now we max out the # of times it can loop. + for ( int iLoopSanityCheck=0; iLoopSanityCheck < 1000; iLoopSanityCheck++ ) + { + // try and place headers as is - before we have to force items to be minimum width + int x = -1; + int i; + for ( i = 0; i < nColumns; i++) + { + column_t &column = m_ColumnsData[m_CurrentColumns[i]]; + Panel *header = column.m_pHeader; + if (column.m_bHidden) + { + header->SetVisible(false); + continue; + } + + header->SetPos(x, 0); + header->SetVisible(true); + + // if we couldn't fit this column - then we need to force items to be minimum width + if ( x+column.m_iMinWidth >= buttonMaxXPos && !bForceShrink ) + { + break; + } + + int hWide = header->GetWide(); + + // calculate the column's width + // make it so the last column always attaches to the scroll bar + if ( i == lastColumnIndex ) + { + hWide = buttonMaxXPos-x; + } + else if (i == m_iColumnDraggerMoved ) // column resizing using dragger + { + hWide += dxPerBar; // adjust width of column + } + else if ( m_iColumnDraggerMoved == -1 ) // window is resizing + { + // either this column is allowed to resize OR we are forcing it because we're shrinking all columns + if ( column.m_bResizesWithWindow || bForceShrink ) + { + Assert ( column.m_iMinWidth <= column.m_iMaxWidth ); + hWide += dxPerBar; // adjust width of column + } + } + + // enforce column mins and max's - unless we're FORCING it to shrink + if ( hWide < column.m_iMinWidth && !bForceShrink ) + { + hWide = column.m_iMinWidth; // adjust width of column + } + else if ( hWide > column.m_iMaxWidth ) + { + hWide = column.m_iMaxWidth; + } + + header->SetSize(hWide, m_vbar->GetWide()); + x += hWide; + + // set the resizers + Panel *sizer = column.m_pResizer; + if ( i == lastColumnIndex ) + { + sizer->SetVisible(false); + } + else + { + sizer->SetVisible(true); + } + sizer->MoveToFront(); + sizer->SetPos(x - 4, 0); + sizer->SetSize(8, m_vbar->GetWide()); + } + + // we made it all the way through + if ( i == nColumns ) + break; + + // we do this AFTER trying first, to let as many columns as possible try and get to their + // desired width before we forcing the minimum width on them + + // get the total desired width of all the columns + int totalDesiredWidth = 0; + for ( i = 0 ; i < nColumns ; i++ ) + { + if (!m_ColumnsData[m_CurrentColumns[i]].m_bHidden) + { + Panel *pHeader = m_ColumnsData[m_CurrentColumns[i]].m_pHeader; + totalDesiredWidth += pHeader->GetWide(); + } + } + + // shrink from the most right column to minimum width until we can fit them all + Assert(totalDesiredWidth > buttonMaxXPos); + for ( i = nColumns-1; i >= 0 ; i--) + { + column_t &column = m_ColumnsData[m_CurrentColumns[i]]; + if (!column.m_bHidden) + { + Panel *pHeader = column.m_pHeader; + + totalDesiredWidth -= pHeader->GetWide(); + if ( totalDesiredWidth + column.m_iMinWidth <= buttonMaxXPos ) + { + int newWidth = buttonMaxXPos - totalDesiredWidth; + pHeader->SetSize( newWidth, m_vbar->GetWide() ); + break; + } + + totalDesiredWidth += column.m_iMinWidth; + pHeader->SetSize(column.m_iMinWidth, m_vbar->GetWide()); + } + } + // If we don't allow this to shrink, then as we resize, it can get stuck in an infinite loop. + dxPerBar -= 5; + if ( dxPerBar < 0 ) + dxPerBar = 0; + + if ( i == -1 ) + { + break; + } + } + + // setup edit mode + if ( m_hEditModePanel.Get() ) + { + m_iTableStartX = 0; + m_iTableStartY = m_iHeaderHeight + 1; + + int nTotalRows = m_VisibleItems.Count(); + int nRowsPerPage = GetRowsPerPage(); + + // find the first visible item to display + int nStartItem = 0; + if (nRowsPerPage <= nTotalRows) + { + nStartItem = m_vbar->GetValue(); + } + + bool bDone = false; + int drawcount = 0; + for (int i = nStartItem; i < nTotalRows && !bDone; i++) + { + int x = 0; + if (!m_VisibleItems.IsValidIndex(i)) + continue; + + int itemID = m_VisibleItems[i]; + + // iterate the columns + for (int j = 0; j < m_CurrentColumns.Count(); j++) + { + Panel *header = m_ColumnsData[m_CurrentColumns[j]].m_pHeader; + + if (!header->IsVisible()) + continue; + + int wide = header->GetWide(); + + if ( itemID == m_iEditModeItemID && + j == m_iEditModeColumn ) + { + + m_hEditModePanel->SetPos( x + m_iTableStartX + 2, (drawcount * m_iRowHeight) + m_iTableStartY); + m_hEditModePanel->SetSize( wide, m_iRowHeight - 1 ); + + bDone = true; + } + + x += wide; + } + + drawcount++; + } + } + + Repaint(); + m_iColumnDraggerMoved = -1; // reset to invalid column +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::OnSizeChanged(int wide, int tall) +{ + BaseClass::OnSizeChanged(wide, tall); + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Renders the cells +//----------------------------------------------------------------------------- +void ListPanel::Paint() +{ + if (m_bNeedsSort) + { + SortList(); + } + + // draw selection areas if any + int wide, tall; + GetSize( wide, tall ); + + m_iTableStartX = 0; + m_iTableStartY = m_iHeaderHeight + 1; + + int nTotalRows = m_VisibleItems.Count(); + int nRowsPerPage = GetRowsPerPage(); + + // find the first visible item to display + int nStartItem = 0; + if (nRowsPerPage <= nTotalRows) + { + nStartItem = m_vbar->GetValue(); + } + + int vbarInset = m_vbar->IsVisible() ? m_vbar->GetWide() : 0; + int maxw = wide - vbarInset - 8; + +// debug timing functions +// double startTime, endTime; +// startTime = system()->GetCurrentTime(); + + // iterate through and draw each cell + bool bDone = false; + int drawcount = 0; + for (int i = nStartItem; i < nTotalRows && !bDone; i++) + { + int x = 0; + if (!m_VisibleItems.IsValidIndex(i)) + continue; + + int itemID = m_VisibleItems[i]; + + // iterate the columns + for (int j = 0; j < m_CurrentColumns.Count(); j++) + { + Panel *header = m_ColumnsData[m_CurrentColumns[j]].m_pHeader; + Panel *render = GetCellRenderer(itemID, j); + + if (!header->IsVisible()) + continue; + + int wide = header->GetWide(); + + if (render) + { + // setup render panel + if (render->GetVParent() != GetVPanel()) + { + render->SetParent(GetVPanel()); + } + if (!render->IsVisible()) + { + render->SetVisible(true); + } + int xpos = x + m_iTableStartX + 2; + + render->SetPos( xpos, (drawcount * m_iRowHeight) + m_iTableStartY); + + int right = min( xpos + wide, maxw ); + int usew = right - xpos; + render->SetSize( usew, m_iRowHeight - 1 ); + + // mark the panel to draw immediately (since it will probably be recycled to draw other cells) + render->Repaint(); + surface()->SolveTraverse(render->GetVPanel()); + int x0, y0, x1, y1; + render->GetClipRect(x0, y0, x1, y1); + if ((y1 - y0) < (m_iRowHeight - 3)) + { + bDone = true; + break; + } + surface()->PaintTraverse(render->GetVPanel()); + } + /* + // work in progress, optimized paint for text + else + { + // just paint it ourselves + char tempText[256]; + // Grab cell text + GetCellText(i, j, tempText, sizeof(tempText)); + surface()->DrawSetTextPos(x + m_iTableStartX + 2, (drawcount * m_iRowHeight) + m_iTableStartY); + + for (const char *pText = tempText; *pText != 0; pText++) + { + surface()->DrawUnicodeChar((wchar_t)*pText); + } + } + */ + + x += wide; + } + + drawcount++; + } + + m_pLabel->SetVisible(false); + + // if the list is empty, draw some help text + if (m_VisibleItems.Count() < 1 && m_pEmptyListText) + { + m_pEmptyListText->SetPos(m_iTableStartX + 8, m_iTableStartY + 4); + m_pEmptyListText->SetSize(wide - 8, m_iRowHeight); + m_pEmptyListText->Paint(); + } + +// endTime = system()->GetCurrentTime(); +// ivgui()->DPrintf2("ListPanel::Paint() (%.3f sec)\n", (float)(endTime - startTime)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::PaintBackground() +{ + BaseClass::PaintBackground(); +} + + +//----------------------------------------------------------------------------- +// Handles multiselect +//----------------------------------------------------------------------------- +void ListPanel::HandleMultiSelection( int itemID, int row, int column ) +{ + // deal with 'multiple' row selection + + // convert the last item selected to a row so we can multiply select by rows NOT items + int lastSelectedRow = (m_LastItemSelected != -1) ? m_VisibleItems.Find( m_LastItemSelected ) : row; + int startRow, endRow; + if ( row < lastSelectedRow ) + { + startRow = row; + endRow = lastSelectedRow; + } + else + { + startRow = lastSelectedRow; + endRow = row; + } + + // clear the selection if neither control key was down - we are going to readd ALL selected items + // in case the user changed the 'direction' of the shift add + if ( !input()->IsKeyDown(KEY_LCONTROL) && !input()->IsKeyDown(KEY_RCONTROL) ) + { + ClearSelectedItems(); + } + + // add any items that we haven't added + for (int i = startRow; i <= endRow; i++) + { + // get the item indexes for these rows + int selectedItemID = m_VisibleItems[i]; + if ( !m_SelectedItems.HasElement(selectedItemID) ) + { + AddSelectedItem( selectedItemID ); + } + } +} + + +//----------------------------------------------------------------------------- +// Handles multiselect +//----------------------------------------------------------------------------- +void ListPanel::HandleAddSelection( int itemID, int row, int column ) +{ + // dealing with row selection + if ( m_SelectedItems.HasElement( itemID ) ) + { + // this row is already selected, remove + m_SelectedItems.FindAndRemove( itemID ); + PostActionSignal( new KeyValues("ItemDeselected") ); + m_LastItemSelected = itemID; + } + else + { + // add the row to the selection + AddSelectedItem( itemID ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::UpdateSelection( MouseCode code, int x, int y, int row, int column ) +{ + // make sure we're clicking on a real item + if ( row < 0 || row >= m_VisibleItems.Count() ) + { + ClearSelectedItems(); + return; + } + + int itemID = m_VisibleItems[ row ]; + + // if we've right-clicked on a selection, don't change the selection + if ( code == MOUSE_RIGHT && m_SelectedItems.HasElement( itemID ) ) + return; + + if ( m_bCanSelectIndividualCells ) + { + if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ) + { + // we're ctrl selecting the same cell, clear it + if ( ( m_LastItemSelected == itemID ) && ( m_iSelectedColumn == column ) && ( m_SelectedItems.Count() == 1 ) ) + { + ClearSelectedItems(); + } + else + { + SetSelectedCell( itemID, column ); + } + } + else + { + SetSelectedCell( itemID, column ); + } + return; + } + + if ( !m_bMultiselectEnabled ) + { + SetSingleSelectedItem( itemID ); + return; + } + + if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ) + { + // check for multi-select + HandleMultiSelection( itemID, row, column ); + } + else if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ) + { + // check for row-add select + HandleAddSelection( itemID, row, column ); + } + else + { + // no CTRL or SHIFT keys + // reset the selection Start point +// if ( ( m_LastItemSelected != itemID ) || ( m_SelectedItems.Count() > 1 ) ) + { + SetSingleSelectedItem( itemID ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::OnMousePressed( MouseCode code ) +{ + if (code == MOUSE_LEFT || code == MOUSE_RIGHT) + { + if ( m_VisibleItems.Count() > 0 ) + { + // determine where we were pressed + int x, y, row, column; + input()->GetCursorPos(x, y); + GetCellAtPos(x, y, row, column); + + UpdateSelection( code, x, y, row, column ); + } + + // get the key focus + RequestFocus(); + } + + // check for context menu open + if (code == MOUSE_RIGHT) + { + if ( m_SelectedItems.Count() > 0 ) + { + PostActionSignal( new KeyValues("OpenContextMenu", "itemID", m_SelectedItems[0] )); + } + else + { + // post it, but with the invalid row + PostActionSignal( new KeyValues("OpenContextMenu", "itemID", -1 )); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Scrolls the list according to the mouse wheel movement +//----------------------------------------------------------------------------- +void ListPanel::OnMouseWheeled(int delta) +{ + if (m_hEditModePanel.Get()) + { + // ignore mouse wheel in edit mode, forward right up to parent + CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); + return; + } + + int val = m_vbar->GetValue(); + val -= (delta * 3); + m_vbar->SetValue(val); +} + +//----------------------------------------------------------------------------- +// Purpose: Double-click act like the the item under the mouse was selected +// and then the enter key hit +//----------------------------------------------------------------------------- +void ListPanel::OnMouseDoublePressed(MouseCode code) +{ + if (code == MOUSE_LEFT) + { + // select the item + OnMousePressed(code); + + // post up an enter key being hit if anything was selected + if (GetSelectedItemsCount() > 0 && !m_bIgnoreDoubleClick ) + { + OnKeyCodeTyped(KEY_ENTER); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#ifdef _X360 +void ListPanel::OnKeyCodePressed(KeyCode code) +{ + int nTotalRows = m_VisibleItems.Count(); + int nTotalColumns = m_CurrentColumns.Count(); + if ( nTotalRows == 0 ) + return; + + // calculate info for adjusting scrolling + int nStartItem = GetStartItem(); + int nRowsPerPage = (int)GetRowsPerPage(); + + int nSelectedRow = 0; + if ( m_DataItems.IsValidIndex( m_LastItemSelected ) ) + { + nSelectedRow = m_VisibleItems.Find( m_LastItemSelected ); + } + int nSelectedColumn = m_iSelectedColumn; + + switch(code) + { + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case KEY_XSTICK2_UP: + if(GetItemCount() < 1 || nSelectedRow == nStartItem) + { + ClearSelectedItems(); + BaseClass::OnKeyCodePressed(code); + return; + } + else + { + nSelectedRow -= 1; + } + break; + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case KEY_XSTICK2_DOWN: + { + int itemId = GetSelectedItem(0); + if(itemId != -1 && GetItemCurrentRow(itemId) == (nTotalRows - 1)) + { + ClearSelectedItems(); + BaseClass::OnKeyCodePressed(code); + return; + } + else + { + nSelectedRow += 1; + } + } + break; + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case KEY_XSTICK2_LEFT: + if (m_bCanSelectIndividualCells && (GetSelectedItemsCount() == 1) && (nSelectedColumn >= 0) ) + { + nSelectedColumn--; + if (nSelectedColumn < 0) + { + nSelectedColumn = 0; + } + break; + } + break; + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + case KEY_XSTICK2_RIGHT: + if (m_bCanSelectIndividualCells && (GetSelectedItemsCount() == 1) && (nSelectedColumn >= 0) ) + { + nSelectedColumn++; + if (nSelectedColumn >= nTotalColumns) + { + nSelectedColumn = nTotalColumns - 1; + } + break; + } + break; + case KEY_XBUTTON_A: + PostActionSignal( new KeyValues("ListPanelItemChosen", "itemID", m_SelectedItems[0] )); + break; + default: + BaseClass::OnKeyCodePressed(code); + break; + } + + // make sure newly selected item is a valid range + nSelectedRow = clamp(nSelectedRow, 0, nTotalRows - 1); + + int row = m_VisibleItems[ nSelectedRow ]; + + // This will select the cell if in single select mode, or the row in multiselect mode + if ( ( row != m_LastItemSelected ) || ( nSelectedColumn != m_iSelectedColumn ) || ( m_SelectedItems.Count() > 1 ) ) + { + SetSelectedCell( row, nSelectedColumn ); + } + + // move the newly selected item to within the visible range + if ( nRowsPerPage < nTotalRows ) + { + int nStartItem = m_vbar->GetValue(); + if ( nSelectedRow < nStartItem ) + { + // move the list back to match + m_vbar->SetValue( nSelectedRow ); + } + else if ( nSelectedRow >= nStartItem + nRowsPerPage ) + { + // move list forward to match + m_vbar->SetValue( nSelectedRow - nRowsPerPage + 1); + } + } + + // redraw + InvalidateLayout(); +} + +#else + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::OnKeyCodePressed(KeyCode code) +{ + if (m_hEditModePanel.Get()) + { + // ignore arrow keys in edit mode + // forward right up to parent so that tab focus change doesn't occur + CallParentFunction(new KeyValues("KeyCodePressed", "code", code)); + return; + } + + int nTotalRows = m_VisibleItems.Count(); + int nTotalColumns = m_CurrentColumns.Count(); + if ( nTotalRows == 0 ) + { + BaseClass::OnKeyCodePressed(code); + return; + } + + // calculate info for adjusting scrolling + int nStartItem = GetStartItem(); + int nRowsPerPage = (int)GetRowsPerPage(); + + int nSelectedRow = 0; + if ( m_DataItems.IsValidIndex( m_LastItemSelected ) ) + { + nSelectedRow = m_VisibleItems.Find( m_LastItemSelected ); + } + int nSelectedColumn = m_iSelectedColumn; + + switch (code) + { + case KEY_HOME: + nSelectedRow = 0; + break; + + case KEY_END: + nSelectedRow = nTotalRows - 1; + break; + + case KEY_PAGEUP: + if (nSelectedRow <= nStartItem) + { + // move up a page + nSelectedRow -= (nRowsPerPage - 1); + } + else + { + // move to the top of the current page + nSelectedRow = nStartItem; + } + break; + + case KEY_PAGEDOWN: + if (nSelectedRow >= (nStartItem + nRowsPerPage-1)) + { + // move down a page + nSelectedRow += (nRowsPerPage - 1); + } + else + { + // move to the bottom of the current page + nSelectedRow = nStartItem + (nRowsPerPage - 1); + } + break; + + case KEY_UP: + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case KEY_XSTICK2_UP: + if ( nTotalRows > 0 ) + { + nSelectedRow--; + break; + } + // fall through + + case KEY_DOWN: + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case KEY_XSTICK2_DOWN: + if ( nTotalRows > 0 ) + { + nSelectedRow++; + break; + } + // fall through + + case KEY_LEFT: + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case KEY_XSTICK2_LEFT: + if (m_bCanSelectIndividualCells && (GetSelectedItemsCount() == 1) && (nSelectedColumn >= 0) ) + { + nSelectedColumn--; + if (nSelectedColumn < 0) + { + nSelectedColumn = 0; + } + break; + } + // fall through + + case KEY_RIGHT: + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + case KEY_XSTICK2_RIGHT: + if (m_bCanSelectIndividualCells && (GetSelectedItemsCount() == 1) && (nSelectedColumn >= 0) ) + { + nSelectedColumn++; + if (nSelectedColumn >= nTotalColumns) + { + nSelectedColumn = nTotalColumns - 1; + } + break; + } + // fall through + + default: + // chain back + BaseClass::OnKeyCodePressed(code); + return; + }; + + // make sure newly selected item is a valid range + nSelectedRow = clamp(nSelectedRow, 0, nTotalRows - 1); + + int row = m_VisibleItems[ nSelectedRow ]; + + // This will select the cell if in single select mode, or the row in multiselect mode + if ( ( row != m_LastItemSelected ) || ( nSelectedColumn != m_iSelectedColumn ) || ( m_SelectedItems.Count() > 1 ) ) + { + SetSelectedCell( row, nSelectedColumn ); + } + + // move the newly selected item to within the visible range + if ( nRowsPerPage < nTotalRows ) + { + int nStartItem = m_vbar->GetValue(); + if ( nSelectedRow < nStartItem ) + { + // move the list back to match + m_vbar->SetValue( nSelectedRow ); + } + else if ( nSelectedRow >= nStartItem + nRowsPerPage ) + { + // move list forward to match + m_vbar->SetValue( nSelectedRow - nRowsPerPage + 1); + } + } + + // redraw + InvalidateLayout(); +} + +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool ListPanel::GetCellBounds( int row, int col, int& x, int& y, int& wide, int& tall ) +{ + if ( col < 0 || col >= m_CurrentColumns.Count() ) + return false; + + if ( row < 0 || row >= m_VisibleItems.Count() ) + return false; + + // Is row on screen? + int startitem = GetStartItem(); + if ( row < startitem || row >= ( startitem + GetRowsPerPage() ) ) + return false; + + y = m_iTableStartY; + y += ( row - startitem ) * m_iRowHeight; + tall = m_iRowHeight; + + // Compute column cell + x = m_iTableStartX; + // walk columns + int c = 0; + while ( c < col) + { + x += m_ColumnsData[m_CurrentColumns[c]].m_pHeader->GetWide(); + c++; + } + wide = m_ColumnsData[m_CurrentColumns[c]].m_pHeader->GetWide(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if any found, row and column are filled out +//----------------------------------------------------------------------------- +bool ListPanel::GetCellAtPos(int x, int y, int &row, int &col) +{ + // convert to local + ScreenToLocal(x, y); + + // move to Start of table + x -= m_iTableStartX; + y -= m_iTableStartY; + + int startitem = GetStartItem(); + // make sure it's still in valid area + if ( x >= 0 && y >= 0 ) + { + // walk the rows (for when row height is independant each row) + // NOTE: if we do height independent rows, we will need to change GetCellBounds as well + for ( row = startitem ; row < m_VisibleItems.Count() ; row++ ) + { + if ( y < ( ( ( row - startitem ) + 1 ) * m_iRowHeight ) ) + break; + } + + // walk columns + int startx = 0; + for ( col = 0 ; col < m_CurrentColumns.Count() ; col++ ) + { + startx += m_ColumnsData[m_CurrentColumns[col]].m_pHeader->GetWide(); + + if ( x < startx ) + break; + } + + // make sure we're not out of range + if ( ! ( row == m_VisibleItems.Count() || col == m_CurrentColumns.Count() ) ) + { + return true; + } + } + + // out-of-bounds + row = col = -1; + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::ApplySchemeSettings(IScheme *pScheme) +{ + // force label to apply scheme settings now so we can override it + m_pLabel->InvalidateLayout(true); + + BaseClass::ApplySchemeSettings(pScheme); + + SetBgColor(GetSchemeColor("ListPanel.BgColor", pScheme)); + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); + + m_pLabel->SetBgColor(GetSchemeColor("ListPanel.BgColor", pScheme)); + + m_LabelFgColor = GetSchemeColor("ListPanel.TextColor", pScheme); + m_DisabledColor = GetSchemeColor("ListPanel.DisabledTextColor", m_LabelFgColor, pScheme); + m_SelectionFgColor = GetSchemeColor("ListPanel.SelectedTextColor", m_LabelFgColor, pScheme); + m_DisabledSelectionFgColor = GetSchemeColor("ListPanel.DisabledSelectedTextColor", m_LabelFgColor, pScheme); + + m_pEmptyListText->SetColor(GetSchemeColor("ListPanel.EmptyListInfoTextColor", pScheme)); + + SetFont( pScheme->GetFont("Default", IsProportional() ) ); + m_pEmptyListText->SetFont( pScheme->GetFont( "Default", IsProportional() ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::SetSortFunc(int col, SortFunc *func) +{ + Assert(col < m_CurrentColumns.Count()); + unsigned char dataColumnIndex = m_CurrentColumns[col]; + + if ( !m_ColumnsData[dataColumnIndex].m_bTypeIsText && func != NULL) + { + m_ColumnsData[dataColumnIndex].m_pHeader->SetMouseClickEnabled(MOUSE_LEFT, 1); + } + + m_ColumnsData[dataColumnIndex].m_pSortFunc = func; + + // resort this column according to new sort func + ResortColumnRBTree(col); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::SetSortColumn(int column) +{ + m_iSortColumn = column; +} + +int ListPanel::GetSortColumn() const +{ + return m_iSortColumn; +} + +void ListPanel::SetSortColumnEx( int iPrimarySortColumn, int iSecondarySortColumn, bool bSortAscending ) +{ + m_iSortColumn = iPrimarySortColumn; + m_iSortColumnSecondary = iSecondarySortColumn; + m_bSortAscending = bSortAscending; +} + +void ListPanel::GetSortColumnEx( int &iPrimarySortColumn, int &iSecondarySortColumn, bool &bSortAscending ) const +{ + iPrimarySortColumn = m_iSortColumn; + iSecondarySortColumn = m_iSortColumnSecondary; + bSortAscending = m_bSortAscending; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::SortList( void ) +{ + m_bNeedsSort = false; + + if ( m_VisibleItems.Count() <= 1 ) + { + return; + } + + // check if the last selected item is on the screen - if so, we should try to maintain it on screen + int startItem = GetStartItem(); + int rowsperpage = (int) GetRowsPerPage(); + int screenPosition = -1; + if ( m_LastItemSelected != -1 && m_SelectedItems.Count() > 0 ) + { + int selectedItemRow = m_VisibleItems.Find(m_LastItemSelected); + if ( selectedItemRow >= startItem && selectedItemRow <= ( startItem + rowsperpage ) ) + { + screenPosition = selectedItemRow - startItem; + } + } + + // get the required sorting functions + s_pCurrentSortingListPanel = this; + + // setup globals for use in qsort + s_pSortFunc = FastSortFunc; + s_bSortAscending = m_bSortAscending; + s_pSortFuncSecondary = FastSortFunc; + s_bSortAscendingSecondary = m_bSortAscendingSecondary; + + // walk the tree and set up the current indices + if (m_CurrentColumns.IsValidIndex(m_iSortColumn)) + { + IndexRBTree_t &rbtree = m_ColumnsData[m_CurrentColumns[m_iSortColumn]].m_SortedTree; + unsigned int index = rbtree.FirstInorder(); + unsigned int lastIndex = rbtree.LastInorder(); + int prevDuplicateIndex = 0; + int sortValue = 1; + while (1) + { + FastSortListPanelItem *dataItem = (FastSortListPanelItem*) rbtree[index].dataItem; + if (dataItem->visible) + { + // only increment the sort value if we're a different token from the previous + if (!prevDuplicateIndex || prevDuplicateIndex != rbtree[index].duplicateIndex) + { + sortValue++; + } + dataItem->primarySortIndexValue = sortValue; + prevDuplicateIndex = rbtree[index].duplicateIndex; + } + + if (index == lastIndex) + break; + + index = rbtree.NextInorder(index); + } + } + + // setup secondary indices + if (m_CurrentColumns.IsValidIndex(m_iSortColumnSecondary)) + { + IndexRBTree_t &rbtree = m_ColumnsData[m_CurrentColumns[m_iSortColumnSecondary]].m_SortedTree; + unsigned int index = rbtree.FirstInorder(); + unsigned int lastIndex = rbtree.LastInorder(); + int sortValue = 1; + int prevDuplicateIndex = 0; + while (1) + { + FastSortListPanelItem *dataItem = (FastSortListPanelItem*) rbtree[index].dataItem; + if (dataItem->visible) + { + // only increment the sort value if we're a different token from the previous + if (!prevDuplicateIndex || prevDuplicateIndex != rbtree[index].duplicateIndex) + { + sortValue++; + } + dataItem->secondarySortIndexValue = sortValue; + + prevDuplicateIndex = rbtree[index].duplicateIndex; + } + + if (index == lastIndex) + break; + + index = rbtree.NextInorder(index); + } + } + + // quick sort the list + qsort(m_VisibleItems.Base(), (size_t) m_VisibleItems.Count(), (size_t) sizeof(int), AscendingSortFunc); + + if ( screenPosition != -1 ) + { + int selectedItemRow = m_VisibleItems.Find(m_LastItemSelected); + + // if we can put the last selected item in exactly the same spot, put it there, otherwise + // we need to be at the top of the list + if (selectedItemRow > screenPosition) + { + m_vbar->SetValue(selectedItemRow - screenPosition); + } + else + { + m_vbar->SetValue(0); + } + } + + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::SetFont(HFont font) +{ + Assert( font ); + if ( !font ) + return; + + m_pTextImage->SetFont(font); + m_iRowHeight = surface()->GetFontTall(font) + 2; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListPanel::OnSliderMoved() +{ + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : deltax - deltas from current position +//----------------------------------------------------------------------------- +void ListPanel::OnColumnResized(int col, int delta) +{ + m_iColumnDraggerMoved = col; + + column_t& column = m_ColumnsData[m_CurrentColumns[col]]; + + Panel *header = column.m_pHeader; + int wide, tall; + header->GetSize(wide, tall); + + + wide += delta; + + // enforce minimum sizes for the header + if ( wide < column.m_iMinWidth ) + { + wide = column.m_iMinWidth; + } + // enforce maximum sizes for the header + if ( wide > column.m_iMaxWidth ) + { + wide = column.m_iMaxWidth; + } + + // make sure we have enough space for the columns to our right + int panelWide, panelTall; + GetSize( panelWide, panelTall ); + int x, y; + header->GetPos(x, y); + int restColumnsMinWidth = 0; + int i; + for ( i = col+1 ; i < m_CurrentColumns.Count() ; i++ ) + { + column_t& nextCol = m_ColumnsData[m_CurrentColumns[i]]; + restColumnsMinWidth += nextCol.m_iMinWidth; + } + panelWide -= ( x + restColumnsMinWidth + m_vbar->GetWide() + WINDOW_BORDER_WIDTH ); + if ( wide > panelWide ) + { + wide = panelWide; + } + + header->SetSize(wide, tall); + + // the adjacent header will be moved automatically in PerformLayout() + header->InvalidateLayout(); + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: sets which column we should sort with +//----------------------------------------------------------------------------- +void ListPanel::OnSetSortColumn(int column) +{ + // if it's the primary column already, flip the sort direction + if (m_iSortColumn == column) + { + m_bSortAscending = !m_bSortAscending; + } + else + { + // switching sort columns, keep the old one as the secondary sort + m_iSortColumnSecondary = m_iSortColumn; + m_bSortAscendingSecondary = m_bSortAscending; + } + + SetSortColumn(column); + + SortList(); +} + +//----------------------------------------------------------------------------- +// Purpose: sets whether the item is visible or not +//----------------------------------------------------------------------------- +void ListPanel::SetItemVisible(int itemID, bool state) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return; + + FastSortListPanelItem *data = (FastSortListPanelItem*) m_DataItems[itemID]; + if (data->visible == state) + return; + + m_bNeedsSort = true; + + data->visible = state; + if (data->visible) + { + // add back to end of list + m_VisibleItems.AddToTail(itemID); + } + else + { + // remove from selection if it is there. + if (m_SelectedItems.HasElement(itemID)) + { + m_SelectedItems.FindAndRemove(itemID); + PostActionSignal( new KeyValues("ItemDeselected") ); + } + + // remove from data + m_VisibleItems.FindAndRemove(itemID); + + InvalidateLayout(); + } +} + + +//----------------------------------------------------------------------------- +// Is the item visible? +//----------------------------------------------------------------------------- +bool ListPanel::IsItemVisible( int itemID ) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return false; + + FastSortListPanelItem *data = (FastSortListPanelItem*) m_DataItems[itemID]; + return data->visible; +} + + +//----------------------------------------------------------------------------- +// Purpose: sets whether the item is disabled or not (effects item color) +//----------------------------------------------------------------------------- +void ListPanel::SetItemDisabled(int itemID, bool state) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return; + + m_DataItems[itemID]->kv->SetInt( "disabled", state ); +} + +//----------------------------------------------------------------------------- +// Purpose: Calculate number of rows per page +//----------------------------------------------------------------------------- +float ListPanel::GetRowsPerPage() +{ + float rowsperpage = (float)( GetTall() - m_iHeaderHeight ) / (float)m_iRowHeight; + return rowsperpage; +} + +//----------------------------------------------------------------------------- +// Purpose: Calculate the item we should Start on +//----------------------------------------------------------------------------- +int ListPanel::GetStartItem() +{ + // if rowsperpage < total number of rows + if ( GetRowsPerPage() < (float) m_VisibleItems.Count() ) + { + return m_vbar->GetValue(); + } + return 0; // otherwise Start at top +} + +//----------------------------------------------------------------------------- +// Purpose: whether or not to select specific cells (off by default) +//----------------------------------------------------------------------------- +void ListPanel::SetSelectIndividualCells(bool state) +{ + m_bCanSelectIndividualCells = state; +} + + +//----------------------------------------------------------------------------- +// whether or not multiple cells/rows can be selected +//----------------------------------------------------------------------------- +void ListPanel::SetMultiselectEnabled( bool bState ) +{ + m_bMultiselectEnabled = bState; +} + +bool ListPanel::IsMultiselectEnabled() const +{ + return m_bMultiselectEnabled; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the text which is displayed when the list is empty +//----------------------------------------------------------------------------- +void ListPanel::SetEmptyListText(const char *text) +{ + m_pEmptyListText->SetText(text); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the text which is displayed when the list is empty +//----------------------------------------------------------------------------- +void ListPanel::SetEmptyListText(const wchar_t *text) +{ + m_pEmptyListText->SetText(text); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: opens the content menu +//----------------------------------------------------------------------------- +void ListPanel::OpenColumnChoiceMenu() +{ + if (!m_bAllowUserAddDeleteColumns) + return; + + Menu *menu = new Menu(this, "ContextMenu"); + + int x, y; + input()->GetCursorPos(x, y); + menu->SetPos(x, y); + + // add all the column choices to the menu + for ( int i = 0 ; i < m_CurrentColumns.Count() ; i++ ) + { + column_t &column = m_ColumnsData[m_CurrentColumns[i]]; + + char name[128]; + column.m_pHeader->GetText(name, sizeof(name)); + int itemID = menu->AddCheckableMenuItem(name, new KeyValues("ToggleColumnVisible", "col", m_CurrentColumns[i]), this); + menu->SetMenuItemChecked(itemID, !column.m_bHidden); + + if (column.m_bUnhidable) + { + menu->SetItemEnabled(itemID, false); + } + } + + menu->SetVisible(true); +} + +//----------------------------------------------------------------------------- +// Purpose: Resizes a column +//----------------------------------------------------------------------------- +void ListPanel::ResizeColumnToContents(int column) +{ + // iterate all the items in the column, getting the size of each + column_t &col = m_ColumnsData[m_CurrentColumns[column]]; + + if (!col.m_bTypeIsText) + return; + + // start with the size of the column text + int wide = 0, minRequiredWidth = 0, tall = 0; + col.m_pHeader->GetContentSize( minRequiredWidth, tall ); + + // iterate every item + for (int i = 0; i < m_VisibleItems.Count(); i++) + { + if (!m_VisibleItems.IsValidIndex(i)) + continue; + + // get the cell + int itemID = m_VisibleItems[i]; + + // get the text + wchar_t tempText[ 256 ]; + GetCellText( itemID, column, tempText, 256 ); + m_pTextImage->SetText(tempText); + + m_pTextImage->GetContentSize(wide, tall); + + if ( wide > minRequiredWidth ) + { + minRequiredWidth = wide; + } + } + + // Introduce a slight buffer between columns + minRequiredWidth += 4; + + // call the resize + col.m_pHeader->GetSize(wide, tall); + OnColumnResized(column, minRequiredWidth - wide); +} + +//----------------------------------------------------------------------------- +// Purpose: Changes the visibilty of a column +//----------------------------------------------------------------------------- +void ListPanel::OnToggleColumnVisible(int col) +{ + if (!m_CurrentColumns.IsValidIndex(col)) + return; + + // toggle the state of the column + column_t &column = m_ColumnsData[m_CurrentColumns[col]]; + SetColumnVisible(col, column.m_bHidden); +} + +//----------------------------------------------------------------------------- +// Purpose: sets user settings +//----------------------------------------------------------------------------- +void ListPanel::ApplyUserConfigSettings(KeyValues *userConfig) +{ + // Check for version mismatch, then don't load settings. (Just revert to the defaults.) + int version = userConfig->GetInt( "configVersion", 1 ); + if ( version != m_nUserConfigFileVersion ) + { + return; + } + + // We save/restore m_lastBarWidth because all of the column widths are saved relative to that size. + // If we don't save it, you can run into this case: + // - Window width is 500, load sizes setup relative to a 1000-width window + // - Set window size to 1000 + // - In PerformLayout, it thinks the window has grown by 500 (since m_lastBarWidth is 500 and new window width is 1000) + // so it pushes out any COLUMN_RESIZEWITHWINDOW columns to their max extent and shrinks everything else to its min extent. + m_lastBarWidth = userConfig->GetInt( "lastBarWidth", 0 ); + + // read which columns are hidden + for ( int i = 0; i < m_CurrentColumns.Count(); i++ ) + { + char name[64]; + _snprintf(name, sizeof(name), "%d_hidden", i); + + int hidden = userConfig->GetInt(name, -1); + if (hidden == 0) + { + SetColumnVisible(i, true); + } + else if (hidden == 1) + { + SetColumnVisible(i, false); + } + + _snprintf(name, sizeof(name), "%d_width", i); + int nWidth = userConfig->GetInt( name, -1 ); + if ( nWidth >= 0 ) + { + column_t &column = m_ColumnsData[m_CurrentColumns[i]]; + column.m_pHeader->SetWide( nWidth ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns user config settings for this control +//----------------------------------------------------------------------------- +void ListPanel::GetUserConfigSettings(KeyValues *userConfig) +{ + if ( m_nUserConfigFileVersion != 1 ) + { + userConfig->SetInt( "configVersion", m_nUserConfigFileVersion ); + } + + userConfig->SetInt( "lastBarWidth", m_lastBarWidth ); + + // save which columns are hidden + for ( int i = 0 ; i < m_CurrentColumns.Count() ; i++ ) + { + column_t &column = m_ColumnsData[m_CurrentColumns[i]]; + + char name[64]; + _snprintf(name, sizeof(name), "%d_hidden", i); + userConfig->SetInt(name, column.m_bHidden ? 1 : 0); + + _snprintf(name, sizeof(name), "%d_width", i); + userConfig->SetInt( name, column.m_pHeader->GetWide() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: optimization, return true if this control has any user config settings +//----------------------------------------------------------------------------- +bool ListPanel::HasUserConfigSettings() +{ + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void ListPanel::SetAllowUserModificationOfColumns(bool allowed) +{ + m_bAllowUserAddDeleteColumns = allowed; +} + +void ListPanel::SetIgnoreDoubleClick( bool state ) +{ + m_bIgnoreDoubleClick = state; +} + +//----------------------------------------------------------------------------- +// Purpose: set up a field for editing +//----------------------------------------------------------------------------- +void ListPanel::EnterEditMode(int itemID, int column, vgui::Panel *editPanel) +{ + m_hEditModePanel = editPanel; + m_iEditModeItemID = itemID; + m_iEditModeColumn = column; + editPanel->SetParent(this); + editPanel->SetVisible(true); + editPanel->RequestFocus(); + editPanel->MoveToFront(); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: leaves editing mode +//----------------------------------------------------------------------------- +void ListPanel::LeaveEditMode() +{ + if (m_hEditModePanel.Get()) + { + m_hEditModePanel->SetVisible(false); + m_hEditModePanel->SetParent((Panel *)NULL); + m_hEditModePanel = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if we are currently in inline editing mode +//----------------------------------------------------------------------------- +bool ListPanel::IsInEditMode() +{ + return (m_hEditModePanel.Get() != NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#ifdef _X360 +void ListPanel::NavigateTo() +{ + BaseClass::NavigateTo(); + // attempt to select the first item in the list when we get focus + if(GetItemCount()) + { + SetSingleSelectedItem(FirstItem()); + } + else // if we have no items, change focus + { + if(!NavigateDown()) + { + NavigateUp(); + } + } +} +#endif diff --git a/mp/src/vgui2/vgui_controls/ListViewPanel.cpp b/mp/src/vgui2/vgui_controls/ListViewPanel.cpp index b188a01b..59f05ab6 100644 --- a/mp/src/vgui2/vgui_controls/ListViewPanel.cpp +++ b/mp/src/vgui2/vgui_controls/ListViewPanel.cpp @@ -1,1082 +1,1082 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -enum -{ - WINDOW_BORDER_WIDTH=2 // the width of the window's border -}; - -namespace vgui -{ -class ListViewItem : public Label -{ - DECLARE_CLASS_SIMPLE( ListViewItem, Label ); - -public: - ListViewItem(Panel *parent) : Label(parent, NULL, "") - { - m_pListViewPanel = (ListViewPanel*) parent; - m_pData = NULL; - m_bSelected = false; - SetPaintBackgroundEnabled(true); - } - - ~ListViewItem() - { - if (m_pData) - { - m_pData->deleteThis(); - m_pData = NULL; - } - } - - void SetData(const KeyValues *data) - { - if (m_pData) - { - m_pData->deleteThis(); - } - m_pData = data->MakeCopy(); - } - - virtual void OnMousePressed( MouseCode code) - { - m_pListViewPanel->OnItemMousePressed(this, code); - } - - virtual void OnMouseDoublePressed( MouseCode code) - { - // double press should only select the item - m_pListViewPanel->OnItemMouseDoublePressed(this, code); - } - - KeyValues *GetData() - { - return m_pData; - } - - void SetSelected(bool bSelected) - { - if (bSelected == m_bSelected) - return; - - m_bSelected = bSelected; - if (bSelected) - { - RequestFocus(); - } - - UpdateImage(); - InvalidateLayout(); - Repaint(); - } - - virtual void PerformLayout() - { - TextImage *textImage = GetTextImage(); - if (m_bSelected) - { - VPANEL focus = input()->GetFocus(); - // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected - if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))) - { - textImage->SetColor(m_ArmedFgColor2); - } - else - { - textImage->SetColor(m_FgColor2); - } - } - else - { - textImage->SetColor(GetFgColor()); - } - BaseClass::PerformLayout(); - Repaint(); - } - - virtual void PaintBackground() - { - int wide, tall; - GetSize(wide, tall); - - if ( m_bSelected ) - { - VPANEL focus = input()->GetFocus(); - // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected - if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))) - { - surface()->DrawSetColor(m_ArmedBgColor); - } - else - { - surface()->DrawSetColor(m_SelectionBG2Color); - } - } - else - { - surface()->DrawSetColor(GetBgColor()); - } - surface()->DrawFilledRect(0, 0, wide, tall); - } - - virtual void ApplySchemeSettings(IScheme *pScheme) - { - BaseClass::ApplySchemeSettings(pScheme); - - m_ArmedFgColor2 = GetSchemeColor("ListPanel.SelectedTextColor", pScheme); - m_ArmedBgColor = GetSchemeColor("ListPanel.SelectedBgColor", pScheme); - - m_FgColor1 = GetSchemeColor("ListPanel.TextColor", pScheme); - m_FgColor2 = GetSchemeColor("ListPanel.SelectedTextColor", pScheme); - - m_BgColor = GetSchemeColor("ListPanel.BgColor", GetBgColor(), pScheme); - m_BgColor = GetSchemeColor("ListPanel.TextBgColor", m_BgColor, pScheme); - m_SelectionBG2Color = GetSchemeColor("ListPanel.SelectedOutOfFocusBgColor", pScheme); - SetBgColor(m_BgColor); - SetFgColor(m_FgColor1); - - UpdateImage(); - } - - void UpdateImage() - { - if ( m_pListViewPanel->m_pImageList ) - { - int imageIndex = 0; - if ( m_bSelected ) - { - imageIndex = m_pData->GetInt("imageSelected", 0); - } - if ( imageIndex == 0 ) - { - imageIndex = m_pData->GetInt("image", 0); - } - if ( m_pListViewPanel->m_pImageList->IsValidIndex(imageIndex) ) - { - SetImageAtIndex(0, m_pListViewPanel->m_pImageList->GetImage(imageIndex), 0); - } - else - { - // use the default - SetImageAtIndex(0, m_pListViewPanel->m_pImageList->GetImage(1), 0); - } - SizeToContents(); - InvalidateLayout(); - } - } - -private: - - Color m_FgColor1; - Color m_FgColor2; - Color m_BgColor; - Color m_ArmedFgColor2; - Color m_ArmedBgColor; - Color m_SelectionBG2Color; - - //IBorder *_keyFocusBorder; // maybe in the future when I'm the 'active' but not selected item, I'll have a border - - KeyValues *m_pData; - ListViewPanel *m_pListViewPanel; - bool m_bSelected; -}; -} - -static bool DefaultSortFunc(KeyValues *kv1, KeyValues *kv2) -{ - const char *string1 = kv1->GetString("text"); - const char *string2 = kv2->GetString("text"); - return Q_stricmp(string1, string2) < 0; -} - -DECLARE_BUILD_FACTORY( ListViewPanel ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -ListViewPanel::ListViewPanel(Panel *parent, const char *panelName) : Panel(parent, panelName) -{ - m_iRowHeight = 20; - m_bNeedsSort = false; - m_hFont = NULL; - m_pImageList = NULL; - m_bDeleteImageListWhenDone = false; - m_pSortFunc = DefaultSortFunc; - m_ShiftStartItemID = -1; - - m_hbar = new ScrollBar(this, "HorizScrollBar", false); - m_hbar->AddActionSignalTarget(this); - m_hbar->SetVisible(false); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -ListViewPanel::~ListViewPanel() -{ - DeleteAllItems(); - - delete m_hbar; - - if ( m_bDeleteImageListWhenDone ) - { - delete m_pImageList; - m_pImageList = NULL; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ListViewPanel::AddItem(const KeyValues *data, bool bScrollToItem, bool bSortOnAdd) -{ - ListViewItem *pNewItem = new ListViewItem(this); - pNewItem->SetData(data); - if (m_hFont) - { - pNewItem->SetFont(m_hFont); - } - int itemID = m_DataItems.AddToTail(pNewItem); - ApplyItemChanges(itemID); - m_SortedItems.AddToTail(itemID); - - if ( bSortOnAdd ) - { - m_bNeedsSort = true; - } - - InvalidateLayout(); - - if ( bScrollToItem ) - { - ScrollToItem(itemID); - } - - return itemID; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::ScrollToItem(int itemID) -{ - if (!m_hbar->IsVisible()) - { - return; - } - int val = m_hbar->GetValue(); - - int wide, tall; - GetSize( wide, tall ); - - int maxWidth = GetItemsMaxWidth(); - int maxColVisible = wide / maxWidth; - int itemsPerCol = GetItemsPerColumn(); - - int itemIndex = m_SortedItems.Find(itemID); - int desiredCol = itemIndex / itemsPerCol; - if (desiredCol < val || desiredCol >= (val + maxColVisible) ) - { - m_hbar->SetValue(desiredCol); - } - - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ListViewPanel::GetItemCount() -{ - return m_DataItems.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -KeyValues *ListViewPanel::GetItem(int itemID) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return NULL; - - return m_DataItems[itemID]->GetData(); -} - -//----------------------------------------------------------------------------- -// Purpose: Get ItemID from position in panel - valid from [0, GetItemCount) -//----------------------------------------------------------------------------- -int ListViewPanel::GetItemIDFromPos(int iPos) -{ - if ( m_SortedItems.IsValidIndex(iPos) ) - { - return m_SortedItems[iPos]; - } - else - { - return m_DataItems.InvalidIndex(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::ApplyItemChanges(int itemID) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return; - - KeyValues *kv = m_DataItems[itemID]->GetData(); - ListViewItem *pLabel = m_DataItems[itemID]; - - pLabel->SetText(kv->GetString("text")); - pLabel->SetTextImageIndex(1); - pLabel->SetImagePreOffset(1, 5); - - TextImage *pTextImage = pLabel->GetTextImage(); - pTextImage->ResizeImageToContent(); - - pLabel->UpdateImage(); - pLabel->SizeToContents(); - pLabel->InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::RemoveItem(int itemID) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return; - - m_DataItems[itemID]->MarkForDeletion(); - - // mark the keyValues for deletion - m_DataItems.Remove(itemID); - m_SortedItems.FindAndRemove(itemID); - m_SelectedItems.FindAndRemove(itemID); - - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::DeleteAllItems() -{ - FOR_EACH_LL( m_DataItems, index ) - { - m_DataItems[index]->MarkForDeletion(); - } - m_DataItems.RemoveAll(); - m_SortedItems.RemoveAll(); - m_SelectedItems.RemoveAll(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ListViewPanel::InvalidItemID() -{ - return m_DataItems.InvalidIndex(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool ListViewPanel::IsValidItemID(int itemID) -{ - return m_DataItems.IsValidIndex(itemID); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::SetSortFunc(ListViewSortFunc_t func) -{ - if ( func ) - { - m_pSortFunc = func; - SortList(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::SortList() -{ - m_SortedItems.RemoveAll(); - - // find all the items in this section - for( int i = m_DataItems.Head(); i != m_DataItems.InvalidIndex(); i = m_DataItems.Next( i ) ) - { - // insert the items sorted - if (m_pSortFunc) - { - int insertionPoint; - for (insertionPoint = 0; insertionPoint < m_SortedItems.Count(); insertionPoint++) - { - if ( m_pSortFunc(m_DataItems[i]->GetData(), m_DataItems[m_SortedItems[insertionPoint]]->GetData() ) ) - break; - } - - if (insertionPoint == m_SortedItems.Count()) - { - m_SortedItems.AddToTail(i); - } - else - { - m_SortedItems.InsertBefore(insertionPoint, i); - } - } - else - { - // just add to the end - m_SortedItems.AddToTail(i); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone) -{ - // get rid of existing list image if there's one and we're supposed to get rid of it - if ( m_pImageList && m_bDeleteImageListWhenDone ) - { - delete m_pImageList; - m_pImageList = NULL; - } - - m_bDeleteImageListWhenDone = deleteImageListWhenDone; - m_pImageList = imageList; - - FOR_EACH_LL( m_DataItems, i ) - { - m_DataItems[i]->UpdateImage(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::SetFont(HFont font) -{ - Assert( font ); - if ( !font ) - return; - - m_hFont = font; - m_iRowHeight = surface()->GetFontTall(font) + 1; - - FOR_EACH_LL( m_DataItems, i ) - { - m_DataItems[i]->SetFont(m_hFont); - TextImage *pTextImage = m_DataItems[i]->GetTextImage(); - pTextImage->ResizeImageToContent(); - m_DataItems[i]->SizeToContents(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ListViewPanel::GetSelectedItemsCount() -{ - return m_SelectedItems.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ListViewPanel::GetSelectedItem(int selectionIndex) -{ - if ( m_SelectedItems.IsValidIndex(selectionIndex) ) - return m_SelectedItems[selectionIndex]; - - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::ClearSelectedItems() -{ - int i; - for (i = 0 ; i < m_SelectedItems.Count(); i++) - { - if ( m_DataItems.IsValidIndex(m_SelectedItems[i]) ) - { - m_DataItems[m_SelectedItems[i]]->SetSelected(false); - } - } - m_SelectedItems.RemoveAll(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::AddSelectedItem(int itemID) -{ - if ( m_SelectedItems.Find(itemID) == -1 ) - { - m_SelectedItems.AddToTail(itemID); - m_DataItems[itemID]->SetSelected(true); - m_LastSelectedItemID = itemID; - m_ShiftStartItemID = itemID; - PostActionSignal(new KeyValues("ListViewItemSelected")); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::SetSingleSelectedItem(int itemID) -{ - ClearSelectedItems(); - AddSelectedItem(itemID); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::OnMouseWheeled(int delta) -{ - int val = m_hbar->GetValue(); - val -= delta; - m_hbar->SetValue(val); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::OnSizeChanged(int wide, int tall) -{ - BaseClass::OnSizeChanged(wide, tall); - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ListViewPanel::GetItemsMaxWidth() -{ - int maxWidth = 0; - FOR_EACH_LL( m_DataItems, i ) - { - int labelWide, labelTall; - m_DataItems[i]->GetSize(labelWide, labelTall); - if (labelWide > maxWidth) - { - maxWidth = labelWide + 25; - } - } - return maxWidth; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::PerformLayout() -{ - if (m_bNeedsSort) - { - SortList(); - } - - if ( m_DataItems.Count() == 0 ) - return; - - int wide, tall; - GetSize(wide, tall); - - int maxWidth = GetItemsMaxWidth(); - if (maxWidth < 24) - { - maxWidth = 24; - } - int maxColVisible = wide / maxWidth; - - m_hbar->SetVisible(false); - int itemsPerCol = GetItemsPerColumn(); - if (itemsPerCol < 1) - { - itemsPerCol = 1; - } - int cols = ( GetItemCount() + (itemsPerCol - 1) ) / itemsPerCol; - - int startItem = 0; - if ( cols > maxColVisible) - { - m_hbar->SetVisible(true); - - // recalulate # per column now that we've made the hbar visible - itemsPerCol = GetItemsPerColumn(); - cols = ( GetItemCount() + (itemsPerCol - 1) ) / (itemsPerCol > 0 ? itemsPerCol : 1 ); - - m_hbar->SetEnabled(false); - m_hbar->SetRangeWindow( maxColVisible ); - m_hbar->SetRange( 0, cols); - m_hbar->SetButtonPressedScrollValue( 1 ); - - m_hbar->SetPos(0, tall - (m_hbar->GetTall()+WINDOW_BORDER_WIDTH)); - m_hbar->SetSize(wide - (WINDOW_BORDER_WIDTH*2), m_hbar->GetTall()); - m_hbar->InvalidateLayout(); - - int val = m_hbar->GetValue(); - startItem += val*itemsPerCol; - } - else - { - m_hbar->SetVisible(false); - } - int lastItemVisible = startItem + (( maxColVisible + 1 )* itemsPerCol) - 1; - - int itemsThisCol = 0; - int x = 0; - int y = 0; - int i; - for ( i = 0 ; i < m_SortedItems.Count() ; i++ ) - { - if ( i >= startItem && i <= lastItemVisible ) - { - m_DataItems[ m_SortedItems[i] ]->SetVisible(true); - m_DataItems[ m_SortedItems[i] ]->SetPos(x, y); - itemsThisCol++; - if ( itemsThisCol == itemsPerCol ) - { - y = 0; - x += maxWidth; - itemsThisCol = 0; - } - else - { - y += m_iRowHeight; - } - } - else - { - m_DataItems[ m_SortedItems[i] ]->SetVisible(false); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::Paint() -{ - BaseClass::Paint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetBgColor(GetSchemeColor("ListPanel.BgColor", pScheme)); - SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); - - m_LabelFgColor = GetSchemeColor("ListPanel.TextColor", pScheme); - m_SelectionFgColor = GetSchemeColor("ListPanel.SelectedTextColor", m_LabelFgColor, pScheme); - - m_hFont = pScheme->GetFont("Default", IsProportional()); - SetFont(m_hFont); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::OnMousePressed( MouseCode code) -{ - if (code == MOUSE_LEFT || code == MOUSE_RIGHT) - { - ClearSelectedItems(); - RequestFocus(); - } - // check for context menu open - if (code == MOUSE_RIGHT) - { - // post it, but with the invalid row - PostActionSignal(new KeyValues("OpenContextMenu", "itemID", -1)); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::OnShiftSelect(int itemID) -{ - // if we dont' have a valid selected ItemID - then we just choose the first item - if ( !m_DataItems.IsValidIndex(m_ShiftStartItemID) ) - { - m_ShiftStartItemID = m_DataItems.Head(); - } - - // find out if the just pressed item is "earlier" or is the 'last selected item' - int lowerPos = -1, upperPos = -1; - int i; - for ( i = 0 ; i < m_SortedItems.Count() ; i++ ) - { - if ( m_SortedItems[i] == itemID ) - { - lowerPos = i; - upperPos = m_SortedItems.Find(m_ShiftStartItemID); - break; - } - else if ( m_SortedItems[i] == m_ShiftStartItemID ) - { - lowerPos = m_SortedItems.Find(m_ShiftStartItemID); - upperPos = i; - break; - } - } - assert(lowerPos <= upperPos); - if ( !input()->IsKeyDown(KEY_LCONTROL) && !input()->IsKeyDown(KEY_RCONTROL) ) - { - ClearSelectedItems(); - } - - for ( i = lowerPos ; i <= upperPos ; i ++) - { - // do not use AddSelectedItem because we don't want to switch the shiftStartItemID - m_DataItems[ m_SortedItems[i] ]->SetSelected(true); - m_SelectedItems.AddToTail(m_SortedItems[i]); - m_LastSelectedItemID = itemID; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::OnItemMousePressed(ListViewItem* pItem, MouseCode code) -{ - int itemID = m_DataItems.Find(pItem); - if (!m_DataItems.IsValidIndex(itemID)) - return; - - // check for context menu open - if (code == MOUSE_RIGHT) - { - // if this is a new item - unselect everything else - if ( m_SelectedItems.Find(itemID) == -1) - { - ClearSelectedItems(); - AddSelectedItem(itemID); - } - - PostActionSignal(new KeyValues("OpenContextMenu", "itemID", itemID)); - } - else - { - if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ) - { - OnShiftSelect(itemID); - } - else if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ) - { - if ( m_SelectedItems.Find(itemID) != -1) - { - m_SelectedItems.FindAndRemove(itemID); - pItem->SetSelected(false); - - // manually select these since we 'last' clicked on these items - m_ShiftStartItemID = itemID; - m_LastSelectedItemID = itemID; - m_DataItems[itemID]->RequestFocus(); - } - else - { - AddSelectedItem(itemID); - } - } - else - { - ClearSelectedItems(); - AddSelectedItem(itemID); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::OnMouseDoublePressed( MouseCode code) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::OnItemMouseDoublePressed(ListViewItem* pItem, MouseCode code) -{ - if (code == MOUSE_LEFT) - { - OnKeyCodeTyped(KEY_ENTER); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::FinishKeyPress(int itemID) -{ - if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ) - { - OnShiftSelect(itemID); - } - else if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ) - { - m_DataItems[itemID]->RequestFocus(); - m_LastSelectedItemID = itemID; - } - else - { - SetSingleSelectedItem(itemID); - } - ScrollToItem(itemID); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::OnKeyCodeTyped( KeyCode code ) -{ - if ( m_DataItems.Count() == 0 ) - return; - - switch (code) - { - case KEY_HOME: - { - if (m_SortedItems.Count() > 0) - { - int itemID = m_SortedItems[0]; - FinishKeyPress(itemID); - } - break; - } - case KEY_END: - { - if (m_DataItems.Count() > 0) - { - int itemID = m_SortedItems[ m_SortedItems.Count() - 1 ]; - FinishKeyPress(itemID); - } - break; - } - - case KEY_UP: - { - int itemPos = m_SortedItems.Find( m_LastSelectedItemID ); - itemPos--; - if (itemPos < 0) - itemPos = 0; - - FinishKeyPress(m_SortedItems[itemPos]); - break; - } - - case KEY_DOWN: - { - int itemPos = m_SortedItems.Find( m_LastSelectedItemID ); - itemPos++; - if (itemPos >= m_DataItems.Count()) - itemPos = m_DataItems.Count() - 1; - - FinishKeyPress(m_SortedItems[itemPos]); - break; - } - - case KEY_LEFT: - { - int itemPos = m_SortedItems.Find( m_LastSelectedItemID ); - itemPos -= GetItemsPerColumn(); - if (itemPos < 0) - { - itemPos = 0; - } - FinishKeyPress(m_SortedItems[itemPos]); - break; - } - - case KEY_RIGHT: - { - int itemPos = m_SortedItems.Find( m_LastSelectedItemID ); - itemPos += GetItemsPerColumn(); - if (itemPos >= m_SortedItems.Count()) - { - itemPos = m_SortedItems.Count() - 1; - } - FinishKeyPress(m_SortedItems[itemPos]); - break; - } - - case KEY_PAGEUP: - { - int wide, tall; - GetSize(wide, tall); - - int maxWidth = GetItemsMaxWidth(); - if (maxWidth == 0) - { - maxWidth = wide; - } - int maxColVisible = wide / maxWidth; - int delta = maxColVisible * GetItemsPerColumn(); - - int itemPos = m_SortedItems.Find( m_LastSelectedItemID ); - itemPos -= delta; - if (itemPos < 0) - { - itemPos = 0; - } - - FinishKeyPress(m_SortedItems[itemPos]); - break; - } - case KEY_PAGEDOWN: - { - int wide, tall; - GetSize(wide, tall); - - int maxWidth = GetItemsMaxWidth(); - if (maxWidth == 0) - { - maxWidth = wide; - } - int maxColVisible = wide / maxWidth; - int delta = maxColVisible * GetItemsPerColumn(); - - int itemPos = m_SortedItems.Find( m_LastSelectedItemID ); - itemPos += delta; - if (itemPos >= m_SortedItems.Count()) - { - itemPos = m_SortedItems.Count() - 1; - } - - FinishKeyPress(m_SortedItems[itemPos]); - break; - } - default: - { - BaseClass::OnKeyCodeTyped(code); - break; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::OnKeyTyped(wchar_t unichar) -{ - if (!iswcntrl(unichar)) - { - wchar_t uniString[2]; - uniString[0] = unichar; - uniString[1] = 0; - - char buf[2]; - g_pVGuiLocalize->ConvertUnicodeToANSI(uniString, buf, sizeof(buf)); - - int i; - int itemPos = m_SortedItems.Find(m_LastSelectedItemID); - if ( m_SortedItems.IsValidIndex(itemPos)) - { - itemPos++; - // start from the item AFTER our last selected Item and go to end - for ( i = itemPos ; i != m_SortedItems.Count(); i++) - { - KeyValues *kv = m_DataItems[ m_SortedItems[i] ]->GetData(); - const char *pszText = kv->GetString("text"); - if (!strnicmp(pszText, buf, 1)) - { - // select the next of this letter - SetSingleSelectedItem(m_SortedItems[i]); - ScrollToItem(m_SortedItems[i]); - return; - } - } - // if the after this item we couldn't fine an item with the same letter, fall through and just start from the beginning of list to the last selected item - } - - for ( i = 0 ; i < m_SortedItems.Count() ; i++ ) - { - // we've gone all the way around - break - if we had a valid index, this is one more that last selectedItem, if not it's an illegal index - if ( i == itemPos) - break; - - KeyValues *kv = m_DataItems[ m_SortedItems[i] ]->GetData(); - const char *pszText = kv->GetString("text"); - if (!strnicmp(pszText, buf, 1)) - { - SetSingleSelectedItem(m_SortedItems[i]); - ScrollToItem(m_SortedItems[i]); - return; - } - } - } - else - BaseClass::OnKeyTyped(unichar); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ListViewPanel::OnSliderMoved() -{ - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int ListViewPanel::GetItemsPerColumn() -{ - int wide, tall; - GetSize(wide, tall); - - if ( m_hbar->IsVisible() ) - { - tall -= m_hbar->GetTall(); - } - - return tall / m_iRowHeight; // should round down -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +enum +{ + WINDOW_BORDER_WIDTH=2 // the width of the window's border +}; + +namespace vgui +{ +class ListViewItem : public Label +{ + DECLARE_CLASS_SIMPLE( ListViewItem, Label ); + +public: + ListViewItem(Panel *parent) : Label(parent, NULL, "") + { + m_pListViewPanel = (ListViewPanel*) parent; + m_pData = NULL; + m_bSelected = false; + SetPaintBackgroundEnabled(true); + } + + ~ListViewItem() + { + if (m_pData) + { + m_pData->deleteThis(); + m_pData = NULL; + } + } + + void SetData(const KeyValues *data) + { + if (m_pData) + { + m_pData->deleteThis(); + } + m_pData = data->MakeCopy(); + } + + virtual void OnMousePressed( MouseCode code) + { + m_pListViewPanel->OnItemMousePressed(this, code); + } + + virtual void OnMouseDoublePressed( MouseCode code) + { + // double press should only select the item + m_pListViewPanel->OnItemMouseDoublePressed(this, code); + } + + KeyValues *GetData() + { + return m_pData; + } + + void SetSelected(bool bSelected) + { + if (bSelected == m_bSelected) + return; + + m_bSelected = bSelected; + if (bSelected) + { + RequestFocus(); + } + + UpdateImage(); + InvalidateLayout(); + Repaint(); + } + + virtual void PerformLayout() + { + TextImage *textImage = GetTextImage(); + if (m_bSelected) + { + VPANEL focus = input()->GetFocus(); + // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected + if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))) + { + textImage->SetColor(m_ArmedFgColor2); + } + else + { + textImage->SetColor(m_FgColor2); + } + } + else + { + textImage->SetColor(GetFgColor()); + } + BaseClass::PerformLayout(); + Repaint(); + } + + virtual void PaintBackground() + { + int wide, tall; + GetSize(wide, tall); + + if ( m_bSelected ) + { + VPANEL focus = input()->GetFocus(); + // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected + if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))) + { + surface()->DrawSetColor(m_ArmedBgColor); + } + else + { + surface()->DrawSetColor(m_SelectionBG2Color); + } + } + else + { + surface()->DrawSetColor(GetBgColor()); + } + surface()->DrawFilledRect(0, 0, wide, tall); + } + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + BaseClass::ApplySchemeSettings(pScheme); + + m_ArmedFgColor2 = GetSchemeColor("ListPanel.SelectedTextColor", pScheme); + m_ArmedBgColor = GetSchemeColor("ListPanel.SelectedBgColor", pScheme); + + m_FgColor1 = GetSchemeColor("ListPanel.TextColor", pScheme); + m_FgColor2 = GetSchemeColor("ListPanel.SelectedTextColor", pScheme); + + m_BgColor = GetSchemeColor("ListPanel.BgColor", GetBgColor(), pScheme); + m_BgColor = GetSchemeColor("ListPanel.TextBgColor", m_BgColor, pScheme); + m_SelectionBG2Color = GetSchemeColor("ListPanel.SelectedOutOfFocusBgColor", pScheme); + SetBgColor(m_BgColor); + SetFgColor(m_FgColor1); + + UpdateImage(); + } + + void UpdateImage() + { + if ( m_pListViewPanel->m_pImageList ) + { + int imageIndex = 0; + if ( m_bSelected ) + { + imageIndex = m_pData->GetInt("imageSelected", 0); + } + if ( imageIndex == 0 ) + { + imageIndex = m_pData->GetInt("image", 0); + } + if ( m_pListViewPanel->m_pImageList->IsValidIndex(imageIndex) ) + { + SetImageAtIndex(0, m_pListViewPanel->m_pImageList->GetImage(imageIndex), 0); + } + else + { + // use the default + SetImageAtIndex(0, m_pListViewPanel->m_pImageList->GetImage(1), 0); + } + SizeToContents(); + InvalidateLayout(); + } + } + +private: + + Color m_FgColor1; + Color m_FgColor2; + Color m_BgColor; + Color m_ArmedFgColor2; + Color m_ArmedBgColor; + Color m_SelectionBG2Color; + + //IBorder *_keyFocusBorder; // maybe in the future when I'm the 'active' but not selected item, I'll have a border + + KeyValues *m_pData; + ListViewPanel *m_pListViewPanel; + bool m_bSelected; +}; +} + +static bool DefaultSortFunc(KeyValues *kv1, KeyValues *kv2) +{ + const char *string1 = kv1->GetString("text"); + const char *string2 = kv2->GetString("text"); + return Q_stricmp(string1, string2) < 0; +} + +DECLARE_BUILD_FACTORY( ListViewPanel ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ListViewPanel::ListViewPanel(Panel *parent, const char *panelName) : Panel(parent, panelName) +{ + m_iRowHeight = 20; + m_bNeedsSort = false; + m_hFont = NULL; + m_pImageList = NULL; + m_bDeleteImageListWhenDone = false; + m_pSortFunc = DefaultSortFunc; + m_ShiftStartItemID = -1; + + m_hbar = new ScrollBar(this, "HorizScrollBar", false); + m_hbar->AddActionSignalTarget(this); + m_hbar->SetVisible(false); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ListViewPanel::~ListViewPanel() +{ + DeleteAllItems(); + + delete m_hbar; + + if ( m_bDeleteImageListWhenDone ) + { + delete m_pImageList; + m_pImageList = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ListViewPanel::AddItem(const KeyValues *data, bool bScrollToItem, bool bSortOnAdd) +{ + ListViewItem *pNewItem = new ListViewItem(this); + pNewItem->SetData(data); + if (m_hFont) + { + pNewItem->SetFont(m_hFont); + } + int itemID = m_DataItems.AddToTail(pNewItem); + ApplyItemChanges(itemID); + m_SortedItems.AddToTail(itemID); + + if ( bSortOnAdd ) + { + m_bNeedsSort = true; + } + + InvalidateLayout(); + + if ( bScrollToItem ) + { + ScrollToItem(itemID); + } + + return itemID; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::ScrollToItem(int itemID) +{ + if (!m_hbar->IsVisible()) + { + return; + } + int val = m_hbar->GetValue(); + + int wide, tall; + GetSize( wide, tall ); + + int maxWidth = GetItemsMaxWidth(); + int maxColVisible = wide / maxWidth; + int itemsPerCol = GetItemsPerColumn(); + + int itemIndex = m_SortedItems.Find(itemID); + int desiredCol = itemIndex / itemsPerCol; + if (desiredCol < val || desiredCol >= (val + maxColVisible) ) + { + m_hbar->SetValue(desiredCol); + } + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ListViewPanel::GetItemCount() +{ + return m_DataItems.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +KeyValues *ListViewPanel::GetItem(int itemID) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return NULL; + + return m_DataItems[itemID]->GetData(); +} + +//----------------------------------------------------------------------------- +// Purpose: Get ItemID from position in panel - valid from [0, GetItemCount) +//----------------------------------------------------------------------------- +int ListViewPanel::GetItemIDFromPos(int iPos) +{ + if ( m_SortedItems.IsValidIndex(iPos) ) + { + return m_SortedItems[iPos]; + } + else + { + return m_DataItems.InvalidIndex(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::ApplyItemChanges(int itemID) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return; + + KeyValues *kv = m_DataItems[itemID]->GetData(); + ListViewItem *pLabel = m_DataItems[itemID]; + + pLabel->SetText(kv->GetString("text")); + pLabel->SetTextImageIndex(1); + pLabel->SetImagePreOffset(1, 5); + + TextImage *pTextImage = pLabel->GetTextImage(); + pTextImage->ResizeImageToContent(); + + pLabel->UpdateImage(); + pLabel->SizeToContents(); + pLabel->InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::RemoveItem(int itemID) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return; + + m_DataItems[itemID]->MarkForDeletion(); + + // mark the keyValues for deletion + m_DataItems.Remove(itemID); + m_SortedItems.FindAndRemove(itemID); + m_SelectedItems.FindAndRemove(itemID); + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::DeleteAllItems() +{ + FOR_EACH_LL( m_DataItems, index ) + { + m_DataItems[index]->MarkForDeletion(); + } + m_DataItems.RemoveAll(); + m_SortedItems.RemoveAll(); + m_SelectedItems.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ListViewPanel::InvalidItemID() +{ + return m_DataItems.InvalidIndex(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool ListViewPanel::IsValidItemID(int itemID) +{ + return m_DataItems.IsValidIndex(itemID); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::SetSortFunc(ListViewSortFunc_t func) +{ + if ( func ) + { + m_pSortFunc = func; + SortList(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::SortList() +{ + m_SortedItems.RemoveAll(); + + // find all the items in this section + for( int i = m_DataItems.Head(); i != m_DataItems.InvalidIndex(); i = m_DataItems.Next( i ) ) + { + // insert the items sorted + if (m_pSortFunc) + { + int insertionPoint; + for (insertionPoint = 0; insertionPoint < m_SortedItems.Count(); insertionPoint++) + { + if ( m_pSortFunc(m_DataItems[i]->GetData(), m_DataItems[m_SortedItems[insertionPoint]]->GetData() ) ) + break; + } + + if (insertionPoint == m_SortedItems.Count()) + { + m_SortedItems.AddToTail(i); + } + else + { + m_SortedItems.InsertBefore(insertionPoint, i); + } + } + else + { + // just add to the end + m_SortedItems.AddToTail(i); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone) +{ + // get rid of existing list image if there's one and we're supposed to get rid of it + if ( m_pImageList && m_bDeleteImageListWhenDone ) + { + delete m_pImageList; + m_pImageList = NULL; + } + + m_bDeleteImageListWhenDone = deleteImageListWhenDone; + m_pImageList = imageList; + + FOR_EACH_LL( m_DataItems, i ) + { + m_DataItems[i]->UpdateImage(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::SetFont(HFont font) +{ + Assert( font ); + if ( !font ) + return; + + m_hFont = font; + m_iRowHeight = surface()->GetFontTall(font) + 1; + + FOR_EACH_LL( m_DataItems, i ) + { + m_DataItems[i]->SetFont(m_hFont); + TextImage *pTextImage = m_DataItems[i]->GetTextImage(); + pTextImage->ResizeImageToContent(); + m_DataItems[i]->SizeToContents(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ListViewPanel::GetSelectedItemsCount() +{ + return m_SelectedItems.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ListViewPanel::GetSelectedItem(int selectionIndex) +{ + if ( m_SelectedItems.IsValidIndex(selectionIndex) ) + return m_SelectedItems[selectionIndex]; + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::ClearSelectedItems() +{ + int i; + for (i = 0 ; i < m_SelectedItems.Count(); i++) + { + if ( m_DataItems.IsValidIndex(m_SelectedItems[i]) ) + { + m_DataItems[m_SelectedItems[i]]->SetSelected(false); + } + } + m_SelectedItems.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::AddSelectedItem(int itemID) +{ + if ( m_SelectedItems.Find(itemID) == -1 ) + { + m_SelectedItems.AddToTail(itemID); + m_DataItems[itemID]->SetSelected(true); + m_LastSelectedItemID = itemID; + m_ShiftStartItemID = itemID; + PostActionSignal(new KeyValues("ListViewItemSelected")); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::SetSingleSelectedItem(int itemID) +{ + ClearSelectedItems(); + AddSelectedItem(itemID); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::OnMouseWheeled(int delta) +{ + int val = m_hbar->GetValue(); + val -= delta; + m_hbar->SetValue(val); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::OnSizeChanged(int wide, int tall) +{ + BaseClass::OnSizeChanged(wide, tall); + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ListViewPanel::GetItemsMaxWidth() +{ + int maxWidth = 0; + FOR_EACH_LL( m_DataItems, i ) + { + int labelWide, labelTall; + m_DataItems[i]->GetSize(labelWide, labelTall); + if (labelWide > maxWidth) + { + maxWidth = labelWide + 25; + } + } + return maxWidth; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::PerformLayout() +{ + if (m_bNeedsSort) + { + SortList(); + } + + if ( m_DataItems.Count() == 0 ) + return; + + int wide, tall; + GetSize(wide, tall); + + int maxWidth = GetItemsMaxWidth(); + if (maxWidth < 24) + { + maxWidth = 24; + } + int maxColVisible = wide / maxWidth; + + m_hbar->SetVisible(false); + int itemsPerCol = GetItemsPerColumn(); + if (itemsPerCol < 1) + { + itemsPerCol = 1; + } + int cols = ( GetItemCount() + (itemsPerCol - 1) ) / itemsPerCol; + + int startItem = 0; + if ( cols > maxColVisible) + { + m_hbar->SetVisible(true); + + // recalulate # per column now that we've made the hbar visible + itemsPerCol = GetItemsPerColumn(); + cols = ( GetItemCount() + (itemsPerCol - 1) ) / (itemsPerCol > 0 ? itemsPerCol : 1 ); + + m_hbar->SetEnabled(false); + m_hbar->SetRangeWindow( maxColVisible ); + m_hbar->SetRange( 0, cols); + m_hbar->SetButtonPressedScrollValue( 1 ); + + m_hbar->SetPos(0, tall - (m_hbar->GetTall()+WINDOW_BORDER_WIDTH)); + m_hbar->SetSize(wide - (WINDOW_BORDER_WIDTH*2), m_hbar->GetTall()); + m_hbar->InvalidateLayout(); + + int val = m_hbar->GetValue(); + startItem += val*itemsPerCol; + } + else + { + m_hbar->SetVisible(false); + } + int lastItemVisible = startItem + (( maxColVisible + 1 )* itemsPerCol) - 1; + + int itemsThisCol = 0; + int x = 0; + int y = 0; + int i; + for ( i = 0 ; i < m_SortedItems.Count() ; i++ ) + { + if ( i >= startItem && i <= lastItemVisible ) + { + m_DataItems[ m_SortedItems[i] ]->SetVisible(true); + m_DataItems[ m_SortedItems[i] ]->SetPos(x, y); + itemsThisCol++; + if ( itemsThisCol == itemsPerCol ) + { + y = 0; + x += maxWidth; + itemsThisCol = 0; + } + else + { + y += m_iRowHeight; + } + } + else + { + m_DataItems[ m_SortedItems[i] ]->SetVisible(false); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::Paint() +{ + BaseClass::Paint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetBgColor(GetSchemeColor("ListPanel.BgColor", pScheme)); + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); + + m_LabelFgColor = GetSchemeColor("ListPanel.TextColor", pScheme); + m_SelectionFgColor = GetSchemeColor("ListPanel.SelectedTextColor", m_LabelFgColor, pScheme); + + m_hFont = pScheme->GetFont("Default", IsProportional()); + SetFont(m_hFont); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::OnMousePressed( MouseCode code) +{ + if (code == MOUSE_LEFT || code == MOUSE_RIGHT) + { + ClearSelectedItems(); + RequestFocus(); + } + // check for context menu open + if (code == MOUSE_RIGHT) + { + // post it, but with the invalid row + PostActionSignal(new KeyValues("OpenContextMenu", "itemID", -1)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::OnShiftSelect(int itemID) +{ + // if we dont' have a valid selected ItemID - then we just choose the first item + if ( !m_DataItems.IsValidIndex(m_ShiftStartItemID) ) + { + m_ShiftStartItemID = m_DataItems.Head(); + } + + // find out if the just pressed item is "earlier" or is the 'last selected item' + int lowerPos = -1, upperPos = -1; + int i; + for ( i = 0 ; i < m_SortedItems.Count() ; i++ ) + { + if ( m_SortedItems[i] == itemID ) + { + lowerPos = i; + upperPos = m_SortedItems.Find(m_ShiftStartItemID); + break; + } + else if ( m_SortedItems[i] == m_ShiftStartItemID ) + { + lowerPos = m_SortedItems.Find(m_ShiftStartItemID); + upperPos = i; + break; + } + } + assert(lowerPos <= upperPos); + if ( !input()->IsKeyDown(KEY_LCONTROL) && !input()->IsKeyDown(KEY_RCONTROL) ) + { + ClearSelectedItems(); + } + + for ( i = lowerPos ; i <= upperPos ; i ++) + { + // do not use AddSelectedItem because we don't want to switch the shiftStartItemID + m_DataItems[ m_SortedItems[i] ]->SetSelected(true); + m_SelectedItems.AddToTail(m_SortedItems[i]); + m_LastSelectedItemID = itemID; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::OnItemMousePressed(ListViewItem* pItem, MouseCode code) +{ + int itemID = m_DataItems.Find(pItem); + if (!m_DataItems.IsValidIndex(itemID)) + return; + + // check for context menu open + if (code == MOUSE_RIGHT) + { + // if this is a new item - unselect everything else + if ( m_SelectedItems.Find(itemID) == -1) + { + ClearSelectedItems(); + AddSelectedItem(itemID); + } + + PostActionSignal(new KeyValues("OpenContextMenu", "itemID", itemID)); + } + else + { + if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ) + { + OnShiftSelect(itemID); + } + else if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ) + { + if ( m_SelectedItems.Find(itemID) != -1) + { + m_SelectedItems.FindAndRemove(itemID); + pItem->SetSelected(false); + + // manually select these since we 'last' clicked on these items + m_ShiftStartItemID = itemID; + m_LastSelectedItemID = itemID; + m_DataItems[itemID]->RequestFocus(); + } + else + { + AddSelectedItem(itemID); + } + } + else + { + ClearSelectedItems(); + AddSelectedItem(itemID); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::OnMouseDoublePressed( MouseCode code) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::OnItemMouseDoublePressed(ListViewItem* pItem, MouseCode code) +{ + if (code == MOUSE_LEFT) + { + OnKeyCodeTyped(KEY_ENTER); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::FinishKeyPress(int itemID) +{ + if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) ) + { + OnShiftSelect(itemID); + } + else if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) ) + { + m_DataItems[itemID]->RequestFocus(); + m_LastSelectedItemID = itemID; + } + else + { + SetSingleSelectedItem(itemID); + } + ScrollToItem(itemID); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::OnKeyCodeTyped( KeyCode code ) +{ + if ( m_DataItems.Count() == 0 ) + return; + + switch (code) + { + case KEY_HOME: + { + if (m_SortedItems.Count() > 0) + { + int itemID = m_SortedItems[0]; + FinishKeyPress(itemID); + } + break; + } + case KEY_END: + { + if (m_DataItems.Count() > 0) + { + int itemID = m_SortedItems[ m_SortedItems.Count() - 1 ]; + FinishKeyPress(itemID); + } + break; + } + + case KEY_UP: + { + int itemPos = m_SortedItems.Find( m_LastSelectedItemID ); + itemPos--; + if (itemPos < 0) + itemPos = 0; + + FinishKeyPress(m_SortedItems[itemPos]); + break; + } + + case KEY_DOWN: + { + int itemPos = m_SortedItems.Find( m_LastSelectedItemID ); + itemPos++; + if (itemPos >= m_DataItems.Count()) + itemPos = m_DataItems.Count() - 1; + + FinishKeyPress(m_SortedItems[itemPos]); + break; + } + + case KEY_LEFT: + { + int itemPos = m_SortedItems.Find( m_LastSelectedItemID ); + itemPos -= GetItemsPerColumn(); + if (itemPos < 0) + { + itemPos = 0; + } + FinishKeyPress(m_SortedItems[itemPos]); + break; + } + + case KEY_RIGHT: + { + int itemPos = m_SortedItems.Find( m_LastSelectedItemID ); + itemPos += GetItemsPerColumn(); + if (itemPos >= m_SortedItems.Count()) + { + itemPos = m_SortedItems.Count() - 1; + } + FinishKeyPress(m_SortedItems[itemPos]); + break; + } + + case KEY_PAGEUP: + { + int wide, tall; + GetSize(wide, tall); + + int maxWidth = GetItemsMaxWidth(); + if (maxWidth == 0) + { + maxWidth = wide; + } + int maxColVisible = wide / maxWidth; + int delta = maxColVisible * GetItemsPerColumn(); + + int itemPos = m_SortedItems.Find( m_LastSelectedItemID ); + itemPos -= delta; + if (itemPos < 0) + { + itemPos = 0; + } + + FinishKeyPress(m_SortedItems[itemPos]); + break; + } + case KEY_PAGEDOWN: + { + int wide, tall; + GetSize(wide, tall); + + int maxWidth = GetItemsMaxWidth(); + if (maxWidth == 0) + { + maxWidth = wide; + } + int maxColVisible = wide / maxWidth; + int delta = maxColVisible * GetItemsPerColumn(); + + int itemPos = m_SortedItems.Find( m_LastSelectedItemID ); + itemPos += delta; + if (itemPos >= m_SortedItems.Count()) + { + itemPos = m_SortedItems.Count() - 1; + } + + FinishKeyPress(m_SortedItems[itemPos]); + break; + } + default: + { + BaseClass::OnKeyCodeTyped(code); + break; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::OnKeyTyped(wchar_t unichar) +{ + if (!iswcntrl(unichar)) + { + wchar_t uniString[2]; + uniString[0] = unichar; + uniString[1] = 0; + + char buf[2]; + g_pVGuiLocalize->ConvertUnicodeToANSI(uniString, buf, sizeof(buf)); + + int i; + int itemPos = m_SortedItems.Find(m_LastSelectedItemID); + if ( m_SortedItems.IsValidIndex(itemPos)) + { + itemPos++; + // start from the item AFTER our last selected Item and go to end + for ( i = itemPos ; i != m_SortedItems.Count(); i++) + { + KeyValues *kv = m_DataItems[ m_SortedItems[i] ]->GetData(); + const char *pszText = kv->GetString("text"); + if (!strnicmp(pszText, buf, 1)) + { + // select the next of this letter + SetSingleSelectedItem(m_SortedItems[i]); + ScrollToItem(m_SortedItems[i]); + return; + } + } + // if the after this item we couldn't fine an item with the same letter, fall through and just start from the beginning of list to the last selected item + } + + for ( i = 0 ; i < m_SortedItems.Count() ; i++ ) + { + // we've gone all the way around - break - if we had a valid index, this is one more that last selectedItem, if not it's an illegal index + if ( i == itemPos) + break; + + KeyValues *kv = m_DataItems[ m_SortedItems[i] ]->GetData(); + const char *pszText = kv->GetString("text"); + if (!strnicmp(pszText, buf, 1)) + { + SetSingleSelectedItem(m_SortedItems[i]); + ScrollToItem(m_SortedItems[i]); + return; + } + } + } + else + BaseClass::OnKeyTyped(unichar); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ListViewPanel::OnSliderMoved() +{ + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int ListViewPanel::GetItemsPerColumn() +{ + int wide, tall; + GetSize(wide, tall); + + if ( m_hbar->IsVisible() ) + { + tall -= m_hbar->GetTall(); + } + + return tall / m_iRowHeight; // should round down +} + diff --git a/mp/src/vgui2/vgui_controls/Menu.cpp b/mp/src/vgui2/vgui_controls/Menu.cpp index ceb875a4..695d3523 100644 --- a/mp/src/vgui2/vgui_controls/Menu.cpp +++ b/mp/src/vgui2/vgui_controls/Menu.cpp @@ -1,2703 +1,2703 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "vgui_controls/pch_vgui_controls.h" - -// memdbgon must be the last include file in a .cpp file -#include "tier0/memdbgon.h" -#define MENU_SEPARATOR_HEIGHT 3 - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: divider line in a menu -//----------------------------------------------------------------------------- -class vgui::MenuSeparator : public Panel -{ -public: - DECLARE_CLASS_SIMPLE( MenuSeparator, Panel ); - - MenuSeparator( Panel *parent, char const *panelName ) : - BaseClass( parent, panelName ) - { - SetPaintEnabled( true ); - SetPaintBackgroundEnabled( true ); - SetPaintBorderEnabled( false ); - } - - virtual void Paint() - { - int w, h; - GetSize( w, h ); - - surface()->DrawSetColor( GetFgColor() ); - surface()->DrawFilledRect( 4, 1, w-1, 2 ); - } - - virtual void ApplySchemeSettings( IScheme *pScheme ) - { - BaseClass::ApplySchemeSettings( pScheme ); - - SetFgColor( pScheme->GetColor( "Menu.SeparatorColor", Color( 142, 142, 142, 255 ) ) ); - SetBgColor( pScheme->GetColor( "Menu.BgColor", Color( 0, 0, 0, 255 ) ) ); - } -}; - -DECLARE_BUILD_FACTORY( Menu ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -Menu::Menu(Panel *parent, const char *panelName) : Panel(parent, panelName) -{ - m_Alignment = Label::a_west; - m_iFixedWidth = 0; - m_iMinimumWidth = 0; - m_iNumVisibleLines = -1; // No limit - m_iCurrentlySelectedItemID = m_MenuItems.InvalidIndex(); - m_pScroller = new ScrollBar(this, "MenuScrollBar", true); - m_pScroller->SetVisible(false); - m_pScroller->AddActionSignalTarget(this); - _sizedForScrollBar = false; - SetZPos(1); - SetVisible(false); - MakePopup(false); - SetParent(parent); - _recalculateWidth = true; - m_bUseMenuManager = true; - m_iInputMode = MOUSE; - m_iCheckImageWidth = 0; - m_iActivatedItem = 0; - - m_bUseFallbackFont = false; - m_hFallbackItemFont = INVALID_FONT; - - if (IsProportional()) - { - m_iMenuItemHeight = scheme()->GetProportionalScaledValueEx( GetScheme(), DEFAULT_MENU_ITEM_HEIGHT ); - } - else - { - m_iMenuItemHeight = DEFAULT_MENU_ITEM_HEIGHT; - } - m_hItemFont = INVALID_FONT; - - - m_eTypeAheadMode = COMPAT_MODE; - m_szTypeAheadBuf[0] = '\0'; - m_iNumTypeAheadChars = 0; - m_fLastTypeAheadTime = 0.0f; -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -Menu::~Menu() -{ - delete m_pScroller; -} - -//----------------------------------------------------------------------------- -// Purpose: Remove all menu items from the menu. -//----------------------------------------------------------------------------- -void Menu::DeleteAllItems() -{ - FOR_EACH_LL( m_MenuItems, i ) - { - m_MenuItems[i]->MarkForDeletion(); - } - - m_MenuItems.RemoveAll(); - m_SortedItems.RemoveAll(); - m_VisibleSortedItems.RemoveAll(); - m_Separators.RemoveAll(); - int c = m_SeparatorPanels.Count(); - for ( int i = 0 ; i < c; ++i ) - { - m_SeparatorPanels[ i ]->MarkForDeletion(); - } - m_SeparatorPanels.RemoveAll(); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a menu item to the menu. -//----------------------------------------------------------------------------- -int Menu::AddMenuItem( MenuItem *panel ) -{ - panel->SetParent( this ); - MEM_ALLOC_CREDIT(); - int itemID = m_MenuItems.AddToTail( panel ); - m_SortedItems.AddToTail(itemID); - InvalidateLayout(false); - _recalculateWidth = true; - panel->SetContentAlignment( m_Alignment ); - if ( INVALID_FONT != m_hItemFont ) - { - panel->SetFont( m_hItemFont ); - } - if ( m_bUseFallbackFont && INVALID_FONT != m_hFallbackItemFont ) - { - Label *l = panel; - TextImage *ti = l->GetTextImage(); - if ( ti ) - { - ti->SetUseFallbackFont( m_bUseFallbackFont, m_hFallbackItemFont ); - } - } - - if ( panel->GetHotKey() ) - { - SetTypeAheadMode( HOT_KEY_MODE ); - } - - return itemID; -} - - -//----------------------------------------------------------------------------- -// Remove a single item -//----------------------------------------------------------------------------- -void Menu::DeleteItem( int itemID ) -{ - // FIXME: This doesn't work with separator panels yet - Assert( m_SeparatorPanels.Count() == 0 ); - - m_MenuItems[itemID]->MarkForDeletion(); - m_MenuItems.Remove( itemID ); - - m_SortedItems.FindAndRemove( itemID ); - m_VisibleSortedItems.FindAndRemove( itemID ); - - InvalidateLayout(false); - _recalculateWidth = true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Add a menu item to the menu. -// Input : *item - MenuItem -// *command - Command text to be sent when menu item is selected -// *target - Target panel of the command -// *userData - any user data associated with this menu item -// Output: itemID - ID of this item -//----------------------------------------------------------------------------- -int Menu::AddMenuItemCharCommand(MenuItem *item, const char *command, Panel *target, const KeyValues *userData) -{ - item->SetCommand(command); - item->AddActionSignalTarget( target ); - item->SetUserData(userData); - return AddMenuItem( item ); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a menu item to the menu. -// Input : *itemName - Name of item -// *itemText - Name of item text that will appear in the manu. -// *message - pointer to the message to send when the item is selected -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -// Output: itemID - ID of this item -//----------------------------------------------------------------------------- -int Menu::AddMenuItemKeyValuesCommand( MenuItem *item, KeyValues *message, Panel *target, const KeyValues *userData ) -{ - item->SetCommand(message); - item->AddActionSignalTarget(target); - item->SetUserData(userData); - return AddMenuItem(item); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a menu item to the menu. -// Input : *itemName - Name of item -// *itemText - Name of item text that will appear in the manu. -// *command - Command text to be sent when menu item is selected -// *target - Target panel of the command -// Output: itemID - ID of this item -//----------------------------------------------------------------------------- -int Menu::AddMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData ) -{ - MenuItem *item = new MenuItem(this, itemName, itemText ); - return AddMenuItemCharCommand(item, command, target, userData); -} - -int Menu::AddMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData ) -{ - MenuItem *item = new MenuItem(this, itemName, wszItemText ); - return AddMenuItemCharCommand(item, command, target, userData); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a menu item to the menu. -// Input : *itemText - Name of item text that will appear in the manu. -// This will also be used as the name of the menu item panel. -// *command - Command text to be sent when menu item is selected -// *target - Target panel of the command -// Output: itemID - ID of this item -//----------------------------------------------------------------------------- -int Menu::AddMenuItem( const char *itemText, const char *command, Panel *target, const KeyValues *userData ) -{ - return AddMenuItem(itemText, itemText, command, target, userData ) ; -} - -//----------------------------------------------------------------------------- -// Purpose: Add a menu item to the menu. -// Input : *itemName - Name of item -// *itemText - Name of item text that will appear in the manu. -// *message - pointer to the message to send when the item is selected -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -//----------------------------------------------------------------------------- -int Menu::AddMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData ) -{ - MenuItem *item = new MenuItem(this, itemName, itemText ); - return AddMenuItemKeyValuesCommand(item, message, target, userData); -} - -int Menu::AddMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, const KeyValues *userData ) -{ - MenuItem *item = new MenuItem(this, itemName, wszItemText ); - return AddMenuItemKeyValuesCommand(item, message, target, userData); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a menu item to the menu. -// Input : *itemText - Name of item text that will appear in the manu. -// This will also be used as the name of the menu item panel. -// *message - pointer to the message to send when the item is selected -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -//----------------------------------------------------------------------------- -int Menu::AddMenuItem( const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData ) -{ - return AddMenuItem(itemText, itemText, message, target, userData ); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a menu item to the menu. -// Input : *itemText - Name of item text that will appear in the manu. -// This will also be the text of the command sent when the -// item is selected. -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -//----------------------------------------------------------------------------- -int Menu::AddMenuItem( const char *itemText, Panel *target , const KeyValues *userData ) -{ - return AddMenuItem(itemText, itemText, target, userData ); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a checkable menu item to the menu. -// Input : *itemName - Name of item -// *itemText - Name of item text that will appear in the manu. -// *command - Command text to be sent when menu item is selected -// *target - Target panel of the command -//----------------------------------------------------------------------------- -int Menu::AddCheckableMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData ) -{ - MenuItem *item = new MenuItem(this, itemName, itemText, NULL, true); - return AddMenuItemCharCommand(item, command, target, userData); -} - -int Menu::AddCheckableMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData ) -{ - MenuItem *item = new MenuItem(this, itemName, wszItemText, NULL, true); - return AddMenuItemCharCommand(item, command, target, userData); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a checkable menu item to the menu. -// Input : *itemText - Name of item text that will appear in the manu. -// This will also be used as the name of the menu item panel. -// *command - Command text to be sent when menu item is selected -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -//----------------------------------------------------------------------------- -int Menu::AddCheckableMenuItem( const char *itemText, const char *command, Panel *target, const KeyValues *userData ) -{ - return AddCheckableMenuItem(itemText, itemText, command, target, userData ); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a checkable menu item to the menu. -// Input : *itemName - Name of item -// *itemText - Name of item text that will appear in the manu. -// *message - pointer to the message to send when the item is selected -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -//----------------------------------------------------------------------------- -int Menu::AddCheckableMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData ) -{ - MenuItem *item = new MenuItem(this, itemName, itemText, NULL, true); - return AddMenuItemKeyValuesCommand(item, message, target, userData); -} - -int Menu::AddCheckableMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, const KeyValues *userData ) -{ - MenuItem *item = new MenuItem(this, itemName, wszItemText, NULL, true); - return AddMenuItemKeyValuesCommand(item, message, target, userData); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a checkable menu item to the menu. -// Input : *itemText - Name of item text that will appear in the manu. -// This will also be used as the name of the menu item panel. -// *message - pointer to the message to send when the item is selected -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -//----------------------------------------------------------------------------- -int Menu::AddCheckableMenuItem( const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData ) -{ - return AddCheckableMenuItem(itemText, itemText, message, target, userData ); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a checkable menu item to the menu. -// Input : *itemText - Name of item text that will appear in the manu. -// This will also be the text of the command sent when the -// item is selected. -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -//----------------------------------------------------------------------------- -int Menu::AddCheckableMenuItem( const char *itemText, Panel *target, const KeyValues *userData ) -{ - return AddCheckableMenuItem(itemText, itemText, target, userData ); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a Cascading menu item to the menu. -// Input : *itemName - Name of item -// *itemText - Name of item text that will appear in the manu. -// *command - Command text to be sent when menu item is selected -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -//----------------------------------------------------------------------------- -int Menu::AddCascadingMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, Menu *cascadeMenu , const KeyValues *userData ) -{ - MenuItem *item = new MenuItem(this, itemName, itemText, cascadeMenu ); - return AddMenuItemCharCommand(item, command, target, userData); -} - -int Menu::AddCascadingMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, Menu *cascadeMenu , const KeyValues *userData ) -{ - MenuItem *item = new MenuItem(this, itemName, wszItemText, cascadeMenu ); - return AddMenuItemCharCommand(item, command, target, userData); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a Cascading menu item to the menu. -// Input : *itemText - Name of item text that will appear in the manu. -// This will also be used as the name of the menu item panel. -// *command - Command text to be sent when menu item is selected -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -//----------------------------------------------------------------------------- -int Menu::AddCascadingMenuItem( const char *itemText, const char *command, Panel *target, Menu *cascadeMenu , const KeyValues *userData ) -{ - return AddCascadingMenuItem( itemText, itemText, command, target, cascadeMenu, userData ); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a Cascading menu item to the menu. -// Input : *itemName - Name of item -// *itemText - Name of item text that will appear in the manu. -// *message - pointer to the message to send when the item is selected -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -//----------------------------------------------------------------------------- -int Menu::AddCascadingMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData ) -{ - MenuItem *item = new MenuItem( this, itemName, itemText, cascadeMenu); - return AddMenuItemKeyValuesCommand(item, message, target, userData); -} - -int Menu::AddCascadingMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData ) -{ - MenuItem *item = new MenuItem( this, itemName, wszItemText, cascadeMenu); - return AddMenuItemKeyValuesCommand(item, message, target, userData); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a Cascading menu item to the menu. -// Input : *itemText - Name of item text that will appear in the manu. -// This will also be used as the name of the menu item panel. -// *message - pointer to the message to send when the item is selected -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -//----------------------------------------------------------------------------- -int Menu::AddCascadingMenuItem( const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData ) -{ - return AddCascadingMenuItem(itemText, itemText, message, target, cascadeMenu, userData ); -} - -//----------------------------------------------------------------------------- -// Purpose: Add a Cascading menu item to the menu. -// Input : *itemText - Name of item text that will appear in the manu. -// This will also be the text of the command sent when the -// item is selected. -// *target - Target panel of the command -// *cascadeMenu - if the menu item opens a cascading menu, this is a -// ptr to the menu that opens on selecting the item -//----------------------------------------------------------------------------- -int Menu::AddCascadingMenuItem( const char *itemText, Panel *target, Menu *cascadeMenu, const KeyValues *userData ) -{ - return AddCascadingMenuItem(itemText, itemText, target, cascadeMenu, userData); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the values of a menu item at the specified index -// Input : index - the index of this item entry -// *message - pointer to the message to send when the item is selected -//----------------------------------------------------------------------------- -void Menu::UpdateMenuItem(int itemID, const char *itemText, KeyValues *message, const KeyValues *userData) -{ - Assert( m_MenuItems.IsValidIndex(itemID) ); - if ( m_MenuItems.IsValidIndex(itemID) ) - { - MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); - // make sure its enabled since disabled items get highlighted. - if (menuItem) - { - menuItem->SetText(itemText); - menuItem->SetCommand(message); - if(userData) - { - menuItem->SetUserData(userData); - } - } - } - _recalculateWidth = true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Sets the values of a menu item at the specified index -//----------------------------------------------------------------------------- -void Menu::UpdateMenuItem(int itemID, const wchar_t *wszItemText, KeyValues *message, const KeyValues *userData) -{ - Assert( m_MenuItems.IsValidIndex(itemID) ); - if ( m_MenuItems.IsValidIndex(itemID) ) - { - MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); - // make sure its enabled since disabled items get highlighted. - if (menuItem) - { - menuItem->SetText(wszItemText); - menuItem->SetCommand(message); - if(userData) - { - menuItem->SetUserData(userData); - } - } - } - _recalculateWidth = true; -} - - -//----------------------------------------------------------------------------- -// Sets the content alignment of all items in the menu -//----------------------------------------------------------------------------- -void Menu::SetContentAlignment( Label::Alignment alignment ) -{ - if ( m_Alignment != alignment ) - { - m_Alignment = alignment; - - // Change the alignment of existing menu items - int nCount = m_MenuItems.Count(); - for ( int i = 0; i < nCount; ++i ) - { - m_MenuItems[i]->SetContentAlignment( alignment ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Locks down a specific width -//----------------------------------------------------------------------------- -void Menu::SetFixedWidth(int width) -{ - // the padding makes it so the menu has the label padding on each side of the menu. - // makes the menu items look centered. - m_iFixedWidth = width; - InvalidateLayout(false); -} - -//----------------------------------------------------------------------------- -// Purpose: sets the height of each menu item -//----------------------------------------------------------------------------- -void Menu::SetMenuItemHeight(int itemHeight) -{ - m_iMenuItemHeight = itemHeight; -} - -int Menu::GetMenuItemHeight() const -{ - return m_iMenuItemHeight; -} - -int Menu::CountVisibleItems() -{ - int count = 0; - int c = m_SortedItems.Count(); - for ( int i = 0 ; i < c; ++i ) - { - if ( m_MenuItems[ m_SortedItems[ i ] ]->IsVisible() ) - ++count; - } - return count; -} - -void Menu::ComputeWorkspaceSize( int& workWide, int& workTall ) -{ - // make sure we factor in insets - int ileft, iright, itop, ibottom; - GetInset(ileft, iright, itop, ibottom); - - int workX, workY; - surface()->GetWorkspaceBounds(workX, workY, workWide, workTall); - workTall -= 20; - workTall -= itop; - workTall -= ibottom; -} - -// Assumes relative coords in screenspace -void Menu::PositionRelativeToPanel( Panel *relative, MenuDirection_e direction, int nAdditionalYOffset /*=0*/, bool showMenu /*=false*/ ) -{ - Assert( relative ); - int rx, ry, rw, rh; - relative->GetBounds( rx, ry, rw, rh ); - relative->LocalToScreen( rx, ry ); - - if ( direction == CURSOR ) - { - // force the menu to appear where the mouse button was pressed - input()->GetCursorPos(rx, ry); - rw = rh = 0; - } - else if ( direction == ALIGN_WITH_PARENT && relative->GetVParent() ) - { - rx = 0, ry = 0; - relative->ParentLocalToScreen(rx, ry); - rx -= 1; // take border into account - ry += rh + nAdditionalYOffset; - rw = rh = 0; - } - else - { - rx = 0, ry = 0; - relative->LocalToScreen(rx, ry); - } - - int workWide, workTall; - ComputeWorkspaceSize( workWide, workTall ); - - // Final pos - int x = 0, y = 0; - - int mWide, mTall; - GetSize( mWide, mTall ); - - switch( direction ) - { - case Menu::UP: // Menu prefers to open upward - { - x = rx; - int topOfReference = ry; - y = topOfReference - mTall; - if ( y < 0 ) - { - int bottomOfReference = ry + rh + 1; - int remainingPixels = workTall - bottomOfReference; - - // Can't fit on bottom, either, move to side - if ( mTall >= remainingPixels ) - { - y = workTall - mTall; - x = rx + rw; - // Try and place it to the left of the button - if ( x + mWide > workWide ) - { - x = rx - mWide; - } - } - else - { - // Room at bottom - y = bottomOfReference; - } - } - } - break; - // Everyone else aligns downward... - default: - case Menu::LEFT: - case Menu::RIGHT: - case Menu::DOWN: - { - x = rx; - int bottomOfReference = ry + rh + 1; - y = bottomOfReference; - if ( bottomOfReference + mTall >= workTall ) - { - // See if there's run straight above - if ( mTall >= ry ) // No room, try and push menu to right or left - { - y = workTall - mTall; - x = rx + rw; - // Try and place it to the left of the button - if ( x + mWide > workWide ) - { - x = rx - mWide; - } - } - else - { - // Room at top - y = ry - mTall; - } - } - } - break; - } - - // Check left rightness - if ( x + mWide > workWide ) - { - x = workWide - mWide; - Assert( x >= 0 ); // yikes!!! - } - else if ( x < 0 ) - { - x = 0; - } - - SetPos( x, y ); - if ( showMenu ) - { - SetVisible( true ); - } -} - -int Menu::ComputeFullMenuHeightWithInsets() -{ - // make sure we factor in insets - int ileft, iright, itop, ibottom; - GetInset(ileft, iright, itop, ibottom); - - int separatorHeight = 3; - - // add up the size of all the child panels - // move the child panels to the correct place in the menu - int totalTall = itop + ibottom; - int i; - for ( i = 0 ; i < m_SortedItems.Count() ; i++ ) // use sortedItems instead of MenuItems due to SetPos() - { - int itemId = m_SortedItems[i]; - - MenuItem *child = m_MenuItems[ itemId ]; - Assert( child ); - if ( !child ) - continue; - // These should all be visible at this point - if ( !child->IsVisible() ) - continue; - - totalTall += m_iMenuItemHeight; - - // Add a separator if needed... - int sepIndex = m_Separators.Find( itemId ); - if ( sepIndex != m_Separators.InvalidIndex() ) - { - totalTall += separatorHeight; - } - } - - return totalTall; -} - -//----------------------------------------------------------------------------- -// Purpose: Reformat according to the new layout -//----------------------------------------------------------------------------- -void Menu::PerformLayout() -{ - MenuItem *parent = GetParentMenuItem(); - bool cascading = parent != NULL ? true : false; - - // make sure we factor in insets - int ileft, iright, itop, ibottom; - GetInset(ileft, iright, itop, ibottom); - - int workWide, workTall; - - ComputeWorkspaceSize( workWide, workTall ); - - int fullHeightWouldRequire = ComputeFullMenuHeightWithInsets(); - - bool bNeedScrollbar = fullHeightWouldRequire >= workTall; - - int maxVisibleItems = CountVisibleItems(); - - if ( m_iNumVisibleLines > 0 && - maxVisibleItems > m_iNumVisibleLines ) - { - bNeedScrollbar = true; - maxVisibleItems = m_iNumVisibleLines; - } - - // if we have a scroll bar - if ( bNeedScrollbar ) - { - // add it to the display - AddScrollBar(); - - // This fills in m_VisibleSortedItems as needed - MakeItemsVisibleInScrollRange( m_iNumVisibleLines, min( fullHeightWouldRequire, workTall ) ); - } - else - { - RemoveScrollBar(); - // Make everything visible - m_VisibleSortedItems.RemoveAll(); - int i; - int c = m_SortedItems.Count(); - for ( i = 0; i < c; ++i ) - { - int itemID = m_SortedItems[ i ]; - MenuItem *child = m_MenuItems[ itemID ]; - if ( !child || !child->IsVisible() ) - continue; - - m_VisibleSortedItems.AddToTail( itemID ); - } - - // Hide the separators, the needed ones will be readded below - c = m_SeparatorPanels.Count(); - for ( i = 0; i < c; ++i ) - { - if ( m_SeparatorPanels[ i ] ) - { - m_SeparatorPanels[ i ]->SetVisible( false ); - } - } - } - - // get the appropriate menu border - LayoutMenuBorder(); - - int trueW = GetWide(); - if ( bNeedScrollbar ) - { - trueW -= m_pScroller->GetWide(); - } - int separatorHeight = MENU_SEPARATOR_HEIGHT; - - // add up the size of all the child panels - // move the child panels to the correct place in the menu - int menuTall = 0; - int totalTall = itop + ibottom; - int i; - for ( i = 0 ; i < m_VisibleSortedItems.Count() ; i++ ) // use sortedItems instead of MenuItems due to SetPos() - { - int itemId = m_VisibleSortedItems[i]; - - MenuItem *child = m_MenuItems[ itemId ]; - Assert( child ); - if ( !child ) - continue; - // These should all be visible at this point - if ( !child->IsVisible() ) - continue; - - if ( totalTall >= workTall ) - break; - - if ( INVALID_FONT != m_hItemFont ) - { - child->SetFont( m_hItemFont ); - } - - // take into account inset - child->SetPos (0, menuTall); - child->SetTall( m_iMenuItemHeight ); // Width is set in a second pass - menuTall += m_iMenuItemHeight; - totalTall += m_iMenuItemHeight; - - // this will make all the menuitems line up in a column with space for the checks to the left. - if ( ( !child->IsCheckable() ) && ( m_iCheckImageWidth > 0 ) ) - { - // Non checkable items have to move over - child->SetTextInset( m_iCheckImageWidth, 0 ); - } - else if ( child->IsCheckable() ) - { - child->SetTextInset(0, 0); //TODO: for some reason I can't comment this out. - } - - // Add a separator if needed... - int sepIndex = m_Separators.Find( itemId ); - if ( sepIndex != m_Separators.InvalidIndex() ) - { - MenuSeparator *sep = m_SeparatorPanels[ sepIndex ]; - Assert( sep ); - sep->SetVisible( true ); - sep->SetBounds( 0, menuTall, trueW, separatorHeight ); - menuTall += separatorHeight; - totalTall += separatorHeight; - } - } - - if (!m_iFixedWidth) - { - _recalculateWidth = true; - CalculateWidth(); - } - else if (m_iFixedWidth) - { - _menuWide = m_iFixedWidth; - // fixed width menus include the scroll bar in their width. - if (_sizedForScrollBar) - { - _menuWide -= m_pScroller->GetWide(); - } - } - - SizeMenuItems(); - - int extraWidth = 0; - if (_sizedForScrollBar) - { - extraWidth = m_pScroller->GetWide(); - } - - int mwide = _menuWide + extraWidth; - if ( mwide > workWide ) - { - mwide = workWide; - } - int mtall = menuTall + itop + ibottom; - if ( mtall > workTall ) - { - // Shouldn't happen - mtall = workTall; - } - - // set the new size of the menu - SetSize( mwide, mtall ); - - // move the menu to the correct position if it is a cascading menu. - if ( cascading ) - { - // move the menu to the correct position if it is a cascading menu. - PositionCascadingMenu(); - } - - // set up scroll bar as appropriate - if ( m_pScroller->IsVisible() ) - { - LayoutScrollBar(); - } - - FOR_EACH_LL( m_MenuItems, j ) - { - m_MenuItems[j]->InvalidateLayout(); // cause each menu item to redo its apply settings now we have sized ourselves - } - - Repaint(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Force the menu to work out how wide it should be -//----------------------------------------------------------------------------- -void Menu::ForceCalculateWidth() -{ - _recalculateWidth = true; - CalculateWidth(); - PerformLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Figure out how wide the menu should be if the menu is not fixed width -//----------------------------------------------------------------------------- -void Menu::CalculateWidth() -{ - if (!_recalculateWidth) - return; - - _menuWide = 0; - if (!m_iFixedWidth) - { - // find the biggest menu item - FOR_EACH_LL( m_MenuItems, i ) - { - int wide, tall; - m_MenuItems[i]->GetContentSize(wide, tall); - if (wide > _menuWide - Label::Content) - { - _menuWide = wide + Label::Content; - } - } - } - - // enfoce a minimumWidth - if (_menuWide < m_iMinimumWidth) - { - _menuWide = m_iMinimumWidth; - } - - _recalculateWidth = false; -} - -//----------------------------------------------------------------------------- -// Purpose: Set up the scroll bar attributes,size and location. -//----------------------------------------------------------------------------- -void Menu::LayoutScrollBar() -{ - //!! need to make it recalculate scroll positions - m_pScroller->SetEnabled(false); - m_pScroller->SetRangeWindow( m_VisibleSortedItems.Count() ); - m_pScroller->SetRange( 0, CountVisibleItems() ); - m_pScroller->SetButtonPressedScrollValue( 1 ); - - int wide, tall; - GetSize (wide, tall); - - // make sure we factor in insets - int ileft, iright, itop, ibottom; - GetInset(ileft, iright, itop, ibottom); - - // with a scroll bar we take off the inset - wide -= iright; - - m_pScroller->SetPos(wide - m_pScroller->GetWide(), 1); - - // scrollbar is inside the menu's borders. - m_pScroller->SetSize(m_pScroller->GetWide(), tall - ibottom - itop); - -} - -//----------------------------------------------------------------------------- -// Purpose: Figure out where to open menu if it is a cascading menu -//----------------------------------------------------------------------------- -void Menu::PositionCascadingMenu() -{ - Assert(GetVParent()); - int parentX, parentY, parentWide, parentTall; - // move the menu to the correct place below the menuItem - ipanel()->GetSize(GetVParent(), parentWide, parentTall); - ipanel()->GetPos(GetVParent(), parentX, parentY); - - parentX += parentWide, parentY = 0; - - ParentLocalToScreen(parentX, parentY); - - SetPos(parentX, parentY); - - // for cascading menus, - // make sure we're on the screen - int workX, workY, workWide, workTall, x, y, wide, tall; - GetBounds(x, y, wide, tall); - surface()->GetWorkspaceBounds(workX, workY, workWide, workTall); - - if (x + wide > workX + workWide) - { - // we're off the right, move the menu to the left side - // orignalX - width of the parentmenuitem - width of this menu. - // add 2 pixels to offset one pixel onto the parent menu. - x -= (parentWide + wide); - x -= 2; - } - else - { - // alignment move it in the amount of the insets. - x += 1; - } - - if ( y + tall > workY + workTall ) - { - int lastWorkY = workY + workTall; - int pixelsOffBottom = ( y + tall ) - lastWorkY; - - y -= pixelsOffBottom; - y -= 2; - } - else - { - y -= 1; - } - SetPos(x, y); - - MoveToFront(); -} - -//----------------------------------------------------------------------------- -// Purpose: Size the menu items so they are the width of the menu. -// Also size the menu items with cascading menus so the arrow fits in there. -//----------------------------------------------------------------------------- -void Menu::SizeMenuItems() -{ - int ileft, iright, itop, ibottom; - GetInset(ileft, iright, itop, ibottom); - - // assign the sizes of all the menu item panels - FOR_EACH_LL( m_MenuItems, i ) - { - MenuItem *child = m_MenuItems[i]; - if (child ) - { - // labels do thier own sizing. this will size the label to the width of the menu, - // this will put the cascading menu arrow on the right side automatically. - child->SetWide(_menuWide - ileft - iright); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Makes menu items visible in relation to where the scroll bar is -//----------------------------------------------------------------------------- -void Menu::MakeItemsVisibleInScrollRange( int maxVisibleItems, int nNumPixelsAvailable ) -{ - // Detach all items from tree - int i; - FOR_EACH_LL( m_MenuItems, item ) - { - m_MenuItems[ item ]->SetBounds( 0, 0, 0, 0 ); - } - for ( i = 0; i < m_SeparatorPanels.Count(); ++i ) - { - m_SeparatorPanels[ i ]->SetVisible( false ); - } - - m_VisibleSortedItems.RemoveAll(); - - int tall = 0; - - int startItem = m_pScroller->GetValue(); - Assert( startItem >= 0 ); - do - { - if ( startItem >= m_SortedItems.Count() ) - break; - - int itemId = m_SortedItems[ startItem ]; - - if ( !m_MenuItems[ itemId ]->IsVisible() ) - { - ++startItem; - continue; - } - - int itemHeight = m_iMenuItemHeight; - int sepIndex = m_Separators.Find( itemId ); - if ( sepIndex != m_Separators.InvalidIndex() ) - { - itemHeight += MENU_SEPARATOR_HEIGHT; - } - - if ( tall + itemHeight > nNumPixelsAvailable ) - break; - - // Too many items - if ( maxVisibleItems > 0 ) - { - if ( m_VisibleSortedItems.Count() >= maxVisibleItems ) - break; - } - - tall += itemHeight; - // Re-attach this one - m_VisibleSortedItems.AddToTail( itemId ); - ++startItem; - } - while ( true ); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the approproate menu border -//----------------------------------------------------------------------------- -void Menu::LayoutMenuBorder() -{ - IBorder *menuBorder; - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - - menuBorder = pScheme->GetBorder("MenuBorder"); - - if ( menuBorder ) - { - SetBorder(menuBorder); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Draw a black border on the right side of the menu items -//----------------------------------------------------------------------------- -void Menu::Paint() -{ - if ( m_pScroller->IsVisible() ) - { - // draw black bar - int wide, tall; - GetSize (wide, tall); - surface()->DrawSetColor(_borderDark); - if( IsProportional() ) - { - surface()->DrawFilledRect(wide - m_pScroller->GetWide(), -1, wide - m_pScroller->GetWide() + 1, tall); - } - else - { - surface()->DrawFilledRect(wide - m_pScroller->GetWide(), -1, wide - m_pScroller->GetWide() + 1, tall); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: sets the max number of items visible (scrollbar appears with more) -// Input : numItems - -//----------------------------------------------------------------------------- -void Menu::SetNumberOfVisibleItems( int numItems ) -{ - m_iNumVisibleLines = numItems; - InvalidateLayout(false); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Menu::EnableUseMenuManager( bool bUseMenuManager ) -{ - m_bUseMenuManager = bUseMenuManager; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -MenuItem *Menu::GetMenuItem(int itemID) -{ - if ( !m_MenuItems.IsValidIndex(itemID) ) - return NULL; - - return m_MenuItems[itemID]; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool Menu::IsValidMenuID(int itemID) -{ - return m_MenuItems.IsValidIndex(itemID); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int Menu::GetInvalidMenuID() -{ - return m_MenuItems.InvalidIndex(); -} - -//----------------------------------------------------------------------------- -// Purpose: When a menuItem is selected, close cascading menus -// if the menuItem selected has a cascading menu attached, we -// want to keep that one open so skip it. -// Passing NULL will close all cascading menus. -//----------------------------------------------------------------------------- -void Menu::CloseOtherMenus(MenuItem *item) -{ - FOR_EACH_LL( m_MenuItems, i ) - { - if (m_MenuItems[i] == item) - continue; - - m_MenuItems[i]->CloseCascadeMenu(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Respond to string commands. -//----------------------------------------------------------------------------- -void Menu::OnCommand( const char *command ) -{ - // forward on the message - PostActionSignal(new KeyValues("Command", "command", command)); - - Panel::OnCommand(command); -} - -//----------------------------------------------------------------------------- -// Purpose: Handle key presses, Activate shortcuts -//----------------------------------------------------------------------------- -void Menu::OnKeyCodeTyped(KeyCode keycode) -{ - vgui::KeyCode code = GetBaseButtonCode( keycode ); - - // Don't allow key inputs when disabled! - if ( !IsEnabled() ) - return; - - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - if (alt) - { - BaseClass::OnKeyCodeTyped( keycode ); - // Ignore alt when in combobox mode - if (m_eTypeAheadMode != TYPE_AHEAD_MODE) - { - PostActionSignal(new KeyValues("MenuClose")); - } - } - - switch (code) - { - case KEY_ESCAPE: - case KEY_XBUTTON_B: - { - // hide the menu on ESC - SetVisible(false); - break; - } - // arrow keys scroll through items on the list. - // they should also scroll the scroll bar if needed - case KEY_UP: - case KEY_XBUTTON_UP: - case KEY_XSTICK1_UP: - { - MoveAlongMenuItemList(MENU_UP, 0); - if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) ) - { - m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); - } - else - { - BaseClass::OnKeyCodeTyped( keycode ); // chain up - } - break; - } - case KEY_DOWN: - case KEY_XBUTTON_DOWN: - case KEY_XSTICK1_DOWN: - { - MoveAlongMenuItemList(MENU_DOWN, 0); - if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) ) - { - m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); - } - else - { - BaseClass::OnKeyCodeTyped( keycode ); // chain up - } - break; - } - // for now left and right arrows just open or close submenus if they are there. - case KEY_RIGHT: - case KEY_XBUTTON_RIGHT: - case KEY_XSTICK1_RIGHT: - { - // make sure a menuItem is currently selected - if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) ) - { - if (m_MenuItems[m_iCurrentlySelectedItemID]->HasMenu()) - { - ActivateItem(m_iCurrentlySelectedItemID); - } - else - { - BaseClass::OnKeyCodeTyped( keycode ); - } - } - else - { - BaseClass::OnKeyCodeTyped( keycode ); - } - break; - } - case KEY_LEFT: - case KEY_XBUTTON_LEFT: - case KEY_XSTICK1_LEFT: - { - // if our parent is a menu item then we are a submenu so close us. - if (GetParentMenuItem()) - { - SetVisible(false); - } - else - { - BaseClass::OnKeyCodeTyped( keycode ); - } - break; - } - case KEY_ENTER: - case KEY_XBUTTON_A: - { - // make sure a menuItem is currently selected - if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) ) - { - ActivateItem(m_iCurrentlySelectedItemID); - } - else - { - BaseClass::OnKeyCodeTyped( keycode ); // chain up - } - break; - } - - case KEY_PAGEUP: - { - if ( m_iNumVisibleLines > 1 ) - { - if ( m_iCurrentlySelectedItemID < m_iNumVisibleLines ) - { - MoveAlongMenuItemList( MENU_UP * m_iCurrentlySelectedItemID, 0 ); - } - else - { - MoveAlongMenuItemList(MENU_UP * m_iNumVisibleLines - 1, 0); - } - } - else - { - MoveAlongMenuItemList(MENU_UP, 0); - } - - if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) ) - { - m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); - } - break; - } - - - case KEY_PAGEDOWN: - { - if ( m_iNumVisibleLines > 1 ) - { - if ( m_iCurrentlySelectedItemID + m_iNumVisibleLines >= GetItemCount() ) - { - MoveAlongMenuItemList(MENU_DOWN * ( GetItemCount() - m_iCurrentlySelectedItemID - 1), 0); - } - else - { - MoveAlongMenuItemList(MENU_DOWN * m_iNumVisibleLines - 1, 0); - } - } - else - { - MoveAlongMenuItemList(MENU_DOWN, 0); - } - - if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) ) - { - m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); - } - break; - } - - case KEY_HOME: - { - MoveAlongMenuItemList( MENU_UP * m_iCurrentlySelectedItemID, 0 ); - if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) ) - { - m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); - } - break; - } - - - case KEY_END: - { - MoveAlongMenuItemList(MENU_DOWN * ( GetItemCount() - m_iCurrentlySelectedItemID - 1), 0); - if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) ) - { - m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); - } - break; - } - } - - // don't chain back -} - -void Menu::OnHotKey(wchar_t unichar) -{ - // iterate the menu items looking for one with the matching hotkey - FOR_EACH_LL( m_MenuItems, i ) - { - MenuItem *panel = m_MenuItems[i]; - if (panel->IsVisible()) - { - Panel *hot = panel->HasHotkey(unichar); - if (hot) - { - // post a message to the menuitem telling it it's hotkey was pressed - PostMessage(hot, new KeyValues("Hotkey")); - return; - } - // if the menuitem is a cascading menuitem and it is open, check its hotkeys too - Menu *cascadingMenu = panel->GetMenu(); - if (cascadingMenu && cascadingMenu->IsVisible()) - { - cascadingMenu->OnKeyTyped(unichar); - } - } - } -} - -void Menu::OnTypeAhead(wchar_t unichar) -{ - // Don't do anything if the menu is empty since there cannot be a selected item. - if ( m_MenuItems.Count() <= 0) - return; - - // expire the type ahead buffer after 0.5 seconds - double tCurrentTime = Sys_FloatTime(); - if ( (tCurrentTime - m_fLastTypeAheadTime) > 0.5f ) - { - m_iNumTypeAheadChars = 0; - m_szTypeAheadBuf[0] = '\0'; - } - m_fLastTypeAheadTime = tCurrentTime; - - // add current character to the type ahead buffer - if ( m_iNumTypeAheadChars+1 < TYPEAHEAD_BUFSIZE ) - { - m_szTypeAheadBuf[m_iNumTypeAheadChars++] = unichar; - } - - int itemToSelect = m_iCurrentlySelectedItemID; - if ( itemToSelect < 0 || itemToSelect >= m_MenuItems.Count()) - { - itemToSelect = 0; - } - - int i = itemToSelect; - do - { - wchar_t menuItemName[255]; - m_MenuItems[i]->GetText(menuItemName, 254); - - // This is supposed to be case insensitive but we don't have a portable case - // insensitive wide-character routine. - if ( wcsncmp( m_szTypeAheadBuf, menuItemName, m_iNumTypeAheadChars) == 0 ) - { - itemToSelect = i; - break; - } - - i = (i+1) % m_MenuItems.Count(); - } while ( i != itemToSelect ); - - if ( itemToSelect >= 0 ) - { - SetCurrentlyHighlightedItem( itemToSelect ); - InvalidateLayout(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handle key presses, Activate shortcuts -// Input : code - -//----------------------------------------------------------------------------- -void Menu::OnKeyTyped(wchar_t unichar) -{ - if (! unichar) - { - return; - } - - switch( m_eTypeAheadMode ) - { - case HOT_KEY_MODE: - OnHotKey(unichar); - return; - - case TYPE_AHEAD_MODE: - OnTypeAhead(unichar); - return; - - case COMPAT_MODE: - default: - break; - } - - int itemToSelect = m_iCurrentlySelectedItemID; - if ( itemToSelect < 0 ) - { - itemToSelect = 0; - } - - int i; - wchar_t menuItemName[255]; - - i = itemToSelect + 1; - if ( i >= m_MenuItems.Count() ) - { - i = 0; - } - - while ( i != itemToSelect ) - { - m_MenuItems[i]->GetText(menuItemName, 254); - - if ( tolower( unichar ) == tolower( menuItemName[0] ) ) - { - itemToSelect = i; - break; - } - - i++; - if ( i >= m_MenuItems.Count() ) - { - i = 0; - } - } - - if ( itemToSelect >= 0 ) - { - SetCurrentlyHighlightedItem( itemToSelect ); - InvalidateLayout(); - } - - // don't chain back -} - - -void Menu::SetTypeAheadMode(MenuTypeAheadMode mode) -{ - m_eTypeAheadMode = mode; -} - -int Menu::GetTypeAheadMode() -{ - return m_eTypeAheadMode; -} - -//----------------------------------------------------------------------------- -// Purpose: Handle the mouse wheel event, scroll the selection -//----------------------------------------------------------------------------- -void Menu::OnMouseWheeled(int delta) -{ - if (!m_pScroller->IsVisible()) - return; - - int val = m_pScroller->GetValue(); - val -= delta; - - m_pScroller->SetValue(val); - - // moving the slider redraws the scrollbar, - // and so we should redraw the menu since the - // menu draws the black border to the right of the scrollbar. - InvalidateLayout(); - - // don't chain back -} - - -//----------------------------------------------------------------------------- -// Purpose: Lose focus, hide menu -//----------------------------------------------------------------------------- -void Menu::OnKillFocus() -{ - // check to see if it's a child taking it - if (!input()->GetFocus() || !ipanel()->HasParent(input()->GetFocus(), GetVPanel())) - { - // if we don't accept keyboard input, then we have to ignore the killfocus if it's not actually being stolen - if (!IsKeyBoardInputEnabled() && !input()->GetFocus()) - return; - - // get the parent of this menu. - MenuItem *item = GetParentMenuItem(); - // if the parent is a menu item, this menu is a cascading menu - // if the panel that is getting focus is the parent menu, don't close this menu. - if ( (item) && (input()->GetFocus() == item->GetVParent()) ) - { - // if we are in mouse mode and we clicked on the menuitem that - // triggers the cascading menu, leave it open. - if (m_iInputMode == MOUSE) - { - // return the focus to the cascading menu. - MoveToFront(); - return; - } - } - - // forward the message to the parent. - PostActionSignal(new KeyValues("MenuClose")); - - // hide this menu - SetVisible(false); - } - -} - -namespace vgui -{ - -class CMenuManager -{ -public: - void AddMenu( Menu *m ) - { - if ( !m ) - return; - - int c = m_Menus.Count(); - for ( int i = 0 ; i < c; ++i ) - { - if ( m_Menus[ i ].Get() == m ) - return; - } - - DHANDLE< Menu > h; - h = m; - m_Menus.AddToTail( h ); - } - - void RemoveMenu( Menu *m ) - { - if ( !m ) - return; - - int c = m_Menus.Count(); - for ( int i = c - 1 ; i >= 0; --i ) - { - if ( m_Menus[ i ].Get() == m ) - { - m_Menus.Remove( i ); - return; - } - } - } - - void OnInternalMousePressed( Panel *other, MouseCode code ) - { - int c = m_Menus.Count(); - if ( !c ) - return; - - int x, y; - input()->GetCursorPos( x, y ); - - bool mouseInsideMenuRelatedPanel = false; - - for ( int i = c - 1; i >= 0 ; --i ) - { - Menu *m = m_Menus[ i ].Get(); - if ( !m ) - { - m_Menus.Remove( i ); - continue; - } - - // See if the mouse is within a menu - if ( IsWithinMenuOrRelative( m, x, y ) ) - { - mouseInsideMenuRelatedPanel = true; - } - } - - if ( mouseInsideMenuRelatedPanel ) - { - return; - } - - AbortMenus(); - } - - void AbortMenus() - { - // Close all of the menus - int c = m_Menus.Count(); - for ( int i = c - 1; i >= 0 ; --i ) - { - Menu *m = m_Menus[ i ].Get(); - if ( !m ) - { - continue; - } - - m_Menus.Remove( i ); - - // Force it to close - m->SetVisible( false ); - } - - m_Menus.RemoveAll(); - } - - bool IsWithinMenuOrRelative( Panel *panel, int x, int y ) - { - VPANEL topMost = panel->IsWithinTraverse( x, y, true ); - if ( topMost ) - { - // It's over the menu - if ( topMost == panel->GetVPanel() ) - { - return true; - } - - // It's over something which is parented to the menu (i.e., a menu item) - if ( ipanel()->HasParent( topMost, panel->GetVPanel() ) ) - { - return true; - } - } - - if ( panel->GetParent() ) - { - Panel *parent = panel->GetParent(); - - topMost = parent->IsWithinTraverse( x, y, true ); - - if ( topMost ) - { - if ( topMost == parent->GetVPanel() ) - { - return true; - } - - /* - // NOTE: this check used to not cast to MenuButton, but it seems wrong to me - // since if the mouse is over another child of the parent panel to the menu then - // the menu stays visible. I think this is bogus. - Panel *pTopMost = ipanel()->GetPanel(topMost, GetControlsModuleName()); - - if ( pTopMost && - ipanel()->HasParent( topMost, parent->GetVPanel() ) && - dynamic_cast< MenuButton * >( pTopMost ) ) - { - Msg( "topMost %s has parent %s\n", - ipanel()->GetName( topMost ), - parent->GetName() ); - - return true; - } - */ - } - } - - return false; - } - -#ifdef DBGFLAG_VALIDATE - void Validate( CValidator &validator, char *pchName ) - { - validator.Push( "CMenuManager", this, pchName ); - m_Menus.Validate( validator, "m_Menus" ); - validator.Pop(); - } -#endif - -private: - - // List of visible menus - CUtlVector< DHANDLE< Menu > > m_Menus; -}; - - -// Singleton helper class -static CMenuManager g_MenuMgr; - -void ValidateMenuGlobals( CValidator &validator ) -{ -#ifdef DBGFLAG_VALIDATE - g_MenuMgr.Validate( validator, "g_MenuMgr" ); -#endif -} - -} // end namespace vgui - -//----------------------------------------------------------------------------- -// Purpose: Static method called on mouse released to see if Menu objects should be aborted -// Input : *other - -// code - -//----------------------------------------------------------------------------- -void Menu::OnInternalMousePressed( Panel *other, MouseCode code ) -{ - g_MenuMgr.OnInternalMousePressed( other, code ); -} - -//----------------------------------------------------------------------------- -// Purpose: Set visibility of menu and its children as appropriate. -//----------------------------------------------------------------------------- -void Menu::SetVisible(bool state) -{ - if (state == IsVisible()) - return; - - if ( state == false ) - { - PostActionSignal(new KeyValues("MenuClose")); - CloseOtherMenus(NULL); - - SetCurrentlySelectedItem(-1); - - g_MenuMgr.RemoveMenu( this ); - } - else if ( state == true ) - { - MoveToFront(); - RequestFocus(); - - // Add to menu manager? - if ( m_bUseMenuManager ) - { - g_MenuMgr.AddMenu( this ); - } - } - - // must be after movetofront() - BaseClass::SetVisible(state); - _sizedForScrollBar = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Menu::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetFgColor(GetSchemeColor("Menu.TextColor", pScheme)); - SetBgColor(GetSchemeColor("Menu.BgColor", pScheme)); - - _borderDark = pScheme->GetColor("BorderDark", Color(255, 255, 255, 0)); - - FOR_EACH_LL( m_MenuItems, i ) - { - if( m_MenuItems[i]->IsCheckable() ) - { - int wide, tall; - m_MenuItems[i]->GetCheckImageSize( wide, tall ); - - m_iCheckImageWidth = max ( m_iCheckImageWidth, wide ); - } - } - _recalculateWidth = true; - CalculateWidth(); - - InvalidateLayout(); -} - -void Menu::SetBgColor( Color newColor ) -{ - BaseClass::SetBgColor( newColor ); - FOR_EACH_LL( m_MenuItems, i ) - { - if( m_MenuItems[i]->HasMenu() ) - { - m_MenuItems[i]->GetMenu()->SetBgColor( newColor ); - } - } -} - -void Menu::SetFgColor( Color newColor ) -{ - BaseClass::SetFgColor( newColor ); - FOR_EACH_LL( m_MenuItems, i ) - { - if( m_MenuItems[i]->HasMenu() ) - { - m_MenuItems[i]->GetMenu()->SetFgColor( newColor ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Menu::SetBorder(class IBorder *border) -{ - Panel::SetBorder(border); -} - -//----------------------------------------------------------------------------- -// Purpose: returns a pointer to a MenuItem that is this menus parent, if it has one -//----------------------------------------------------------------------------- -MenuItem *Menu::GetParentMenuItem() -{ - return dynamic_cast(GetParent()); -} - -//----------------------------------------------------------------------------- -// Purpose: Hide the menu when an item has been selected -//----------------------------------------------------------------------------- -void Menu::OnMenuItemSelected(Panel *panel) -{ - SetVisible(false); - m_pScroller->SetVisible(false); - - // chain this message up through the hierarchy so - // all the parent menus will close - - // get the parent of this menu. - MenuItem *item = GetParentMenuItem(); - // if the parent is a menu item, this menu is a cascading menu - if (item) - { - // get the parent of the menuitem. it should be a menu. - Menu *parentMenu = item->GetParentMenu(); - if (parentMenu) - { - // send the message to this parent menu - KeyValues *kv = new KeyValues("MenuItemSelected"); - kv->SetPtr("panel", panel); - ivgui()->PostMessage(parentMenu->GetVPanel(), kv, GetVPanel()); - } - } - - bool activeItemSet = false; - - FOR_EACH_LL( m_MenuItems, i ) - { - if( m_MenuItems[i] == panel ) - { - activeItemSet = true; - m_iActivatedItem = i; - break; - } - } - if( !activeItemSet ) - { - FOR_EACH_LL( m_MenuItems, i ) - { - if(m_MenuItems[i]->HasMenu() ) - { - /* - // GetActiveItem needs to return -1 or similar if it hasn't been set... - if( m_MenuItems[i]->GetActiveItem() ) - { - m_iActivatedItem = m_MenuItems[i]->GetActiveItem(); - }*/ - } - } - } - - // also pass it to the parent so they can respond if they like - if (GetVParent()) - { - KeyValues *kv = new KeyValues("MenuItemSelected"); - kv->SetPtr("panel", panel); - - ivgui()->PostMessage(GetVParent(), kv, GetVPanel()); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int Menu::GetActiveItem() -{ - return m_iActivatedItem; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -KeyValues *Menu::GetItemUserData(int itemID) -{ - if ( m_MenuItems.IsValidIndex( itemID ) ) - { - MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); - // make sure its enabled since disabled items get highlighted. - if (menuItem && menuItem->IsEnabled()) - { - return menuItem->GetUserData(); - } - } - return NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void Menu::GetItemText(int itemID, wchar_t *text, int bufLenInBytes) -{ - if ( m_MenuItems.IsValidIndex( itemID ) ) - { - MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); - if (menuItem) - { - menuItem->GetText(text, bufLenInBytes); - return; - } - } - text[0] = 0; -} - -void Menu::GetItemText(int itemID, char *text, int bufLenInBytes) -{ - if ( m_MenuItems.IsValidIndex( itemID ) ) - { - MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); - if (menuItem) - { - menuItem->GetText( text, bufLenInBytes ); - return; - } - } - text[0] = 0; -} - - - -//----------------------------------------------------------------------------- -// Purpose: Activate the n'th item in the menu list, as if that menu item had been selected by the user -//----------------------------------------------------------------------------- -void Menu::ActivateItem(int itemID) -{ - if ( m_MenuItems.IsValidIndex( itemID ) ) - { - MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); - // make sure its enabled since disabled items get highlighted. - if (menuItem && menuItem->IsEnabled()) - { - menuItem->FireActionSignal(); - m_iActivatedItem = itemID; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Menu::SilentActivateItem(int itemID) -{ - if ( m_MenuItems.IsValidIndex( itemID ) ) - { - MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); - // make sure its enabled since disabled items get highlighted. - if (menuItem && menuItem->IsEnabled()) - { - m_iActivatedItem = itemID; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Menu::ActivateItemByRow(int row) -{ - if (m_SortedItems.IsValidIndex(row)) - { - ActivateItem(m_SortedItems[row]); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Return the number of items currently in the menu list -//----------------------------------------------------------------------------- -int Menu::GetItemCount() -{ - return m_MenuItems.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int Menu::GetMenuID(int index) -{ - if ( !m_SortedItems.IsValidIndex(index) ) - return m_MenuItems.InvalidIndex(); - - return m_SortedItems[index]; -} - -//----------------------------------------------------------------------------- -// Purpose: Return the number of items currently visible in the menu list -//----------------------------------------------------------------------------- -int Menu::GetCurrentlyVisibleItemsCount() -{ - if (m_MenuItems.Count() < m_iNumVisibleLines) - { - int cMenuItems = 0; - FOR_EACH_LL(m_MenuItems, i) - { - if (m_MenuItems[i]->IsVisible()) - { - ++cMenuItems; - } - } - - return cMenuItems; - } - return m_iNumVisibleLines; -} - -//----------------------------------------------------------------------------- -// Purpose: Enables/disables choices in the list -// itemText - string name of item in the list -// state - true enables, false disables -//----------------------------------------------------------------------------- -void Menu::SetItemEnabled(const char *itemName, bool state) -{ - FOR_EACH_LL( m_MenuItems, i ) - { - if ((Q_stricmp(itemName, m_MenuItems[i]->GetName())) == 0) - { - m_MenuItems[i]->SetEnabled(state); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Enables/disables choices in the list -//----------------------------------------------------------------------------- -void Menu::SetItemEnabled(int itemID, bool state) -{ - if ( !m_MenuItems.IsValidIndex(itemID) ) - return; - - m_MenuItems[itemID]->SetEnabled(state); -} - -//----------------------------------------------------------------------------- -// Purpose: shows/hides choices in the list -//----------------------------------------------------------------------------- -void Menu::SetItemVisible(const char *itemName, bool state) -{ - FOR_EACH_LL( m_MenuItems, i ) - { - if ((Q_stricmp(itemName, m_MenuItems[i]->GetName())) == 0) - { - m_MenuItems[i]->SetVisible(state); - InvalidateLayout(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: shows/hides choices in the list -//----------------------------------------------------------------------------- -void Menu::SetItemVisible(int itemID, bool state) -{ - if ( !m_MenuItems.IsValidIndex(itemID) ) - return; - - m_MenuItems[itemID]->SetVisible(state); -} - -//----------------------------------------------------------------------------- -// Purpose: Make the scroll bar visible and narrow the menu -// also make items visible or invisible in the list as appropriate -//----------------------------------------------------------------------------- -void Menu::AddScrollBar() -{ - m_pScroller->SetVisible(true); - _sizedForScrollBar = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Make the scroll bar invisible and widen the menu -//----------------------------------------------------------------------------- -void Menu::RemoveScrollBar() -{ - m_pScroller->SetVisible(false); - _sizedForScrollBar = false; -} - -//----------------------------------------------------------------------------- -// Purpose: Invalidate layout if the slider is moved so items scroll -//----------------------------------------------------------------------------- -void Menu::OnSliderMoved() -{ - CloseOtherMenus(NULL); // close any cascading menus - - // Invalidate so we redraw the menu! - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Toggle into mouse mode. -//----------------------------------------------------------------------------- -void Menu::OnCursorMoved(int x, int y) -{ - m_iInputMode = MOUSE; - - // chain up - CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); - RequestFocus(); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Toggle into keyboard mode. -//----------------------------------------------------------------------------- -void Menu::OnKeyCodePressed(KeyCode code) -{ - m_iInputMode = KEYBOARD; - // send the message to this parent in case this is a cascading menu - if (GetVParent()) - { - ivgui()->PostMessage(GetVParent(), new KeyValues("KeyModeSet"), GetVPanel()); - } - - BaseClass::OnKeyCodePressed( code ); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the item currently highlighted in the menu by ptr -//----------------------------------------------------------------------------- -void Menu::SetCurrentlySelectedItem(MenuItem *item) -{ - int itemNum = -1; - // find it in our list of menuitems - FOR_EACH_LL( m_MenuItems, i ) - { - MenuItem *child = m_MenuItems[i]; - if (child == item) - { - itemNum = i; - break; - } - } - Assert( itemNum >= 0 ); - - SetCurrentlySelectedItem(itemNum); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Menu::ClearCurrentlyHighlightedItem() -{ - if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) ) - { - m_MenuItems[m_iCurrentlySelectedItemID]->DisarmItem(); - } - m_iCurrentlySelectedItemID = m_MenuItems.InvalidIndex(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the item currently highlighted in the menu by index -//----------------------------------------------------------------------------- -void Menu::SetCurrentlySelectedItem(int itemID) -{ - // dont deselect if its the same item - if (itemID == m_iCurrentlySelectedItemID) - return; - - if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) ) - { - m_MenuItems[m_iCurrentlySelectedItemID]->DisarmItem(); - } - - PostActionSignal(new KeyValues("MenuItemHighlight", "itemID", itemID)); - m_iCurrentlySelectedItemID = itemID; -} - -//----------------------------------------------------------------------------- -// This will set the item to be currenly selected and highlight it -// will not open cascading menu. This was added for comboboxes -// to have the combobox item highlighted in the menu when they open the -// dropdown. -//----------------------------------------------------------------------------- -void Menu::SetCurrentlyHighlightedItem(int itemID) -{ - SetCurrentlySelectedItem(itemID); - int row = m_SortedItems.Find(itemID); - // If we have no items, then row will be -1. The dev console, for example... - Assert( ( m_SortedItems.Count() == 0 ) || ( row != -1 ) ); - if ( row == -1 ) - return; - - // if there is a scroll bar, and we scroll off lets move it. - if ( m_pScroller->IsVisible() ) - { - // now if we are off the scroll bar, it means we moved the scroll bar - // by hand or set the item off the list - // so just snap the scroll bar straight to the item. - if ( ( row > m_pScroller->GetValue() + m_iNumVisibleLines - 1 ) || - ( row < m_pScroller->GetValue() ) ) - { - if ( !m_pScroller->IsVisible() ) - return; - - m_pScroller->SetValue(row); - } - } - - if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) ) - { - if ( !m_MenuItems[m_iCurrentlySelectedItemID]->IsArmed() ) - { - m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int Menu::GetCurrentlyHighlightedItem() -{ - return m_iCurrentlySelectedItemID; -} - -//----------------------------------------------------------------------------- -// Purpose: Respond to cursor entering a menuItem. -//----------------------------------------------------------------------------- -void Menu::OnCursorEnteredMenuItem(int VPanel) -{ - VPANEL menuItem = (VPANEL)VPanel; - // if we are in mouse mode - if (m_iInputMode == MOUSE) - { - MenuItem *item = static_cast(ipanel()->GetPanel(menuItem, GetModuleName())); - // arm the menu - item->ArmItem(); - // open the cascading menu if there is one. - item->OpenCascadeMenu(); - SetCurrentlySelectedItem(item); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Respond to cursor exiting a menuItem -//----------------------------------------------------------------------------- -void Menu::OnCursorExitedMenuItem(int VPanel) -{ - VPANEL menuItem = (VPANEL)VPanel; - // only care if we are in mouse mode - if (m_iInputMode == MOUSE) - { - MenuItem *item = static_cast(ipanel()->GetPanel(menuItem, GetModuleName())); - // unhighlight the item. - // note menuItems with cascading menus will stay lit. - item->DisarmItem(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Move up or down one in the list of items in the menu -// Direction is MENU_UP or MENU_DOWN -//----------------------------------------------------------------------------- -void Menu::MoveAlongMenuItemList(int direction, int loopCount) -{ - // Early out if no menu items to scroll through - if (m_MenuItems.Count() <= 0) - return; - - int itemID = m_iCurrentlySelectedItemID; - int row = m_SortedItems.Find(itemID); - row += direction; - - if ( row > m_SortedItems.Count() - 1 ) - { - if ( m_pScroller->IsVisible() ) - { - // stop at bottom of scrolled list - row = m_SortedItems.Count() - 1; - } - else - { - // if no scroll bar we circle around - row = 0; - } - } - else if (row < 0) - { - if ( m_pScroller->IsVisible() ) - { - // stop at top of scrolled list - row = m_pScroller->GetValue(); - } - else - { - // if no scroll bar circle around - row = m_SortedItems.Count()-1; - } - } - - // if there is a scroll bar, and we scroll off lets move it. - if ( m_pScroller->IsVisible() ) - { - if ( row > m_pScroller->GetValue() + m_iNumVisibleLines - 1) - { - int val = m_pScroller->GetValue(); - val -= -direction; - - m_pScroller->SetValue(val); - - // moving the slider redraws the scrollbar, - // and so we should redraw the menu since the - // menu draws the black border to the right of the scrollbar. - InvalidateLayout(); - } - else if ( row < m_pScroller->GetValue() ) - { - int val = m_pScroller->GetValue(); - val -= -direction; - - m_pScroller->SetValue(val); - - // moving the slider redraws the scrollbar, - // and so we should redraw the menu since the - // menu draws the black border to the right of the scrollbar. - InvalidateLayout(); - } - - // now if we are still off the scroll bar, it means we moved the scroll bar - // by hand and created a situation in which we moved an item down, but the - // scroll bar is already too far down and should scroll up or vice versa - // so just snap the scroll bar straight to the item. - if ( ( row > m_pScroller->GetValue() + m_iNumVisibleLines - 1) || - ( row < m_pScroller->GetValue() ) ) - { - m_pScroller->SetValue(row); - } - } - - // switch it back to an itemID from row - if ( m_SortedItems.IsValidIndex( row ) ) - { - SetCurrentlySelectedItem( m_SortedItems[row] ); - } - - // don't allow us to loop around more than once - if (loopCount < m_MenuItems.Count()) - { - // see if the text is empty, if so skip - wchar_t text[256]; - m_MenuItems[m_iCurrentlySelectedItemID]->GetText(text, 255); - if (text[0] == 0 || !m_MenuItems[m_iCurrentlySelectedItemID]->IsVisible()) - { - // menu item is empty, keep moving along - MoveAlongMenuItemList(direction, loopCount + 1); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Return which type of events the menu is currently interested in -// MenuItems need to know because behaviour is different depending on mode. -//----------------------------------------------------------------------------- -int Menu::GetMenuMode() -{ - return m_iInputMode; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the menu to key mode if a child menu goes into keymode -// This mode change has to be chained up through the menu heirarchy -// so cascading menus will work when you do a bunch of stuff in keymode -// in high level menus and then switch to keymode in lower level menus. -//----------------------------------------------------------------------------- -void Menu::OnKeyModeSet() -{ - m_iInputMode = KEYBOARD; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the checked state of a menuItem -//----------------------------------------------------------------------------- -void Menu::SetMenuItemChecked(int itemID, bool state) -{ - m_MenuItems[itemID]->SetChecked(state); -} - -//----------------------------------------------------------------------------- -// Purpose: Check if item is checked. -//----------------------------------------------------------------------------- -bool Menu::IsChecked(int itemID) -{ - return m_MenuItems[itemID]->IsChecked(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the minmum width the menu has to be. This -// is useful if you have a menu that is sized to the largest item in it -// but you don't want the menu to be thinner than the menu button -//----------------------------------------------------------------------------- -void Menu::SetMinimumWidth(int width) -{ - m_iMinimumWidth = width; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the minmum width the menu -//----------------------------------------------------------------------------- -int Menu::GetMinimumWidth() -{ - return m_iMinimumWidth; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -void Menu::AddSeparator() -{ - int lastID = m_MenuItems.Count() - 1; - m_Separators.AddToTail( lastID ); - m_SeparatorPanels.AddToTail( new MenuSeparator( this, "MenuSeparator" ) ); -} - -void Menu::AddSeparatorAfterItem( int itemID ) -{ - Assert( m_MenuItems.IsValidIndex( itemID ) ); - m_Separators.AddToTail( itemID ); - m_SeparatorPanels.AddToTail( new MenuSeparator( this, "MenuSeparator" ) ); -} - -void Menu::MoveMenuItem( int itemID, int moveBeforeThisItemID ) -{ - int c = m_SortedItems.Count(); - int i; - for ( i = 0; i < c; ++i ) - { - if ( m_SortedItems[i] == itemID ) - { - m_SortedItems.Remove( i ); - break; - } - } - - // Didn't find it - if ( i >= c ) - { - return; - } - - // Now find insert pos - c = m_SortedItems.Count(); - for ( i = 0; i < c; ++i ) - { - if ( m_SortedItems[i] == moveBeforeThisItemID ) - { - m_SortedItems.InsertBefore( i, itemID ); - break; - } - } -} - -void Menu::SetFont( HFont font ) -{ - m_hItemFont = font; - if ( font ) - { - m_iMenuItemHeight = surface()->GetFontTall( font ) + 2; - } - InvalidateLayout(); -} - - -void Menu::SetCurrentKeyBinding( int itemID, char const *hotkey ) -{ - if ( m_MenuItems.IsValidIndex( itemID ) ) - { - MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); - menuItem->SetCurrentKeyBinding( hotkey ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Static method to display a context menu -// Input : *parent - -// *menu - -//----------------------------------------------------------------------------- -void Menu::PlaceContextMenu( Panel *parent, Menu *menu ) -{ - Assert( parent ); - Assert( menu ); - if ( !menu || !parent ) - return; - - menu->SetVisible(false); - menu->SetParent( parent ); - menu->AddActionSignalTarget( parent ); - - // get cursor position, this is local to this text edit window - int cursorX, cursorY; - input()->GetCursorPos(cursorX, cursorY); - - menu->SetVisible(true); - - // relayout the menu immediately so that we know it's size - menu->InvalidateLayout(true); - int menuWide, menuTall; - menu->GetSize(menuWide, menuTall); - - // work out where the cursor is and therefore the best place to put the menu - int wide, tall; - surface()->GetScreenSize(wide, tall); - - if (wide - menuWide > cursorX) - { - // menu hanging right - if (tall - menuTall > cursorY) - { - // menu hanging down - menu->SetPos(cursorX, cursorY); - } - else - { - // menu hanging up - menu->SetPos(cursorX, cursorY - menuTall); - } - } - else - { - // menu hanging left - if (tall - menuTall > cursorY) - { - // menu hanging down - menu->SetPos(cursorX - menuWide, cursorY); - } - else - { - // menu hanging up - menu->SetPos(cursorX - menuWide, cursorY - menuTall); - } - } - - menu->RequestFocus(); -} - -void Menu::SetUseFallbackFont( bool bState, HFont hFallback ) -{ - m_hFallbackItemFont = hFallback; - m_bUseFallbackFont = bState; -} - -#ifdef DBGFLAG_VALIDATE -//----------------------------------------------------------------------------- -// Purpose: Run a global validation pass on all of our data structures and memory -// allocations. -// Input: validator - Our global validator object -// pchName - Our name (typically a member var in our container) -//----------------------------------------------------------------------------- -void Menu::Validate( CValidator &validator, char *pchName ) -{ - validator.Push( "vgui::Menu", this, pchName ); - - m_MenuItems.Validate( validator, "m_MenuItems" ); - m_SortedItems.Validate( validator, "m_SortedItems" ); - - BaseClass::Validate( validator, "vgui::Menu" ); - - validator.Pop(); -} -#endif // DBGFLAG_VALIDATE +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "vgui_controls/pch_vgui_controls.h" + +// memdbgon must be the last include file in a .cpp file +#include "tier0/memdbgon.h" +#define MENU_SEPARATOR_HEIGHT 3 + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: divider line in a menu +//----------------------------------------------------------------------------- +class vgui::MenuSeparator : public Panel +{ +public: + DECLARE_CLASS_SIMPLE( MenuSeparator, Panel ); + + MenuSeparator( Panel *parent, char const *panelName ) : + BaseClass( parent, panelName ) + { + SetPaintEnabled( true ); + SetPaintBackgroundEnabled( true ); + SetPaintBorderEnabled( false ); + } + + virtual void Paint() + { + int w, h; + GetSize( w, h ); + + surface()->DrawSetColor( GetFgColor() ); + surface()->DrawFilledRect( 4, 1, w-1, 2 ); + } + + virtual void ApplySchemeSettings( IScheme *pScheme ) + { + BaseClass::ApplySchemeSettings( pScheme ); + + SetFgColor( pScheme->GetColor( "Menu.SeparatorColor", Color( 142, 142, 142, 255 ) ) ); + SetBgColor( pScheme->GetColor( "Menu.BgColor", Color( 0, 0, 0, 255 ) ) ); + } +}; + +DECLARE_BUILD_FACTORY( Menu ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Menu::Menu(Panel *parent, const char *panelName) : Panel(parent, panelName) +{ + m_Alignment = Label::a_west; + m_iFixedWidth = 0; + m_iMinimumWidth = 0; + m_iNumVisibleLines = -1; // No limit + m_iCurrentlySelectedItemID = m_MenuItems.InvalidIndex(); + m_pScroller = new ScrollBar(this, "MenuScrollBar", true); + m_pScroller->SetVisible(false); + m_pScroller->AddActionSignalTarget(this); + _sizedForScrollBar = false; + SetZPos(1); + SetVisible(false); + MakePopup(false); + SetParent(parent); + _recalculateWidth = true; + m_bUseMenuManager = true; + m_iInputMode = MOUSE; + m_iCheckImageWidth = 0; + m_iActivatedItem = 0; + + m_bUseFallbackFont = false; + m_hFallbackItemFont = INVALID_FONT; + + if (IsProportional()) + { + m_iMenuItemHeight = scheme()->GetProportionalScaledValueEx( GetScheme(), DEFAULT_MENU_ITEM_HEIGHT ); + } + else + { + m_iMenuItemHeight = DEFAULT_MENU_ITEM_HEIGHT; + } + m_hItemFont = INVALID_FONT; + + + m_eTypeAheadMode = COMPAT_MODE; + m_szTypeAheadBuf[0] = '\0'; + m_iNumTypeAheadChars = 0; + m_fLastTypeAheadTime = 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +Menu::~Menu() +{ + delete m_pScroller; +} + +//----------------------------------------------------------------------------- +// Purpose: Remove all menu items from the menu. +//----------------------------------------------------------------------------- +void Menu::DeleteAllItems() +{ + FOR_EACH_LL( m_MenuItems, i ) + { + m_MenuItems[i]->MarkForDeletion(); + } + + m_MenuItems.RemoveAll(); + m_SortedItems.RemoveAll(); + m_VisibleSortedItems.RemoveAll(); + m_Separators.RemoveAll(); + int c = m_SeparatorPanels.Count(); + for ( int i = 0 ; i < c; ++i ) + { + m_SeparatorPanels[ i ]->MarkForDeletion(); + } + m_SeparatorPanels.RemoveAll(); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a menu item to the menu. +//----------------------------------------------------------------------------- +int Menu::AddMenuItem( MenuItem *panel ) +{ + panel->SetParent( this ); + MEM_ALLOC_CREDIT(); + int itemID = m_MenuItems.AddToTail( panel ); + m_SortedItems.AddToTail(itemID); + InvalidateLayout(false); + _recalculateWidth = true; + panel->SetContentAlignment( m_Alignment ); + if ( INVALID_FONT != m_hItemFont ) + { + panel->SetFont( m_hItemFont ); + } + if ( m_bUseFallbackFont && INVALID_FONT != m_hFallbackItemFont ) + { + Label *l = panel; + TextImage *ti = l->GetTextImage(); + if ( ti ) + { + ti->SetUseFallbackFont( m_bUseFallbackFont, m_hFallbackItemFont ); + } + } + + if ( panel->GetHotKey() ) + { + SetTypeAheadMode( HOT_KEY_MODE ); + } + + return itemID; +} + + +//----------------------------------------------------------------------------- +// Remove a single item +//----------------------------------------------------------------------------- +void Menu::DeleteItem( int itemID ) +{ + // FIXME: This doesn't work with separator panels yet + Assert( m_SeparatorPanels.Count() == 0 ); + + m_MenuItems[itemID]->MarkForDeletion(); + m_MenuItems.Remove( itemID ); + + m_SortedItems.FindAndRemove( itemID ); + m_VisibleSortedItems.FindAndRemove( itemID ); + + InvalidateLayout(false); + _recalculateWidth = true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Add a menu item to the menu. +// Input : *item - MenuItem +// *command - Command text to be sent when menu item is selected +// *target - Target panel of the command +// *userData - any user data associated with this menu item +// Output: itemID - ID of this item +//----------------------------------------------------------------------------- +int Menu::AddMenuItemCharCommand(MenuItem *item, const char *command, Panel *target, const KeyValues *userData) +{ + item->SetCommand(command); + item->AddActionSignalTarget( target ); + item->SetUserData(userData); + return AddMenuItem( item ); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a menu item to the menu. +// Input : *itemName - Name of item +// *itemText - Name of item text that will appear in the manu. +// *message - pointer to the message to send when the item is selected +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +// Output: itemID - ID of this item +//----------------------------------------------------------------------------- +int Menu::AddMenuItemKeyValuesCommand( MenuItem *item, KeyValues *message, Panel *target, const KeyValues *userData ) +{ + item->SetCommand(message); + item->AddActionSignalTarget(target); + item->SetUserData(userData); + return AddMenuItem(item); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a menu item to the menu. +// Input : *itemName - Name of item +// *itemText - Name of item text that will appear in the manu. +// *command - Command text to be sent when menu item is selected +// *target - Target panel of the command +// Output: itemID - ID of this item +//----------------------------------------------------------------------------- +int Menu::AddMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData ) +{ + MenuItem *item = new MenuItem(this, itemName, itemText ); + return AddMenuItemCharCommand(item, command, target, userData); +} + +int Menu::AddMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData ) +{ + MenuItem *item = new MenuItem(this, itemName, wszItemText ); + return AddMenuItemCharCommand(item, command, target, userData); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a menu item to the menu. +// Input : *itemText - Name of item text that will appear in the manu. +// This will also be used as the name of the menu item panel. +// *command - Command text to be sent when menu item is selected +// *target - Target panel of the command +// Output: itemID - ID of this item +//----------------------------------------------------------------------------- +int Menu::AddMenuItem( const char *itemText, const char *command, Panel *target, const KeyValues *userData ) +{ + return AddMenuItem(itemText, itemText, command, target, userData ) ; +} + +//----------------------------------------------------------------------------- +// Purpose: Add a menu item to the menu. +// Input : *itemName - Name of item +// *itemText - Name of item text that will appear in the manu. +// *message - pointer to the message to send when the item is selected +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +//----------------------------------------------------------------------------- +int Menu::AddMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData ) +{ + MenuItem *item = new MenuItem(this, itemName, itemText ); + return AddMenuItemKeyValuesCommand(item, message, target, userData); +} + +int Menu::AddMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, const KeyValues *userData ) +{ + MenuItem *item = new MenuItem(this, itemName, wszItemText ); + return AddMenuItemKeyValuesCommand(item, message, target, userData); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a menu item to the menu. +// Input : *itemText - Name of item text that will appear in the manu. +// This will also be used as the name of the menu item panel. +// *message - pointer to the message to send when the item is selected +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +//----------------------------------------------------------------------------- +int Menu::AddMenuItem( const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData ) +{ + return AddMenuItem(itemText, itemText, message, target, userData ); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a menu item to the menu. +// Input : *itemText - Name of item text that will appear in the manu. +// This will also be the text of the command sent when the +// item is selected. +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +//----------------------------------------------------------------------------- +int Menu::AddMenuItem( const char *itemText, Panel *target , const KeyValues *userData ) +{ + return AddMenuItem(itemText, itemText, target, userData ); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a checkable menu item to the menu. +// Input : *itemName - Name of item +// *itemText - Name of item text that will appear in the manu. +// *command - Command text to be sent when menu item is selected +// *target - Target panel of the command +//----------------------------------------------------------------------------- +int Menu::AddCheckableMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData ) +{ + MenuItem *item = new MenuItem(this, itemName, itemText, NULL, true); + return AddMenuItemCharCommand(item, command, target, userData); +} + +int Menu::AddCheckableMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData ) +{ + MenuItem *item = new MenuItem(this, itemName, wszItemText, NULL, true); + return AddMenuItemCharCommand(item, command, target, userData); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a checkable menu item to the menu. +// Input : *itemText - Name of item text that will appear in the manu. +// This will also be used as the name of the menu item panel. +// *command - Command text to be sent when menu item is selected +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +//----------------------------------------------------------------------------- +int Menu::AddCheckableMenuItem( const char *itemText, const char *command, Panel *target, const KeyValues *userData ) +{ + return AddCheckableMenuItem(itemText, itemText, command, target, userData ); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a checkable menu item to the menu. +// Input : *itemName - Name of item +// *itemText - Name of item text that will appear in the manu. +// *message - pointer to the message to send when the item is selected +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +//----------------------------------------------------------------------------- +int Menu::AddCheckableMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData ) +{ + MenuItem *item = new MenuItem(this, itemName, itemText, NULL, true); + return AddMenuItemKeyValuesCommand(item, message, target, userData); +} + +int Menu::AddCheckableMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, const KeyValues *userData ) +{ + MenuItem *item = new MenuItem(this, itemName, wszItemText, NULL, true); + return AddMenuItemKeyValuesCommand(item, message, target, userData); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a checkable menu item to the menu. +// Input : *itemText - Name of item text that will appear in the manu. +// This will also be used as the name of the menu item panel. +// *message - pointer to the message to send when the item is selected +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +//----------------------------------------------------------------------------- +int Menu::AddCheckableMenuItem( const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData ) +{ + return AddCheckableMenuItem(itemText, itemText, message, target, userData ); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a checkable menu item to the menu. +// Input : *itemText - Name of item text that will appear in the manu. +// This will also be the text of the command sent when the +// item is selected. +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +//----------------------------------------------------------------------------- +int Menu::AddCheckableMenuItem( const char *itemText, Panel *target, const KeyValues *userData ) +{ + return AddCheckableMenuItem(itemText, itemText, target, userData ); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a Cascading menu item to the menu. +// Input : *itemName - Name of item +// *itemText - Name of item text that will appear in the manu. +// *command - Command text to be sent when menu item is selected +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +//----------------------------------------------------------------------------- +int Menu::AddCascadingMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, Menu *cascadeMenu , const KeyValues *userData ) +{ + MenuItem *item = new MenuItem(this, itemName, itemText, cascadeMenu ); + return AddMenuItemCharCommand(item, command, target, userData); +} + +int Menu::AddCascadingMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, Menu *cascadeMenu , const KeyValues *userData ) +{ + MenuItem *item = new MenuItem(this, itemName, wszItemText, cascadeMenu ); + return AddMenuItemCharCommand(item, command, target, userData); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a Cascading menu item to the menu. +// Input : *itemText - Name of item text that will appear in the manu. +// This will also be used as the name of the menu item panel. +// *command - Command text to be sent when menu item is selected +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +//----------------------------------------------------------------------------- +int Menu::AddCascadingMenuItem( const char *itemText, const char *command, Panel *target, Menu *cascadeMenu , const KeyValues *userData ) +{ + return AddCascadingMenuItem( itemText, itemText, command, target, cascadeMenu, userData ); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a Cascading menu item to the menu. +// Input : *itemName - Name of item +// *itemText - Name of item text that will appear in the manu. +// *message - pointer to the message to send when the item is selected +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +//----------------------------------------------------------------------------- +int Menu::AddCascadingMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData ) +{ + MenuItem *item = new MenuItem( this, itemName, itemText, cascadeMenu); + return AddMenuItemKeyValuesCommand(item, message, target, userData); +} + +int Menu::AddCascadingMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData ) +{ + MenuItem *item = new MenuItem( this, itemName, wszItemText, cascadeMenu); + return AddMenuItemKeyValuesCommand(item, message, target, userData); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a Cascading menu item to the menu. +// Input : *itemText - Name of item text that will appear in the manu. +// This will also be used as the name of the menu item panel. +// *message - pointer to the message to send when the item is selected +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +//----------------------------------------------------------------------------- +int Menu::AddCascadingMenuItem( const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData ) +{ + return AddCascadingMenuItem(itemText, itemText, message, target, cascadeMenu, userData ); +} + +//----------------------------------------------------------------------------- +// Purpose: Add a Cascading menu item to the menu. +// Input : *itemText - Name of item text that will appear in the manu. +// This will also be the text of the command sent when the +// item is selected. +// *target - Target panel of the command +// *cascadeMenu - if the menu item opens a cascading menu, this is a +// ptr to the menu that opens on selecting the item +//----------------------------------------------------------------------------- +int Menu::AddCascadingMenuItem( const char *itemText, Panel *target, Menu *cascadeMenu, const KeyValues *userData ) +{ + return AddCascadingMenuItem(itemText, itemText, target, cascadeMenu, userData); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the values of a menu item at the specified index +// Input : index - the index of this item entry +// *message - pointer to the message to send when the item is selected +//----------------------------------------------------------------------------- +void Menu::UpdateMenuItem(int itemID, const char *itemText, KeyValues *message, const KeyValues *userData) +{ + Assert( m_MenuItems.IsValidIndex(itemID) ); + if ( m_MenuItems.IsValidIndex(itemID) ) + { + MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); + // make sure its enabled since disabled items get highlighted. + if (menuItem) + { + menuItem->SetText(itemText); + menuItem->SetCommand(message); + if(userData) + { + menuItem->SetUserData(userData); + } + } + } + _recalculateWidth = true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets the values of a menu item at the specified index +//----------------------------------------------------------------------------- +void Menu::UpdateMenuItem(int itemID, const wchar_t *wszItemText, KeyValues *message, const KeyValues *userData) +{ + Assert( m_MenuItems.IsValidIndex(itemID) ); + if ( m_MenuItems.IsValidIndex(itemID) ) + { + MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); + // make sure its enabled since disabled items get highlighted. + if (menuItem) + { + menuItem->SetText(wszItemText); + menuItem->SetCommand(message); + if(userData) + { + menuItem->SetUserData(userData); + } + } + } + _recalculateWidth = true; +} + + +//----------------------------------------------------------------------------- +// Sets the content alignment of all items in the menu +//----------------------------------------------------------------------------- +void Menu::SetContentAlignment( Label::Alignment alignment ) +{ + if ( m_Alignment != alignment ) + { + m_Alignment = alignment; + + // Change the alignment of existing menu items + int nCount = m_MenuItems.Count(); + for ( int i = 0; i < nCount; ++i ) + { + m_MenuItems[i]->SetContentAlignment( alignment ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Locks down a specific width +//----------------------------------------------------------------------------- +void Menu::SetFixedWidth(int width) +{ + // the padding makes it so the menu has the label padding on each side of the menu. + // makes the menu items look centered. + m_iFixedWidth = width; + InvalidateLayout(false); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the height of each menu item +//----------------------------------------------------------------------------- +void Menu::SetMenuItemHeight(int itemHeight) +{ + m_iMenuItemHeight = itemHeight; +} + +int Menu::GetMenuItemHeight() const +{ + return m_iMenuItemHeight; +} + +int Menu::CountVisibleItems() +{ + int count = 0; + int c = m_SortedItems.Count(); + for ( int i = 0 ; i < c; ++i ) + { + if ( m_MenuItems[ m_SortedItems[ i ] ]->IsVisible() ) + ++count; + } + return count; +} + +void Menu::ComputeWorkspaceSize( int& workWide, int& workTall ) +{ + // make sure we factor in insets + int ileft, iright, itop, ibottom; + GetInset(ileft, iright, itop, ibottom); + + int workX, workY; + surface()->GetWorkspaceBounds(workX, workY, workWide, workTall); + workTall -= 20; + workTall -= itop; + workTall -= ibottom; +} + +// Assumes relative coords in screenspace +void Menu::PositionRelativeToPanel( Panel *relative, MenuDirection_e direction, int nAdditionalYOffset /*=0*/, bool showMenu /*=false*/ ) +{ + Assert( relative ); + int rx, ry, rw, rh; + relative->GetBounds( rx, ry, rw, rh ); + relative->LocalToScreen( rx, ry ); + + if ( direction == CURSOR ) + { + // force the menu to appear where the mouse button was pressed + input()->GetCursorPos(rx, ry); + rw = rh = 0; + } + else if ( direction == ALIGN_WITH_PARENT && relative->GetVParent() ) + { + rx = 0, ry = 0; + relative->ParentLocalToScreen(rx, ry); + rx -= 1; // take border into account + ry += rh + nAdditionalYOffset; + rw = rh = 0; + } + else + { + rx = 0, ry = 0; + relative->LocalToScreen(rx, ry); + } + + int workWide, workTall; + ComputeWorkspaceSize( workWide, workTall ); + + // Final pos + int x = 0, y = 0; + + int mWide, mTall; + GetSize( mWide, mTall ); + + switch( direction ) + { + case Menu::UP: // Menu prefers to open upward + { + x = rx; + int topOfReference = ry; + y = topOfReference - mTall; + if ( y < 0 ) + { + int bottomOfReference = ry + rh + 1; + int remainingPixels = workTall - bottomOfReference; + + // Can't fit on bottom, either, move to side + if ( mTall >= remainingPixels ) + { + y = workTall - mTall; + x = rx + rw; + // Try and place it to the left of the button + if ( x + mWide > workWide ) + { + x = rx - mWide; + } + } + else + { + // Room at bottom + y = bottomOfReference; + } + } + } + break; + // Everyone else aligns downward... + default: + case Menu::LEFT: + case Menu::RIGHT: + case Menu::DOWN: + { + x = rx; + int bottomOfReference = ry + rh + 1; + y = bottomOfReference; + if ( bottomOfReference + mTall >= workTall ) + { + // See if there's run straight above + if ( mTall >= ry ) // No room, try and push menu to right or left + { + y = workTall - mTall; + x = rx + rw; + // Try and place it to the left of the button + if ( x + mWide > workWide ) + { + x = rx - mWide; + } + } + else + { + // Room at top + y = ry - mTall; + } + } + } + break; + } + + // Check left rightness + if ( x + mWide > workWide ) + { + x = workWide - mWide; + Assert( x >= 0 ); // yikes!!! + } + else if ( x < 0 ) + { + x = 0; + } + + SetPos( x, y ); + if ( showMenu ) + { + SetVisible( true ); + } +} + +int Menu::ComputeFullMenuHeightWithInsets() +{ + // make sure we factor in insets + int ileft, iright, itop, ibottom; + GetInset(ileft, iright, itop, ibottom); + + int separatorHeight = 3; + + // add up the size of all the child panels + // move the child panels to the correct place in the menu + int totalTall = itop + ibottom; + int i; + for ( i = 0 ; i < m_SortedItems.Count() ; i++ ) // use sortedItems instead of MenuItems due to SetPos() + { + int itemId = m_SortedItems[i]; + + MenuItem *child = m_MenuItems[ itemId ]; + Assert( child ); + if ( !child ) + continue; + // These should all be visible at this point + if ( !child->IsVisible() ) + continue; + + totalTall += m_iMenuItemHeight; + + // Add a separator if needed... + int sepIndex = m_Separators.Find( itemId ); + if ( sepIndex != m_Separators.InvalidIndex() ) + { + totalTall += separatorHeight; + } + } + + return totalTall; +} + +//----------------------------------------------------------------------------- +// Purpose: Reformat according to the new layout +//----------------------------------------------------------------------------- +void Menu::PerformLayout() +{ + MenuItem *parent = GetParentMenuItem(); + bool cascading = parent != NULL ? true : false; + + // make sure we factor in insets + int ileft, iright, itop, ibottom; + GetInset(ileft, iright, itop, ibottom); + + int workWide, workTall; + + ComputeWorkspaceSize( workWide, workTall ); + + int fullHeightWouldRequire = ComputeFullMenuHeightWithInsets(); + + bool bNeedScrollbar = fullHeightWouldRequire >= workTall; + + int maxVisibleItems = CountVisibleItems(); + + if ( m_iNumVisibleLines > 0 && + maxVisibleItems > m_iNumVisibleLines ) + { + bNeedScrollbar = true; + maxVisibleItems = m_iNumVisibleLines; + } + + // if we have a scroll bar + if ( bNeedScrollbar ) + { + // add it to the display + AddScrollBar(); + + // This fills in m_VisibleSortedItems as needed + MakeItemsVisibleInScrollRange( m_iNumVisibleLines, min( fullHeightWouldRequire, workTall ) ); + } + else + { + RemoveScrollBar(); + // Make everything visible + m_VisibleSortedItems.RemoveAll(); + int i; + int c = m_SortedItems.Count(); + for ( i = 0; i < c; ++i ) + { + int itemID = m_SortedItems[ i ]; + MenuItem *child = m_MenuItems[ itemID ]; + if ( !child || !child->IsVisible() ) + continue; + + m_VisibleSortedItems.AddToTail( itemID ); + } + + // Hide the separators, the needed ones will be readded below + c = m_SeparatorPanels.Count(); + for ( i = 0; i < c; ++i ) + { + if ( m_SeparatorPanels[ i ] ) + { + m_SeparatorPanels[ i ]->SetVisible( false ); + } + } + } + + // get the appropriate menu border + LayoutMenuBorder(); + + int trueW = GetWide(); + if ( bNeedScrollbar ) + { + trueW -= m_pScroller->GetWide(); + } + int separatorHeight = MENU_SEPARATOR_HEIGHT; + + // add up the size of all the child panels + // move the child panels to the correct place in the menu + int menuTall = 0; + int totalTall = itop + ibottom; + int i; + for ( i = 0 ; i < m_VisibleSortedItems.Count() ; i++ ) // use sortedItems instead of MenuItems due to SetPos() + { + int itemId = m_VisibleSortedItems[i]; + + MenuItem *child = m_MenuItems[ itemId ]; + Assert( child ); + if ( !child ) + continue; + // These should all be visible at this point + if ( !child->IsVisible() ) + continue; + + if ( totalTall >= workTall ) + break; + + if ( INVALID_FONT != m_hItemFont ) + { + child->SetFont( m_hItemFont ); + } + + // take into account inset + child->SetPos (0, menuTall); + child->SetTall( m_iMenuItemHeight ); // Width is set in a second pass + menuTall += m_iMenuItemHeight; + totalTall += m_iMenuItemHeight; + + // this will make all the menuitems line up in a column with space for the checks to the left. + if ( ( !child->IsCheckable() ) && ( m_iCheckImageWidth > 0 ) ) + { + // Non checkable items have to move over + child->SetTextInset( m_iCheckImageWidth, 0 ); + } + else if ( child->IsCheckable() ) + { + child->SetTextInset(0, 0); //TODO: for some reason I can't comment this out. + } + + // Add a separator if needed... + int sepIndex = m_Separators.Find( itemId ); + if ( sepIndex != m_Separators.InvalidIndex() ) + { + MenuSeparator *sep = m_SeparatorPanels[ sepIndex ]; + Assert( sep ); + sep->SetVisible( true ); + sep->SetBounds( 0, menuTall, trueW, separatorHeight ); + menuTall += separatorHeight; + totalTall += separatorHeight; + } + } + + if (!m_iFixedWidth) + { + _recalculateWidth = true; + CalculateWidth(); + } + else if (m_iFixedWidth) + { + _menuWide = m_iFixedWidth; + // fixed width menus include the scroll bar in their width. + if (_sizedForScrollBar) + { + _menuWide -= m_pScroller->GetWide(); + } + } + + SizeMenuItems(); + + int extraWidth = 0; + if (_sizedForScrollBar) + { + extraWidth = m_pScroller->GetWide(); + } + + int mwide = _menuWide + extraWidth; + if ( mwide > workWide ) + { + mwide = workWide; + } + int mtall = menuTall + itop + ibottom; + if ( mtall > workTall ) + { + // Shouldn't happen + mtall = workTall; + } + + // set the new size of the menu + SetSize( mwide, mtall ); + + // move the menu to the correct position if it is a cascading menu. + if ( cascading ) + { + // move the menu to the correct position if it is a cascading menu. + PositionCascadingMenu(); + } + + // set up scroll bar as appropriate + if ( m_pScroller->IsVisible() ) + { + LayoutScrollBar(); + } + + FOR_EACH_LL( m_MenuItems, j ) + { + m_MenuItems[j]->InvalidateLayout(); // cause each menu item to redo its apply settings now we have sized ourselves + } + + Repaint(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Force the menu to work out how wide it should be +//----------------------------------------------------------------------------- +void Menu::ForceCalculateWidth() +{ + _recalculateWidth = true; + CalculateWidth(); + PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Figure out how wide the menu should be if the menu is not fixed width +//----------------------------------------------------------------------------- +void Menu::CalculateWidth() +{ + if (!_recalculateWidth) + return; + + _menuWide = 0; + if (!m_iFixedWidth) + { + // find the biggest menu item + FOR_EACH_LL( m_MenuItems, i ) + { + int wide, tall; + m_MenuItems[i]->GetContentSize(wide, tall); + if (wide > _menuWide - Label::Content) + { + _menuWide = wide + Label::Content; + } + } + } + + // enfoce a minimumWidth + if (_menuWide < m_iMinimumWidth) + { + _menuWide = m_iMinimumWidth; + } + + _recalculateWidth = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Set up the scroll bar attributes,size and location. +//----------------------------------------------------------------------------- +void Menu::LayoutScrollBar() +{ + //!! need to make it recalculate scroll positions + m_pScroller->SetEnabled(false); + m_pScroller->SetRangeWindow( m_VisibleSortedItems.Count() ); + m_pScroller->SetRange( 0, CountVisibleItems() ); + m_pScroller->SetButtonPressedScrollValue( 1 ); + + int wide, tall; + GetSize (wide, tall); + + // make sure we factor in insets + int ileft, iright, itop, ibottom; + GetInset(ileft, iright, itop, ibottom); + + // with a scroll bar we take off the inset + wide -= iright; + + m_pScroller->SetPos(wide - m_pScroller->GetWide(), 1); + + // scrollbar is inside the menu's borders. + m_pScroller->SetSize(m_pScroller->GetWide(), tall - ibottom - itop); + +} + +//----------------------------------------------------------------------------- +// Purpose: Figure out where to open menu if it is a cascading menu +//----------------------------------------------------------------------------- +void Menu::PositionCascadingMenu() +{ + Assert(GetVParent()); + int parentX, parentY, parentWide, parentTall; + // move the menu to the correct place below the menuItem + ipanel()->GetSize(GetVParent(), parentWide, parentTall); + ipanel()->GetPos(GetVParent(), parentX, parentY); + + parentX += parentWide, parentY = 0; + + ParentLocalToScreen(parentX, parentY); + + SetPos(parentX, parentY); + + // for cascading menus, + // make sure we're on the screen + int workX, workY, workWide, workTall, x, y, wide, tall; + GetBounds(x, y, wide, tall); + surface()->GetWorkspaceBounds(workX, workY, workWide, workTall); + + if (x + wide > workX + workWide) + { + // we're off the right, move the menu to the left side + // orignalX - width of the parentmenuitem - width of this menu. + // add 2 pixels to offset one pixel onto the parent menu. + x -= (parentWide + wide); + x -= 2; + } + else + { + // alignment move it in the amount of the insets. + x += 1; + } + + if ( y + tall > workY + workTall ) + { + int lastWorkY = workY + workTall; + int pixelsOffBottom = ( y + tall ) - lastWorkY; + + y -= pixelsOffBottom; + y -= 2; + } + else + { + y -= 1; + } + SetPos(x, y); + + MoveToFront(); +} + +//----------------------------------------------------------------------------- +// Purpose: Size the menu items so they are the width of the menu. +// Also size the menu items with cascading menus so the arrow fits in there. +//----------------------------------------------------------------------------- +void Menu::SizeMenuItems() +{ + int ileft, iright, itop, ibottom; + GetInset(ileft, iright, itop, ibottom); + + // assign the sizes of all the menu item panels + FOR_EACH_LL( m_MenuItems, i ) + { + MenuItem *child = m_MenuItems[i]; + if (child ) + { + // labels do thier own sizing. this will size the label to the width of the menu, + // this will put the cascading menu arrow on the right side automatically. + child->SetWide(_menuWide - ileft - iright); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Makes menu items visible in relation to where the scroll bar is +//----------------------------------------------------------------------------- +void Menu::MakeItemsVisibleInScrollRange( int maxVisibleItems, int nNumPixelsAvailable ) +{ + // Detach all items from tree + int i; + FOR_EACH_LL( m_MenuItems, item ) + { + m_MenuItems[ item ]->SetBounds( 0, 0, 0, 0 ); + } + for ( i = 0; i < m_SeparatorPanels.Count(); ++i ) + { + m_SeparatorPanels[ i ]->SetVisible( false ); + } + + m_VisibleSortedItems.RemoveAll(); + + int tall = 0; + + int startItem = m_pScroller->GetValue(); + Assert( startItem >= 0 ); + do + { + if ( startItem >= m_SortedItems.Count() ) + break; + + int itemId = m_SortedItems[ startItem ]; + + if ( !m_MenuItems[ itemId ]->IsVisible() ) + { + ++startItem; + continue; + } + + int itemHeight = m_iMenuItemHeight; + int sepIndex = m_Separators.Find( itemId ); + if ( sepIndex != m_Separators.InvalidIndex() ) + { + itemHeight += MENU_SEPARATOR_HEIGHT; + } + + if ( tall + itemHeight > nNumPixelsAvailable ) + break; + + // Too many items + if ( maxVisibleItems > 0 ) + { + if ( m_VisibleSortedItems.Count() >= maxVisibleItems ) + break; + } + + tall += itemHeight; + // Re-attach this one + m_VisibleSortedItems.AddToTail( itemId ); + ++startItem; + } + while ( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the approproate menu border +//----------------------------------------------------------------------------- +void Menu::LayoutMenuBorder() +{ + IBorder *menuBorder; + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + + menuBorder = pScheme->GetBorder("MenuBorder"); + + if ( menuBorder ) + { + SetBorder(menuBorder); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draw a black border on the right side of the menu items +//----------------------------------------------------------------------------- +void Menu::Paint() +{ + if ( m_pScroller->IsVisible() ) + { + // draw black bar + int wide, tall; + GetSize (wide, tall); + surface()->DrawSetColor(_borderDark); + if( IsProportional() ) + { + surface()->DrawFilledRect(wide - m_pScroller->GetWide(), -1, wide - m_pScroller->GetWide() + 1, tall); + } + else + { + surface()->DrawFilledRect(wide - m_pScroller->GetWide(), -1, wide - m_pScroller->GetWide() + 1, tall); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: sets the max number of items visible (scrollbar appears with more) +// Input : numItems - +//----------------------------------------------------------------------------- +void Menu::SetNumberOfVisibleItems( int numItems ) +{ + m_iNumVisibleLines = numItems; + InvalidateLayout(false); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Menu::EnableUseMenuManager( bool bUseMenuManager ) +{ + m_bUseMenuManager = bUseMenuManager; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +MenuItem *Menu::GetMenuItem(int itemID) +{ + if ( !m_MenuItems.IsValidIndex(itemID) ) + return NULL; + + return m_MenuItems[itemID]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool Menu::IsValidMenuID(int itemID) +{ + return m_MenuItems.IsValidIndex(itemID); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int Menu::GetInvalidMenuID() +{ + return m_MenuItems.InvalidIndex(); +} + +//----------------------------------------------------------------------------- +// Purpose: When a menuItem is selected, close cascading menus +// if the menuItem selected has a cascading menu attached, we +// want to keep that one open so skip it. +// Passing NULL will close all cascading menus. +//----------------------------------------------------------------------------- +void Menu::CloseOtherMenus(MenuItem *item) +{ + FOR_EACH_LL( m_MenuItems, i ) + { + if (m_MenuItems[i] == item) + continue; + + m_MenuItems[i]->CloseCascadeMenu(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Respond to string commands. +//----------------------------------------------------------------------------- +void Menu::OnCommand( const char *command ) +{ + // forward on the message + PostActionSignal(new KeyValues("Command", "command", command)); + + Panel::OnCommand(command); +} + +//----------------------------------------------------------------------------- +// Purpose: Handle key presses, Activate shortcuts +//----------------------------------------------------------------------------- +void Menu::OnKeyCodeTyped(KeyCode keycode) +{ + vgui::KeyCode code = GetBaseButtonCode( keycode ); + + // Don't allow key inputs when disabled! + if ( !IsEnabled() ) + return; + + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + if (alt) + { + BaseClass::OnKeyCodeTyped( keycode ); + // Ignore alt when in combobox mode + if (m_eTypeAheadMode != TYPE_AHEAD_MODE) + { + PostActionSignal(new KeyValues("MenuClose")); + } + } + + switch (code) + { + case KEY_ESCAPE: + case KEY_XBUTTON_B: + { + // hide the menu on ESC + SetVisible(false); + break; + } + // arrow keys scroll through items on the list. + // they should also scroll the scroll bar if needed + case KEY_UP: + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + { + MoveAlongMenuItemList(MENU_UP, 0); + if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) ) + { + m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); + } + else + { + BaseClass::OnKeyCodeTyped( keycode ); // chain up + } + break; + } + case KEY_DOWN: + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + { + MoveAlongMenuItemList(MENU_DOWN, 0); + if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) ) + { + m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); + } + else + { + BaseClass::OnKeyCodeTyped( keycode ); // chain up + } + break; + } + // for now left and right arrows just open or close submenus if they are there. + case KEY_RIGHT: + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + { + // make sure a menuItem is currently selected + if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) ) + { + if (m_MenuItems[m_iCurrentlySelectedItemID]->HasMenu()) + { + ActivateItem(m_iCurrentlySelectedItemID); + } + else + { + BaseClass::OnKeyCodeTyped( keycode ); + } + } + else + { + BaseClass::OnKeyCodeTyped( keycode ); + } + break; + } + case KEY_LEFT: + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + { + // if our parent is a menu item then we are a submenu so close us. + if (GetParentMenuItem()) + { + SetVisible(false); + } + else + { + BaseClass::OnKeyCodeTyped( keycode ); + } + break; + } + case KEY_ENTER: + case KEY_XBUTTON_A: + { + // make sure a menuItem is currently selected + if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) ) + { + ActivateItem(m_iCurrentlySelectedItemID); + } + else + { + BaseClass::OnKeyCodeTyped( keycode ); // chain up + } + break; + } + + case KEY_PAGEUP: + { + if ( m_iNumVisibleLines > 1 ) + { + if ( m_iCurrentlySelectedItemID < m_iNumVisibleLines ) + { + MoveAlongMenuItemList( MENU_UP * m_iCurrentlySelectedItemID, 0 ); + } + else + { + MoveAlongMenuItemList(MENU_UP * m_iNumVisibleLines - 1, 0); + } + } + else + { + MoveAlongMenuItemList(MENU_UP, 0); + } + + if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) ) + { + m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); + } + break; + } + + + case KEY_PAGEDOWN: + { + if ( m_iNumVisibleLines > 1 ) + { + if ( m_iCurrentlySelectedItemID + m_iNumVisibleLines >= GetItemCount() ) + { + MoveAlongMenuItemList(MENU_DOWN * ( GetItemCount() - m_iCurrentlySelectedItemID - 1), 0); + } + else + { + MoveAlongMenuItemList(MENU_DOWN * m_iNumVisibleLines - 1, 0); + } + } + else + { + MoveAlongMenuItemList(MENU_DOWN, 0); + } + + if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) ) + { + m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); + } + break; + } + + case KEY_HOME: + { + MoveAlongMenuItemList( MENU_UP * m_iCurrentlySelectedItemID, 0 ); + if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) ) + { + m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); + } + break; + } + + + case KEY_END: + { + MoveAlongMenuItemList(MENU_DOWN * ( GetItemCount() - m_iCurrentlySelectedItemID - 1), 0); + if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) ) + { + m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); + } + break; + } + } + + // don't chain back +} + +void Menu::OnHotKey(wchar_t unichar) +{ + // iterate the menu items looking for one with the matching hotkey + FOR_EACH_LL( m_MenuItems, i ) + { + MenuItem *panel = m_MenuItems[i]; + if (panel->IsVisible()) + { + Panel *hot = panel->HasHotkey(unichar); + if (hot) + { + // post a message to the menuitem telling it it's hotkey was pressed + PostMessage(hot, new KeyValues("Hotkey")); + return; + } + // if the menuitem is a cascading menuitem and it is open, check its hotkeys too + Menu *cascadingMenu = panel->GetMenu(); + if (cascadingMenu && cascadingMenu->IsVisible()) + { + cascadingMenu->OnKeyTyped(unichar); + } + } + } +} + +void Menu::OnTypeAhead(wchar_t unichar) +{ + // Don't do anything if the menu is empty since there cannot be a selected item. + if ( m_MenuItems.Count() <= 0) + return; + + // expire the type ahead buffer after 0.5 seconds + double tCurrentTime = Sys_FloatTime(); + if ( (tCurrentTime - m_fLastTypeAheadTime) > 0.5f ) + { + m_iNumTypeAheadChars = 0; + m_szTypeAheadBuf[0] = '\0'; + } + m_fLastTypeAheadTime = tCurrentTime; + + // add current character to the type ahead buffer + if ( m_iNumTypeAheadChars+1 < TYPEAHEAD_BUFSIZE ) + { + m_szTypeAheadBuf[m_iNumTypeAheadChars++] = unichar; + } + + int itemToSelect = m_iCurrentlySelectedItemID; + if ( itemToSelect < 0 || itemToSelect >= m_MenuItems.Count()) + { + itemToSelect = 0; + } + + int i = itemToSelect; + do + { + wchar_t menuItemName[255]; + m_MenuItems[i]->GetText(menuItemName, 254); + + // This is supposed to be case insensitive but we don't have a portable case + // insensitive wide-character routine. + if ( wcsncmp( m_szTypeAheadBuf, menuItemName, m_iNumTypeAheadChars) == 0 ) + { + itemToSelect = i; + break; + } + + i = (i+1) % m_MenuItems.Count(); + } while ( i != itemToSelect ); + + if ( itemToSelect >= 0 ) + { + SetCurrentlyHighlightedItem( itemToSelect ); + InvalidateLayout(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle key presses, Activate shortcuts +// Input : code - +//----------------------------------------------------------------------------- +void Menu::OnKeyTyped(wchar_t unichar) +{ + if (! unichar) + { + return; + } + + switch( m_eTypeAheadMode ) + { + case HOT_KEY_MODE: + OnHotKey(unichar); + return; + + case TYPE_AHEAD_MODE: + OnTypeAhead(unichar); + return; + + case COMPAT_MODE: + default: + break; + } + + int itemToSelect = m_iCurrentlySelectedItemID; + if ( itemToSelect < 0 ) + { + itemToSelect = 0; + } + + int i; + wchar_t menuItemName[255]; + + i = itemToSelect + 1; + if ( i >= m_MenuItems.Count() ) + { + i = 0; + } + + while ( i != itemToSelect ) + { + m_MenuItems[i]->GetText(menuItemName, 254); + + if ( tolower( unichar ) == tolower( menuItemName[0] ) ) + { + itemToSelect = i; + break; + } + + i++; + if ( i >= m_MenuItems.Count() ) + { + i = 0; + } + } + + if ( itemToSelect >= 0 ) + { + SetCurrentlyHighlightedItem( itemToSelect ); + InvalidateLayout(); + } + + // don't chain back +} + + +void Menu::SetTypeAheadMode(MenuTypeAheadMode mode) +{ + m_eTypeAheadMode = mode; +} + +int Menu::GetTypeAheadMode() +{ + return m_eTypeAheadMode; +} + +//----------------------------------------------------------------------------- +// Purpose: Handle the mouse wheel event, scroll the selection +//----------------------------------------------------------------------------- +void Menu::OnMouseWheeled(int delta) +{ + if (!m_pScroller->IsVisible()) + return; + + int val = m_pScroller->GetValue(); + val -= delta; + + m_pScroller->SetValue(val); + + // moving the slider redraws the scrollbar, + // and so we should redraw the menu since the + // menu draws the black border to the right of the scrollbar. + InvalidateLayout(); + + // don't chain back +} + + +//----------------------------------------------------------------------------- +// Purpose: Lose focus, hide menu +//----------------------------------------------------------------------------- +void Menu::OnKillFocus() +{ + // check to see if it's a child taking it + if (!input()->GetFocus() || !ipanel()->HasParent(input()->GetFocus(), GetVPanel())) + { + // if we don't accept keyboard input, then we have to ignore the killfocus if it's not actually being stolen + if (!IsKeyBoardInputEnabled() && !input()->GetFocus()) + return; + + // get the parent of this menu. + MenuItem *item = GetParentMenuItem(); + // if the parent is a menu item, this menu is a cascading menu + // if the panel that is getting focus is the parent menu, don't close this menu. + if ( (item) && (input()->GetFocus() == item->GetVParent()) ) + { + // if we are in mouse mode and we clicked on the menuitem that + // triggers the cascading menu, leave it open. + if (m_iInputMode == MOUSE) + { + // return the focus to the cascading menu. + MoveToFront(); + return; + } + } + + // forward the message to the parent. + PostActionSignal(new KeyValues("MenuClose")); + + // hide this menu + SetVisible(false); + } + +} + +namespace vgui +{ + +class CMenuManager +{ +public: + void AddMenu( Menu *m ) + { + if ( !m ) + return; + + int c = m_Menus.Count(); + for ( int i = 0 ; i < c; ++i ) + { + if ( m_Menus[ i ].Get() == m ) + return; + } + + DHANDLE< Menu > h; + h = m; + m_Menus.AddToTail( h ); + } + + void RemoveMenu( Menu *m ) + { + if ( !m ) + return; + + int c = m_Menus.Count(); + for ( int i = c - 1 ; i >= 0; --i ) + { + if ( m_Menus[ i ].Get() == m ) + { + m_Menus.Remove( i ); + return; + } + } + } + + void OnInternalMousePressed( Panel *other, MouseCode code ) + { + int c = m_Menus.Count(); + if ( !c ) + return; + + int x, y; + input()->GetCursorPos( x, y ); + + bool mouseInsideMenuRelatedPanel = false; + + for ( int i = c - 1; i >= 0 ; --i ) + { + Menu *m = m_Menus[ i ].Get(); + if ( !m ) + { + m_Menus.Remove( i ); + continue; + } + + // See if the mouse is within a menu + if ( IsWithinMenuOrRelative( m, x, y ) ) + { + mouseInsideMenuRelatedPanel = true; + } + } + + if ( mouseInsideMenuRelatedPanel ) + { + return; + } + + AbortMenus(); + } + + void AbortMenus() + { + // Close all of the menus + int c = m_Menus.Count(); + for ( int i = c - 1; i >= 0 ; --i ) + { + Menu *m = m_Menus[ i ].Get(); + if ( !m ) + { + continue; + } + + m_Menus.Remove( i ); + + // Force it to close + m->SetVisible( false ); + } + + m_Menus.RemoveAll(); + } + + bool IsWithinMenuOrRelative( Panel *panel, int x, int y ) + { + VPANEL topMost = panel->IsWithinTraverse( x, y, true ); + if ( topMost ) + { + // It's over the menu + if ( topMost == panel->GetVPanel() ) + { + return true; + } + + // It's over something which is parented to the menu (i.e., a menu item) + if ( ipanel()->HasParent( topMost, panel->GetVPanel() ) ) + { + return true; + } + } + + if ( panel->GetParent() ) + { + Panel *parent = panel->GetParent(); + + topMost = parent->IsWithinTraverse( x, y, true ); + + if ( topMost ) + { + if ( topMost == parent->GetVPanel() ) + { + return true; + } + + /* + // NOTE: this check used to not cast to MenuButton, but it seems wrong to me + // since if the mouse is over another child of the parent panel to the menu then + // the menu stays visible. I think this is bogus. + Panel *pTopMost = ipanel()->GetPanel(topMost, GetControlsModuleName()); + + if ( pTopMost && + ipanel()->HasParent( topMost, parent->GetVPanel() ) && + dynamic_cast< MenuButton * >( pTopMost ) ) + { + Msg( "topMost %s has parent %s\n", + ipanel()->GetName( topMost ), + parent->GetName() ); + + return true; + } + */ + } + } + + return false; + } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, char *pchName ) + { + validator.Push( "CMenuManager", this, pchName ); + m_Menus.Validate( validator, "m_Menus" ); + validator.Pop(); + } +#endif + +private: + + // List of visible menus + CUtlVector< DHANDLE< Menu > > m_Menus; +}; + + +// Singleton helper class +static CMenuManager g_MenuMgr; + +void ValidateMenuGlobals( CValidator &validator ) +{ +#ifdef DBGFLAG_VALIDATE + g_MenuMgr.Validate( validator, "g_MenuMgr" ); +#endif +} + +} // end namespace vgui + +//----------------------------------------------------------------------------- +// Purpose: Static method called on mouse released to see if Menu objects should be aborted +// Input : *other - +// code - +//----------------------------------------------------------------------------- +void Menu::OnInternalMousePressed( Panel *other, MouseCode code ) +{ + g_MenuMgr.OnInternalMousePressed( other, code ); +} + +//----------------------------------------------------------------------------- +// Purpose: Set visibility of menu and its children as appropriate. +//----------------------------------------------------------------------------- +void Menu::SetVisible(bool state) +{ + if (state == IsVisible()) + return; + + if ( state == false ) + { + PostActionSignal(new KeyValues("MenuClose")); + CloseOtherMenus(NULL); + + SetCurrentlySelectedItem(-1); + + g_MenuMgr.RemoveMenu( this ); + } + else if ( state == true ) + { + MoveToFront(); + RequestFocus(); + + // Add to menu manager? + if ( m_bUseMenuManager ) + { + g_MenuMgr.AddMenu( this ); + } + } + + // must be after movetofront() + BaseClass::SetVisible(state); + _sizedForScrollBar = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Menu::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetFgColor(GetSchemeColor("Menu.TextColor", pScheme)); + SetBgColor(GetSchemeColor("Menu.BgColor", pScheme)); + + _borderDark = pScheme->GetColor("BorderDark", Color(255, 255, 255, 0)); + + FOR_EACH_LL( m_MenuItems, i ) + { + if( m_MenuItems[i]->IsCheckable() ) + { + int wide, tall; + m_MenuItems[i]->GetCheckImageSize( wide, tall ); + + m_iCheckImageWidth = max ( m_iCheckImageWidth, wide ); + } + } + _recalculateWidth = true; + CalculateWidth(); + + InvalidateLayout(); +} + +void Menu::SetBgColor( Color newColor ) +{ + BaseClass::SetBgColor( newColor ); + FOR_EACH_LL( m_MenuItems, i ) + { + if( m_MenuItems[i]->HasMenu() ) + { + m_MenuItems[i]->GetMenu()->SetBgColor( newColor ); + } + } +} + +void Menu::SetFgColor( Color newColor ) +{ + BaseClass::SetFgColor( newColor ); + FOR_EACH_LL( m_MenuItems, i ) + { + if( m_MenuItems[i]->HasMenu() ) + { + m_MenuItems[i]->GetMenu()->SetFgColor( newColor ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Menu::SetBorder(class IBorder *border) +{ + Panel::SetBorder(border); +} + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to a MenuItem that is this menus parent, if it has one +//----------------------------------------------------------------------------- +MenuItem *Menu::GetParentMenuItem() +{ + return dynamic_cast(GetParent()); +} + +//----------------------------------------------------------------------------- +// Purpose: Hide the menu when an item has been selected +//----------------------------------------------------------------------------- +void Menu::OnMenuItemSelected(Panel *panel) +{ + SetVisible(false); + m_pScroller->SetVisible(false); + + // chain this message up through the hierarchy so + // all the parent menus will close + + // get the parent of this menu. + MenuItem *item = GetParentMenuItem(); + // if the parent is a menu item, this menu is a cascading menu + if (item) + { + // get the parent of the menuitem. it should be a menu. + Menu *parentMenu = item->GetParentMenu(); + if (parentMenu) + { + // send the message to this parent menu + KeyValues *kv = new KeyValues("MenuItemSelected"); + kv->SetPtr("panel", panel); + ivgui()->PostMessage(parentMenu->GetVPanel(), kv, GetVPanel()); + } + } + + bool activeItemSet = false; + + FOR_EACH_LL( m_MenuItems, i ) + { + if( m_MenuItems[i] == panel ) + { + activeItemSet = true; + m_iActivatedItem = i; + break; + } + } + if( !activeItemSet ) + { + FOR_EACH_LL( m_MenuItems, i ) + { + if(m_MenuItems[i]->HasMenu() ) + { + /* + // GetActiveItem needs to return -1 or similar if it hasn't been set... + if( m_MenuItems[i]->GetActiveItem() ) + { + m_iActivatedItem = m_MenuItems[i]->GetActiveItem(); + }*/ + } + } + } + + // also pass it to the parent so they can respond if they like + if (GetVParent()) + { + KeyValues *kv = new KeyValues("MenuItemSelected"); + kv->SetPtr("panel", panel); + + ivgui()->PostMessage(GetVParent(), kv, GetVPanel()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int Menu::GetActiveItem() +{ + return m_iActivatedItem; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +KeyValues *Menu::GetItemUserData(int itemID) +{ + if ( m_MenuItems.IsValidIndex( itemID ) ) + { + MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); + // make sure its enabled since disabled items get highlighted. + if (menuItem && menuItem->IsEnabled()) + { + return menuItem->GetUserData(); + } + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void Menu::GetItemText(int itemID, wchar_t *text, int bufLenInBytes) +{ + if ( m_MenuItems.IsValidIndex( itemID ) ) + { + MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); + if (menuItem) + { + menuItem->GetText(text, bufLenInBytes); + return; + } + } + text[0] = 0; +} + +void Menu::GetItemText(int itemID, char *text, int bufLenInBytes) +{ + if ( m_MenuItems.IsValidIndex( itemID ) ) + { + MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); + if (menuItem) + { + menuItem->GetText( text, bufLenInBytes ); + return; + } + } + text[0] = 0; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Activate the n'th item in the menu list, as if that menu item had been selected by the user +//----------------------------------------------------------------------------- +void Menu::ActivateItem(int itemID) +{ + if ( m_MenuItems.IsValidIndex( itemID ) ) + { + MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); + // make sure its enabled since disabled items get highlighted. + if (menuItem && menuItem->IsEnabled()) + { + menuItem->FireActionSignal(); + m_iActivatedItem = itemID; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Menu::SilentActivateItem(int itemID) +{ + if ( m_MenuItems.IsValidIndex( itemID ) ) + { + MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); + // make sure its enabled since disabled items get highlighted. + if (menuItem && menuItem->IsEnabled()) + { + m_iActivatedItem = itemID; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Menu::ActivateItemByRow(int row) +{ + if (m_SortedItems.IsValidIndex(row)) + { + ActivateItem(m_SortedItems[row]); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Return the number of items currently in the menu list +//----------------------------------------------------------------------------- +int Menu::GetItemCount() +{ + return m_MenuItems.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int Menu::GetMenuID(int index) +{ + if ( !m_SortedItems.IsValidIndex(index) ) + return m_MenuItems.InvalidIndex(); + + return m_SortedItems[index]; +} + +//----------------------------------------------------------------------------- +// Purpose: Return the number of items currently visible in the menu list +//----------------------------------------------------------------------------- +int Menu::GetCurrentlyVisibleItemsCount() +{ + if (m_MenuItems.Count() < m_iNumVisibleLines) + { + int cMenuItems = 0; + FOR_EACH_LL(m_MenuItems, i) + { + if (m_MenuItems[i]->IsVisible()) + { + ++cMenuItems; + } + } + + return cMenuItems; + } + return m_iNumVisibleLines; +} + +//----------------------------------------------------------------------------- +// Purpose: Enables/disables choices in the list +// itemText - string name of item in the list +// state - true enables, false disables +//----------------------------------------------------------------------------- +void Menu::SetItemEnabled(const char *itemName, bool state) +{ + FOR_EACH_LL( m_MenuItems, i ) + { + if ((Q_stricmp(itemName, m_MenuItems[i]->GetName())) == 0) + { + m_MenuItems[i]->SetEnabled(state); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Enables/disables choices in the list +//----------------------------------------------------------------------------- +void Menu::SetItemEnabled(int itemID, bool state) +{ + if ( !m_MenuItems.IsValidIndex(itemID) ) + return; + + m_MenuItems[itemID]->SetEnabled(state); +} + +//----------------------------------------------------------------------------- +// Purpose: shows/hides choices in the list +//----------------------------------------------------------------------------- +void Menu::SetItemVisible(const char *itemName, bool state) +{ + FOR_EACH_LL( m_MenuItems, i ) + { + if ((Q_stricmp(itemName, m_MenuItems[i]->GetName())) == 0) + { + m_MenuItems[i]->SetVisible(state); + InvalidateLayout(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: shows/hides choices in the list +//----------------------------------------------------------------------------- +void Menu::SetItemVisible(int itemID, bool state) +{ + if ( !m_MenuItems.IsValidIndex(itemID) ) + return; + + m_MenuItems[itemID]->SetVisible(state); +} + +//----------------------------------------------------------------------------- +// Purpose: Make the scroll bar visible and narrow the menu +// also make items visible or invisible in the list as appropriate +//----------------------------------------------------------------------------- +void Menu::AddScrollBar() +{ + m_pScroller->SetVisible(true); + _sizedForScrollBar = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Make the scroll bar invisible and widen the menu +//----------------------------------------------------------------------------- +void Menu::RemoveScrollBar() +{ + m_pScroller->SetVisible(false); + _sizedForScrollBar = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Invalidate layout if the slider is moved so items scroll +//----------------------------------------------------------------------------- +void Menu::OnSliderMoved() +{ + CloseOtherMenus(NULL); // close any cascading menus + + // Invalidate so we redraw the menu! + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle into mouse mode. +//----------------------------------------------------------------------------- +void Menu::OnCursorMoved(int x, int y) +{ + m_iInputMode = MOUSE; + + // chain up + CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); + RequestFocus(); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle into keyboard mode. +//----------------------------------------------------------------------------- +void Menu::OnKeyCodePressed(KeyCode code) +{ + m_iInputMode = KEYBOARD; + // send the message to this parent in case this is a cascading menu + if (GetVParent()) + { + ivgui()->PostMessage(GetVParent(), new KeyValues("KeyModeSet"), GetVPanel()); + } + + BaseClass::OnKeyCodePressed( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the item currently highlighted in the menu by ptr +//----------------------------------------------------------------------------- +void Menu::SetCurrentlySelectedItem(MenuItem *item) +{ + int itemNum = -1; + // find it in our list of menuitems + FOR_EACH_LL( m_MenuItems, i ) + { + MenuItem *child = m_MenuItems[i]; + if (child == item) + { + itemNum = i; + break; + } + } + Assert( itemNum >= 0 ); + + SetCurrentlySelectedItem(itemNum); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Menu::ClearCurrentlyHighlightedItem() +{ + if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) ) + { + m_MenuItems[m_iCurrentlySelectedItemID]->DisarmItem(); + } + m_iCurrentlySelectedItemID = m_MenuItems.InvalidIndex(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the item currently highlighted in the menu by index +//----------------------------------------------------------------------------- +void Menu::SetCurrentlySelectedItem(int itemID) +{ + // dont deselect if its the same item + if (itemID == m_iCurrentlySelectedItemID) + return; + + if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) ) + { + m_MenuItems[m_iCurrentlySelectedItemID]->DisarmItem(); + } + + PostActionSignal(new KeyValues("MenuItemHighlight", "itemID", itemID)); + m_iCurrentlySelectedItemID = itemID; +} + +//----------------------------------------------------------------------------- +// This will set the item to be currenly selected and highlight it +// will not open cascading menu. This was added for comboboxes +// to have the combobox item highlighted in the menu when they open the +// dropdown. +//----------------------------------------------------------------------------- +void Menu::SetCurrentlyHighlightedItem(int itemID) +{ + SetCurrentlySelectedItem(itemID); + int row = m_SortedItems.Find(itemID); + // If we have no items, then row will be -1. The dev console, for example... + Assert( ( m_SortedItems.Count() == 0 ) || ( row != -1 ) ); + if ( row == -1 ) + return; + + // if there is a scroll bar, and we scroll off lets move it. + if ( m_pScroller->IsVisible() ) + { + // now if we are off the scroll bar, it means we moved the scroll bar + // by hand or set the item off the list + // so just snap the scroll bar straight to the item. + if ( ( row > m_pScroller->GetValue() + m_iNumVisibleLines - 1 ) || + ( row < m_pScroller->GetValue() ) ) + { + if ( !m_pScroller->IsVisible() ) + return; + + m_pScroller->SetValue(row); + } + } + + if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) ) + { + if ( !m_MenuItems[m_iCurrentlySelectedItemID]->IsArmed() ) + { + m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int Menu::GetCurrentlyHighlightedItem() +{ + return m_iCurrentlySelectedItemID; +} + +//----------------------------------------------------------------------------- +// Purpose: Respond to cursor entering a menuItem. +//----------------------------------------------------------------------------- +void Menu::OnCursorEnteredMenuItem(int VPanel) +{ + VPANEL menuItem = (VPANEL)VPanel; + // if we are in mouse mode + if (m_iInputMode == MOUSE) + { + MenuItem *item = static_cast(ipanel()->GetPanel(menuItem, GetModuleName())); + // arm the menu + item->ArmItem(); + // open the cascading menu if there is one. + item->OpenCascadeMenu(); + SetCurrentlySelectedItem(item); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Respond to cursor exiting a menuItem +//----------------------------------------------------------------------------- +void Menu::OnCursorExitedMenuItem(int VPanel) +{ + VPANEL menuItem = (VPANEL)VPanel; + // only care if we are in mouse mode + if (m_iInputMode == MOUSE) + { + MenuItem *item = static_cast(ipanel()->GetPanel(menuItem, GetModuleName())); + // unhighlight the item. + // note menuItems with cascading menus will stay lit. + item->DisarmItem(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Move up or down one in the list of items in the menu +// Direction is MENU_UP or MENU_DOWN +//----------------------------------------------------------------------------- +void Menu::MoveAlongMenuItemList(int direction, int loopCount) +{ + // Early out if no menu items to scroll through + if (m_MenuItems.Count() <= 0) + return; + + int itemID = m_iCurrentlySelectedItemID; + int row = m_SortedItems.Find(itemID); + row += direction; + + if ( row > m_SortedItems.Count() - 1 ) + { + if ( m_pScroller->IsVisible() ) + { + // stop at bottom of scrolled list + row = m_SortedItems.Count() - 1; + } + else + { + // if no scroll bar we circle around + row = 0; + } + } + else if (row < 0) + { + if ( m_pScroller->IsVisible() ) + { + // stop at top of scrolled list + row = m_pScroller->GetValue(); + } + else + { + // if no scroll bar circle around + row = m_SortedItems.Count()-1; + } + } + + // if there is a scroll bar, and we scroll off lets move it. + if ( m_pScroller->IsVisible() ) + { + if ( row > m_pScroller->GetValue() + m_iNumVisibleLines - 1) + { + int val = m_pScroller->GetValue(); + val -= -direction; + + m_pScroller->SetValue(val); + + // moving the slider redraws the scrollbar, + // and so we should redraw the menu since the + // menu draws the black border to the right of the scrollbar. + InvalidateLayout(); + } + else if ( row < m_pScroller->GetValue() ) + { + int val = m_pScroller->GetValue(); + val -= -direction; + + m_pScroller->SetValue(val); + + // moving the slider redraws the scrollbar, + // and so we should redraw the menu since the + // menu draws the black border to the right of the scrollbar. + InvalidateLayout(); + } + + // now if we are still off the scroll bar, it means we moved the scroll bar + // by hand and created a situation in which we moved an item down, but the + // scroll bar is already too far down and should scroll up or vice versa + // so just snap the scroll bar straight to the item. + if ( ( row > m_pScroller->GetValue() + m_iNumVisibleLines - 1) || + ( row < m_pScroller->GetValue() ) ) + { + m_pScroller->SetValue(row); + } + } + + // switch it back to an itemID from row + if ( m_SortedItems.IsValidIndex( row ) ) + { + SetCurrentlySelectedItem( m_SortedItems[row] ); + } + + // don't allow us to loop around more than once + if (loopCount < m_MenuItems.Count()) + { + // see if the text is empty, if so skip + wchar_t text[256]; + m_MenuItems[m_iCurrentlySelectedItemID]->GetText(text, 255); + if (text[0] == 0 || !m_MenuItems[m_iCurrentlySelectedItemID]->IsVisible()) + { + // menu item is empty, keep moving along + MoveAlongMenuItemList(direction, loopCount + 1); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Return which type of events the menu is currently interested in +// MenuItems need to know because behaviour is different depending on mode. +//----------------------------------------------------------------------------- +int Menu::GetMenuMode() +{ + return m_iInputMode; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the menu to key mode if a child menu goes into keymode +// This mode change has to be chained up through the menu heirarchy +// so cascading menus will work when you do a bunch of stuff in keymode +// in high level menus and then switch to keymode in lower level menus. +//----------------------------------------------------------------------------- +void Menu::OnKeyModeSet() +{ + m_iInputMode = KEYBOARD; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the checked state of a menuItem +//----------------------------------------------------------------------------- +void Menu::SetMenuItemChecked(int itemID, bool state) +{ + m_MenuItems[itemID]->SetChecked(state); +} + +//----------------------------------------------------------------------------- +// Purpose: Check if item is checked. +//----------------------------------------------------------------------------- +bool Menu::IsChecked(int itemID) +{ + return m_MenuItems[itemID]->IsChecked(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the minmum width the menu has to be. This +// is useful if you have a menu that is sized to the largest item in it +// but you don't want the menu to be thinner than the menu button +//----------------------------------------------------------------------------- +void Menu::SetMinimumWidth(int width) +{ + m_iMinimumWidth = width; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the minmum width the menu +//----------------------------------------------------------------------------- +int Menu::GetMinimumWidth() +{ + return m_iMinimumWidth; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void Menu::AddSeparator() +{ + int lastID = m_MenuItems.Count() - 1; + m_Separators.AddToTail( lastID ); + m_SeparatorPanels.AddToTail( new MenuSeparator( this, "MenuSeparator" ) ); +} + +void Menu::AddSeparatorAfterItem( int itemID ) +{ + Assert( m_MenuItems.IsValidIndex( itemID ) ); + m_Separators.AddToTail( itemID ); + m_SeparatorPanels.AddToTail( new MenuSeparator( this, "MenuSeparator" ) ); +} + +void Menu::MoveMenuItem( int itemID, int moveBeforeThisItemID ) +{ + int c = m_SortedItems.Count(); + int i; + for ( i = 0; i < c; ++i ) + { + if ( m_SortedItems[i] == itemID ) + { + m_SortedItems.Remove( i ); + break; + } + } + + // Didn't find it + if ( i >= c ) + { + return; + } + + // Now find insert pos + c = m_SortedItems.Count(); + for ( i = 0; i < c; ++i ) + { + if ( m_SortedItems[i] == moveBeforeThisItemID ) + { + m_SortedItems.InsertBefore( i, itemID ); + break; + } + } +} + +void Menu::SetFont( HFont font ) +{ + m_hItemFont = font; + if ( font ) + { + m_iMenuItemHeight = surface()->GetFontTall( font ) + 2; + } + InvalidateLayout(); +} + + +void Menu::SetCurrentKeyBinding( int itemID, char const *hotkey ) +{ + if ( m_MenuItems.IsValidIndex( itemID ) ) + { + MenuItem *menuItem = dynamic_cast(m_MenuItems[itemID]); + menuItem->SetCurrentKeyBinding( hotkey ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Static method to display a context menu +// Input : *parent - +// *menu - +//----------------------------------------------------------------------------- +void Menu::PlaceContextMenu( Panel *parent, Menu *menu ) +{ + Assert( parent ); + Assert( menu ); + if ( !menu || !parent ) + return; + + menu->SetVisible(false); + menu->SetParent( parent ); + menu->AddActionSignalTarget( parent ); + + // get cursor position, this is local to this text edit window + int cursorX, cursorY; + input()->GetCursorPos(cursorX, cursorY); + + menu->SetVisible(true); + + // relayout the menu immediately so that we know it's size + menu->InvalidateLayout(true); + int menuWide, menuTall; + menu->GetSize(menuWide, menuTall); + + // work out where the cursor is and therefore the best place to put the menu + int wide, tall; + surface()->GetScreenSize(wide, tall); + + if (wide - menuWide > cursorX) + { + // menu hanging right + if (tall - menuTall > cursorY) + { + // menu hanging down + menu->SetPos(cursorX, cursorY); + } + else + { + // menu hanging up + menu->SetPos(cursorX, cursorY - menuTall); + } + } + else + { + // menu hanging left + if (tall - menuTall > cursorY) + { + // menu hanging down + menu->SetPos(cursorX - menuWide, cursorY); + } + else + { + // menu hanging up + menu->SetPos(cursorX - menuWide, cursorY - menuTall); + } + } + + menu->RequestFocus(); +} + +void Menu::SetUseFallbackFont( bool bState, HFont hFallback ) +{ + m_hFallbackItemFont = hFallback; + m_bUseFallbackFont = bState; +} + +#ifdef DBGFLAG_VALIDATE +//----------------------------------------------------------------------------- +// Purpose: Run a global validation pass on all of our data structures and memory +// allocations. +// Input: validator - Our global validator object +// pchName - Our name (typically a member var in our container) +//----------------------------------------------------------------------------- +void Menu::Validate( CValidator &validator, char *pchName ) +{ + validator.Push( "vgui::Menu", this, pchName ); + + m_MenuItems.Validate( validator, "m_MenuItems" ); + m_SortedItems.Validate( validator, "m_SortedItems" ); + + BaseClass::Validate( validator, "vgui::Menu" ); + + validator.Pop(); +} +#endif // DBGFLAG_VALIDATE diff --git a/mp/src/vgui2/vgui_controls/MenuBar.cpp b/mp/src/vgui2/vgui_controls/MenuBar.cpp index f0f5dd11..43ae560a 100644 --- a/mp/src/vgui2/vgui_controls/MenuBar.cpp +++ b/mp/src/vgui2/vgui_controls/MenuBar.cpp @@ -1,252 +1,252 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - - -enum -{ - MENUBARINDENT = 4, // indent from top and bottom of panel. -}; - -DECLARE_BUILD_FACTORY( MenuBar ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -MenuBar::MenuBar(Panel *parent, const char *panelName) : - Panel(parent, panelName), - m_nRightEdge( 0 ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -MenuBar::~MenuBar() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void MenuBar::AddButton(MenuButton *button) -{ - button->SetParent(this); - button->AddActionSignalTarget(this); - m_pMenuButtons.AddToTail(button); -} - -//----------------------------------------------------------------------------- -// This will add the menu to the menu bar -//----------------------------------------------------------------------------- -void MenuBar::AddMenu( const char *pButtonName, Menu *pMenu ) -{ - MenuButton *pMenuButton = new MenuButton(this, pButtonName, pButtonName); - pMenuButton->SetMenu(pMenu); - AddButton(pMenuButton); -} - - -//----------------------------------------------------------------------------- -// Purpose: Handle key presses, Activate shortcuts -//----------------------------------------------------------------------------- -void MenuBar::OnKeyCodeTyped(KeyCode code) -{ - switch(code) - { - case KEY_RIGHT: - { - // iterate the menu items looking for one that is open - // if we find one open, open the one to the right - for (int i = 0; i < m_pMenuButtons.Count() - 1; i++) - { - MenuButton *panel = m_pMenuButtons[i]; - if (panel->IsDepressed()) - { - m_pMenuButtons[i]->DoClick(); - m_pMenuButtons[i+1]->DoClick(); - break; - } - } - break; - } - case KEY_LEFT: - { - // iterate the menu items looking for one that is open - // if we find one open, open the one to the left - for (int i = 1; i < m_pMenuButtons.Count(); i++) - { - MenuButton *panel = m_pMenuButtons[i]; - if (panel->IsDepressed()) - { - m_pMenuButtons[i]->DoClick(); - m_pMenuButtons[i-1]->DoClick(); - break; - } - } - break; - } - default: - { - break; - } - } - - // don't chain back -} - -//----------------------------------------------------------------------------- -// Purpose: Handle key presses, Activate shortcuts -// Input : code - -//----------------------------------------------------------------------------- -void MenuBar::OnKeyTyped(wchar_t unichar) -{ - if (unichar) - { - // iterate the menu items looking for one with the matching hotkey - for (int i = 0; i < m_pMenuButtons.Count(); i++) - { - MenuButton *panel = m_pMenuButtons[i]; - if (panel->IsVisible()) - { - Panel *hot = panel->HasHotkey(unichar); - if (hot) - { - // post a message to the menuitem telling it it's hotkey was pressed - PostMessage(hot, new KeyValues("Hotkey")); - return; - } - } - } - } - - // don't chain back -} - -void MenuBar::Paint() -{ - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - for ( int i = 0; i < m_pMenuButtons.Count(); i++) - { - if (!m_pMenuButtons[i]->IsArmed()) - m_pMenuButtons[i]->SetDefaultBorder(NULL); - else - { - m_pMenuButtons[i]->SetDefaultBorder(pScheme->GetBorder( "ButtonBorder")); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Message map -//----------------------------------------------------------------------------- -void MenuBar::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - // get the borders we need - SetBorder(pScheme->GetBorder("ButtonBorder")); - - // get the background color - SetBgColor(pScheme->GetColor( "MenuBar.BgColor", GetBgColor() )); - -} - - - -//----------------------------------------------------------------------------- -// Purpose: Reformat according to the new layout -//----------------------------------------------------------------------------- -void MenuBar::PerformLayout() -{ - int nBarWidth, nBarHeight; - GetSize( nBarWidth, nBarHeight ); - - // Now position + resize all buttons - int x = MENUBARINDENT; - for ( int i = 0; i < m_pMenuButtons.Count(); ++i ) - { - int nWide, nTall; - - m_pMenuButtons[i]->GetContentSize(nWide, nTall); - m_pMenuButtons[i]->SetPos( x, MENUBARINDENT ); - m_pMenuButtons[i]->SetSize( nWide + Label::Content, nBarHeight - 2 * MENUBARINDENT ); - - x += nWide + MENUBARINDENT; - } - - m_nRightEdge = x; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the size of the menus in the bar (so other children can be added to menu bar) -// Input : w - -// int&h - -//----------------------------------------------------------------------------- -void MenuBar::GetContentSize( int& w, int&h ) -{ - w = m_nRightEdge + 2; - h = GetTall(); -} - -//----------------------------------------------------------------------------- -// Purpose: Message map -//----------------------------------------------------------------------------- -void MenuBar::OnMenuClose() -{ - RequestFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: Message map -//----------------------------------------------------------------------------- -void MenuBar::OnCursorEnteredMenuButton(int VPanel) -{ - VPANEL menuButton = (VPANEL)VPanel; - // see if we had a menu open - for ( int i = 0; i < m_pMenuButtons.Count(); i++) - { - // one of our buttons was pressed. - if (m_pMenuButtons[i]->IsDepressed()) - { - int oldbutton = i; - // now see if menuButton is one of ours. - for ( int j = 0; j < m_pMenuButtons.Count(); j++) - { - MenuButton *button = static_cast(ipanel()->GetPanel(menuButton, GetModuleName())); - // it is one of ours. - if ( button == m_pMenuButtons[j]) - { - // if its a different button than the one we already had open, - if (j != oldbutton) - { - // close this menu and open the one we just entered - m_pMenuButtons[oldbutton]->DoClick(); - button->DoClick(); - } - } - } - } - } -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + + +enum +{ + MENUBARINDENT = 4, // indent from top and bottom of panel. +}; + +DECLARE_BUILD_FACTORY( MenuBar ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +MenuBar::MenuBar(Panel *parent, const char *panelName) : + Panel(parent, panelName), + m_nRightEdge( 0 ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +MenuBar::~MenuBar() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void MenuBar::AddButton(MenuButton *button) +{ + button->SetParent(this); + button->AddActionSignalTarget(this); + m_pMenuButtons.AddToTail(button); +} + +//----------------------------------------------------------------------------- +// This will add the menu to the menu bar +//----------------------------------------------------------------------------- +void MenuBar::AddMenu( const char *pButtonName, Menu *pMenu ) +{ + MenuButton *pMenuButton = new MenuButton(this, pButtonName, pButtonName); + pMenuButton->SetMenu(pMenu); + AddButton(pMenuButton); +} + + +//----------------------------------------------------------------------------- +// Purpose: Handle key presses, Activate shortcuts +//----------------------------------------------------------------------------- +void MenuBar::OnKeyCodeTyped(KeyCode code) +{ + switch(code) + { + case KEY_RIGHT: + { + // iterate the menu items looking for one that is open + // if we find one open, open the one to the right + for (int i = 0; i < m_pMenuButtons.Count() - 1; i++) + { + MenuButton *panel = m_pMenuButtons[i]; + if (panel->IsDepressed()) + { + m_pMenuButtons[i]->DoClick(); + m_pMenuButtons[i+1]->DoClick(); + break; + } + } + break; + } + case KEY_LEFT: + { + // iterate the menu items looking for one that is open + // if we find one open, open the one to the left + for (int i = 1; i < m_pMenuButtons.Count(); i++) + { + MenuButton *panel = m_pMenuButtons[i]; + if (panel->IsDepressed()) + { + m_pMenuButtons[i]->DoClick(); + m_pMenuButtons[i-1]->DoClick(); + break; + } + } + break; + } + default: + { + break; + } + } + + // don't chain back +} + +//----------------------------------------------------------------------------- +// Purpose: Handle key presses, Activate shortcuts +// Input : code - +//----------------------------------------------------------------------------- +void MenuBar::OnKeyTyped(wchar_t unichar) +{ + if (unichar) + { + // iterate the menu items looking for one with the matching hotkey + for (int i = 0; i < m_pMenuButtons.Count(); i++) + { + MenuButton *panel = m_pMenuButtons[i]; + if (panel->IsVisible()) + { + Panel *hot = panel->HasHotkey(unichar); + if (hot) + { + // post a message to the menuitem telling it it's hotkey was pressed + PostMessage(hot, new KeyValues("Hotkey")); + return; + } + } + } + } + + // don't chain back +} + +void MenuBar::Paint() +{ + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + for ( int i = 0; i < m_pMenuButtons.Count(); i++) + { + if (!m_pMenuButtons[i]->IsArmed()) + m_pMenuButtons[i]->SetDefaultBorder(NULL); + else + { + m_pMenuButtons[i]->SetDefaultBorder(pScheme->GetBorder( "ButtonBorder")); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Message map +//----------------------------------------------------------------------------- +void MenuBar::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + // get the borders we need + SetBorder(pScheme->GetBorder("ButtonBorder")); + + // get the background color + SetBgColor(pScheme->GetColor( "MenuBar.BgColor", GetBgColor() )); + +} + + + +//----------------------------------------------------------------------------- +// Purpose: Reformat according to the new layout +//----------------------------------------------------------------------------- +void MenuBar::PerformLayout() +{ + int nBarWidth, nBarHeight; + GetSize( nBarWidth, nBarHeight ); + + // Now position + resize all buttons + int x = MENUBARINDENT; + for ( int i = 0; i < m_pMenuButtons.Count(); ++i ) + { + int nWide, nTall; + + m_pMenuButtons[i]->GetContentSize(nWide, nTall); + m_pMenuButtons[i]->SetPos( x, MENUBARINDENT ); + m_pMenuButtons[i]->SetSize( nWide + Label::Content, nBarHeight - 2 * MENUBARINDENT ); + + x += nWide + MENUBARINDENT; + } + + m_nRightEdge = x; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the size of the menus in the bar (so other children can be added to menu bar) +// Input : w - +// int&h - +//----------------------------------------------------------------------------- +void MenuBar::GetContentSize( int& w, int&h ) +{ + w = m_nRightEdge + 2; + h = GetTall(); +} + +//----------------------------------------------------------------------------- +// Purpose: Message map +//----------------------------------------------------------------------------- +void MenuBar::OnMenuClose() +{ + RequestFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Message map +//----------------------------------------------------------------------------- +void MenuBar::OnCursorEnteredMenuButton(int VPanel) +{ + VPANEL menuButton = (VPANEL)VPanel; + // see if we had a menu open + for ( int i = 0; i < m_pMenuButtons.Count(); i++) + { + // one of our buttons was pressed. + if (m_pMenuButtons[i]->IsDepressed()) + { + int oldbutton = i; + // now see if menuButton is one of ours. + for ( int j = 0; j < m_pMenuButtons.Count(); j++) + { + MenuButton *button = static_cast(ipanel()->GetPanel(menuButton, GetModuleName())); + // it is one of ours. + if ( button == m_pMenuButtons[j]) + { + // if its a different button than the one we already had open, + if (j != oldbutton) + { + // close this menu and open the one we just entered + m_pMenuButtons[oldbutton]->DoClick(); + button->DoClick(); + } + } + } + } + } +} diff --git a/mp/src/vgui2/vgui_controls/MenuButton.cpp b/mp/src/vgui2/vgui_controls/MenuButton.cpp index 27f8741a..19b35ae6 100644 --- a/mp/src/vgui2/vgui_controls/MenuButton.cpp +++ b/mp/src/vgui2/vgui_controls/MenuButton.cpp @@ -1,351 +1,351 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#define PROTECTED_THINGS_DISABLE - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY_DEFAULT_TEXT( MenuButton, MenuButton ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -MenuButton::MenuButton(Panel *parent, const char *panelName, const char *text) : Button(parent, panelName, text) -{ - m_pMenu = NULL; - m_iDirection = Menu::DOWN; - m_pDropMenuImage = NULL; - m_nImageIndex = -1; - _openOffsetY = 0; - m_bDropMenuButtonStyle = true; // set to true so SetDropMenuButtonStyle() forces real init. - - SetDropMenuButtonStyle( false ); - SetUseCaptureMouse( false ); - SetButtonActivationType( ACTIVATE_ONPRESSED ); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -MenuButton::~MenuButton() -{ - delete m_pDropMenuImage; -} - -//----------------------------------------------------------------------------- -// Purpose: attaches a menu to the menu button -//----------------------------------------------------------------------------- -void MenuButton::SetMenu(Menu *menu) -{ - m_pMenu = menu; - - if (menu) - { - m_pMenu->SetVisible(false); - m_pMenu->AddActionSignalTarget(this); - m_pMenu->SetParent(this); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Never draw a focus border -//----------------------------------------------------------------------------- -void MenuButton::DrawFocusBorder(int tx0, int ty0, int tx1, int ty1) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the direction from the menu button the menu should open -//----------------------------------------------------------------------------- -void MenuButton::SetOpenDirection(Menu::MenuDirection_e direction) -{ - m_iDirection = direction; -} - - -//----------------------------------------------------------------------------- -// Purpose: hides the menu -//----------------------------------------------------------------------------- -void MenuButton::HideMenu(void) -{ - if (!m_pMenu) - return; - - // hide the menu - m_pMenu->SetVisible(false); - - // unstick the button - BaseClass::ForceDepressed(false); - Repaint(); - - OnHideMenu(m_pMenu); -} - - -//----------------------------------------------------------------------------- -// Purpose: Called when the menu button loses focus; hides the menu -//----------------------------------------------------------------------------- -void MenuButton::OnKillFocus( KeyValues *pParams ) -{ - VPANEL hPanel = (VPANEL)pParams->GetPtr( "newPanel" ); - if ( m_pMenu && !m_pMenu->HasFocus() && hPanel != m_pMenu->GetVPanel() ) - { - HideMenu(); - } - BaseClass::OnKillFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: Called when the menu is closed -//----------------------------------------------------------------------------- -void MenuButton::OnMenuClose() -{ - HideMenu(); - PostActionSignal(new KeyValues("MenuClose")); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the offset from where menu would normally be placed -// Only is used if menu is ALIGN_WITH_PARENT -//----------------------------------------------------------------------------- -void MenuButton::SetOpenOffsetY(int yOffset) -{ - _openOffsetY = yOffset; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool MenuButton::CanBeDefaultButton(void) -{ - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Handles hotkey accesses -//----------------------------------------------------------------------------- -void MenuButton::DoClick() -{ - if ( IsDropMenuButtonStyle() && - m_pDropMenuImage ) - { - int mx, my; - // force the menu to appear where the mouse button was pressed - input()->GetCursorPos( mx, my ); - ScreenToLocal( mx, my ); - - int contentW, contentH; - m_pDropMenuImage->GetContentSize( contentW, contentH ); - int drawX = GetWide() - contentW - 2; - if ( mx <= drawX || !OnCheckMenuItemCount() ) - { - // Treat it like a "regular" button click - BaseClass::DoClick(); - return; - } - } - - if ( !m_pMenu ) - { - return; - } - - // menu is already visible, hide the menu - if (m_pMenu->IsVisible()) - { - HideMenu(); - return; - } - - // do nothing if menu is not enabled - if (!m_pMenu->IsEnabled()) - { - return; - } - // force the menu to compute required width/height - m_pMenu->PerformLayout(); - - // Now position it so it can fit in the workspace - m_pMenu->PositionRelativeToPanel(this, m_iDirection, _openOffsetY ); - - // make sure we're at the top of the draw order (and therefore our children as well) - MoveToFront(); - - // notify - OnShowMenu(m_pMenu); - - // keep the button depressed - BaseClass::ForceDepressed(true); - - // show the menu - m_pMenu->SetVisible(true); - - // bring to focus - m_pMenu->RequestFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void MenuButton::OnKeyCodeTyped(KeyCode code) -{ - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - - if (!shift && !ctrl && !alt) - { - switch (code) - { - case KEY_ENTER: - { - if ( !IsDropMenuButtonStyle() ) - { - DoClick(); - } - break; - } - } - } - BaseClass::OnKeyCodeTyped(code); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void MenuButton::OnCursorEntered() -{ - Button::OnCursorEntered(); - // post a message to the parent menu. - // forward the message on to the parent of this menu. - KeyValues *msg = new KeyValues ("CursorEnteredMenuButton"); - // tell the parent this menuitem is the one that was entered so it can open the menu if it wants - msg->SetInt("VPanel", GetVPanel()); - ivgui()->PostMessage(GetVParent(), msg, NULL); -} - -// This style is like the IE "back" button where the left side acts like a regular button, the the right side has a little -// combo box dropdown indicator and presents and submenu -void MenuButton::SetDropMenuButtonStyle( bool state ) -{ - bool changed = m_bDropMenuButtonStyle != state; - m_bDropMenuButtonStyle = state; - if ( !changed ) - return; - - if ( state ) - { - m_pDropMenuImage = new TextImage( "u" ); - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - m_pDropMenuImage->SetFont(pScheme->GetFont("Marlett", IsProportional())); - // m_pDropMenuImage->SetContentAlignment(Label::a_west); - // m_pDropMenuImage->SetTextInset(3, 0); - m_nImageIndex = AddImage( m_pDropMenuImage, 0 ); - } - else - { - ResetToSimpleTextImage(); - delete m_pDropMenuImage; - m_pDropMenuImage = NULL; - m_nImageIndex = -1; - } -} - -void MenuButton::ApplySchemeSettings( IScheme *pScheme ) -{ - BaseClass::ApplySchemeSettings( pScheme ); - - if ( m_pDropMenuImage ) - { - SetImageAtIndex( 1, m_pDropMenuImage, 0 ); - } -} - - -void MenuButton::PerformLayout() -{ - BaseClass::PerformLayout(); - if ( !IsDropMenuButtonStyle() ) - return; - - Assert( m_nImageIndex >= 0 ); - if ( m_nImageIndex < 0 || !m_pDropMenuImage ) - return; - - int w, h; - GetSize( w, h ); - - int contentW, contentH; - m_pDropMenuImage->ResizeImageToContent(); - m_pDropMenuImage->GetContentSize( contentW, contentH ); - - SetImageBounds( m_nImageIndex, w - contentW - 2, contentW ); -} - -bool MenuButton::IsDropMenuButtonStyle() const -{ - return m_bDropMenuButtonStyle; -} - -void MenuButton::Paint(void) -{ - BaseClass::Paint(); - - if ( !IsDropMenuButtonStyle() ) - return; - - int contentW, contentH; - m_pDropMenuImage->GetContentSize( contentW, contentH ); - m_pDropMenuImage->SetColor( IsEnabled() ? GetButtonFgColor() : GetDisabledFgColor1() ); - - int drawX = GetWide() - contentW - 2; - - surface()->DrawSetColor( IsEnabled() ? GetButtonFgColor() : GetDisabledFgColor1() ); - surface()->DrawFilledRect( drawX, 3, drawX + 1, GetTall() - 3 ); -} - -void MenuButton::OnCursorMoved( int x, int y ) -{ - BaseClass::OnCursorMoved( x, y ); - - if ( !IsDropMenuButtonStyle() ) - return; - - int contentW, contentH; - m_pDropMenuImage->GetContentSize( contentW, contentH ); - int drawX = GetWide() - contentW - 2; - if ( x <= drawX || !OnCheckMenuItemCount() ) - { - SetButtonActivationType(ACTIVATE_ONPRESSEDANDRELEASED); - SetUseCaptureMouse(true); - } - else - { - SetButtonActivationType(ACTIVATE_ONPRESSED); - SetUseCaptureMouse(false); - } -} - -Menu *MenuButton::GetMenu() -{ - Assert( m_pMenu ); - return m_pMenu; +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#define PROTECTED_THINGS_DISABLE + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY_DEFAULT_TEXT( MenuButton, MenuButton ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +MenuButton::MenuButton(Panel *parent, const char *panelName, const char *text) : Button(parent, panelName, text) +{ + m_pMenu = NULL; + m_iDirection = Menu::DOWN; + m_pDropMenuImage = NULL; + m_nImageIndex = -1; + _openOffsetY = 0; + m_bDropMenuButtonStyle = true; // set to true so SetDropMenuButtonStyle() forces real init. + + SetDropMenuButtonStyle( false ); + SetUseCaptureMouse( false ); + SetButtonActivationType( ACTIVATE_ONPRESSED ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +MenuButton::~MenuButton() +{ + delete m_pDropMenuImage; +} + +//----------------------------------------------------------------------------- +// Purpose: attaches a menu to the menu button +//----------------------------------------------------------------------------- +void MenuButton::SetMenu(Menu *menu) +{ + m_pMenu = menu; + + if (menu) + { + m_pMenu->SetVisible(false); + m_pMenu->AddActionSignalTarget(this); + m_pMenu->SetParent(this); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Never draw a focus border +//----------------------------------------------------------------------------- +void MenuButton::DrawFocusBorder(int tx0, int ty0, int tx1, int ty1) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the direction from the menu button the menu should open +//----------------------------------------------------------------------------- +void MenuButton::SetOpenDirection(Menu::MenuDirection_e direction) +{ + m_iDirection = direction; +} + + +//----------------------------------------------------------------------------- +// Purpose: hides the menu +//----------------------------------------------------------------------------- +void MenuButton::HideMenu(void) +{ + if (!m_pMenu) + return; + + // hide the menu + m_pMenu->SetVisible(false); + + // unstick the button + BaseClass::ForceDepressed(false); + Repaint(); + + OnHideMenu(m_pMenu); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when the menu button loses focus; hides the menu +//----------------------------------------------------------------------------- +void MenuButton::OnKillFocus( KeyValues *pParams ) +{ + VPANEL hPanel = (VPANEL)pParams->GetPtr( "newPanel" ); + if ( m_pMenu && !m_pMenu->HasFocus() && hPanel != m_pMenu->GetVPanel() ) + { + HideMenu(); + } + BaseClass::OnKillFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the menu is closed +//----------------------------------------------------------------------------- +void MenuButton::OnMenuClose() +{ + HideMenu(); + PostActionSignal(new KeyValues("MenuClose")); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the offset from where menu would normally be placed +// Only is used if menu is ALIGN_WITH_PARENT +//----------------------------------------------------------------------------- +void MenuButton::SetOpenOffsetY(int yOffset) +{ + _openOffsetY = yOffset; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool MenuButton::CanBeDefaultButton(void) +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Handles hotkey accesses +//----------------------------------------------------------------------------- +void MenuButton::DoClick() +{ + if ( IsDropMenuButtonStyle() && + m_pDropMenuImage ) + { + int mx, my; + // force the menu to appear where the mouse button was pressed + input()->GetCursorPos( mx, my ); + ScreenToLocal( mx, my ); + + int contentW, contentH; + m_pDropMenuImage->GetContentSize( contentW, contentH ); + int drawX = GetWide() - contentW - 2; + if ( mx <= drawX || !OnCheckMenuItemCount() ) + { + // Treat it like a "regular" button click + BaseClass::DoClick(); + return; + } + } + + if ( !m_pMenu ) + { + return; + } + + // menu is already visible, hide the menu + if (m_pMenu->IsVisible()) + { + HideMenu(); + return; + } + + // do nothing if menu is not enabled + if (!m_pMenu->IsEnabled()) + { + return; + } + // force the menu to compute required width/height + m_pMenu->PerformLayout(); + + // Now position it so it can fit in the workspace + m_pMenu->PositionRelativeToPanel(this, m_iDirection, _openOffsetY ); + + // make sure we're at the top of the draw order (and therefore our children as well) + MoveToFront(); + + // notify + OnShowMenu(m_pMenu); + + // keep the button depressed + BaseClass::ForceDepressed(true); + + // show the menu + m_pMenu->SetVisible(true); + + // bring to focus + m_pMenu->RequestFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void MenuButton::OnKeyCodeTyped(KeyCode code) +{ + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + + if (!shift && !ctrl && !alt) + { + switch (code) + { + case KEY_ENTER: + { + if ( !IsDropMenuButtonStyle() ) + { + DoClick(); + } + break; + } + } + } + BaseClass::OnKeyCodeTyped(code); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void MenuButton::OnCursorEntered() +{ + Button::OnCursorEntered(); + // post a message to the parent menu. + // forward the message on to the parent of this menu. + KeyValues *msg = new KeyValues ("CursorEnteredMenuButton"); + // tell the parent this menuitem is the one that was entered so it can open the menu if it wants + msg->SetInt("VPanel", GetVPanel()); + ivgui()->PostMessage(GetVParent(), msg, NULL); +} + +// This style is like the IE "back" button where the left side acts like a regular button, the the right side has a little +// combo box dropdown indicator and presents and submenu +void MenuButton::SetDropMenuButtonStyle( bool state ) +{ + bool changed = m_bDropMenuButtonStyle != state; + m_bDropMenuButtonStyle = state; + if ( !changed ) + return; + + if ( state ) + { + m_pDropMenuImage = new TextImage( "u" ); + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + m_pDropMenuImage->SetFont(pScheme->GetFont("Marlett", IsProportional())); + // m_pDropMenuImage->SetContentAlignment(Label::a_west); + // m_pDropMenuImage->SetTextInset(3, 0); + m_nImageIndex = AddImage( m_pDropMenuImage, 0 ); + } + else + { + ResetToSimpleTextImage(); + delete m_pDropMenuImage; + m_pDropMenuImage = NULL; + m_nImageIndex = -1; + } +} + +void MenuButton::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + if ( m_pDropMenuImage ) + { + SetImageAtIndex( 1, m_pDropMenuImage, 0 ); + } +} + + +void MenuButton::PerformLayout() +{ + BaseClass::PerformLayout(); + if ( !IsDropMenuButtonStyle() ) + return; + + Assert( m_nImageIndex >= 0 ); + if ( m_nImageIndex < 0 || !m_pDropMenuImage ) + return; + + int w, h; + GetSize( w, h ); + + int contentW, contentH; + m_pDropMenuImage->ResizeImageToContent(); + m_pDropMenuImage->GetContentSize( contentW, contentH ); + + SetImageBounds( m_nImageIndex, w - contentW - 2, contentW ); +} + +bool MenuButton::IsDropMenuButtonStyle() const +{ + return m_bDropMenuButtonStyle; +} + +void MenuButton::Paint(void) +{ + BaseClass::Paint(); + + if ( !IsDropMenuButtonStyle() ) + return; + + int contentW, contentH; + m_pDropMenuImage->GetContentSize( contentW, contentH ); + m_pDropMenuImage->SetColor( IsEnabled() ? GetButtonFgColor() : GetDisabledFgColor1() ); + + int drawX = GetWide() - contentW - 2; + + surface()->DrawSetColor( IsEnabled() ? GetButtonFgColor() : GetDisabledFgColor1() ); + surface()->DrawFilledRect( drawX, 3, drawX + 1, GetTall() - 3 ); +} + +void MenuButton::OnCursorMoved( int x, int y ) +{ + BaseClass::OnCursorMoved( x, y ); + + if ( !IsDropMenuButtonStyle() ) + return; + + int contentW, contentH; + m_pDropMenuImage->GetContentSize( contentW, contentH ); + int drawX = GetWide() - contentW - 2; + if ( x <= drawX || !OnCheckMenuItemCount() ) + { + SetButtonActivationType(ACTIVATE_ONPRESSEDANDRELEASED); + SetUseCaptureMouse(true); + } + else + { + SetButtonActivationType(ACTIVATE_ONPRESSED); + SetUseCaptureMouse(false); + } +} + +Menu *MenuButton::GetMenu() +{ + Assert( m_pMenu ); + return m_pMenu; } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/MenuItem.cpp b/mp/src/vgui2/vgui_controls/MenuItem.cpp index 355bfaa4..49c39570 100644 --- a/mp/src/vgui2/vgui_controls/MenuItem.cpp +++ b/mp/src/vgui2/vgui_controls/MenuItem.cpp @@ -1,647 +1,647 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include "vgui/ISurface.h" -#include - -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: Check box image -//----------------------------------------------------------------------------- -class MenuItemCheckImage : public TextImage -{ -public: - MenuItemCheckImage(MenuItem *item) : TextImage( "g" ) - { - _menuItem = item; - - SetSize(20, 13); - } - - virtual void Paint() - { - DrawSetTextFont(GetFont()); - - // draw background - DrawSetTextColor(_menuItem->GetBgColor()); - DrawPrintChar(0, 0, 'g'); - - // draw check - if (_menuItem->IsChecked()) - { - if (_menuItem->IsEnabled()) - { - DrawSetTextColor(_menuItem->GetButtonFgColor()); - DrawPrintChar(0, 2, 'a'); - } - else if (!_menuItem->IsEnabled()) - { - // draw disabled version, with embossed look - // offset image - DrawSetTextColor(_menuItem->GetDisabledFgColor1()); - DrawPrintChar(1, 3, 'a'); - - // overlayed image - DrawSetTextColor(_menuItem->GetDisabledFgColor2()); - DrawPrintChar(0, 2, 'a'); - } - } - } - -private: - MenuItem *_menuItem; -}; - -DECLARE_BUILD_FACTORY_DEFAULT_TEXT( MenuItem, MenuItem ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -// Input: parent - the parent of this menu item, usually a menu -// text - the name of the menu item as it appears in the menu -// cascadeMenu - if this item triggers the opening of a cascading menu -// provide a pointer to it. -// MenuItems cannot be both checkable and trigger a cascade menu. -//----------------------------------------------------------------------------- -MenuItem::MenuItem(Menu *parent, const char *panelName, const char *text, Menu *cascadeMenu, bool checkable) : Button(parent, panelName, text) -{ - m_pCascadeMenu = cascadeMenu; - m_bCheckable = checkable; - SetButtonActivationType(ACTIVATE_ONRELEASED); - m_pUserData = NULL; - m_pCurrentKeyBinding = NULL; - - // only one arg should be passed in. - Assert (!(cascadeMenu && checkable)); - - Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -// Input: parent - the parent of this menu item, usually a menu -// text - the name of the menu item as it appears in the menu -// cascadeMenu - if this item triggers the opening of a cascading menu -// provide a pointer to it. -// MenuItems cannot be both checkable and trigger a cascade menu. -//----------------------------------------------------------------------------- -MenuItem::MenuItem(Menu *parent, const char *panelName, const wchar_t *wszText, Menu *cascadeMenu, bool checkable) : Button(parent, panelName, wszText) -{ - m_pCascadeMenu = cascadeMenu; - m_bCheckable = checkable; - SetButtonActivationType(ACTIVATE_ONRELEASED); - m_pUserData = NULL; - m_pCurrentKeyBinding = NULL; - - // only one arg should be passed in. - Assert (!(cascadeMenu && checkable)); - - Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -MenuItem::~MenuItem() -{ - delete m_pCascadeMenu; - delete m_pCascadeArrow; - delete m_pCheck; - if (m_pUserData) - { - m_pUserData->deleteThis(); - } - delete m_pCurrentKeyBinding; -} - -//----------------------------------------------------------------------------- -// Purpose: Basic initializer -//----------------------------------------------------------------------------- -void MenuItem::Init( void ) -{ - m_pCascadeArrow = NULL; - m_pCheck = NULL; - - if (m_pCascadeMenu) - { - m_pCascadeMenu->SetParent(this); - m_pCascadeArrow = new TextImage("4"); // this makes a right pointing arrow. - - m_pCascadeMenu->AddActionSignalTarget(this); - } - else if (m_bCheckable) - { - // move the text image over so we have room for the check - SetTextImageIndex(1); - m_pCheck = new MenuItemCheckImage(this); - SetImageAtIndex(0, m_pCheck, CHECK_INSET); - SetChecked(false); - } - - SetButtonBorderEnabled( false ); - SetUseCaptureMouse( false ); - SetContentAlignment( Label::a_west ); - - // note menus handle all the sizing of menuItem panels -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Menu *MenuItem::GetParentMenu() -{ - return (Menu *)GetParent(); -} - -//----------------------------------------------------------------------------- -// Purpose: Layout the Textimage and the Arrow part of the menuItem -//----------------------------------------------------------------------------- -void MenuItem::PerformLayout() -{ - Button::PerformLayout(); - // make the arrow image match the button layout. - // this will make it brighten and dim like the menu buttons. - if (m_pCascadeArrow) - { - m_pCascadeArrow->SetColor(GetButtonFgColor()); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Close the cascading menu if we have one. -//----------------------------------------------------------------------------- -void MenuItem::CloseCascadeMenu() -{ - if (m_pCascadeMenu) - { - if (m_pCascadeMenu->IsVisible()) - { - m_pCascadeMenu->SetVisible(false); - } - // disarm even if menu wasn't visible! - SetArmed(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handle cursor moving in a menuItem. -//----------------------------------------------------------------------------- -void MenuItem::OnCursorMoved(int x, int y) -{ - // if menu is in keymode and we moved the mouse - // highlight this item - if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD) - { - OnCursorEntered(); - } - - // chain up to parent - CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); -} - -//----------------------------------------------------------------------------- -// Purpose: Handle mouse cursor entering a menuItem. -//----------------------------------------------------------------------------- -void MenuItem::OnCursorEntered() -{ - // post a message to the parent menu. - // forward the message on to the parent of this menu. - KeyValues *msg = new KeyValues ("CursorEnteredMenuItem"); - // tell the parent this menuitem is the one that was entered so it can highlight it - msg->SetInt("VPanel", GetVPanel()); - - ivgui()->PostMessage(GetVParent(), msg, NULL); -} - -//----------------------------------------------------------------------------- -// Purpose: Handle mouse cursor exiting a menuItem. -//----------------------------------------------------------------------------- -void MenuItem::OnCursorExited() -{ - // post a message to the parent menu. - // forward the message on to the parent of this menu. - KeyValues *msg = new KeyValues ("CursorExitedMenuItem"); - // tell the parent this menuitem is the one that was entered so it can unhighlight it - msg->SetInt("VPanel", GetVPanel()); - - ivgui()->PostMessage(GetVParent(), msg, NULL); -} - -//----------------------------------------------------------------------------- -// Purpose: Handle mouse cursor exiting a menuItem. -//----------------------------------------------------------------------------- -void MenuItem::OnKeyCodeReleased(KeyCode code) -{ - if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD && m_pCascadeMenu) - { - return; - } - // only disarm if we are not opening a cascading menu using keys. - Button::OnKeyCodeReleased(code); -} - -//----------------------------------------------------------------------------- -// Purpose: Highlight a menu item -// Menu item buttons highlight if disabled, but won't activate. -//----------------------------------------------------------------------------- -void MenuItem::ArmItem() -{ - // close all other menus - GetParentMenu()->CloseOtherMenus(this); - // arm the menuItem. - Button::SetArmed(true); - - // When you have a submenu with no scroll bar the menu - // border will not be drawn correctly. This fixes it. - Menu *parent = GetParentMenu(); - if ( parent ) - { - parent->ForceCalculateWidth(); - } - - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Unhighlight a menu item -//----------------------------------------------------------------------------- -void MenuItem::DisarmItem() -{ - // normal behaviour is that the button becomes unarmed - // do not unarm if there is a cascading menu. CloseCascadeMenu handles this. - // and the menu handles it since we close at different times depending - // on whether menu is handling mouse or key events. - if (!m_pCascadeMenu) - { - Button::OnCursorExited(); - } - - // When you have a submenu with no scroll bar the menu - // border will not be drawn correctly. This fixes it. - Menu *parent = GetParentMenu(); - if ( parent ) - { - parent->ForceCalculateWidth(); - } - Repaint(); -} - -bool MenuItem::IsItemArmed() -{ - return Button::IsArmed(); -} - -//----------------------------------------------------------------------------- -// Purpose: Pass kill focus events up to parent, This will tell all panels -// in the hierarchy to hide themselves, and enables cascading menus to -// all disappear on selecting an item at the end of the tree. -//----------------------------------------------------------------------------- -void MenuItem::OnKillFocus() -{ - GetParentMenu()->OnKillFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: fire the menu item as if it has been selected and -// Tell the owner that it is closing -//----------------------------------------------------------------------------- -void MenuItem::FireActionSignal() -{ - // cascading menus items don't trigger the parent menu to disappear - // (they trigger the cascading menu to open/close when cursor is moved over/off them) - if (!m_pCascadeMenu) - { - KeyValues *kv = new KeyValues("MenuItemSelected"); - kv->SetPtr("panel", this); - ivgui()->PostMessage(GetVParent(), kv, GetVPanel()); - - // ivgui()->PostMessage(GetVParent(), new KeyValues("MenuItemSelected"), GetVPanel()); - Button::FireActionSignal(); - // toggle the check next to the item if it is checkable - if (m_bCheckable) - { - SetChecked( !m_bChecked ); - } - } - else - { - // if we are in keyboard mode, open the child menu. - if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD) - { - OpenCascadeMenu(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Opens the cascading menu. -//----------------------------------------------------------------------------- -void MenuItem::OpenCascadeMenu() -{ - if (m_pCascadeMenu) - { - // perform layout on menu, this way it will open in the right spot - // if the window's been moved - m_pCascadeMenu->PerformLayout(); - m_pCascadeMenu->SetVisible(true); - ArmItem(); - } -} - -//----------------------------------------------------------------------------- -// Purpse: Return true if this item triggers a cascading menu -//----------------------------------------------------------------------------- -bool MenuItem::HasMenu() -{ - return (m_pCascadeMenu != NULL); -} - -//----------------------------------------------------------------------------- -// Purpose: Apply the resource scheme to the menu. -//----------------------------------------------------------------------------- -void MenuItem::ApplySchemeSettings(IScheme *pScheme) -{ - // chain back first - Button::ApplySchemeSettings(pScheme); - - // get color settings - SetDefaultColor(GetSchemeColor("Menu.TextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.BgColor", GetBgColor(), pScheme)); - SetArmedColor(GetSchemeColor("Menu.ArmedTextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.ArmedBgColor", GetBgColor(), pScheme)); - SetDepressedColor(GetSchemeColor("Menu.ArmedTextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.ArmedBgColor", GetBgColor(), pScheme)); - - SetTextInset(atoi(pScheme->GetResourceString("Menu.TextInset")), 0); - - // reload images since applyschemesettings in label wipes them out. - if ( m_pCascadeArrow ) - { - m_pCascadeArrow->SetFont(pScheme->GetFont("Marlett", IsProportional() )); - m_pCascadeArrow->ResizeImageToContent(); - AddImage(m_pCascadeArrow, 0); - } - else if (m_bCheckable) - { - ( static_cast(m_pCheck) )->SetFont( pScheme->GetFont("Marlett", IsProportional())); - SetImageAtIndex(0, m_pCheck, CHECK_INSET); - ( static_cast(m_pCheck) )->ResizeImageToContent(); - } - - if ( m_pCurrentKeyBinding ) - { - m_pCurrentKeyBinding->SetFont(pScheme->GetFont("Default", IsProportional() )); - m_pCurrentKeyBinding->ResizeImageToContent(); - } - - // Have the menu redo the layout - // Get the parent to resize - Menu * parent = GetParentMenu(); - if ( parent ) - { - parent->ForceCalculateWidth(); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Return the size of the text portion of the label. -// for normal menu items this is the same as the label size, but for -// cascading menus it gives you the size of the text portion only, without -// the arrow. -//----------------------------------------------------------------------------- -void MenuItem::GetTextImageSize(int &wide, int &tall) -{ - GetTextImage()->GetSize(wide, tall); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the size of the text portion of the label. -// For normal menu items this is the same as the label size, but for -// cascading menus it sizes textImage portion only, without -// the arrow. -//----------------------------------------------------------------------------- -void MenuItem::SetTextImageSize(int wide, int tall) -{ - GetTextImage()->SetSize(wide, tall); -} - -//----------------------------------------------------------------------------- -// Purpose: Return the size of the arrow portion of the label. -// If the menuItem is not a cascading menu, 0 is returned. -//----------------------------------------------------------------------------- -void MenuItem::GetArrowImageSize(int &wide, int &tall) -{ - wide = 0, tall = 0; - if (m_pCascadeArrow) - { - m_pCascadeArrow->GetSize(wide, tall); - return; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Return the size of the check portion of the label. -//----------------------------------------------------------------------------- -void MenuItem::GetCheckImageSize(int &wide, int &tall) -{ - wide = 0, tall = 0; - if (m_pCheck) - { - // resize the image to the contents size - ( static_cast(m_pCheck) )->ResizeImageToContent(); - m_pCheck->GetSize(wide, tall); - - // include the inset for the check, since nobody but us know about the inset - wide += CHECK_INSET; - return; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Return a the menu that this menuItem contains -// This is useful when the parent menu's commands must be -// sent through all menus that are open as well (like hotkeys) -//----------------------------------------------------------------------------- -Menu *MenuItem::GetMenu() -{ - return m_pCascadeMenu; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the border style for the button. Menu items have no border so -// return null. -//----------------------------------------------------------------------------- -IBorder *MenuItem::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) -{ - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the menu to key mode if a child menu goes into keymode -//----------------------------------------------------------------------------- -void MenuItem::OnKeyModeSet() -{ - // send the message to this parent in case this is a cascading menu - ivgui()->PostMessage(GetVParent(), new KeyValues("KeyModeSet"), GetVPanel()); -} - - -//----------------------------------------------------------------------------- -// Purpose: Return if this menuitem is checkable or not -// This is used by menus to perform the layout properly. -//----------------------------------------------------------------------------- -bool MenuItem::IsCheckable() -{ - return m_bCheckable; -} - -//----------------------------------------------------------------------------- -// Purpose: Return if this menuitem is checked or not -//----------------------------------------------------------------------------- -bool MenuItem::IsChecked() -{ - return m_bChecked; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the checked state of a checkable menuitem -// Does nothing if item is not checkable -//----------------------------------------------------------------------------- -void MenuItem::SetChecked(bool state) -{ - if (m_bCheckable) - { - m_bChecked = state; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool MenuItem::CanBeDefaultButton(void) -{ - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -KeyValues *MenuItem::GetUserData() -{ - if ( HasMenu() ) - { - return m_pCascadeMenu->GetItemUserData( m_pCascadeMenu->GetActiveItem() ); - } - else - { - return m_pUserData; - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets the user data -//----------------------------------------------------------------------------- -void MenuItem::SetUserData(const KeyValues *kv) -{ - if (m_pUserData) - { - m_pUserData->deleteThis(); - m_pUserData = NULL; - } - - if ( kv ) - { - m_pUserData = kv->MakeCopy(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Passing in NULL removes this object -// Input : *keyName - -//----------------------------------------------------------------------------- -void MenuItem::SetCurrentKeyBinding( char const *keyName ) -{ - if ( !keyName ) - { - delete m_pCurrentKeyBinding; - m_pCurrentKeyBinding = NULL; - return; - } - - if ( !m_pCurrentKeyBinding ) - { - m_pCurrentKeyBinding = new TextImage( keyName ); - } - else - { - char curtext[ 256 ]; - m_pCurrentKeyBinding->GetText( curtext, sizeof( curtext ) ); - if ( !Q_strcmp( curtext, keyName ) ) - return; - - m_pCurrentKeyBinding->SetText( keyName ); - } - - InvalidateLayout( false, true ); -} - -#define KEYBINDING_INSET 5 - -void MenuItem::Paint() -{ - BaseClass::Paint(); - if ( !m_pCurrentKeyBinding ) - return; - - int w, h; - GetSize( w, h ); - int iw, ih; - m_pCurrentKeyBinding->GetSize( iw, ih ); - - int x = w - iw - KEYBINDING_INSET; - int y = ( h - ih ) / 2; - - if ( IsEnabled() ) - { - m_pCurrentKeyBinding->SetPos( x, y ); - m_pCurrentKeyBinding->SetColor( GetButtonFgColor() ); - m_pCurrentKeyBinding->Paint(); - } - else - { - m_pCurrentKeyBinding->SetPos( x + 1 , y + 1 ); - m_pCurrentKeyBinding->SetColor( GetDisabledFgColor1() ); - m_pCurrentKeyBinding->Paint(); - - surface()->DrawFlushText(); - - m_pCurrentKeyBinding->SetPos( x, y ); - m_pCurrentKeyBinding->SetColor( GetDisabledFgColor2() ); - m_pCurrentKeyBinding->Paint(); - } -} - -void MenuItem::GetContentSize( int& cw, int &ch ) -{ - BaseClass::GetContentSize( cw, ch ); - if ( !m_pCurrentKeyBinding ) - return; - - int iw, ih; - m_pCurrentKeyBinding->GetSize( iw, ih ); - - cw += iw + KEYBINDING_INSET; - ch = max( ch, ih ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include "vgui/ISurface.h" +#include + +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Check box image +//----------------------------------------------------------------------------- +class MenuItemCheckImage : public TextImage +{ +public: + MenuItemCheckImage(MenuItem *item) : TextImage( "g" ) + { + _menuItem = item; + + SetSize(20, 13); + } + + virtual void Paint() + { + DrawSetTextFont(GetFont()); + + // draw background + DrawSetTextColor(_menuItem->GetBgColor()); + DrawPrintChar(0, 0, 'g'); + + // draw check + if (_menuItem->IsChecked()) + { + if (_menuItem->IsEnabled()) + { + DrawSetTextColor(_menuItem->GetButtonFgColor()); + DrawPrintChar(0, 2, 'a'); + } + else if (!_menuItem->IsEnabled()) + { + // draw disabled version, with embossed look + // offset image + DrawSetTextColor(_menuItem->GetDisabledFgColor1()); + DrawPrintChar(1, 3, 'a'); + + // overlayed image + DrawSetTextColor(_menuItem->GetDisabledFgColor2()); + DrawPrintChar(0, 2, 'a'); + } + } + } + +private: + MenuItem *_menuItem; +}; + +DECLARE_BUILD_FACTORY_DEFAULT_TEXT( MenuItem, MenuItem ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +// Input: parent - the parent of this menu item, usually a menu +// text - the name of the menu item as it appears in the menu +// cascadeMenu - if this item triggers the opening of a cascading menu +// provide a pointer to it. +// MenuItems cannot be both checkable and trigger a cascade menu. +//----------------------------------------------------------------------------- +MenuItem::MenuItem(Menu *parent, const char *panelName, const char *text, Menu *cascadeMenu, bool checkable) : Button(parent, panelName, text) +{ + m_pCascadeMenu = cascadeMenu; + m_bCheckable = checkable; + SetButtonActivationType(ACTIVATE_ONRELEASED); + m_pUserData = NULL; + m_pCurrentKeyBinding = NULL; + + // only one arg should be passed in. + Assert (!(cascadeMenu && checkable)); + + Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +// Input: parent - the parent of this menu item, usually a menu +// text - the name of the menu item as it appears in the menu +// cascadeMenu - if this item triggers the opening of a cascading menu +// provide a pointer to it. +// MenuItems cannot be both checkable and trigger a cascade menu. +//----------------------------------------------------------------------------- +MenuItem::MenuItem(Menu *parent, const char *panelName, const wchar_t *wszText, Menu *cascadeMenu, bool checkable) : Button(parent, panelName, wszText) +{ + m_pCascadeMenu = cascadeMenu; + m_bCheckable = checkable; + SetButtonActivationType(ACTIVATE_ONRELEASED); + m_pUserData = NULL; + m_pCurrentKeyBinding = NULL; + + // only one arg should be passed in. + Assert (!(cascadeMenu && checkable)); + + Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +MenuItem::~MenuItem() +{ + delete m_pCascadeMenu; + delete m_pCascadeArrow; + delete m_pCheck; + if (m_pUserData) + { + m_pUserData->deleteThis(); + } + delete m_pCurrentKeyBinding; +} + +//----------------------------------------------------------------------------- +// Purpose: Basic initializer +//----------------------------------------------------------------------------- +void MenuItem::Init( void ) +{ + m_pCascadeArrow = NULL; + m_pCheck = NULL; + + if (m_pCascadeMenu) + { + m_pCascadeMenu->SetParent(this); + m_pCascadeArrow = new TextImage("4"); // this makes a right pointing arrow. + + m_pCascadeMenu->AddActionSignalTarget(this); + } + else if (m_bCheckable) + { + // move the text image over so we have room for the check + SetTextImageIndex(1); + m_pCheck = new MenuItemCheckImage(this); + SetImageAtIndex(0, m_pCheck, CHECK_INSET); + SetChecked(false); + } + + SetButtonBorderEnabled( false ); + SetUseCaptureMouse( false ); + SetContentAlignment( Label::a_west ); + + // note menus handle all the sizing of menuItem panels +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Menu *MenuItem::GetParentMenu() +{ + return (Menu *)GetParent(); +} + +//----------------------------------------------------------------------------- +// Purpose: Layout the Textimage and the Arrow part of the menuItem +//----------------------------------------------------------------------------- +void MenuItem::PerformLayout() +{ + Button::PerformLayout(); + // make the arrow image match the button layout. + // this will make it brighten and dim like the menu buttons. + if (m_pCascadeArrow) + { + m_pCascadeArrow->SetColor(GetButtonFgColor()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Close the cascading menu if we have one. +//----------------------------------------------------------------------------- +void MenuItem::CloseCascadeMenu() +{ + if (m_pCascadeMenu) + { + if (m_pCascadeMenu->IsVisible()) + { + m_pCascadeMenu->SetVisible(false); + } + // disarm even if menu wasn't visible! + SetArmed(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle cursor moving in a menuItem. +//----------------------------------------------------------------------------- +void MenuItem::OnCursorMoved(int x, int y) +{ + // if menu is in keymode and we moved the mouse + // highlight this item + if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD) + { + OnCursorEntered(); + } + + // chain up to parent + CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); +} + +//----------------------------------------------------------------------------- +// Purpose: Handle mouse cursor entering a menuItem. +//----------------------------------------------------------------------------- +void MenuItem::OnCursorEntered() +{ + // post a message to the parent menu. + // forward the message on to the parent of this menu. + KeyValues *msg = new KeyValues ("CursorEnteredMenuItem"); + // tell the parent this menuitem is the one that was entered so it can highlight it + msg->SetInt("VPanel", GetVPanel()); + + ivgui()->PostMessage(GetVParent(), msg, NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: Handle mouse cursor exiting a menuItem. +//----------------------------------------------------------------------------- +void MenuItem::OnCursorExited() +{ + // post a message to the parent menu. + // forward the message on to the parent of this menu. + KeyValues *msg = new KeyValues ("CursorExitedMenuItem"); + // tell the parent this menuitem is the one that was entered so it can unhighlight it + msg->SetInt("VPanel", GetVPanel()); + + ivgui()->PostMessage(GetVParent(), msg, NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: Handle mouse cursor exiting a menuItem. +//----------------------------------------------------------------------------- +void MenuItem::OnKeyCodeReleased(KeyCode code) +{ + if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD && m_pCascadeMenu) + { + return; + } + // only disarm if we are not opening a cascading menu using keys. + Button::OnKeyCodeReleased(code); +} + +//----------------------------------------------------------------------------- +// Purpose: Highlight a menu item +// Menu item buttons highlight if disabled, but won't activate. +//----------------------------------------------------------------------------- +void MenuItem::ArmItem() +{ + // close all other menus + GetParentMenu()->CloseOtherMenus(this); + // arm the menuItem. + Button::SetArmed(true); + + // When you have a submenu with no scroll bar the menu + // border will not be drawn correctly. This fixes it. + Menu *parent = GetParentMenu(); + if ( parent ) + { + parent->ForceCalculateWidth(); + } + + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Unhighlight a menu item +//----------------------------------------------------------------------------- +void MenuItem::DisarmItem() +{ + // normal behaviour is that the button becomes unarmed + // do not unarm if there is a cascading menu. CloseCascadeMenu handles this. + // and the menu handles it since we close at different times depending + // on whether menu is handling mouse or key events. + if (!m_pCascadeMenu) + { + Button::OnCursorExited(); + } + + // When you have a submenu with no scroll bar the menu + // border will not be drawn correctly. This fixes it. + Menu *parent = GetParentMenu(); + if ( parent ) + { + parent->ForceCalculateWidth(); + } + Repaint(); +} + +bool MenuItem::IsItemArmed() +{ + return Button::IsArmed(); +} + +//----------------------------------------------------------------------------- +// Purpose: Pass kill focus events up to parent, This will tell all panels +// in the hierarchy to hide themselves, and enables cascading menus to +// all disappear on selecting an item at the end of the tree. +//----------------------------------------------------------------------------- +void MenuItem::OnKillFocus() +{ + GetParentMenu()->OnKillFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: fire the menu item as if it has been selected and +// Tell the owner that it is closing +//----------------------------------------------------------------------------- +void MenuItem::FireActionSignal() +{ + // cascading menus items don't trigger the parent menu to disappear + // (they trigger the cascading menu to open/close when cursor is moved over/off them) + if (!m_pCascadeMenu) + { + KeyValues *kv = new KeyValues("MenuItemSelected"); + kv->SetPtr("panel", this); + ivgui()->PostMessage(GetVParent(), kv, GetVPanel()); + + // ivgui()->PostMessage(GetVParent(), new KeyValues("MenuItemSelected"), GetVPanel()); + Button::FireActionSignal(); + // toggle the check next to the item if it is checkable + if (m_bCheckable) + { + SetChecked( !m_bChecked ); + } + } + else + { + // if we are in keyboard mode, open the child menu. + if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD) + { + OpenCascadeMenu(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Opens the cascading menu. +//----------------------------------------------------------------------------- +void MenuItem::OpenCascadeMenu() +{ + if (m_pCascadeMenu) + { + // perform layout on menu, this way it will open in the right spot + // if the window's been moved + m_pCascadeMenu->PerformLayout(); + m_pCascadeMenu->SetVisible(true); + ArmItem(); + } +} + +//----------------------------------------------------------------------------- +// Purpse: Return true if this item triggers a cascading menu +//----------------------------------------------------------------------------- +bool MenuItem::HasMenu() +{ + return (m_pCascadeMenu != NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: Apply the resource scheme to the menu. +//----------------------------------------------------------------------------- +void MenuItem::ApplySchemeSettings(IScheme *pScheme) +{ + // chain back first + Button::ApplySchemeSettings(pScheme); + + // get color settings + SetDefaultColor(GetSchemeColor("Menu.TextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.BgColor", GetBgColor(), pScheme)); + SetArmedColor(GetSchemeColor("Menu.ArmedTextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.ArmedBgColor", GetBgColor(), pScheme)); + SetDepressedColor(GetSchemeColor("Menu.ArmedTextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.ArmedBgColor", GetBgColor(), pScheme)); + + SetTextInset(atoi(pScheme->GetResourceString("Menu.TextInset")), 0); + + // reload images since applyschemesettings in label wipes them out. + if ( m_pCascadeArrow ) + { + m_pCascadeArrow->SetFont(pScheme->GetFont("Marlett", IsProportional() )); + m_pCascadeArrow->ResizeImageToContent(); + AddImage(m_pCascadeArrow, 0); + } + else if (m_bCheckable) + { + ( static_cast(m_pCheck) )->SetFont( pScheme->GetFont("Marlett", IsProportional())); + SetImageAtIndex(0, m_pCheck, CHECK_INSET); + ( static_cast(m_pCheck) )->ResizeImageToContent(); + } + + if ( m_pCurrentKeyBinding ) + { + m_pCurrentKeyBinding->SetFont(pScheme->GetFont("Default", IsProportional() )); + m_pCurrentKeyBinding->ResizeImageToContent(); + } + + // Have the menu redo the layout + // Get the parent to resize + Menu * parent = GetParentMenu(); + if ( parent ) + { + parent->ForceCalculateWidth(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Return the size of the text portion of the label. +// for normal menu items this is the same as the label size, but for +// cascading menus it gives you the size of the text portion only, without +// the arrow. +//----------------------------------------------------------------------------- +void MenuItem::GetTextImageSize(int &wide, int &tall) +{ + GetTextImage()->GetSize(wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the size of the text portion of the label. +// For normal menu items this is the same as the label size, but for +// cascading menus it sizes textImage portion only, without +// the arrow. +//----------------------------------------------------------------------------- +void MenuItem::SetTextImageSize(int wide, int tall) +{ + GetTextImage()->SetSize(wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: Return the size of the arrow portion of the label. +// If the menuItem is not a cascading menu, 0 is returned. +//----------------------------------------------------------------------------- +void MenuItem::GetArrowImageSize(int &wide, int &tall) +{ + wide = 0, tall = 0; + if (m_pCascadeArrow) + { + m_pCascadeArrow->GetSize(wide, tall); + return; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Return the size of the check portion of the label. +//----------------------------------------------------------------------------- +void MenuItem::GetCheckImageSize(int &wide, int &tall) +{ + wide = 0, tall = 0; + if (m_pCheck) + { + // resize the image to the contents size + ( static_cast(m_pCheck) )->ResizeImageToContent(); + m_pCheck->GetSize(wide, tall); + + // include the inset for the check, since nobody but us know about the inset + wide += CHECK_INSET; + return; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Return a the menu that this menuItem contains +// This is useful when the parent menu's commands must be +// sent through all menus that are open as well (like hotkeys) +//----------------------------------------------------------------------------- +Menu *MenuItem::GetMenu() +{ + return m_pCascadeMenu; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the border style for the button. Menu items have no border so +// return null. +//----------------------------------------------------------------------------- +IBorder *MenuItem::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) +{ + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the menu to key mode if a child menu goes into keymode +//----------------------------------------------------------------------------- +void MenuItem::OnKeyModeSet() +{ + // send the message to this parent in case this is a cascading menu + ivgui()->PostMessage(GetVParent(), new KeyValues("KeyModeSet"), GetVPanel()); +} + + +//----------------------------------------------------------------------------- +// Purpose: Return if this menuitem is checkable or not +// This is used by menus to perform the layout properly. +//----------------------------------------------------------------------------- +bool MenuItem::IsCheckable() +{ + return m_bCheckable; +} + +//----------------------------------------------------------------------------- +// Purpose: Return if this menuitem is checked or not +//----------------------------------------------------------------------------- +bool MenuItem::IsChecked() +{ + return m_bChecked; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the checked state of a checkable menuitem +// Does nothing if item is not checkable +//----------------------------------------------------------------------------- +void MenuItem::SetChecked(bool state) +{ + if (m_bCheckable) + { + m_bChecked = state; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool MenuItem::CanBeDefaultButton(void) +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +KeyValues *MenuItem::GetUserData() +{ + if ( HasMenu() ) + { + return m_pCascadeMenu->GetItemUserData( m_pCascadeMenu->GetActiveItem() ); + } + else + { + return m_pUserData; + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets the user data +//----------------------------------------------------------------------------- +void MenuItem::SetUserData(const KeyValues *kv) +{ + if (m_pUserData) + { + m_pUserData->deleteThis(); + m_pUserData = NULL; + } + + if ( kv ) + { + m_pUserData = kv->MakeCopy(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Passing in NULL removes this object +// Input : *keyName - +//----------------------------------------------------------------------------- +void MenuItem::SetCurrentKeyBinding( char const *keyName ) +{ + if ( !keyName ) + { + delete m_pCurrentKeyBinding; + m_pCurrentKeyBinding = NULL; + return; + } + + if ( !m_pCurrentKeyBinding ) + { + m_pCurrentKeyBinding = new TextImage( keyName ); + } + else + { + char curtext[ 256 ]; + m_pCurrentKeyBinding->GetText( curtext, sizeof( curtext ) ); + if ( !Q_strcmp( curtext, keyName ) ) + return; + + m_pCurrentKeyBinding->SetText( keyName ); + } + + InvalidateLayout( false, true ); +} + +#define KEYBINDING_INSET 5 + +void MenuItem::Paint() +{ + BaseClass::Paint(); + if ( !m_pCurrentKeyBinding ) + return; + + int w, h; + GetSize( w, h ); + int iw, ih; + m_pCurrentKeyBinding->GetSize( iw, ih ); + + int x = w - iw - KEYBINDING_INSET; + int y = ( h - ih ) / 2; + + if ( IsEnabled() ) + { + m_pCurrentKeyBinding->SetPos( x, y ); + m_pCurrentKeyBinding->SetColor( GetButtonFgColor() ); + m_pCurrentKeyBinding->Paint(); + } + else + { + m_pCurrentKeyBinding->SetPos( x + 1 , y + 1 ); + m_pCurrentKeyBinding->SetColor( GetDisabledFgColor1() ); + m_pCurrentKeyBinding->Paint(); + + surface()->DrawFlushText(); + + m_pCurrentKeyBinding->SetPos( x, y ); + m_pCurrentKeyBinding->SetColor( GetDisabledFgColor2() ); + m_pCurrentKeyBinding->Paint(); + } +} + +void MenuItem::GetContentSize( int& cw, int &ch ) +{ + BaseClass::GetContentSize( cw, ch ); + if ( !m_pCurrentKeyBinding ) + return; + + int iw, ih; + m_pCurrentKeyBinding->GetSize( iw, ih ); + + cw += iw + KEYBINDING_INSET; + ch = max( ch, ih ); +} diff --git a/mp/src/vgui2/vgui_controls/MessageBox.cpp b/mp/src/vgui2/vgui_controls/MessageBox.cpp index d46cb280..e825e6bc 100644 --- a/mp/src/vgui2/vgui_controls/MessageBox.cpp +++ b/mp/src/vgui2/vgui_controls/MessageBox.cpp @@ -1,395 +1,395 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include - -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -vgui::Panel *MessageBox_Factory() -{ - return new MessageBox("MessageBox", "MessageBoxText"); -} - -DECLARE_BUILD_FACTORY_CUSTOM( MessageBox, MessageBox_Factory ); -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -MessageBox::MessageBox(const char *title, const char *text, Panel *parent) : Frame(parent, NULL, false) -{ - SetTitle(title, true); - m_pMessageLabel = new Label(this, NULL, text); - - Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -MessageBox::MessageBox(const wchar_t *wszTitle, const wchar_t *wszText, Panel *parent) : Frame(parent, NULL, false) -{ - SetTitle(wszTitle, true); - m_pMessageLabel = new Label(this, NULL, wszText); - - Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor Helper -//----------------------------------------------------------------------------- -void MessageBox::Init() -{ - SetDeleteSelfOnClose(true); - m_pFrameOver = NULL; - m_bShowMessageBoxOverCursor = false; - - SetMenuButtonResponsive(false); - SetMinimizeButtonVisible(false); - SetCloseButtonVisible(false); - SetSizeable(false); - - m_pOkButton = new Button(this, NULL, "#MessageBox_OK"); - m_pOkButton->SetCommand( "OnOk" ); - m_pOkButton->AddActionSignalTarget(this); - - m_pCancelButton = new Button(this, NULL, "#MessageBox_Cancel"); - m_pCancelButton->SetCommand( "OnCancel" ); - m_pCancelButton->AddActionSignalTarget(this); - m_pCancelButton->SetVisible( false ); - - m_OkCommand = m_CancelCommand = NULL; - m_bNoAutoClose = false; -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -MessageBox::~MessageBox() -{ - if ( m_OkCommand ) - { - m_OkCommand->deleteThis(); - } - if ( m_CancelCommand ) - { - m_CancelCommand->deleteThis(); - } -} - - -//----------------------------------------------------------------------------- -// Shows the message box over the cursor -//----------------------------------------------------------------------------- -void MessageBox::ShowMessageBoxOverCursor( bool bEnable ) -{ - m_bShowMessageBoxOverCursor = bEnable; -} - - -//----------------------------------------------------------------------------- -// Purpose: size the message label properly -//----------------------------------------------------------------------------- -void MessageBox::OnCommand( const char *pCommand ) -{ - if ( vgui::input()->GetAppModalSurface() == GetVPanel() ) - { - vgui::input()->ReleaseAppModalSurface(); - } - - if ( !Q_stricmp( pCommand, "OnOk" ) ) - { - if ( m_OkCommand ) - { - PostActionSignal(m_OkCommand->MakeCopy()); - } - } - else if ( !Q_stricmp( pCommand, "OnCancel" ) ) - { - if ( m_CancelCommand ) - { - PostActionSignal(m_CancelCommand->MakeCopy()); - } - } - - if ( !m_bNoAutoClose ) - { - OnShutdownRequest(); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: size the message label properly -//----------------------------------------------------------------------------- -void MessageBox::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - int wide, tall; - m_pMessageLabel->GetContentSize(wide, tall); - m_pMessageLabel->SetSize(wide, tall); - - wide += 100; - tall += 100; - SetSize(wide, tall); - - if ( m_bShowMessageBoxOverCursor ) - { - PlaceUnderCursor(); - return; - } - - // move to the middle of the screen - if ( m_pFrameOver ) - { - int frameX, frameY; - int frameWide, frameTall; - m_pFrameOver->GetPos(frameX, frameY); - m_pFrameOver->GetSize(frameWide, frameTall); - - SetPos((frameWide - wide) / 2 + frameX, (frameTall - tall) / 2 + frameY); - } - else - { - int swide, stall; - surface()->GetScreenSize(swide, stall); - // put the dialog in the middle of the screen - SetPos((swide - wide) / 2, (stall - tall) / 2); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Put the message box into a modal state -// Does not suspend execution - use addActionSignal to get return value -//----------------------------------------------------------------------------- -void MessageBox::DoModal(Frame* pFrameOver) -{ - ShowWindow(pFrameOver); -/* - // move to the middle of the screen - // get the screen size - int wide, tall; - // get our dialog size - GetSize(wide, tall); - - if (pFrameOver) - { - int frameX, frameY; - int frameWide, frameTall; - pFrameOver->GetPos(frameX, frameY); - pFrameOver->GetSize(frameWide, frameTall); - - SetPos((frameWide - wide) / 2 + frameX, (frameTall - tall) / 2 + frameY); - } - else - { - int swide, stall; - surface()->GetScreenSize(swide, stall); - // put the dialog in the middle of the screen - SetPos((swide - wide) / 2, (stall - tall) / 2); - } - - SetVisible( true ); - SetEnabled( true ); - MoveToFront(); - - if (m_pOkButton->IsVisible()) - m_pOkButton->RequestFocus(); - else // handle message boxes with no button - RequestFocus(); -*/ - input()->SetAppModalSurface(GetVPanel()); -} - -void MessageBox::ShowWindow(Frame *pFrameOver) -{ - m_pFrameOver = pFrameOver; - - SetVisible( true ); - SetEnabled( true ); - MoveToFront(); - - if ( m_pOkButton->IsVisible() ) - { - m_pOkButton->RequestFocus(); - } - else // handle message boxes with no button - { - RequestFocus(); - } - - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Put the text and OK buttons in correct place -//----------------------------------------------------------------------------- -void MessageBox::PerformLayout() -{ - int x, y, wide, tall; - GetClientArea(x, y, wide, tall); - wide += x; - tall += y; - - int boxWidth, boxTall; - GetSize(boxWidth, boxTall); - - int oldWide, oldTall; - m_pOkButton->GetSize(oldWide, oldTall); - - int btnWide, btnTall; - m_pOkButton->GetContentSize(btnWide, btnTall); - btnWide = max(oldWide, btnWide + 10); - btnTall = max(oldTall, btnTall + 10); - m_pOkButton->SetSize(btnWide, btnTall); - - int btnWide2 = 0, btnTall2 = 0; - if ( m_pCancelButton->IsVisible() ) - { - m_pCancelButton->GetSize(oldWide, oldTall); - - m_pCancelButton->GetContentSize(btnWide2, btnTall2); - btnWide2 = max(oldWide, btnWide2 + 10); - btnTall2 = max(oldTall, btnTall2 + 10); - m_pCancelButton->SetSize(btnWide2, btnTall2); - } - - boxWidth = max(boxWidth, m_pMessageLabel->GetWide() + 100); - boxWidth = max(boxWidth, (btnWide + btnWide2) * 2 + 30); - SetSize(boxWidth, boxTall); - - GetSize(boxWidth, boxTall); - - m_pMessageLabel->SetPos((wide/2)-(m_pMessageLabel->GetWide()/2) + x, y + 5 ); - if ( !m_pCancelButton->IsVisible() ) - { - m_pOkButton->SetPos((wide/2)-(m_pOkButton->GetWide()/2) + x, tall - m_pOkButton->GetTall() - 15); - } - else - { - m_pOkButton->SetPos((wide/4)-(m_pOkButton->GetWide()/2) + x, tall - m_pOkButton->GetTall() - 15); - m_pCancelButton->SetPos((3*wide/4)-(m_pOkButton->GetWide()/2) + x, tall - m_pOkButton->GetTall() - 15); - } - - BaseClass::PerformLayout(); - GetSize(boxWidth, boxTall); -} - - -//----------------------------------------------------------------------------- -// Purpose: Set a string command to be sent when the OK button is pressed. -//----------------------------------------------------------------------------- -void MessageBox::SetCommand(const char *command) -{ - if (m_OkCommand) - { - m_OkCommand->deleteThis(); - } - m_OkCommand = new KeyValues("Command", "command", command); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the command -//----------------------------------------------------------------------------- -void MessageBox::SetCommand(KeyValues *command) -{ - if (m_OkCommand) - { - m_OkCommand->deleteThis(); - } - m_OkCommand = command; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void MessageBox::OnShutdownRequest() -{ - // Shutdown the dialog - PostMessage(this, new KeyValues("Close")); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the visibility of the OK button. -//----------------------------------------------------------------------------- -void MessageBox::SetOKButtonVisible(bool state) -{ - m_pOkButton->SetVisible(state); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the Text on the OK button -//----------------------------------------------------------------------------- -void MessageBox::SetOKButtonText(const char *buttonText) -{ - m_pOkButton->SetText(buttonText); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the Text on the OK button -//----------------------------------------------------------------------------- -void MessageBox::SetOKButtonText(const wchar_t *wszButtonText) -{ - m_pOkButton->SetText(wszButtonText); - InvalidateLayout(); -} - - -//----------------------------------------------------------------------------- -// Cancel button (off by default) -//----------------------------------------------------------------------------- -void MessageBox::SetCancelButtonVisible(bool state) -{ - m_pCancelButton->SetVisible(state); - InvalidateLayout(); -} - -void MessageBox::SetCancelButtonText(const char *buttonText) -{ - m_pCancelButton->SetText(buttonText); - InvalidateLayout(); -} - -void MessageBox::SetCancelButtonText(const wchar_t *wszButtonText) -{ - m_pCancelButton->SetText(wszButtonText); - InvalidateLayout(); -} - -void MessageBox::SetCancelCommand( KeyValues *command ) -{ - if (m_CancelCommand) - { - m_CancelCommand->deleteThis(); - } - m_CancelCommand = command; -} - - -//----------------------------------------------------------------------------- -// Purpose: Toggles visibility of the close box. -//----------------------------------------------------------------------------- -void MessageBox::DisableCloseButton(bool state) -{ - BaseClass::SetCloseButtonVisible(state); - m_bNoAutoClose = true; -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include + +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +vgui::Panel *MessageBox_Factory() +{ + return new MessageBox("MessageBox", "MessageBoxText"); +} + +DECLARE_BUILD_FACTORY_CUSTOM( MessageBox, MessageBox_Factory ); +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +MessageBox::MessageBox(const char *title, const char *text, Panel *parent) : Frame(parent, NULL, false) +{ + SetTitle(title, true); + m_pMessageLabel = new Label(this, NULL, text); + + Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +MessageBox::MessageBox(const wchar_t *wszTitle, const wchar_t *wszText, Panel *parent) : Frame(parent, NULL, false) +{ + SetTitle(wszTitle, true); + m_pMessageLabel = new Label(this, NULL, wszText); + + Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor Helper +//----------------------------------------------------------------------------- +void MessageBox::Init() +{ + SetDeleteSelfOnClose(true); + m_pFrameOver = NULL; + m_bShowMessageBoxOverCursor = false; + + SetMenuButtonResponsive(false); + SetMinimizeButtonVisible(false); + SetCloseButtonVisible(false); + SetSizeable(false); + + m_pOkButton = new Button(this, NULL, "#MessageBox_OK"); + m_pOkButton->SetCommand( "OnOk" ); + m_pOkButton->AddActionSignalTarget(this); + + m_pCancelButton = new Button(this, NULL, "#MessageBox_Cancel"); + m_pCancelButton->SetCommand( "OnCancel" ); + m_pCancelButton->AddActionSignalTarget(this); + m_pCancelButton->SetVisible( false ); + + m_OkCommand = m_CancelCommand = NULL; + m_bNoAutoClose = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +MessageBox::~MessageBox() +{ + if ( m_OkCommand ) + { + m_OkCommand->deleteThis(); + } + if ( m_CancelCommand ) + { + m_CancelCommand->deleteThis(); + } +} + + +//----------------------------------------------------------------------------- +// Shows the message box over the cursor +//----------------------------------------------------------------------------- +void MessageBox::ShowMessageBoxOverCursor( bool bEnable ) +{ + m_bShowMessageBoxOverCursor = bEnable; +} + + +//----------------------------------------------------------------------------- +// Purpose: size the message label properly +//----------------------------------------------------------------------------- +void MessageBox::OnCommand( const char *pCommand ) +{ + if ( vgui::input()->GetAppModalSurface() == GetVPanel() ) + { + vgui::input()->ReleaseAppModalSurface(); + } + + if ( !Q_stricmp( pCommand, "OnOk" ) ) + { + if ( m_OkCommand ) + { + PostActionSignal(m_OkCommand->MakeCopy()); + } + } + else if ( !Q_stricmp( pCommand, "OnCancel" ) ) + { + if ( m_CancelCommand ) + { + PostActionSignal(m_CancelCommand->MakeCopy()); + } + } + + if ( !m_bNoAutoClose ) + { + OnShutdownRequest(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: size the message label properly +//----------------------------------------------------------------------------- +void MessageBox::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + int wide, tall; + m_pMessageLabel->GetContentSize(wide, tall); + m_pMessageLabel->SetSize(wide, tall); + + wide += 100; + tall += 100; + SetSize(wide, tall); + + if ( m_bShowMessageBoxOverCursor ) + { + PlaceUnderCursor(); + return; + } + + // move to the middle of the screen + if ( m_pFrameOver ) + { + int frameX, frameY; + int frameWide, frameTall; + m_pFrameOver->GetPos(frameX, frameY); + m_pFrameOver->GetSize(frameWide, frameTall); + + SetPos((frameWide - wide) / 2 + frameX, (frameTall - tall) / 2 + frameY); + } + else + { + int swide, stall; + surface()->GetScreenSize(swide, stall); + // put the dialog in the middle of the screen + SetPos((swide - wide) / 2, (stall - tall) / 2); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Put the message box into a modal state +// Does not suspend execution - use addActionSignal to get return value +//----------------------------------------------------------------------------- +void MessageBox::DoModal(Frame* pFrameOver) +{ + ShowWindow(pFrameOver); +/* + // move to the middle of the screen + // get the screen size + int wide, tall; + // get our dialog size + GetSize(wide, tall); + + if (pFrameOver) + { + int frameX, frameY; + int frameWide, frameTall; + pFrameOver->GetPos(frameX, frameY); + pFrameOver->GetSize(frameWide, frameTall); + + SetPos((frameWide - wide) / 2 + frameX, (frameTall - tall) / 2 + frameY); + } + else + { + int swide, stall; + surface()->GetScreenSize(swide, stall); + // put the dialog in the middle of the screen + SetPos((swide - wide) / 2, (stall - tall) / 2); + } + + SetVisible( true ); + SetEnabled( true ); + MoveToFront(); + + if (m_pOkButton->IsVisible()) + m_pOkButton->RequestFocus(); + else // handle message boxes with no button + RequestFocus(); +*/ + input()->SetAppModalSurface(GetVPanel()); +} + +void MessageBox::ShowWindow(Frame *pFrameOver) +{ + m_pFrameOver = pFrameOver; + + SetVisible( true ); + SetEnabled( true ); + MoveToFront(); + + if ( m_pOkButton->IsVisible() ) + { + m_pOkButton->RequestFocus(); + } + else // handle message boxes with no button + { + RequestFocus(); + } + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Put the text and OK buttons in correct place +//----------------------------------------------------------------------------- +void MessageBox::PerformLayout() +{ + int x, y, wide, tall; + GetClientArea(x, y, wide, tall); + wide += x; + tall += y; + + int boxWidth, boxTall; + GetSize(boxWidth, boxTall); + + int oldWide, oldTall; + m_pOkButton->GetSize(oldWide, oldTall); + + int btnWide, btnTall; + m_pOkButton->GetContentSize(btnWide, btnTall); + btnWide = max(oldWide, btnWide + 10); + btnTall = max(oldTall, btnTall + 10); + m_pOkButton->SetSize(btnWide, btnTall); + + int btnWide2 = 0, btnTall2 = 0; + if ( m_pCancelButton->IsVisible() ) + { + m_pCancelButton->GetSize(oldWide, oldTall); + + m_pCancelButton->GetContentSize(btnWide2, btnTall2); + btnWide2 = max(oldWide, btnWide2 + 10); + btnTall2 = max(oldTall, btnTall2 + 10); + m_pCancelButton->SetSize(btnWide2, btnTall2); + } + + boxWidth = max(boxWidth, m_pMessageLabel->GetWide() + 100); + boxWidth = max(boxWidth, (btnWide + btnWide2) * 2 + 30); + SetSize(boxWidth, boxTall); + + GetSize(boxWidth, boxTall); + + m_pMessageLabel->SetPos((wide/2)-(m_pMessageLabel->GetWide()/2) + x, y + 5 ); + if ( !m_pCancelButton->IsVisible() ) + { + m_pOkButton->SetPos((wide/2)-(m_pOkButton->GetWide()/2) + x, tall - m_pOkButton->GetTall() - 15); + } + else + { + m_pOkButton->SetPos((wide/4)-(m_pOkButton->GetWide()/2) + x, tall - m_pOkButton->GetTall() - 15); + m_pCancelButton->SetPos((3*wide/4)-(m_pOkButton->GetWide()/2) + x, tall - m_pOkButton->GetTall() - 15); + } + + BaseClass::PerformLayout(); + GetSize(boxWidth, boxTall); +} + + +//----------------------------------------------------------------------------- +// Purpose: Set a string command to be sent when the OK button is pressed. +//----------------------------------------------------------------------------- +void MessageBox::SetCommand(const char *command) +{ + if (m_OkCommand) + { + m_OkCommand->deleteThis(); + } + m_OkCommand = new KeyValues("Command", "command", command); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the command +//----------------------------------------------------------------------------- +void MessageBox::SetCommand(KeyValues *command) +{ + if (m_OkCommand) + { + m_OkCommand->deleteThis(); + } + m_OkCommand = command; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void MessageBox::OnShutdownRequest() +{ + // Shutdown the dialog + PostMessage(this, new KeyValues("Close")); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the visibility of the OK button. +//----------------------------------------------------------------------------- +void MessageBox::SetOKButtonVisible(bool state) +{ + m_pOkButton->SetVisible(state); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the Text on the OK button +//----------------------------------------------------------------------------- +void MessageBox::SetOKButtonText(const char *buttonText) +{ + m_pOkButton->SetText(buttonText); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the Text on the OK button +//----------------------------------------------------------------------------- +void MessageBox::SetOKButtonText(const wchar_t *wszButtonText) +{ + m_pOkButton->SetText(wszButtonText); + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Cancel button (off by default) +//----------------------------------------------------------------------------- +void MessageBox::SetCancelButtonVisible(bool state) +{ + m_pCancelButton->SetVisible(state); + InvalidateLayout(); +} + +void MessageBox::SetCancelButtonText(const char *buttonText) +{ + m_pCancelButton->SetText(buttonText); + InvalidateLayout(); +} + +void MessageBox::SetCancelButtonText(const wchar_t *wszButtonText) +{ + m_pCancelButton->SetText(wszButtonText); + InvalidateLayout(); +} + +void MessageBox::SetCancelCommand( KeyValues *command ) +{ + if (m_CancelCommand) + { + m_CancelCommand->deleteThis(); + } + m_CancelCommand = command; +} + + +//----------------------------------------------------------------------------- +// Purpose: Toggles visibility of the close box. +//----------------------------------------------------------------------------- +void MessageBox::DisableCloseButton(bool state) +{ + BaseClass::SetCloseButtonVisible(state); + m_bNoAutoClose = true; +} diff --git a/mp/src/vgui2/vgui_controls/MessageDialog.cpp b/mp/src/vgui2/vgui_controls/MessageDialog.cpp index 202888c0..dc0d491d 100644 --- a/mp/src/vgui2/vgui_controls/MessageDialog.cpp +++ b/mp/src/vgui2/vgui_controls/MessageDialog.cpp @@ -1,359 +1,359 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "vgui_controls/MessageDialog.h" -#include "vgui/ILocalize.h" -#include "vgui/ISurface.h" - -// NOTE: This has to be the last file included! -#include "tier0/memdbgon.h" - - -//----------------------------------------------------------------------------- -// CMessageDialog -//----------------------------------------------------------------------------- -CMessageDialog::CMessageDialog( vgui::Panel *pParent, const uint nType, const char *pTitle, const char *pMsg, const char *pCmdA, const char *pCmdB, vgui::Panel *pCreator, bool bShowActivity ) - : BaseClass( pParent, "MessageDialog" ) -{ - SetSize( 500, 200 ); - SetDeleteSelfOnClose( true ); - SetTitleBarVisible( false ); - SetCloseButtonVisible( false ); - SetSizeable( false ); - - m_pControlSettings = NULL; - m_pCreator = pCreator ? pCreator : pParent; - - m_nType = nType; - m_pTitle = new vgui::Label( this, "TitleLabel", pTitle ); - m_pMsg = new vgui::Label( this, "MessageLabel", pMsg ); - m_pAnimatingPanel = new vgui::AnimatingImagePanel( this, "AnimatingPanel" ); - - m_bShowActivity = bShowActivity; - - if ( nType & MD_SIMPLEFRAME ) - { - SetPaintBackgroundEnabled( true ); - m_pBackground = NULL; - } - else - { - m_pBackground = new vgui::ImagePanel( this, "Background" ); - if ( nType & MD_WARNING ) - { - m_pBackground->SetName( "WarningBackground" ); - } - else if ( nType & MD_ERROR ) - { - m_pBackground->SetName( "ErrorBackground" ); - } - } - - Q_memset( m_pCommands, 0, sizeof( m_pCommands ) ); - Q_memset( m_Buttons, 0, sizeof( m_Buttons ) ); - - if ( pCmdA ) - { - const int len = Q_strlen( pCmdA ) + 1; - m_pCommands[BTN_A] = (char*)malloc( len ); - Q_strncpy( m_pCommands[BTN_A], pCmdA, len ); - } - - if ( pCmdB ) - { - const int len = Q_strlen( pCmdB ) + 1; - m_pCommands[BTN_B] = (char*)malloc( len ); - Q_strncpy( m_pCommands[BTN_B], pCmdB, len ); - } - - // invalid until pressed - m_ButtonPressed = BTN_INVALID; -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -CMessageDialog::~CMessageDialog() -{ - if ( m_ButtonPressed != BTN_INVALID && ( m_nType & MD_COMMANDAFTERCLOSE ) ) - { - // caller wants the command sent after closure - m_pCreator->OnCommand( m_pCommands[m_ButtonPressed] ); - } - else if ( m_nType & MD_COMMANDONFORCECLOSE ) - { - // caller wants the command sent after closure - m_pCreator->OnCommand( m_pCommands[BTN_A] ); - } - - for ( int i = 0; i < MAX_BUTTONS; ++i ) - { - free( m_pCommands[i] ); - m_pCommands[i] = NULL; - - delete m_Buttons[i].pIcon; - delete m_Buttons[i].pText; - } - - delete m_pTitle; - m_pTitle = NULL; - - delete m_pMsg; - m_pMsg = NULL; - - delete m_pBackground; - m_pBackground = NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the keyvalues to pass to LoadControlSettings() -//----------------------------------------------------------------------------- -void CMessageDialog::SetControlSettingsKeys( KeyValues *pKeys ) -{ - m_pControlSettings = pKeys; -} - -//----------------------------------------------------------------------------- -// Purpose: Create a new button -//----------------------------------------------------------------------------- -void CMessageDialog::CreateButtonLabel( ButtonLabel_s *pButton, const char *pIcon, const char *pText ) -{ - pButton->nWide = m_ButtonIconLabelSpace; - pButton->bCreated = true; - - pButton->pIcon = new vgui::Label( this, "icon", pIcon ); - SETUP_PANEL( pButton->pIcon ); - pButton->pIcon->SetFont( m_hButtonFont ); - pButton->pIcon->SizeToContents(); - pButton->nWide += pButton->pIcon->GetWide(); - - pButton->pText = new vgui::Label( this, "text", pText ); - SETUP_PANEL( pButton->pText ); - pButton->pText->SetFont( m_hTextFont ); - pButton->pText->SizeToContents(); - pButton->pText->SetFgColor( m_ButtonTextColor ); - pButton->nWide += pButton->pText->GetWide(); -} - -//----------------------------------------------------------------------------- -// Purpose: Create and arrange the panel button labels according to the dialog type -//----------------------------------------------------------------------------- -void CMessageDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) -{ - BaseClass::ApplySchemeSettings( pScheme ); - - LoadControlSettings( "resource/UI/MessageDialog.res", "GAME", m_pControlSettings ); - - m_hButtonFont = pScheme->GetFont( "GameUIButtons" ); - m_hTextFont = pScheme->GetFont( "MenuLarge" ); - - if ( m_nType & MD_OK ) - { - CreateButtonLabel( &m_Buttons[BTN_A], "#GameUI_Icons_A_BUTTON", "#GameUI_OK" ); - } - else if ( m_nType & MD_CANCEL ) - { - CreateButtonLabel( &m_Buttons[BTN_B], "#GameUI_Icons_B_BUTTON", "#GameUI_Cancel" ); - } - else if ( m_nType & MD_OKCANCEL ) - { - CreateButtonLabel( &m_Buttons[BTN_A], "#GameUI_Icons_A_BUTTON", "#GameUI_OK" ); - CreateButtonLabel( &m_Buttons[BTN_B], "#GameUI_Icons_B_BUTTON", "#GameUI_Cancel" ); - } - else if ( m_nType & MD_YESNO ) - { - CreateButtonLabel( &m_Buttons[BTN_A], "#GameUI_Icons_A_BUTTON", "#GameUI_Yes" ); - CreateButtonLabel( &m_Buttons[BTN_B], "#GameUI_Icons_B_BUTTON", "#GameUI_No" ); - } - - // count the buttons and add up their widths - int cButtons = 0; - int nTotalWide = 0; - for ( int i = 0; i < MAX_BUTTONS; ++i ) - { - if ( m_Buttons[i].bCreated ) - { - ++cButtons; - nTotalWide += m_Buttons[i].nWide; - } - } - - // make sure text and icons are center-aligned vertically with each other - int nButtonTall = vgui::surface()->GetFontTall( m_hButtonFont ); - int nTextTall = vgui::surface()->GetFontTall( m_hTextFont ); - int nVerticalAdjust = ( nButtonTall - nTextTall ) / 2; - - // position the buttons with even horizontal spacing - int xpos = 0; - int ypos = GetTall() - max( nButtonTall, nTextTall ) - m_ButtonMargin; - int nSpacing = ( GetWide() - nTotalWide ) / ( cButtons + 1 ); - for ( int i = 0; i < MAX_BUTTONS; ++i ) - { - if ( m_Buttons[i].bCreated ) - { - xpos += nSpacing; - m_Buttons[i].pIcon->SetPos( xpos, ypos ); - xpos += m_Buttons[i].pIcon->GetWide() + m_ButtonIconLabelSpace; - m_Buttons[i].pText->SetPos( xpos, ypos + nVerticalAdjust ); - xpos += m_Buttons[i].pText->GetWide(); - } - } - - m_clrNotSimpleBG = pScheme->GetColor( "MessageDialog.MatchmakingBG", Color( 200, 184, 151, 255 ) ); - m_clrNotSimpleBGBlack = pScheme->GetColor( "MessageDialog.MatchmakingBGBlack", Color( 52, 48, 55, 255 ) ); - - if ( !m_bShowActivity ) - { - if ( m_pAnimatingPanel ) - { - if ( m_pAnimatingPanel->IsVisible() ) - { - - m_pAnimatingPanel->SetVisible( false ); - } - - m_pAnimatingPanel->StopAnimation(); - } - } - else - { - if ( m_pAnimatingPanel ) - { - if ( !m_pAnimatingPanel->IsVisible() ) - { - m_pAnimatingPanel->SetVisible( true ); - } - - m_pAnimatingPanel->StartAnimation(); - } - } - - MoveToCenterOfScreen(); - - if ( m_bShowActivity && m_ActivityIndent ) - { - // If we're animating, we push our text label in, and reduce its width - int iX,iY,iW,iH; - m_pMsg->GetBounds( iX, iY, iW, iH ); - m_pMsg->SetBounds( iX + m_ActivityIndent, iY, max(0,iW-m_ActivityIndent), iH ); - } - - // Invalidate the scheme on our message label so that it recalculates - // its line breaks in case it was resized when we loaded our .res file. - m_pMsg->InvalidateLayout( false, true ); -} - -//----------------------------------------------------------------------------- -// Purpose: Create and arrange the panel button labels according to the dialog type -//----------------------------------------------------------------------------- -void CMessageDialog::ApplySettings( KeyValues *inResourceData ) -{ - BaseClass::ApplySettings( inResourceData ); - - m_pTitle->SetFgColor( inResourceData->GetColor( "titlecolor" ) ); - - m_pMsg->SetFgColor( inResourceData->GetColor( "messagecolor" ) ); - - m_ButtonTextColor = inResourceData->GetColor( "buttontextcolor" ); - - m_FooterTall = inResourceData->GetInt( "footer_tall", 0 ); - m_ButtonMargin = inResourceData->GetInt( "button_margin", 25 ); - m_ButtonIconLabelSpace = inResourceData->GetInt( "button_separator", 10 ); - m_ActivityIndent = inResourceData->GetInt( "activity_indent", 0 ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -uint CMessageDialog::GetType( void ) -{ - return m_nType; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CMessageDialog::DoCommand( int button ) -{ - if ( button == BTN_INVALID || ( m_nType & MD_COMMANDONFORCECLOSE ) ) - { - return; - } - - if ( m_pCommands[button] ) - { - m_ButtonPressed = button; - if ( !( m_nType & MD_COMMANDAFTERCLOSE ) ) - { - // caller wants the command sent before closure - m_pCreator->OnCommand( m_pCommands[m_ButtonPressed] ); - } - m_pCreator->OnCommand( "ReleaseModalWindow" ); - Close(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CMessageDialog::OnKeyCodePressed( vgui::KeyCode code ) -{ - if ( m_ButtonPressed != BTN_INVALID || GetAlpha() != 255 ) - { - // inhibit any further key activity or during transitions - return; - } - - switch ( GetBaseButtonCode( code ) ) - { - case KEY_XBUTTON_A: - DoCommand( BTN_A ); - break; - - case KEY_XBUTTON_B: - DoCommand( BTN_B ); - break; - - default: - break; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CMessageDialog::PaintBackground( void ) -{ - int wide, tall; - GetSize( wide, tall ); - - if ( !( m_nType & MD_SIMPLEFRAME ) ) - { - int nAlpha = GetAlpha(); - - m_clrNotSimpleBG[3] = nAlpha; - m_clrNotSimpleBGBlack[3] = nAlpha; - - DrawBox( 0, 0, wide, tall, m_clrNotSimpleBGBlack, 1.0f ); - DrawBox( 0, 0, wide, tall - m_FooterTall, m_clrNotSimpleBG, 1.0f ); - - return; - } - - Color col = GetBgColor(); - DrawBox( 0, 0, wide, tall, col, 1.0f ); - - // offset the inset by title - int titleX, titleY, titleWide, titleTall; - m_pTitle->GetBounds( titleX, titleY, titleWide, titleTall ); - int y = titleY + titleTall; - - // draw an inset - Color darkColor; - darkColor.SetColor( 0.70f * (float)col.r(), 0.70f * (float)col.g(), 0.70f * (float)col.b(), col.a() ); - vgui::surface()->DrawSetColor( darkColor ); - vgui::surface()->DrawFilledRect( 8, y, wide - 8, tall - 8 ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "vgui_controls/MessageDialog.h" +#include "vgui/ILocalize.h" +#include "vgui/ISurface.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// CMessageDialog +//----------------------------------------------------------------------------- +CMessageDialog::CMessageDialog( vgui::Panel *pParent, const uint nType, const char *pTitle, const char *pMsg, const char *pCmdA, const char *pCmdB, vgui::Panel *pCreator, bool bShowActivity ) + : BaseClass( pParent, "MessageDialog" ) +{ + SetSize( 500, 200 ); + SetDeleteSelfOnClose( true ); + SetTitleBarVisible( false ); + SetCloseButtonVisible( false ); + SetSizeable( false ); + + m_pControlSettings = NULL; + m_pCreator = pCreator ? pCreator : pParent; + + m_nType = nType; + m_pTitle = new vgui::Label( this, "TitleLabel", pTitle ); + m_pMsg = new vgui::Label( this, "MessageLabel", pMsg ); + m_pAnimatingPanel = new vgui::AnimatingImagePanel( this, "AnimatingPanel" ); + + m_bShowActivity = bShowActivity; + + if ( nType & MD_SIMPLEFRAME ) + { + SetPaintBackgroundEnabled( true ); + m_pBackground = NULL; + } + else + { + m_pBackground = new vgui::ImagePanel( this, "Background" ); + if ( nType & MD_WARNING ) + { + m_pBackground->SetName( "WarningBackground" ); + } + else if ( nType & MD_ERROR ) + { + m_pBackground->SetName( "ErrorBackground" ); + } + } + + Q_memset( m_pCommands, 0, sizeof( m_pCommands ) ); + Q_memset( m_Buttons, 0, sizeof( m_Buttons ) ); + + if ( pCmdA ) + { + const int len = Q_strlen( pCmdA ) + 1; + m_pCommands[BTN_A] = (char*)malloc( len ); + Q_strncpy( m_pCommands[BTN_A], pCmdA, len ); + } + + if ( pCmdB ) + { + const int len = Q_strlen( pCmdB ) + 1; + m_pCommands[BTN_B] = (char*)malloc( len ); + Q_strncpy( m_pCommands[BTN_B], pCmdB, len ); + } + + // invalid until pressed + m_ButtonPressed = BTN_INVALID; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CMessageDialog::~CMessageDialog() +{ + if ( m_ButtonPressed != BTN_INVALID && ( m_nType & MD_COMMANDAFTERCLOSE ) ) + { + // caller wants the command sent after closure + m_pCreator->OnCommand( m_pCommands[m_ButtonPressed] ); + } + else if ( m_nType & MD_COMMANDONFORCECLOSE ) + { + // caller wants the command sent after closure + m_pCreator->OnCommand( m_pCommands[BTN_A] ); + } + + for ( int i = 0; i < MAX_BUTTONS; ++i ) + { + free( m_pCommands[i] ); + m_pCommands[i] = NULL; + + delete m_Buttons[i].pIcon; + delete m_Buttons[i].pText; + } + + delete m_pTitle; + m_pTitle = NULL; + + delete m_pMsg; + m_pMsg = NULL; + + delete m_pBackground; + m_pBackground = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the keyvalues to pass to LoadControlSettings() +//----------------------------------------------------------------------------- +void CMessageDialog::SetControlSettingsKeys( KeyValues *pKeys ) +{ + m_pControlSettings = pKeys; +} + +//----------------------------------------------------------------------------- +// Purpose: Create a new button +//----------------------------------------------------------------------------- +void CMessageDialog::CreateButtonLabel( ButtonLabel_s *pButton, const char *pIcon, const char *pText ) +{ + pButton->nWide = m_ButtonIconLabelSpace; + pButton->bCreated = true; + + pButton->pIcon = new vgui::Label( this, "icon", pIcon ); + SETUP_PANEL( pButton->pIcon ); + pButton->pIcon->SetFont( m_hButtonFont ); + pButton->pIcon->SizeToContents(); + pButton->nWide += pButton->pIcon->GetWide(); + + pButton->pText = new vgui::Label( this, "text", pText ); + SETUP_PANEL( pButton->pText ); + pButton->pText->SetFont( m_hTextFont ); + pButton->pText->SizeToContents(); + pButton->pText->SetFgColor( m_ButtonTextColor ); + pButton->nWide += pButton->pText->GetWide(); +} + +//----------------------------------------------------------------------------- +// Purpose: Create and arrange the panel button labels according to the dialog type +//----------------------------------------------------------------------------- +void CMessageDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + LoadControlSettings( "resource/UI/MessageDialog.res", "GAME", m_pControlSettings ); + + m_hButtonFont = pScheme->GetFont( "GameUIButtons" ); + m_hTextFont = pScheme->GetFont( "MenuLarge" ); + + if ( m_nType & MD_OK ) + { + CreateButtonLabel( &m_Buttons[BTN_A], "#GameUI_Icons_A_BUTTON", "#GameUI_OK" ); + } + else if ( m_nType & MD_CANCEL ) + { + CreateButtonLabel( &m_Buttons[BTN_B], "#GameUI_Icons_B_BUTTON", "#GameUI_Cancel" ); + } + else if ( m_nType & MD_OKCANCEL ) + { + CreateButtonLabel( &m_Buttons[BTN_A], "#GameUI_Icons_A_BUTTON", "#GameUI_OK" ); + CreateButtonLabel( &m_Buttons[BTN_B], "#GameUI_Icons_B_BUTTON", "#GameUI_Cancel" ); + } + else if ( m_nType & MD_YESNO ) + { + CreateButtonLabel( &m_Buttons[BTN_A], "#GameUI_Icons_A_BUTTON", "#GameUI_Yes" ); + CreateButtonLabel( &m_Buttons[BTN_B], "#GameUI_Icons_B_BUTTON", "#GameUI_No" ); + } + + // count the buttons and add up their widths + int cButtons = 0; + int nTotalWide = 0; + for ( int i = 0; i < MAX_BUTTONS; ++i ) + { + if ( m_Buttons[i].bCreated ) + { + ++cButtons; + nTotalWide += m_Buttons[i].nWide; + } + } + + // make sure text and icons are center-aligned vertically with each other + int nButtonTall = vgui::surface()->GetFontTall( m_hButtonFont ); + int nTextTall = vgui::surface()->GetFontTall( m_hTextFont ); + int nVerticalAdjust = ( nButtonTall - nTextTall ) / 2; + + // position the buttons with even horizontal spacing + int xpos = 0; + int ypos = GetTall() - max( nButtonTall, nTextTall ) - m_ButtonMargin; + int nSpacing = ( GetWide() - nTotalWide ) / ( cButtons + 1 ); + for ( int i = 0; i < MAX_BUTTONS; ++i ) + { + if ( m_Buttons[i].bCreated ) + { + xpos += nSpacing; + m_Buttons[i].pIcon->SetPos( xpos, ypos ); + xpos += m_Buttons[i].pIcon->GetWide() + m_ButtonIconLabelSpace; + m_Buttons[i].pText->SetPos( xpos, ypos + nVerticalAdjust ); + xpos += m_Buttons[i].pText->GetWide(); + } + } + + m_clrNotSimpleBG = pScheme->GetColor( "MessageDialog.MatchmakingBG", Color( 200, 184, 151, 255 ) ); + m_clrNotSimpleBGBlack = pScheme->GetColor( "MessageDialog.MatchmakingBGBlack", Color( 52, 48, 55, 255 ) ); + + if ( !m_bShowActivity ) + { + if ( m_pAnimatingPanel ) + { + if ( m_pAnimatingPanel->IsVisible() ) + { + + m_pAnimatingPanel->SetVisible( false ); + } + + m_pAnimatingPanel->StopAnimation(); + } + } + else + { + if ( m_pAnimatingPanel ) + { + if ( !m_pAnimatingPanel->IsVisible() ) + { + m_pAnimatingPanel->SetVisible( true ); + } + + m_pAnimatingPanel->StartAnimation(); + } + } + + MoveToCenterOfScreen(); + + if ( m_bShowActivity && m_ActivityIndent ) + { + // If we're animating, we push our text label in, and reduce its width + int iX,iY,iW,iH; + m_pMsg->GetBounds( iX, iY, iW, iH ); + m_pMsg->SetBounds( iX + m_ActivityIndent, iY, max(0,iW-m_ActivityIndent), iH ); + } + + // Invalidate the scheme on our message label so that it recalculates + // its line breaks in case it was resized when we loaded our .res file. + m_pMsg->InvalidateLayout( false, true ); +} + +//----------------------------------------------------------------------------- +// Purpose: Create and arrange the panel button labels according to the dialog type +//----------------------------------------------------------------------------- +void CMessageDialog::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + m_pTitle->SetFgColor( inResourceData->GetColor( "titlecolor" ) ); + + m_pMsg->SetFgColor( inResourceData->GetColor( "messagecolor" ) ); + + m_ButtonTextColor = inResourceData->GetColor( "buttontextcolor" ); + + m_FooterTall = inResourceData->GetInt( "footer_tall", 0 ); + m_ButtonMargin = inResourceData->GetInt( "button_margin", 25 ); + m_ButtonIconLabelSpace = inResourceData->GetInt( "button_separator", 10 ); + m_ActivityIndent = inResourceData->GetInt( "activity_indent", 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +uint CMessageDialog::GetType( void ) +{ + return m_nType; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMessageDialog::DoCommand( int button ) +{ + if ( button == BTN_INVALID || ( m_nType & MD_COMMANDONFORCECLOSE ) ) + { + return; + } + + if ( m_pCommands[button] ) + { + m_ButtonPressed = button; + if ( !( m_nType & MD_COMMANDAFTERCLOSE ) ) + { + // caller wants the command sent before closure + m_pCreator->OnCommand( m_pCommands[m_ButtonPressed] ); + } + m_pCreator->OnCommand( "ReleaseModalWindow" ); + Close(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMessageDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + if ( m_ButtonPressed != BTN_INVALID || GetAlpha() != 255 ) + { + // inhibit any further key activity or during transitions + return; + } + + switch ( GetBaseButtonCode( code ) ) + { + case KEY_XBUTTON_A: + DoCommand( BTN_A ); + break; + + case KEY_XBUTTON_B: + DoCommand( BTN_B ); + break; + + default: + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMessageDialog::PaintBackground( void ) +{ + int wide, tall; + GetSize( wide, tall ); + + if ( !( m_nType & MD_SIMPLEFRAME ) ) + { + int nAlpha = GetAlpha(); + + m_clrNotSimpleBG[3] = nAlpha; + m_clrNotSimpleBGBlack[3] = nAlpha; + + DrawBox( 0, 0, wide, tall, m_clrNotSimpleBGBlack, 1.0f ); + DrawBox( 0, 0, wide, tall - m_FooterTall, m_clrNotSimpleBG, 1.0f ); + + return; + } + + Color col = GetBgColor(); + DrawBox( 0, 0, wide, tall, col, 1.0f ); + + // offset the inset by title + int titleX, titleY, titleWide, titleTall; + m_pTitle->GetBounds( titleX, titleY, titleWide, titleTall ); + int y = titleY + titleTall; + + // draw an inset + Color darkColor; + darkColor.SetColor( 0.70f * (float)col.r(), 0.70f * (float)col.g(), 0.70f * (float)col.b(), col.a() ); + vgui::surface()->DrawSetColor( darkColor ); + vgui::surface()->DrawFilledRect( 8, y, wide - 8, tall - 8 ); +} diff --git a/mp/src/vgui2/vgui_controls/Panel.cpp b/mp/src/vgui2/vgui_controls/Panel.cpp index 7fb2e630..3f3634fe 100644 --- a/mp/src/vgui2/vgui_controls/Panel.cpp +++ b/mp/src/vgui2/vgui_controls/Panel.cpp @@ -1,8451 +1,8451 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - - -#include -#include -#include -#include -#include // isdigit() - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include "vgui_controls/Menu.h" -#include "vgui_controls/MenuItem.h" - -#include "UtlSortVector.h" - -#include "tier1/utldict.h" -#include "tier1/utlbuffer.h" -#include "mempool.h" -#include "filesystem.h" -#include "tier0/icommandline.h" - -#include "tier0/vprof.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -#define TRIPLE_PRESS_MSEC 300 - - -extern int GetBuildModeDialogCount(); - -static char *CopyString( const char *in ) -{ - if ( !in ) - return NULL; - - int len = strlen( in ); - char *n = new char[ len + 1 ]; - Q_strncpy( n, in, len + 1 ); - return n; -} - -#if defined( VGUI_USEDRAGDROP ) -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -struct vgui::DragDrop_t -{ - DragDrop_t() : - m_bDragEnabled( false ), - m_bShowDragHelper( true ), - m_bDropEnabled( false ), - m_bDragStarted( false ), - m_nDragStartTolerance( 8 ), - m_bDragging( false ), - m_lDropHoverTime( 0 ), - m_bDropMenuShown( false ), - m_bPreventChaining( false ) - { - m_nStartPos[ 0 ] = m_nStartPos[ 1 ] = 0; - m_nLastPos[ 0 ] = m_nLastPos[ 1 ] = 0; - } - - // Drag related data - bool m_bDragEnabled; - bool m_bShowDragHelper; - bool m_bDragging; - bool m_bDragStarted; - // How many pixels the dragged box must move before showing the outline rect... - int m_nDragStartTolerance; - int m_nStartPos[ 2 ]; - int m_nLastPos[ 2 ]; - CUtlVector< KeyValues * > m_DragData; - CUtlVector< PHandle > m_DragPanels; - - // Drop related data - bool m_bDropEnabled; - // A droppable panel can have a hover context menu, which will show up after m_flHoverContextTime of hovering - float m_flHoverContextTime; - - PHandle m_hCurrentDrop; - // Amount of time hovering over current drop target - long m_lDropHoverTime; - bool m_bDropMenuShown; - DHANDLE< Menu > m_hDropContextMenu; - - // Misc data - bool m_bPreventChaining; -}; - -//----------------------------------------------------------------------------- -// Purpose: Helper for painting to the full screen... -//----------------------------------------------------------------------------- -class CDragDropHelperPanel : public Panel -{ - DECLARE_CLASS_SIMPLE( CDragDropHelperPanel, Panel ); -public: - CDragDropHelperPanel(); - - virtual VPANEL IsWithinTraverse(int x, int y, bool traversePopups); - virtual void PostChildPaint(); - - void AddPanel( Panel *current ); - - void RemovePanel( Panel *search ); - -private: - struct DragHelperPanel_t - { - PHandle m_hPanel; - }; - - CUtlVector< DragHelperPanel_t > m_PaintList; -}; - -vgui::DHANDLE< CDragDropHelperPanel > s_DragDropHelper; -#endif - -#if defined( VGUI_USEKEYBINDINGMAPS ) - -BoundKey_t::BoundKey_t(): - isbuiltin( true ), - bindingname( 0 ), - keycode( KEY_NONE ), - modifiers( 0 ) -{ -} - -BoundKey_t::BoundKey_t( const BoundKey_t& src ) -{ - isbuiltin = src.isbuiltin; - bindingname = isbuiltin ? src.bindingname : CopyString( src.bindingname ); - keycode = src.keycode; - modifiers = src.modifiers; -} - -BoundKey_t& BoundKey_t::operator =( const BoundKey_t& src ) -{ - if ( this == &src ) - return *this; - isbuiltin = src.isbuiltin; - bindingname = isbuiltin ? src.bindingname : CopyString( src.bindingname ); - keycode = src.keycode; - modifiers = src.modifiers; - return *this; -} - - -BoundKey_t::~BoundKey_t() -{ - if ( !isbuiltin ) - { - delete[] bindingname; - } -} - -KeyBindingMap_t::KeyBindingMap_t() : - bindingname( 0 ), - func( 0 ), - helpstring( 0 ), - docstring( 0 ), - passive( false ) -{ -} - -KeyBindingMap_t::KeyBindingMap_t( const KeyBindingMap_t& src ) -{ - bindingname = src.bindingname; - helpstring = src.helpstring; - docstring = src.docstring; - - func = src.func; - passive = src.passive; -} - -KeyBindingMap_t::~KeyBindingMap_t() -{ -} - -class CKeyBindingsMgr -{ -public: - - CKeyBindingsMgr() : - m_Bindings( 0, 0, KeyBindingContextHandleLessFunc ), - m_nKeyBindingContexts( 0 ) - { - } - - struct KBContext_t - { - KBContext_t() : - m_KeyBindingsFile( UTL_INVAL_SYMBOL ), - m_KeyBindingsPathID( UTL_INVAL_SYMBOL ) - { - m_Handle = INVALID_KEYBINDINGCONTEXT_HANDLE; - } - - KBContext_t( const KBContext_t& src ) - { - m_Handle = src.m_Handle; - m_KeyBindingsFile = src.m_KeyBindingsFile; - m_KeyBindingsPathID = src.m_KeyBindingsPathID; - int c = src.m_Panels.Count(); - for ( int i = 0; i < c; ++i ) - { - m_Panels.AddToTail( src.m_Panels[ i ] ); - } - } - - KeyBindingContextHandle_t m_Handle; - CUtlSymbol m_KeyBindingsFile; - CUtlSymbol m_KeyBindingsPathID; - CUtlVector< Panel * > m_Panels; - }; - - static bool KeyBindingContextHandleLessFunc( const KBContext_t& lhs, const KBContext_t& rhs ) - { - return lhs.m_Handle < rhs.m_Handle; - } - - KeyBindingContextHandle_t CreateContext( char const *filename, char const *pathID ) - { - KBContext_t entry; - - entry.m_Handle = (KeyBindingContextHandle_t)++m_nKeyBindingContexts; - entry.m_KeyBindingsFile = filename; - if ( pathID ) - { - entry.m_KeyBindingsPathID = pathID; - } - else - { - entry.m_KeyBindingsPathID = UTL_INVAL_SYMBOL; - } - - m_Bindings.Insert( entry ); - - return entry.m_Handle; - } - - void AddPanelToContext( KeyBindingContextHandle_t handle, Panel *panel ) - { - if ( !panel->GetName() || !panel->GetName()[ 0 ] ) - { - Warning( "Can't add Keybindings Context for unnamed panels\n" ); - return; - } - - KBContext_t *entry = Find( handle ); - Assert( entry ); - if ( entry ) - { - int idx = entry->m_Panels.Find( panel ); - if ( idx == entry->m_Panels.InvalidIndex() ) - { - entry->m_Panels.AddToTail( panel ); - } - } - } - - void OnPanelDeleted( KeyBindingContextHandle_t handle, Panel *panel ) - { - KBContext_t *kb = Find( handle ); - if ( kb ) - { - kb->m_Panels.FindAndRemove( panel ); - } - } - - KBContext_t *Find( KeyBindingContextHandle_t handle ) - { - KBContext_t search; - search.m_Handle = handle; - int idx = m_Bindings.Find( search ); - if ( idx == m_Bindings.InvalidIndex() ) - { - return NULL; - } - return &m_Bindings[ idx ]; - } - - char const *GetKeyBindingsFile( KeyBindingContextHandle_t handle ) - { - KBContext_t *kb = Find( handle ); - if ( kb ) - { - return kb->m_KeyBindingsFile.String(); - } - Assert( 0 ); - return ""; - } - - char const *GetKeyBindingsFilePathID( KeyBindingContextHandle_t handle ) - { - KBContext_t *kb = Find( handle ); - if ( kb ) - { - return kb->m_KeyBindingsPathID.String(); - } - Assert( 0 ); - return NULL; - } - - int GetPanelsWithKeyBindingsCount( KeyBindingContextHandle_t handle ) - { - KBContext_t *kb = Find( handle ); - if ( kb ) - { - return kb->m_Panels.Count(); - } - Assert( 0 ); - return 0; - } - - //----------------------------------------------------------------------------- - // Purpose: static method - // Input : index - - // Output : Panel - //----------------------------------------------------------------------------- - Panel *GetPanelWithKeyBindings( KeyBindingContextHandle_t handle, int index ) - { - KBContext_t *kb = Find( handle ); - if ( kb ) - { - Assert( index >= 0 && index < kb->m_Panels.Count() ); - return kb->m_Panels[ index ]; - } - Assert( 0 ); - return 0; - } - - CUtlRBTree< KBContext_t, int > m_Bindings; - int m_nKeyBindingContexts; -}; - -static CKeyBindingsMgr g_KBMgr; - -//----------------------------------------------------------------------------- -// Purpose: Static method to allocate a context -// Input : - -// Output : KeyBindingContextHandle_t -//----------------------------------------------------------------------------- -KeyBindingContextHandle_t Panel::CreateKeyBindingsContext( char const *filename, char const *pathID /*=0*/ ) -{ - return g_KBMgr.CreateContext( filename, pathID ); -} - - -//----------------------------------------------------------------------------- -// Purpose: static method -// Input : - -// Output : int -//----------------------------------------------------------------------------- -int Panel::GetPanelsWithKeyBindingsCount( KeyBindingContextHandle_t handle ) -{ - return g_KBMgr.GetPanelsWithKeyBindingsCount( handle ); -} - -//----------------------------------------------------------------------------- -// Purpose: static method -// Input : index - -// Output : Panel -//----------------------------------------------------------------------------- -Panel *Panel::GetPanelWithKeyBindings( KeyBindingContextHandle_t handle, int index ) -{ - return g_KBMgr.GetPanelWithKeyBindings( handle, index ); -} - - -//----------------------------------------------------------------------------- -// Returns the number of keybindings -//----------------------------------------------------------------------------- -int Panel::GetKeyMappingCount( ) -{ - int nCount = 0; - PanelKeyBindingMap *map = GetKBMap(); - while ( map ) - { - nCount += map->entries.Count(); - map = map->baseMap; - } - return nCount; -} - - -//----------------------------------------------------------------------------- -// Purpose: static method. Reverts key bindings for all registered panels (panels with keybindings actually -// loaded from file -// Input : - -//----------------------------------------------------------------------------- -void Panel::RevertKeyBindings( KeyBindingContextHandle_t handle ) -{ - int c = GetPanelsWithKeyBindingsCount( handle ); - for ( int i = 0; i < c; ++i ) - { - Panel *kbPanel = GetPanelWithKeyBindings( handle, i ); - Assert( kbPanel ); - kbPanel->RevertKeyBindingsToDefault(); - } -} - -static void BufPrint( CUtlBuffer& buf, int level, char const *fmt, ... ) -{ - char string[ 2048 ]; - va_list argptr; - va_start( argptr, fmt ); - _vsnprintf( string, sizeof( string ) - 1, fmt, argptr ); - va_end( argptr ); - string[ sizeof( string ) - 1 ] = 0; - - while ( --level >= 0 ) - { - buf.Printf( " " ); - } - buf.Printf( "%s", string ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : handle - -//----------------------------------------------------------------------------- -void Panel::SaveKeyBindings( KeyBindingContextHandle_t handle ) -{ - char const *filename = g_KBMgr.GetKeyBindingsFile( handle ); - char const *pathID = g_KBMgr.GetKeyBindingsFilePathID( handle ); - - SaveKeyBindingsToFile( handle, filename, pathID ); -} - -//----------------------------------------------------------------------------- -// Purpose: static method. Saves key binding files out for all keybindings -// Input : - -//----------------------------------------------------------------------------- -void Panel::SaveKeyBindingsToFile( KeyBindingContextHandle_t handle, char const *filename, char const *pathID /*= 0*/ ) -{ - CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); - - BufPrint( buf, 0, "keybindings\n" ); - BufPrint( buf, 0, "{\n" ); - - int c = GetPanelsWithKeyBindingsCount( handle ); - for ( int i = 0; i < c; ++i ) - { - Panel *kbPanel = GetPanelWithKeyBindings( handle, i ); - Assert( kbPanel ); - if ( !kbPanel ) - continue; - - Assert( kbPanel->GetName() ); - Assert( kbPanel->GetName()[ 0 ] ); - - if ( !kbPanel->GetName() || !kbPanel->GetName()[ 0 ] ) - continue; - - BufPrint( buf, 1, "\"%s\"\n", kbPanel->GetName() ); - BufPrint( buf, 1, "{\n" ); - - kbPanel->SaveKeyBindingsToBuffer( 2, buf ); - - BufPrint( buf, 1, "}\n" ); - } - - BufPrint( buf, 0, "}\n" ); - - if ( g_pFullFileSystem->FileExists( filename, pathID ) && - !g_pFullFileSystem->IsFileWritable( filename, pathID ) ) - { - Warning( "Panel::SaveKeyBindings '%s' is read-only!!!\n", filename ); - } - - FileHandle_t h = g_pFullFileSystem->Open( filename, "wb", pathID ); - if ( FILESYSTEM_INVALID_HANDLE != h ) - { - g_pFullFileSystem->Write( buf.Base(), buf.TellPut(), h ); - g_pFullFileSystem->Close( h ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : handle - -// *panelOfInterest - -//----------------------------------------------------------------------------- -void Panel::LoadKeyBindingsForOnePanel( KeyBindingContextHandle_t handle, Panel *panelOfInterest ) -{ - if ( !panelOfInterest ) - return; - if ( !panelOfInterest->GetName() ) - return; - if ( !panelOfInterest->GetName()[ 0 ] ) - return; - - char const *filename = g_KBMgr.GetKeyBindingsFile( handle ); - char const *pathID = g_KBMgr.GetKeyBindingsFilePathID( handle ); - - KeyValues *kv = new KeyValues( "keybindings" ); - if ( kv->LoadFromFile( g_pFullFileSystem, filename, pathID ) ) - { - int c = GetPanelsWithKeyBindingsCount( handle ); - for ( int i = 0; i < c; ++i ) - { - Panel *kbPanel = GetPanelWithKeyBindings( handle, i ); - Assert( kbPanel ); - - char const *panelName = kbPanel->GetName(); - if ( !panelName ) - { - continue; - } - - if ( Q_stricmp( panelOfInterest->GetName(), panelName ) ) - continue; - - KeyValues *subKey = kv->FindKey( panelName, false ); - if ( !subKey ) - { - Warning( "Panel::ReloadKeyBindings: Can't find entry for panel '%s'\n", panelName ); - continue; - } - - kbPanel->ParseKeyBindings( subKey ); - } - } - kv->deleteThis(); -} - -//----------------------------------------------------------------------------- -// Purpose: static method. Loads all key bindings again -// Input : - -//----------------------------------------------------------------------------- - -void Panel::ReloadKeyBindings( KeyBindingContextHandle_t handle ) -{ - char const *filename = g_KBMgr.GetKeyBindingsFile( handle ); - char const *pathID = g_KBMgr.GetKeyBindingsFilePathID( handle ); - - KeyValues *kv = new KeyValues( "keybindings" ); - if ( kv->LoadFromFile( g_pFullFileSystem, filename, pathID ) ) - { - int c = GetPanelsWithKeyBindingsCount( handle ); - for ( int i = 0; i < c; ++i ) - { - Panel *kbPanel = GetPanelWithKeyBindings( handle, i ); - Assert( kbPanel ); - - char const *panelName = kbPanel->GetName(); - if ( !panelName ) - { - continue; - } - - KeyValues *subKey = kv->FindKey( panelName, false ); - if ( !subKey ) - { - Warning( "Panel::ReloadKeyBindings: Can't find entry for panel '%s'\n", panelName ); - continue; - } - - kbPanel->ParseKeyBindings( subKey ); - } - } - kv->deleteThis(); -} -#endif // VGUI_USEKEYBINDINGMAPS - -DECLARE_BUILD_FACTORY( Panel ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -Panel::Panel() -{ - Init(0, 0, 64, 24); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -Panel::Panel(Panel *parent) -{ - Init(0, 0, 64, 24); - SetParent(parent); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -Panel::Panel(Panel *parent, const char *panelName) -{ - Init(0, 0, 64, 24); - SetName(panelName); - SetParent(parent); - SetBuildModeEditable(true); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -Panel::Panel( Panel *parent, const char *panelName, HScheme scheme ) -{ - Init(0, 0, 64, 24); - SetName(panelName); - SetParent(parent); - SetBuildModeEditable(true); - SetScheme( scheme ); -} - -//----------------------------------------------------------------------------- -// Purpose: Setup -//----------------------------------------------------------------------------- -void Panel::Init( int x, int y, int wide, int tall ) -{ - _panelName = NULL; - _tooltipText = NULL; - _pinToSibling = NULL; - m_hMouseEventHandler = NULL; - _pinCornerToSibling = PIN_TOPLEFT; - _pinToSiblingCorner = PIN_TOPLEFT; - - // get ourselves an internal panel - _vpanel = ivgui()->AllocPanel(); - ipanel()->Init(_vpanel, this); - - SetPos(x, y); - SetSize(wide, tall); - _flags.SetFlag( NEEDS_LAYOUT | NEEDS_SCHEME_UPDATE | NEEDS_DEFAULT_SETTINGS_APPLIED ); - _flags.SetFlag( AUTODELETE_ENABLED | PAINT_BORDER_ENABLED | PAINT_BACKGROUND_ENABLED | PAINT_ENABLED ); -#if defined( VGUI_USEKEYBINDINGMAPS ) - _flags.SetFlag( ALLOW_CHAIN_KEYBINDING_TO_PARENT ); -#endif - m_nPinDeltaX = m_nPinDeltaY = 0; - m_nResizeDeltaX = m_nResizeDeltaY = 0; - _autoResizeDirection = AUTORESIZE_NO; - _pinCorner = PIN_TOPLEFT; - _cursor = dc_arrow; - _border = NULL; - _buildGroup = UTLHANDLE_INVALID; - _tabPosition = 0; - m_iScheme = 0; - m_bIsSilent = false; - m_bParentNeedsCursorMoveEvents = false; - - _buildModeFlags = 0; // not editable or deletable in buildmode dialog by default - - m_pTooltips = NULL; - m_bToolTipOverridden = false; - - m_flAlpha = 255.0f; - m_nPaintBackgroundType = 0; - - //============================================================================= - // HPE_BEGIN: - // [tj] Default to rounding all corners (for draw style 2) - //============================================================================= - m_roundedCorners = PANEL_ROUND_CORNER_ALL; - //============================================================================= - // HPE_END - //============================================================================= - - m_nBgTextureId1 = -1; - m_nBgTextureId2 = -1; - m_nBgTextureId3 = -1; - m_nBgTextureId4 = -1; -#if defined( VGUI_USEDRAGDROP ) - m_pDragDrop = new DragDrop_t; - -#endif - - m_lLastDoublePressTime = 0L; - -#if defined( VGUI_USEKEYBINDINGMAPS ) - m_hKeyBindingsContext = INVALID_KEYBINDINGCONTEXT_HANDLE; -#endif - - REGISTER_COLOR_AS_OVERRIDABLE( _fgColor, "fgcolor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _bgColor, "bgcolor_override" ); - - m_bIsConsoleStylePanel = false; - m_NavUp = NULL; - m_NavDown = NULL; - m_NavLeft = NULL; - m_NavRight = NULL; - m_NavToRelay = NULL; - m_NavActivate = NULL; - m_NavBack = NULL; - m_sNavUpName = NULL; - m_sNavDownName = NULL; - m_sNavLeftName = NULL; - m_sNavRightName = NULL; - m_sNavToRelayName = NULL; - m_sNavActivateName = NULL; - m_sNavBackName = NULL; - - m_PassUnhandledInput = true; - m_LastNavDirection = ND_NONE; - m_bWorldPositionCurrentFrame = false; - m_bForceStereoRenderToFrameBuffer = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -Panel::~Panel() -{ - // @note Tom Bui: only cleanup if we've created it - if ( !m_bToolTipOverridden ) - { - if ( m_pTooltips ) - { - delete m_pTooltips; - } - } -#if defined( VGUI_USEKEYBINDINGMAPS ) - if ( IsValidKeyBindingsContext() ) - { - g_KBMgr.OnPanelDeleted( m_hKeyBindingsContext, this ); - } -#endif // VGUI_USEKEYBINDINGMAPS -#if defined( VGUI_USEDRAGDROP ) - if ( m_pDragDrop->m_bDragging ) - { - OnFinishDragging( false, (MouseCode)-1 ); - } -#endif // VGUI_USEDRAGDROP - - _flags.ClearFlag( AUTODELETE_ENABLED ); - _flags.SetFlag( MARKED_FOR_DELETION ); - - // remove panel from any list - SetParent((VPANEL)NULL); - - // Stop our children from pointing at us, and delete them if possible - while (ipanel()->GetChildCount(GetVPanel())) - { - VPANEL child = ipanel()->GetChild(GetVPanel(), 0); - if (ipanel()->IsAutoDeleteSet(child)) - { - ipanel()->DeletePanel(child); - } - else - { - ipanel()->SetParent(child, NULL); - } - } - - // delete VPanel - ivgui()->FreePanel(_vpanel); - // free our name - delete [] _panelName; - - if ( _tooltipText && _tooltipText[0] ) - { - delete [] _tooltipText; - } - - delete [] _pinToSibling; - - _vpanel = NULL; -#if defined( VGUI_USEDRAGDROP ) - delete m_pDragDrop; -#endif // VGUI_USEDRAGDROP -} - -//----------------------------------------------------------------------------- -// Purpose: fully construct this panel so its ready for use right now (i.e fonts loaded, colors set, default label text set, ...) -//----------------------------------------------------------------------------- -void Panel::MakeReadyForUse() -{ -// PerformApplySchemeSettings(); - UpdateSiblingPin(); - surface()->SolveTraverse( GetVPanel(), true ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetName( const char *panelName ) -{ - // No change? - if ( _panelName && - panelName && - !Q_strcmp( _panelName, panelName ) ) - { - return; - } - - if (_panelName) - { - delete [] _panelName; - _panelName = NULL; - } - - if (panelName) - { - int len = Q_strlen(panelName) + 1; - _panelName = new char[ len ]; - Q_strncpy( _panelName, panelName, len ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: returns the given name of the panel -//----------------------------------------------------------------------------- -const char *Panel::GetName() -{ - if (_panelName) - return _panelName; - - return ""; -} - -//----------------------------------------------------------------------------- -// Purpose: returns the name of the module that this instance of panel was compiled into -//----------------------------------------------------------------------------- -const char *Panel::GetModuleName() -{ - return vgui::GetControlsModuleName(); -} - -//----------------------------------------------------------------------------- -// Purpose: returns the classname of the panel (as specified in the panelmaps) -//----------------------------------------------------------------------------- -const char *Panel::GetClassName() -{ - // loop up the panel map name - PanelMessageMap *panelMap = GetMessageMap(); - if ( panelMap ) - { - return panelMap->pfnClassName(); - } - - return "Panel"; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetPos(int x, int y) -{ - if (!CommandLine()->FindParm("-hushasserts")) - { - Assert( abs(x) < 32768 && abs(y) < 32768 ); - } - ipanel()->SetPos(GetVPanel(), x, y); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::GetPos(int &x, int &y) -{ - ipanel()->GetPos(GetVPanel(), x, y); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetSize(int wide, int tall) -{ - Assert( abs(wide) < 32768 && abs(tall) < 32768 ); - ipanel()->SetSize(GetVPanel(), wide, tall); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::GetSize(int &wide, int &tall) -{ - ipanel()->GetSize(GetVPanel(), wide, tall); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetBounds(int x, int y, int wide, int tall) -{ - SetPos(x,y); - SetSize(wide,tall); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::GetBounds(int &x, int &y, int &wide, int &tall) -{ - GetPos(x, y); - GetSize(wide, tall); -} - -//----------------------------------------------------------------------------- -// Purpose: returns safe handle to parent -//----------------------------------------------------------------------------- -VPANEL Panel::GetVParent() -{ - if ( ipanel() ) - { - return ipanel()->GetParent(GetVPanel()); - } - - return 0; -} - -//----------------------------------------------------------------------------- -// Purpose: returns a pointer to a controls version of a Panel pointer -//----------------------------------------------------------------------------- -Panel *Panel::GetParent() -{ - // get the parent and convert it to a Panel * - // this is OK, the hierarchy is guaranteed to be all from the same module, except for the root node - // the root node always returns NULL when a GetParent() is done so everything is OK - if ( ipanel() ) - { - VPANEL parent = ipanel()->GetParent(GetVPanel()); - if (parent) - { - Panel *pParent = ipanel()->GetPanel(parent, GetControlsModuleName()); - Assert(!pParent || !strcmp(pParent->GetModuleName(), GetControlsModuleName())); - return pParent; - } - } - - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Screen size change notification handler -//----------------------------------------------------------------------------- -void Panel::OnScreenSizeChanged(int nOldWide, int nOldTall) -{ - // post to all children - for (int i = 0; i < ipanel()->GetChildCount(GetVPanel()); i++) - { - VPANEL child = ipanel()->GetChild(GetVPanel(), i); - PostMessage(child, new KeyValues("OnScreenSizeChanged", "oldwide", nOldWide, "oldtall", nOldTall), NULL); - } - - // make any currently fullsize window stay fullsize - int x, y, wide, tall; - GetBounds(x, y, wide, tall); - int screenWide, screenTall; - surface()->GetScreenSize(screenWide, screenTall); - if (x == 0 && y == 0 && nOldWide == wide && tall == nOldTall) - { - // fullsize - surface()->GetScreenSize(wide, tall); - SetBounds(0, 0, wide, tall); - } - - // panel needs to re-get it's scheme settings - _flags.SetFlag( NEEDS_SCHEME_UPDATE ); - - // invalidate our settings - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetVisible(bool state) -{ - ipanel()->SetVisible(GetVPanel(), state); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool Panel::IsVisible() -{ - if (ipanel()) - { - return ipanel()->IsVisible(GetVPanel()); - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetEnabled(bool state) -{ - if (state != ipanel()->IsEnabled( GetVPanel())) - { - ipanel()->SetEnabled(GetVPanel(), state); - InvalidateLayout(false); - Repaint(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool Panel::IsEnabled() -{ - return ipanel()->IsEnabled(GetVPanel()); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool Panel::IsPopup() -{ - return ipanel()->IsPopup(GetVPanel()); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::Repaint() -{ - _flags.SetFlag( NEEDS_REPAINT ); - if (surface()) - { - surface()->Invalidate(GetVPanel()); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::Think() -{ - if (IsVisible()) - { - // update any tooltips - if (m_pTooltips) - { - m_pTooltips->PerformLayout(); - } - if ( _flags.IsFlagSet( NEEDS_LAYOUT ) ) - { - InternalPerformLayout(); - } - } - - OnThink(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::PaintTraverse( bool repaint, bool allowForce ) -{ - if ( m_bWorldPositionCurrentFrame ) - { - surface()->SolveTraverse( GetVPanel() ); - } - - if ( !IsVisible() ) - { - return; - } - - float oldAlphaMultiplier = surface()->DrawGetAlphaMultiplier(); - float newAlphaMultiplier = oldAlphaMultiplier * m_flAlpha * 1.0f/255.0f; - - if ( IsXbox() && !newAlphaMultiplier ) - { - // xbox optimization not suitable for pc - // xbox panels are compliant and can early out and not traverse their children - // when they have no opacity - return; - } - - if ( !repaint && - allowForce && - _flags.IsFlagSet( NEEDS_REPAINT ) ) - { - repaint = true; - _flags.ClearFlag( NEEDS_REPAINT ); - } - - VPANEL vpanel = GetVPanel(); - - bool bPushedViewport = false; - if( GetForceStereoRenderToFrameBuffer() ) - { - CMatRenderContextPtr pRenderContext( materials ); - if( pRenderContext->GetRenderTarget() ) - { - surface()->PushFullscreenViewport(); - bPushedViewport = true; - } - } - - int clipRect[4]; - ipanel()->GetClipRect( vpanel, clipRect[0], clipRect[1], clipRect[2], clipRect[3] ); - if ( ( clipRect[2] <= clipRect[0] ) || ( clipRect[3] <= clipRect[1] ) ) - { - repaint = false; - } - - // set global alpha - surface()->DrawSetAlphaMultiplier( newAlphaMultiplier ); - - bool bBorderPaintFirst = _border ? _border->PaintFirst() : false; - - // draw the border first if requested to - if ( bBorderPaintFirst && repaint && _flags.IsFlagSet( PAINT_BORDER_ENABLED ) && ( _border != null ) ) - { - // Paint the border over the background with no inset - surface()->PushMakeCurrent( vpanel, false ); - PaintBorder(); - surface()->PopMakeCurrent( vpanel ); - } - - if ( repaint ) - { - // draw the background with no inset - if ( _flags.IsFlagSet( PAINT_BACKGROUND_ENABLED ) ) - { - surface()->PushMakeCurrent( vpanel, false ); - PaintBackground(); - surface()->PopMakeCurrent( vpanel ); - } - - // draw the front of the panel with the inset - if ( _flags.IsFlagSet( PAINT_ENABLED ) ) - { - surface()->PushMakeCurrent( vpanel, true ); - Paint(); - surface()->PopMakeCurrent( vpanel ); - } - } - - // traverse and paint all our children - CUtlVector< VPANEL > &children = ipanel()->GetChildren( vpanel ); - int childCount = children.Count(); - for (int i = 0; i < childCount; i++) - { - VPANEL child = children[ i ]; - bool bVisible = ipanel()->IsVisible( child ); - - if ( surface()->ShouldPaintChildPanel( child ) ) - { - if ( bVisible ) - { - ipanel()->PaintTraverse( child, repaint, allowForce ); - } - } - else - { - // Invalidate the child panel so that it gets redrawn - surface()->Invalidate( child ); - - // keep traversing the tree, just don't allow anyone to paint after here - if ( bVisible ) - { - ipanel()->PaintTraverse( child, false, false ); - } - } - } - - // draw the border last - if ( repaint ) - { - if ( !bBorderPaintFirst && _flags.IsFlagSet( PAINT_BORDER_ENABLED ) && ( _border != null ) ) - { - // Paint the border over the background with no inset - surface()->PushMakeCurrent( vpanel, false ); - PaintBorder(); - surface()->PopMakeCurrent( vpanel ); - } - -#ifdef _DEBUG - // IsBuildGroupEnabled recurses up all the parents and ends up being very expensive as it wanders all over memory - if ( GetBuildModeDialogCount() && IsBuildGroupEnabled() ) //&& HasFocus() ) - { - // outline all selected panels - CUtlVector *controlGroup = _buildGroup->GetControlGroup(); - for (int i=0; i < controlGroup->Size(); ++i) - { - // outline all selected panels - CUtlVector *controlGroup = _buildGroup->GetControlGroup(); - for (int i=0; i < controlGroup->Size(); ++i) - { - surface()->PushMakeCurrent( ((*controlGroup)[i].Get())->GetVPanel(), false ); - ((*controlGroup)[i].Get())->PaintBuildOverlay(); - surface()->PopMakeCurrent( ((*controlGroup)[i].Get())->GetVPanel() ); - } - - _buildGroup->DrawRulers(); - } - } -#endif - - // All of our children have painted, etc, now allow painting in top of them - if ( _flags.IsFlagSet( POST_CHILD_PAINT_ENABLED ) ) - { - surface()->PushMakeCurrent( vpanel, false ); - PostChildPaint(); - surface()->PopMakeCurrent( vpanel ); - } - } - - surface()->DrawSetAlphaMultiplier( oldAlphaMultiplier ); - - surface()->SwapBuffers( vpanel ); - - if( bPushedViewport ) - { - surface()->PopFullscreenViewport(); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::PaintBorder() -{ - _border->Paint(GetVPanel()); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::PaintBackground() -{ - int wide, tall; - GetSize( wide, tall ); - if ( m_SkipChild.Get() && m_SkipChild->IsVisible() ) - { - if ( GetPaintBackgroundType() == 2 ) - { - int cornerWide, cornerTall; - GetCornerTextureSize( cornerWide, cornerTall ); - - Color col = GetBgColor(); - DrawHollowBox( 0, 0, wide, tall, col, 1.0f ); - - wide -= 2 * cornerWide; - tall -= 2 * cornerTall; - - FillRectSkippingPanel( GetBgColor(), cornerWide, cornerTall, wide, tall, m_SkipChild.Get() ); - } - else - { - FillRectSkippingPanel( GetBgColor(), 0, 0, wide, tall, m_SkipChild.Get() ); - } - } - else - { - Color col = GetBgColor(); - - switch ( m_nPaintBackgroundType ) - { - default: - case 0: - { - surface()->DrawSetColor(col); - surface()->DrawFilledRect(0, 0, wide, tall); - } - break; - case 1: - { - DrawTexturedBox( 0, 0, wide, tall, col, 1.0f ); - } - break; - case 2: - { - DrawBox( 0, 0, wide, tall, col, 1.0f ); - } - break; - case 3: - { - DrawBoxFade( 0, 0, wide, tall, col, 1.0f, 255, 0, true ); - } - break; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::Paint() -{ - // empty on purpose - // PaintBackground is painted and default behavior is for Paint to do nothing -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::PostChildPaint() -{ - // Empty on purpose - // This is called if _postChildPaintEnabled is true and allows painting to - // continue on the surface after all of the panel's children have painted - // themselves. Allows drawing an overlay on top of the children, etc. -} - -//----------------------------------------------------------------------------- -// Purpose: Draws a black rectangle around the panel. -//----------------------------------------------------------------------------- -void Panel::PaintBuildOverlay() -{ - int wide,tall; - GetSize(wide,tall); - surface()->DrawSetColor(0, 0, 0, 255); - - surface()->DrawFilledRect(0,0,wide,2); //top - surface()->DrawFilledRect(0,tall-2,wide,tall); //bottom - surface()->DrawFilledRect(0,2,2,tall-2); //left - surface()->DrawFilledRect(wide-2,2,wide,tall-2); //right -} - -//----------------------------------------------------------------------------- -// Purpose: Returns true if the panel's draw code will fully cover it's area -//----------------------------------------------------------------------------- -bool Panel::IsOpaque() -{ - // FIXME: Add code to account for the 'SkipChild' functionality in Frame - if ( IsVisible() && _flags.IsFlagSet( PAINT_BACKGROUND_ENABLED ) && ( _bgColor[3] == 255 ) ) - return true; - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if the settings are aligned to the right of the screen -//----------------------------------------------------------------------------- -bool Panel::IsRightAligned() -{ - return (_buildModeFlags & BUILDMODE_SAVE_XPOS_RIGHTALIGNED); -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if the settings are aligned to the bottom of the screen -//----------------------------------------------------------------------------- -bool Panel::IsBottomAligned() -{ - return (_buildModeFlags & BUILDMODE_SAVE_YPOS_BOTTOMALIGNED); -} - -//----------------------------------------------------------------------------- -// Purpose: sets the parent -//----------------------------------------------------------------------------- -void Panel::SetParent(Panel *newParent) -{ - // Assert that the parent is from the same module as the child - // FIXME: !!! work out how to handle this properly! - // Assert(!newParent || !strcmp(newParent->GetModuleName(), GetControlsModuleName())); - - if (newParent) - { - SetParent(newParent->GetVPanel()); - } - else - { - SetParent((VPANEL)NULL); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetParent(VPANEL newParent) -{ - if (newParent) - { - ipanel()->SetParent(GetVPanel(), newParent); - } - else - { - ipanel()->SetParent(GetVPanel(), NULL); - } - - if (GetVParent() && !IsPopup()) - { - SetProportional(ipanel()->IsProportional(GetVParent())); - - // most of the time KBInput == parents kbinput - if (ipanel()->IsKeyBoardInputEnabled(GetVParent()) != IsKeyBoardInputEnabled()) - { - SetKeyBoardInputEnabled(ipanel()->IsKeyBoardInputEnabled(GetVParent())); - } - - if (ipanel()->IsMouseInputEnabled(GetVParent()) != IsMouseInputEnabled()) - { - SetMouseInputEnabled(ipanel()->IsMouseInputEnabled(GetVParent())); - } - } - - UpdateSiblingPin(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::OnChildAdded(VPANEL child) -{ - Assert( !_flags.IsFlagSet( IN_PERFORM_LAYOUT ) ); -} - -//----------------------------------------------------------------------------- -// Purpose: default message handler -//----------------------------------------------------------------------------- -void Panel::OnSizeChanged(int newWide, int newTall) -{ - InvalidateLayout(); // our size changed so force us to layout again -} - -//----------------------------------------------------------------------------- -// Purpose: sets Z ordering - lower numbers are always behind higher z's -//----------------------------------------------------------------------------- -void Panel::SetZPos(int z) -{ - ipanel()->SetZPos(GetVPanel(), z); -} - -//----------------------------------------------------------------------------- -// Purpose: sets Z ordering - lower numbers are always behind higher z's -//----------------------------------------------------------------------------- -int Panel::GetZPos() -{ - return ( ipanel()->GetZPos( GetVPanel() ) ); -} - -//----------------------------------------------------------------------------- -// Purpose: sets alpha modifier for panel and all child panels [0..255] -//----------------------------------------------------------------------------- -void Panel::SetAlpha(int alpha) -{ - m_flAlpha = alpha; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -int Panel::GetAlpha() -{ - return (int)m_flAlpha; -} - -//----------------------------------------------------------------------------- -// Purpose: Moves the panel to the front of the z-order -//----------------------------------------------------------------------------- -void Panel::MoveToFront(void) -{ - // FIXME: only use ipanel() as per src branch? - if (IsPopup()) - { - surface()->BringToFront(GetVPanel()); - } - else - { - ipanel()->MoveToFront(GetVPanel()); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Iterates up the hierarchy looking for a particular parent -//----------------------------------------------------------------------------- -bool Panel::HasParent(VPANEL potentialParent) -{ - if (!potentialParent) - return false; - - return ipanel()->HasParent(GetVPanel(), potentialParent); -} - -//----------------------------------------------------------------------------- -// Purpose: Finds the index of a child panel by string name -// Output : int - -1 if no panel of that name is found -//----------------------------------------------------------------------------- -int Panel::FindChildIndexByName(const char *childName) -{ - for (int i = 0; i < GetChildCount(); i++) - { - Panel *pChild = GetChild(i); - if (!pChild) - continue; - - if (!stricmp(pChild->GetName(), childName)) - { - return i; - } - } - - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: Finds a child panel by string name -// Output : Panel * - NULL if no panel of that name is found -//----------------------------------------------------------------------------- -Panel *Panel::FindChildByName(const char *childName, bool recurseDown) -{ - for (int i = 0; i < GetChildCount(); i++) - { - Panel *pChild = GetChild(i); - if (!pChild) - continue; - - if (!V_stricmp(pChild->GetName(), childName)) - { - return pChild; - } - - if (recurseDown) - { - Panel *panel = pChild->FindChildByName(childName, recurseDown); - if ( panel ) - { - return panel; - } - } - } - - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Finds a sibling panel by name -//----------------------------------------------------------------------------- -Panel *Panel::FindSiblingByName(const char *siblingName) -{ - if ( !GetVParent() ) - return NULL; - - int siblingCount = ipanel()->GetChildCount(GetVParent()); - for (int i = 0; i < siblingCount; i++) - { - VPANEL sibling = ipanel()->GetChild(GetVParent(), i); - Panel *panel = ipanel()->GetPanel(sibling, GetControlsModuleName()); - if (!stricmp(panel->GetName(), siblingName)) - { - return panel; - } - } - - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Dispatches immediately a message to the parent -//----------------------------------------------------------------------------- -void Panel::CallParentFunction(KeyValues *message) -{ - if (GetVParent()) - { - ipanel()->SendMessage(GetVParent(), message, GetVPanel()); - } - if (message) - { - message->deleteThis(); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: if set to true, panel automatically frees itself when parent is deleted -//----------------------------------------------------------------------------- -void Panel::SetAutoDelete( bool state ) -{ - _flags.SetFlag( AUTODELETE_ENABLED, state ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool Panel::IsAutoDeleteSet() -{ - return _flags.IsFlagSet( AUTODELETE_ENABLED ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Just calls 'delete this' -//----------------------------------------------------------------------------- -void Panel::DeletePanel() -{ - // Avoid re-entrancy - _flags.SetFlag( MARKED_FOR_DELETION ); - _flags.ClearFlag( AUTODELETE_ENABLED ); - delete this; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -HScheme Panel::GetScheme() -{ - if (m_iScheme) - { - return m_iScheme; // return our internal scheme - } - - if (GetVParent()) // recurse down the heirarchy - { - return ipanel()->GetScheme(GetVParent()); - } - - return scheme()->GetDefaultScheme(); -} - -//----------------------------------------------------------------------------- -// Purpose: set the scheme to render this panel with by name -//----------------------------------------------------------------------------- -void Panel::SetScheme(const char *tag) -{ - if (strlen(tag) > 0 && scheme()->GetScheme(tag)) // check the scheme exists - { - SetScheme(scheme()->GetScheme(tag)); - } -} - -//----------------------------------------------------------------------------- -// Purpose: set the scheme to render this panel with -//----------------------------------------------------------------------------- -void Panel::SetScheme(HScheme scheme) -{ - if (scheme != m_iScheme) - { - m_iScheme = scheme; - - // This will cause the new scheme to be applied at a later point -// InvalidateLayout( false, true ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: returns the char of this panels hotkey -//----------------------------------------------------------------------------- -Panel *Panel::HasHotkey(wchar_t key) -{ - return NULL; -} - -#if defined( VGUI_USEDRAGDROP ) -static vgui::PHandle g_DragDropCapture; -#endif // VGUI_USEDRAGDROP - -void Panel::InternalCursorMoved(int x, int y) -{ -#if defined( VGUI_USEDRAGDROP ) - if ( g_DragDropCapture.Get() ) - { - bool started = g_DragDropCapture->GetDragDropInfo()->m_bDragStarted; - - g_DragDropCapture->OnContinueDragging(); - - if ( started ) - { - bool isEscapeKeyDown = input()->IsKeyDown( KEY_ESCAPE ); - if ( isEscapeKeyDown ) - { - g_DragDropCapture->OnFinishDragging( true, (MouseCode)-1, true ); - } - return; - } - } -#endif // VGUI_USEDRAGDROP - - if ( !ShouldHandleInputMessage() ) - return; - - if ( IsCursorNone() ) - return; - - if ( !IsMouseInputEnabled() ) - { - return; - } - - if (IsBuildGroupEnabled()) - { - if ( _buildGroup->CursorMoved(x, y, this) ) - { - return; - } - } - - if (m_pTooltips) - { - if ( _tooltipText ) - { - m_pTooltips->SetText( _tooltipText ); - } - m_pTooltips->ShowTooltip(this); - } - - ScreenToLocal(x, y); - - OnCursorMoved(x, y); -} - -void Panel::InternalCursorEntered() -{ - if (IsCursorNone() || !IsMouseInputEnabled()) - return; - - if (IsBuildGroupEnabled()) - return; - - if (m_pTooltips) - { - m_pTooltips->ResetDelay(); - - if ( _tooltipText ) - { - m_pTooltips->SetText( _tooltipText ); - } - m_pTooltips->ShowTooltip(this); - } - - OnCursorEntered(); -} - -void Panel::InternalCursorExited() -{ - if (IsCursorNone() || !IsMouseInputEnabled()) - return; - - if (IsBuildGroupEnabled()) - return; - - if (m_pTooltips) - { - m_pTooltips->HideTooltip(); - } - - OnCursorExited(); -} - -bool Panel::IsChildOfSurfaceModalPanel() -{ - VPANEL appModalPanel = input()->GetAppModalSurface(); - if ( !appModalPanel ) - return true; - - if ( ipanel()->HasParent( GetVPanel(), appModalPanel ) ) - return true; - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Panel::IsChildOfModalSubTree() -{ - VPANEL subTree = input()->GetModalSubTree(); - if ( !subTree ) - return true; - - if ( HasParent( subTree ) ) - return true; - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Checks to see if message is being subverted due to modal subtree logic -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -static bool ShouldHandleInputMessage( VPANEL p ) -{ - // If there is not modal subtree, then always handle the msg - if ( !input()->GetModalSubTree() ) - return true; - - // What state are we in? - bool bChildOfModal = false; - VPANEL subTree = input()->GetModalSubTree(); - if ( !subTree ) - { - bChildOfModal = true; - } - else if ( ipanel()->HasParent( p, subTree ) ) - { - bChildOfModal = true; - } - - if ( input()->ShouldModalSubTreeReceiveMessages() ) - return bChildOfModal; - - return !bChildOfModal; -} - -bool Panel::ShouldHandleInputMessage() -{ - return ::ShouldHandleInputMessage( GetVPanel() ); -} - -void Panel::InternalMousePressed(int code) -{ - long curtime = system()->GetTimeMillis(); - if ( IsTriplePressAllowed() ) - { - long elapsed = curtime - m_lLastDoublePressTime; - if ( elapsed < TRIPLE_PRESS_MSEC ) - { - InternalMouseTriplePressed( code ); - return; - } - } - - // The menu system passively watches for mouse released messages so it - // can clear any open menus if the release is somewhere other than on a menu - Menu::OnInternalMousePressed( this, (MouseCode)code ); - - if ( !ShouldHandleInputMessage() ) - return; - - if ( IsCursorNone() ) - return; - - if ( !IsMouseInputEnabled()) - { -#if defined( VGUI_USEDRAGDROP ) - DragDropStartDragging(); -#endif - return; - } - - if (IsBuildGroupEnabled()) - { - if ( _buildGroup->MousePressed((MouseCode)code, this) ) - { - return; - } - } - - Panel *pMouseHandler = m_hMouseEventHandler.Get(); - if ( pMouseHandler ) - { - pMouseHandler->OnMousePressed( (MouseCode)code ); - } - else - { - OnMousePressed( (MouseCode)code ); - } - -#if defined( VGUI_USEDRAGDROP ) - DragDropStartDragging(); -#endif -} - -void Panel::InternalMouseDoublePressed(int code) -{ - m_lLastDoublePressTime = system()->GetTimeMillis(); - - if ( !ShouldHandleInputMessage() ) - return; - - if ( IsCursorNone() ) - return; - - if ( !IsMouseInputEnabled()) - { - return; - } - - if (IsBuildGroupEnabled()) - { - if ( _buildGroup->MouseDoublePressed((MouseCode)code, this) ) - { - return; - } - } - - Panel *pMouseHandler = m_hMouseEventHandler.Get(); - if ( pMouseHandler ) - { - pMouseHandler->OnMouseDoublePressed( (MouseCode)code ); - } - else - { - OnMouseDoublePressed( (MouseCode)code ); - } -} - -#if defined( VGUI_USEDRAGDROP ) -void Panel::SetStartDragWhenMouseExitsPanel( bool state ) -{ - _flags.SetFlag( DRAG_REQUIRES_PANEL_EXIT, state ); -} - -bool Panel::IsStartDragWhenMouseExitsPanel() const -{ - return _flags.IsFlagSet( DRAG_REQUIRES_PANEL_EXIT ); -} -#endif // VGUI_USEDRAGDROP - -void Panel::SetTriplePressAllowed( bool state ) -{ - _flags.SetFlag( TRIPLE_PRESS_ALLOWED, state ); -} - -bool Panel::IsTriplePressAllowed() const -{ - return _flags.IsFlagSet( TRIPLE_PRESS_ALLOWED ); -} - -void Panel::InternalMouseTriplePressed( int code ) -{ - Assert( IsTriplePressAllowed() ); - m_lLastDoublePressTime = 0L; - - if ( !ShouldHandleInputMessage() ) - return; - - if ( IsCursorNone() ) - return; - - if ( !IsMouseInputEnabled()) - { -#if defined( VGUI_USEDRAGDROP ) - DragDropStartDragging(); -#endif - return; - } - - if (IsBuildGroupEnabled()) - { - return; - } - - OnMouseTriplePressed((MouseCode)code); -#if defined( VGUI_USEDRAGDROP ) - DragDropStartDragging(); -#endif -} - -void Panel::InternalMouseReleased(int code) -{ -#if defined( VGUI_USEDRAGDROP ) - if ( g_DragDropCapture.Get() ) - { - bool started = g_DragDropCapture->GetDragDropInfo()->m_bDragStarted; - g_DragDropCapture->OnFinishDragging( true, (MouseCode)code ); - if ( started ) - { - return; - } - } -#endif - - if ( !ShouldHandleInputMessage() ) - return; - - if ( IsCursorNone() ) - return; - - if ( !IsMouseInputEnabled()) - { - return; - } - - if (IsBuildGroupEnabled()) - { - if ( _buildGroup->MouseReleased((MouseCode)code, this) ) - { - return; - } - } - - OnMouseReleased((MouseCode)code); -} - -void Panel::InternalMouseWheeled(int delta) -{ - if (IsBuildGroupEnabled() || !IsMouseInputEnabled()) - { - return; - } - - if ( !ShouldHandleInputMessage() ) - return; - - OnMouseWheeled(delta); -} - -void Panel::InternalKeyCodePressed(int code) -{ - if ( !ShouldHandleInputMessage() ) - return; - - if (IsKeyBoardInputEnabled()) - { - OnKeyCodePressed((KeyCode)code); - } - else - { - CallParentFunction(new KeyValues("KeyCodePressed", "code", code)); - } -} - -#if defined( VGUI_USEKEYBINDINGMAPS ) -//----------------------------------------------------------------------------- -// Purpose: -// Input : *bindingName - -// keycode - -// modifiers - -//----------------------------------------------------------------------------- -void Panel::AddKeyBinding( char const *bindingName, int keycode, int modifiers ) -{ - PanelKeyBindingMap *map = LookupMapForBinding( bindingName ); - if ( !map ) - { - Assert( 0 ); - return; - } - - BoundKey_t kb; - kb.isbuiltin = false; - kb.bindingname = CopyString( bindingName ); - kb.keycode = keycode; - kb.modifiers = modifiers; - - map->boundkeys.AddToTail( kb ); -} - -KeyBindingMap_t *Panel::LookupBinding( char const *bindingName ) -{ - PanelKeyBindingMap *map = GetKBMap(); - while( map ) - { - int c = map->entries.Count(); - for( int i = 0; i < c ; ++i ) - { - KeyBindingMap_t *binding = &map->entries[ i ]; - if ( !Q_stricmp( binding->bindingname, bindingName ) ) - return binding; - } - - map = map->baseMap; - } - - return NULL; -} - -PanelKeyBindingMap *Panel::LookupMapForBinding( char const *bindingName ) -{ - PanelKeyBindingMap *map = GetKBMap(); - while( map ) - { - int c = map->entries.Count(); - for( int i = 0; i < c ; ++i ) - { - KeyBindingMap_t *binding = &map->entries[ i ]; - if ( !Q_stricmp( binding->bindingname, bindingName ) ) - return map; - } - - map = map->baseMap; - } - - return NULL; -} - -KeyBindingMap_t *Panel::LookupBindingByKeyCode( KeyCode code, int modifiers ) -{ - PanelKeyBindingMap *map = GetKBMap(); - while( map ) - { - int c = map->boundkeys.Count(); - for( int i = 0; i < c ; ++i ) - { - BoundKey_t *kb = &map->boundkeys[ i ]; - if ( kb->keycode == code && kb->modifiers == modifiers ) - { - KeyBindingMap_t *binding = LookupBinding( kb->bindingname ); - Assert( binding ); - if ( binding ) - { - return binding; - } - } - } - - map = map->baseMap; - } - - return NULL; -} - -BoundKey_t *Panel::LookupDefaultKey( char const *bindingName ) -{ - PanelKeyBindingMap *map = GetKBMap(); - while( map ) - { - int c = map->defaultkeys.Count(); - for( int i = 0; i < c ; ++i ) - { - BoundKey_t *kb = &map->defaultkeys[ i ]; - if ( !Q_stricmp( kb->bindingname, bindingName ) ) - { - return kb; - } - } - - map = map->baseMap; - } - return NULL; -} - -void Panel::LookupBoundKeys( char const *bindingName, CUtlVector< BoundKey_t * >& list ) -{ - PanelKeyBindingMap *map = GetKBMap(); - while( map ) - { - int c = map->boundkeys.Count(); - for( int i = 0; i < c ; ++i ) - { - BoundKey_t *kb = &map->boundkeys[ i ]; - if ( !Q_stricmp( kb->bindingname, bindingName ) ) - { - list.AddToTail( kb ); - } - } - - map = map->baseMap; - } -} - -void Panel::RevertKeyBindingsToDefault() -{ - PanelKeyBindingMap *map = GetKBMap(); - while( map ) - { - map->boundkeys.RemoveAll(); - map->boundkeys = map->defaultkeys; - - map = map->baseMap; - } -} - -void Panel::RemoveAllKeyBindings() -{ - PanelKeyBindingMap *map = GetKBMap(); - while( map ) - { - map->boundkeys.RemoveAll(); - map = map->baseMap; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -void Panel::ReloadKeyBindings() -{ - RevertKeyBindingsToDefault(); - LoadKeyBindingsForOnePanel( GetKeyBindingsContext(), this ); -} - -#define MAKE_STRING( x ) #x -#define KEY_NAME( str, disp ) { KEY_##str, MAKE_STRING( KEY_##str ), disp } - -struct KeyNames_t -{ - KeyCode code; - char const *string; - char const *displaystring; -}; - -static KeyNames_t g_KeyNames[] = -{ -KEY_NAME( NONE, "None" ), -KEY_NAME( 0, "0" ), -KEY_NAME( 1, "1" ), -KEY_NAME( 2, "2" ), -KEY_NAME( 3, "3" ), -KEY_NAME( 4, "4" ), -KEY_NAME( 5, "5" ), -KEY_NAME( 6, "6" ), -KEY_NAME( 7, "7" ), -KEY_NAME( 8, "8" ), -KEY_NAME( 9, "9" ), -KEY_NAME( A, "A" ), -KEY_NAME( B, "B" ), -KEY_NAME( C, "C" ), -KEY_NAME( D, "D" ), -KEY_NAME( E, "E" ), -KEY_NAME( F, "F" ), -KEY_NAME( G, "G" ), -KEY_NAME( H, "H" ), -KEY_NAME( I, "I" ), -KEY_NAME( J, "J" ), -KEY_NAME( K, "K" ), -KEY_NAME( L, "L" ), -KEY_NAME( M, "M" ), -KEY_NAME( N, "N" ), -KEY_NAME( O, "O" ), -KEY_NAME( P, "P" ), -KEY_NAME( Q, "Q" ), -KEY_NAME( R, "R" ), -KEY_NAME( S, "S" ), -KEY_NAME( T, "T" ), -KEY_NAME( U, "U" ), -KEY_NAME( V, "V" ), -KEY_NAME( W, "W" ), -KEY_NAME( X, "X" ), -KEY_NAME( Y, "Y" ), -KEY_NAME( Z, "Z" ), -KEY_NAME( PAD_0, "Key Pad 0" ), -KEY_NAME( PAD_1, "Key Pad 1" ), -KEY_NAME( PAD_2, "Key Pad 2" ), -KEY_NAME( PAD_3, "Key Pad 3" ), -KEY_NAME( PAD_4, "Key Pad 4" ), -KEY_NAME( PAD_5, "Key Pad 5" ), -KEY_NAME( PAD_6, "Key Pad 6" ), -KEY_NAME( PAD_7, "Key Pad 7" ), -KEY_NAME( PAD_8, "Key Pad 8" ), -KEY_NAME( PAD_9, "Key Pad 9" ), -KEY_NAME( PAD_DIVIDE, "Key Pad /" ), -KEY_NAME( PAD_MULTIPLY, "Key Pad *" ), -KEY_NAME( PAD_MINUS, "Key Pad -" ), -KEY_NAME( PAD_PLUS, "Key Pad +" ), -KEY_NAME( PAD_ENTER, "Key Pad Enter" ), -KEY_NAME( PAD_DECIMAL, "Key Pad ." ), -KEY_NAME( LBRACKET, "[" ), -KEY_NAME( RBRACKET, "]" ), -KEY_NAME( SEMICOLON, "," ), -KEY_NAME( APOSTROPHE, "'" ), -KEY_NAME( BACKQUOTE, "`" ), -KEY_NAME( COMMA, "," ), -KEY_NAME( PERIOD, "." ), -KEY_NAME( SLASH, "/" ), -KEY_NAME( BACKSLASH, "\\" ), -KEY_NAME( MINUS, "-" ), -KEY_NAME( EQUAL, "=" ), -KEY_NAME( ENTER, "Enter" ), -KEY_NAME( SPACE, "Space" ), -KEY_NAME( BACKSPACE, "Backspace" ), -KEY_NAME( TAB, "Tab" ), -KEY_NAME( CAPSLOCK, "Caps Lock" ), -KEY_NAME( NUMLOCK, "Num Lock" ), -KEY_NAME( ESCAPE, "Escape" ), -KEY_NAME( SCROLLLOCK, "Scroll Lock" ), -KEY_NAME( INSERT, "Ins" ), -KEY_NAME( DELETE, "Del" ), -KEY_NAME( HOME, "Home" ), -KEY_NAME( END, "End" ), -KEY_NAME( PAGEUP, "PgUp" ), -KEY_NAME( PAGEDOWN, "PgDn" ), -KEY_NAME( BREAK, "Break" ), -KEY_NAME( LSHIFT, "Shift" ), -KEY_NAME( RSHIFT, "Shift" ), -KEY_NAME( LALT, "Alt" ), -KEY_NAME( RALT, "Alt" ), -KEY_NAME( LCONTROL, "Ctrl" ), -KEY_NAME( RCONTROL, "Ctrl" ), -KEY_NAME( LWIN, "Windows" ), -KEY_NAME( RWIN, "Windows" ), -KEY_NAME( APP, "App" ), -KEY_NAME( UP, "Up" ), -KEY_NAME( LEFT, "Left" ), -KEY_NAME( DOWN, "Down" ), -KEY_NAME( RIGHT, "Right" ), -KEY_NAME( F1, "F1" ), -KEY_NAME( F2, "F2" ), -KEY_NAME( F3, "F3" ), -KEY_NAME( F4, "F4" ), -KEY_NAME( F5, "F5" ), -KEY_NAME( F6, "F6" ), -KEY_NAME( F7, "F7" ), -KEY_NAME( F8, "F8" ), -KEY_NAME( F9, "F9" ), -KEY_NAME( F10, "F10" ), -KEY_NAME( F11, "F11" ), -KEY_NAME( F12, "F12" ), -KEY_NAME( CAPSLOCKTOGGLE, "Caps Lock Toggle" ), -KEY_NAME( NUMLOCKTOGGLE, "Num Lock Toggle" ), -KEY_NAME( SCROLLLOCKTOGGLE, "Scroll Lock Toggle" ), -}; - -char const *Panel::KeyCodeToString( KeyCode code ) -{ - int c = ARRAYSIZE( g_KeyNames ); - for ( int i = 0; i < c ; ++i ) - { - if ( g_KeyNames[ i ].code == code ) - return g_KeyNames[ i ].string; - } - - return ""; -} - -wchar_t const *Panel::KeyCodeToDisplayString( KeyCode code ) -{ - int c = ARRAYSIZE( g_KeyNames ); - for ( int i = 0; i < c ; ++i ) - { - if ( g_KeyNames[ i ].code == code ) - { - char const *str = g_KeyNames[ i ].displaystring; - wchar_t *wstr = g_pVGuiLocalize->Find( str ); - if ( wstr ) - { - return wstr; - } - - static wchar_t buf[ 64 ]; - g_pVGuiLocalize->ConvertANSIToUnicode( str, buf, sizeof( buf ) ); - return buf; - } - } - - return L""; -} - -static void AddModifierToString( char const *modifiername, char *buf, size_t bufsize ) -{ - char add[ 32 ]; - if ( Q_strlen( buf ) > 0 ) - { - Q_snprintf( add, sizeof( add ), "+%s", modifiername ); - } - else - { - Q_strncpy( add, modifiername, sizeof( add ) ); - } - - Q_strncat( buf, add, bufsize, COPY_ALL_CHARACTERS ); - -} - -wchar_t const *Panel::KeyCodeModifiersToDisplayString( KeyCode code, int modifiers ) -{ - char sz[ 256 ]; - sz[ 0 ] = 0; - - if ( modifiers & MODIFIER_SHIFT ) - { - AddModifierToString( "Shift", sz, sizeof( sz ) ); - } - if ( modifiers & MODIFIER_CONTROL ) - { - AddModifierToString( "Ctrl", sz, sizeof( sz ) ); - } - if ( modifiers & MODIFIER_ALT ) - { - AddModifierToString( "Alt", sz, sizeof( sz ) ); - } - - if ( Q_strlen( sz ) > 0 ) - { - Q_strncat( sz, "+", sizeof( sz ), COPY_ALL_CHARACTERS ); - } - - static wchar_t unicode[ 256 ]; - V_swprintf_safe( unicode, L"%S%s", sz, Panel::KeyCodeToDisplayString( (KeyCode)code ) ); - return unicode; -} - -KeyCode Panel::StringToKeyCode( char const *str ) -{ - int c = ARRAYSIZE( g_KeyNames ); - for ( int i = 0; i < c ; ++i ) - { - if ( !Q_stricmp( str, g_KeyNames[ i ].string ) ) - return g_KeyNames[ i ].code; - } - - return KEY_NONE; -} - -static void WriteKeyBindingToBuffer( CUtlBuffer& buf, int level, const BoundKey_t& binding ) -{ - BufPrint( buf, level, "\"keycode\"\t\"%s\"\n", Panel::KeyCodeToString( (KeyCode)binding.keycode ) ); - if ( binding.modifiers & MODIFIER_SHIFT ) - { - BufPrint( buf, level, "\"shift\"\t\"1\"\n" ); - } - if ( binding.modifiers & MODIFIER_CONTROL ) - { - BufPrint( buf, level, "\"ctrl\"\t\"1\"\n" ); - } - if ( binding.modifiers & MODIFIER_ALT ) - { - BufPrint( buf, level, "\"alt\"\t\"1\"\n" ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *filename - -// *pathID - -//----------------------------------------------------------------------------- -void Panel::SaveKeyBindingsToBuffer( int level, CUtlBuffer& buf ) -{ - Assert( IsValidKeyBindingsContext() ); - - Assert( buf.IsText() ); - - PanelKeyBindingMap *map = GetKBMap(); - while( map ) - { - int c = map->boundkeys.Count(); - for( int i = 0; i < c ; ++i ) - { - const BoundKey_t& binding = map->boundkeys[ i ]; - - // Spew to file - BufPrint( buf, level, "\"%s\"\n", binding.bindingname ); - BufPrint( buf, level, "{\n" ); - - WriteKeyBindingToBuffer( buf, level + 1, binding ); - - BufPrint( buf, level, "}\n" ); - } - - map = map->baseMap; - } -} - -bool Panel::ParseKeyBindings( KeyValues *kv ) -{ - Assert( IsValidKeyBindingsContext() ); - if ( !IsValidKeyBindingsContext() ) - return false; - - // To have KB the panel must have a name - Assert( GetName() && GetName()[ 0 ] ); - if ( !GetName() || !GetName()[ 0 ] ) - return false; - - bool success = false; - - g_KBMgr.AddPanelToContext( GetKeyBindingsContext(), this ); - - RemoveAllKeyBindings(); - - // Walk through bindings - for ( KeyValues *binding = kv->GetFirstSubKey(); binding != NULL; binding = binding->GetNextKey() ) - { - char const *bindingName = binding->GetName(); - if ( !bindingName || !bindingName[ 0 ] ) - continue; - - KeyBindingMap_t *b = LookupBinding( bindingName ); - if ( b ) - { - success = true; - const char *keycode = binding->GetString( "keycode", "" ); - int modifiers = 0; - if ( binding->GetInt( "shift", 0 ) != 0 ) - { - modifiers |= MODIFIER_SHIFT; - } - if ( binding->GetInt( "ctrl", 0 ) != 0 ) - { - modifiers |= MODIFIER_CONTROL; - } - if ( binding->GetInt( "alt", 0 ) != 0 ) - { - modifiers |= MODIFIER_ALT; - } - - KeyBindingMap_t *bound = LookupBindingByKeyCode( StringToKeyCode( keycode ), modifiers ); - if ( !bound ) - { - AddKeyBinding( bindingName, StringToKeyCode( keycode ), modifiers ); - } - } - else - { - Warning( "KeyBinding for panel '%s' contained unknown binding '%s'\n", GetName() ? GetName() : "???", bindingName ); - } - } - - // Now for each binding which is currently "unbound" to any key, use the default binding - PanelKeyBindingMap *map = GetKBMap(); - while( map ) - { - int c = map->entries.Count(); - for( int i = 0; i < c ; ++i ) - { - KeyBindingMap_t *binding = &map->entries[ i ]; - - // See if there is a bound key - CUtlVector< BoundKey_t * > list; - LookupBoundKeys( binding->bindingname, list ); - if ( list.Count() == 0 ) - { - // Assign the default binding to this key - BoundKey_t *defaultKey = LookupDefaultKey( binding->bindingname ); - if ( defaultKey ) - { - KeyBindingMap_t *alreadyBound = LookupBindingByKeyCode( (KeyCode)defaultKey->keycode, defaultKey->modifiers ); - if ( alreadyBound ) - { - Warning( "No binding for '%s', defautl key already bound to '%s'\n", binding->bindingname, alreadyBound->bindingname ); - } - else - { - AddKeyBinding( defaultKey->bindingname, defaultKey->keycode, defaultKey->modifiers ); - } - } - } - } - - map = map->baseMap; - } - - return success; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : handle - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -void Panel::SetKeyBindingsContext( KeyBindingContextHandle_t handle ) -{ - Assert( !IsValidKeyBindingsContext() || handle == GetKeyBindingsContext() ); - g_KBMgr.AddPanelToContext( handle, this ); - m_hKeyBindingsContext = handle; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : KeyBindingContextHandle_t -//----------------------------------------------------------------------------- -KeyBindingContextHandle_t Panel::GetKeyBindingsContext() const -{ - return m_hKeyBindingsContext; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Panel::IsValidKeyBindingsContext() const -{ - return GetKeyBindingsContext() != INVALID_KEYBINDINGCONTEXT_HANDLE; -} - -char const *Panel::GetKeyBindingsFile() const -{ - Assert( IsValidKeyBindingsContext() ); - return g_KBMgr.GetKeyBindingsFile( GetKeyBindingsContext() ); -} - -char const *Panel::GetKeyBindingsFilePathID() const -{ - Assert( IsValidKeyBindingsContext() ); - return g_KBMgr.GetKeyBindingsFilePathID( GetKeyBindingsContext() ); -} - -void Panel::EditKeyBindings() -{ - Assert( 0 ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Set this to false to disallow IsKeyRebound chaining to GetParent() Panels... -// Input : state - -//----------------------------------------------------------------------------- -void Panel::SetAllowKeyBindingChainToParent( bool state ) -{ - _flags.SetFlag( ALLOW_CHAIN_KEYBINDING_TO_PARENT, state ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Panel::IsKeyBindingChainToParentAllowed() const -{ - return _flags.IsFlagSet( ALLOW_CHAIN_KEYBINDING_TO_PARENT ); -} - -bool Panel::IsKeyOverridden( KeyCode code, int modifiers ) -{ - // By default assume all keys should pass through binding system - return false; -} - -bool Panel::IsKeyRebound( KeyCode code, int modifiers ) -{ - if ( IsKeyBoardInputEnabled() ) - { - KeyBindingMap_t* binding = LookupBindingByKeyCode( code, modifiers ); - // Only dispatch if we're part of the current modal subtree - if ( binding && IsChildOfSurfaceModalPanel() ) - { - // Found match, post message to panel - if ( binding->func ) - { - // dispatch the func - (this->*binding->func)(); - } - else - { - Assert( 0 ); - } - - if ( !binding->passive ) - { - // Exit this function... - return true; - } - } - } - - // Chain to parent - Panel* pParent = GetParent(); - if ( IsKeyBindingChainToParentAllowed() && pParent && !IsKeyOverridden( code, modifiers ) ) - return pParent->IsKeyRebound( code, modifiers ); - - // No suitable binding found - return false; -} - -static bool s_bSuppressRebindChecks = false; -#endif // VGUI_USEKEYBINDINGMAPS - -void Panel::InternalKeyCodeTyped( int code ) -{ - if ( !ShouldHandleInputMessage() ) - { - input()->OnKeyCodeUnhandled( code ); - return; - } - - if (IsKeyBoardInputEnabled()) - { - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - - int modifiers = 0; - if ( shift ) - { - modifiers |= MODIFIER_SHIFT; - } - if ( ctrl ) - { - modifiers |= MODIFIER_CONTROL; - } - if ( alt ) - { - modifiers |= MODIFIER_ALT; - } - - // Things in build mode don't have accelerators - if (IsBuildGroupEnabled()) - { - _buildGroup->KeyCodeTyped((KeyCode)code, this); - return; - } - - if ( !s_bSuppressRebindChecks && IsKeyRebound( (KeyCode)code, modifiers ) ) - { - return; - } - - bool oldVal = s_bSuppressRebindChecks; - s_bSuppressRebindChecks = true; - OnKeyCodeTyped((KeyCode)code); - s_bSuppressRebindChecks = oldVal; - } - else - { - if ( GetVPanel() == surface()->GetEmbeddedPanel() ) - { - input()->OnKeyCodeUnhandled( code ); - } - CallParentFunction(new KeyValues("KeyCodeTyped", "code", code)); - } -} - -void Panel::InternalKeyTyped(int unichar) -{ - if ( !ShouldHandleInputMessage() ) - return; - - if (IsKeyBoardInputEnabled()) - { - if ( IsBuildGroupEnabled() ) - { - if ( _buildGroup->KeyTyped( (wchar_t)unichar, this ) ) - { - return; - } - } - - OnKeyTyped((wchar_t)unichar); - } - else - { - CallParentFunction(new KeyValues("KeyTyped", "unichar", unichar)); - } -} - -void Panel::InternalKeyCodeReleased(int code) -{ - if ( !ShouldHandleInputMessage() ) - return; - - if (IsKeyBoardInputEnabled()) - { - if (IsBuildGroupEnabled()) - { - if ( _buildGroup->KeyCodeReleased((KeyCode)code, this) ) - { - return; - } - } - - OnKeyCodeReleased((KeyCode)code); - } - else - { - CallParentFunction(new KeyValues("KeyCodeReleased", "code", code)); - } -} - -void Panel::InternalKeyFocusTicked() -{ - if (IsBuildGroupEnabled()) - return; - - OnKeyFocusTicked(); -} - -void Panel::InternalMouseFocusTicked() -{ - if (IsBuildGroupEnabled()) - { - // must repaint so the numbers will be accurate - if (_buildGroup->HasRulersOn()) - { - PaintTraverse(true); - } - return; - } - - // update cursor - InternalSetCursor(); - OnMouseFocusTicked(); -} - - -void Panel::InternalSetCursor() -{ - bool visible = IsVisible(); - - if (visible) - { -#if defined( VGUI_USEDRAGDROP ) - // Drag drop is overriding cursor? - if ( m_pDragDrop->m_bDragging || - g_DragDropCapture.Get() != NULL ) - return; -#endif - // chain up and make sure all our parents are also visible - VPANEL p = GetVParent(); - while (p) - { - visible &= ipanel()->IsVisible(p); - p = ipanel()->GetParent(p); - } - - // only change the cursor if this panel is visible, and if its part of the main VGUI tree - if (visible && HasParent(surface()->GetEmbeddedPanel())) - { - HCursor cursor = GetCursor(); - - if (IsBuildGroupEnabled()) - { - cursor = _buildGroup->GetCursor(this); - } - - if (input()->GetCursorOveride()) - { - cursor = input()->GetCursorOveride(); - } - - surface()->SetCursor(cursor); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Called every frame the panel is visible, designed to be overridden -//----------------------------------------------------------------------------- -void Panel::OnThink() -{ -#if defined( VGUI_USEDRAGDROP ) - if ( IsPC() && - m_pDragDrop->m_bDragEnabled && - m_pDragDrop->m_bDragging && - m_pDragDrop->m_bDragStarted ) - { - bool isEscapeKeyDown = input()->IsKeyDown( KEY_ESCAPE ); - if ( isEscapeKeyDown ) - { - OnContinueDragging(); - OnFinishDragging( true, (MouseCode)-1, true ); - return; - } - - if ( m_pDragDrop->m_hCurrentDrop != 0 ) - { - if ( !input()->IsMouseDown( MOUSE_LEFT ) ) - { - OnContinueDragging(); - OnFinishDragging( true, (MouseCode)-1 ); - return; - } - - // allow the cursor to change based upon things like changing keystate, etc. - surface()->SetCursor( m_pDragDrop->m_hCurrentDrop->GetDropCursor( m_pDragDrop->m_DragData ) ); - - if ( !m_pDragDrop->m_bDropMenuShown ) - { - // See if the hover time has gotten larger - float hoverSeconds = ( system()->GetTimeMillis() - m_pDragDrop->m_lDropHoverTime ) * 0.001f; - DragDrop_t *dropInfo = m_pDragDrop->m_hCurrentDrop->GetDragDropInfo(); - - if ( dropInfo->m_flHoverContextTime != 0.0f ) - { - if ( hoverSeconds >= dropInfo->m_flHoverContextTime ) - { - m_pDragDrop->m_bDropMenuShown = true; - - CUtlVector< KeyValues * > data; - - GetDragData( data ); - - int x, y; - input()->GetCursorPos( x, y ); - - if ( m_pDragDrop->m_hDropContextMenu.Get() ) - { - delete m_pDragDrop->m_hDropContextMenu.Get(); - } - - Menu *menu = new Menu( m_pDragDrop->m_hCurrentDrop.Get(), "DropContext" ); - - bool useMenu = m_pDragDrop->m_hCurrentDrop->GetDropContextMenu( menu, data ); - if ( useMenu ) - { - m_pDragDrop->m_hDropContextMenu = menu; - - menu->SetPos( x, y ); - menu->SetVisible( true ); - menu->MakePopup(); - surface()->MovePopupToFront( menu->GetVPanel() ); - if ( menu->GetItemCount() > 0 ) - { - int id = menu->GetMenuID( 0 ); - menu->SetCurrentlyHighlightedItem( id ); - MenuItem *item = menu->GetMenuItem( id ); - item->SetArmed( true ); - } - } - else - { - delete menu; - } - - m_pDragDrop->m_hCurrentDrop->OnDropContextHoverShow( data ); - } - } - } - } - } -#endif -} - -// input messages handlers (designed for override) -void Panel::OnCursorMoved(int x, int y) -{ - if( ParentNeedsCursorMoveEvents() ) - { - // figure out x and y in parent space - int thisX, thisY; - ipanel()->GetPos( GetVPanel(), thisX, thisY ); - CallParentFunction( new KeyValues( "OnCursorMoved", "x", x + thisX, "y", y + thisY ) ); - } -} - -void Panel::OnCursorEntered() -{ -} - -void Panel::OnCursorExited() -{ -} - -void Panel::OnMousePressed(MouseCode code) -{ -} - -void Panel::OnMouseDoublePressed(MouseCode code) -{ -} - -void Panel::OnMouseTriplePressed(MouseCode code) -{ -} - -void Panel::OnMouseReleased(MouseCode code) -{ -} - -void Panel::OnMouseWheeled(int delta) -{ - CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); -} - -// base implementation forwards Key messages to the Panel's parent - override to 'swallow' the input -void Panel::OnKeyCodePressed(KeyCode code) -{ - static ConVarRef vgui_nav_lock( "vgui_nav_lock" ); - - bool handled = false; - switch( GetBaseButtonCode( code ) ) - { - case KEY_XBUTTON_UP: - case KEY_XSTICK1_UP: - case KEY_XSTICK2_UP: - case KEY_UP: - if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateUp() ) - { - vgui_nav_lock.SetValue( 1 ); - vgui::surface()->PlaySound( "UI/menu_focus.wav" ); - handled = true; - } - break; - case KEY_XBUTTON_DOWN: - case KEY_XSTICK1_DOWN: - case KEY_XSTICK2_DOWN: - case KEY_DOWN: - if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateDown() ) - { - vgui_nav_lock.SetValue( 1 ); - vgui::surface()->PlaySound( "UI/menu_focus.wav" ); - handled = true; - } - break; - case KEY_XBUTTON_LEFT: - case KEY_XSTICK1_LEFT: - case KEY_XSTICK2_LEFT: - case KEY_LEFT: - if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateLeft() ) - { - vgui_nav_lock.SetValue( 1 ); - vgui::surface()->PlaySound( "UI/menu_focus.wav" ); - handled = true; - } - break; - case KEY_XBUTTON_RIGHT: - case KEY_XSTICK1_RIGHT: - case KEY_XSTICK2_RIGHT: - case KEY_RIGHT: - if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateRight() ) - { - vgui_nav_lock.SetValue( 1 ); - vgui::surface()->PlaySound( "UI/menu_focus.wav" ); - handled = true; - } - break; - case KEY_XBUTTON_B: - if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateBack() ) - { - vgui_nav_lock.SetValue( 1 ); - vgui::surface()->PlaySound( "UI/menu_focus.wav" ); - handled = true; - } - break; - } - - if( !handled && !m_PassUnhandledInput ) - return; - - CallParentFunction(new KeyValues("KeyCodePressed", "code", code)); -} - -void Panel::OnKeyCodeTyped(KeyCode keycode) -{ - vgui::KeyCode code = GetBaseButtonCode( keycode ); - - // handle focus change - if ( IsX360() || IsConsoleStylePanel() ) - { - // eat these typed codes, will get handled in OnKeyCodePressed - switch ( code ) - { - case KEY_XBUTTON_UP: - case KEY_XSTICK1_UP: - case KEY_XSTICK2_UP: - case KEY_XBUTTON_DOWN: - case KEY_XSTICK1_DOWN: - case KEY_XSTICK2_DOWN: - case KEY_XBUTTON_LEFT: - case KEY_XSTICK1_LEFT: - case KEY_XSTICK2_LEFT: - case KEY_XBUTTON_RIGHT: - case KEY_XSTICK1_RIGHT: - case KEY_XSTICK2_RIGHT: - case KEY_XBUTTON_A: - case KEY_XBUTTON_B: - case KEY_XBUTTON_X: - case KEY_XBUTTON_Y: - case KEY_XBUTTON_LEFT_SHOULDER: - case KEY_XBUTTON_RIGHT_SHOULDER: - case KEY_XBUTTON_BACK: - case KEY_XBUTTON_START: - case KEY_XBUTTON_STICK1: - case KEY_XBUTTON_STICK2: - case KEY_XBUTTON_LTRIGGER: - case KEY_XBUTTON_RTRIGGER: - - case KEY_UP: - case KEY_DOWN: - case KEY_LEFT: - case KEY_RIGHT: - return; - } - - // legacy handling - need to re-enable for older apps? - /* - if ( code == KEY_XSTICK1_RIGHT || code == KEY_XBUTTON_RIGHT ) - { - RequestFocusNext(); - return; - } - else if ( code == KEY_XSTICK1_LEFT || code == KEY_XBUTTON_LEFT ) - { - RequestFocusPrev(); - return; - } - */ - } - - if (code == KEY_TAB) - { - bool bShiftDown = input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT); - - if ( IsConsoleStylePanel() ) - { - if ( bShiftDown ) - { - NavigateUp(); - } - else - { - NavigateDown(); - } - } - else - { - // if shift is down goto previous tab position, otherwise goto next - if ( bShiftDown ) - { - RequestFocusPrev(); - } - else - { - RequestFocusNext(); - } - } - } - else - { - // forward up - if ( GetVPanel() == surface()->GetEmbeddedPanel() ) - { - input()->OnKeyCodeUnhandled( keycode ); - } - CallParentFunction(new KeyValues("KeyCodeTyped", "code", keycode)); - } -} - -void Panel::OnKeyTyped(wchar_t unichar) -{ - CallParentFunction(new KeyValues("KeyTyped", "unichar", unichar)); -} - -void Panel::OnKeyCodeReleased(KeyCode code) -{ - CallParentFunction(new KeyValues("KeyCodeReleased", "code", code)); -} - -void Panel::OnKeyFocusTicked() -{ - CallParentFunction(new KeyValues("KeyFocusTicked")); -} - -void Panel::OnMouseFocusTicked() -{ - CallParentFunction(new KeyValues("OnMouseFocusTicked")); -} - -bool Panel::IsWithin(int x,int y) -{ - // check against our clip rect - int clipRect[4]; - ipanel()->GetClipRect(GetVPanel(), clipRect[0], clipRect[1], clipRect[2], clipRect[3]); - - if (x < clipRect[0]) - { - return false; - } - - if (y < clipRect[1]) - { - return false; - } - - if (x >= clipRect[2]) - { - return false; - } - - if (y >= clipRect[3]) - { - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: determines which is the topmost panel under the coordinates (x, y) -//----------------------------------------------------------------------------- -VPANEL Panel::IsWithinTraverse(int x, int y, bool traversePopups) -{ - // if this one is not visible, its children won't be either - // also if it doesn't want mouse input its children can't get it either - if (!IsVisible() || !IsMouseInputEnabled()) - return NULL; - - if (traversePopups) - { - // check popups first - int i; - CUtlVector< VPANEL > &children = ipanel()->GetChildren( GetVPanel() ); - int childCount = children.Count(); - for (i = childCount - 1; i >= 0; i--) - { - VPANEL panel = children[ i ]; - if (ipanel()->IsPopup(panel)) - { - panel = ipanel()->IsWithinTraverse(panel, x, y, true); - if (panel != null) - { - return panel; - } - } - } - - // check children recursive, if you find one, just return first one - // this checks in backwards order so the last child drawn for this panel is chosen which - // coincides to how it would be visibly displayed - for (i = childCount - 1; i >= 0; i--) - { - VPANEL panel = children[ i ]; - // we've already checked popups so ignore - if (!ipanel()->IsPopup(panel)) - { - panel = ipanel()->IsWithinTraverse(panel, x, y, true); - if (panel != 0) - { - return panel; - } - } - } - - // check ourself - if ( !IsMouseInputDisabledForThisPanel() && IsWithin(x, y) ) - { - return GetVPanel(); - } - } - else - { - // since we're not checking popups, it must be within us, so we can check ourself first - if (IsWithin(x, y)) - { - // check children recursive, if you find one, just return first one - // this checks in backwards order so the last child drawn for this panel is chosen which - // coincides to how it would be visibly displayed - CUtlVector< VPANEL > &children = ipanel()->GetChildren( GetVPanel() ); - int childCount = children.Count(); - for (int i = childCount - 1; i >= 0; i--) - { - VPANEL panel = children[ i ]; - // ignore popups - if (!ipanel()->IsPopup(panel)) - { - panel = ipanel()->IsWithinTraverse(panel, x, y, false); - if (panel != 0) - { - return panel; - } - } - } - - // not a child, must be us - if ( !IsMouseInputDisabledForThisPanel() ) - return GetVPanel(); - } - } - - return NULL; -} - -void Panel::LocalToScreen(int& x,int& y) -{ - int px, py; - ipanel()->GetAbsPos(GetVPanel(), px, py); - - x = x + px; - y = y + py; -} - -void Panel::ScreenToLocal(int& x,int& y) -{ - int px, py; - ipanel()->GetAbsPos(GetVPanel(), px, py); - - x = x - px; - y = y - py; -} - -void Panel::ParentLocalToScreen(int &x, int &y) -{ - int px, py; - ipanel()->GetAbsPos(GetVParent(), px, py); - - x = x + px; - y = y + py; -} - -void Panel::MakePopup(bool showTaskbarIcon,bool disabled) -{ - surface()->CreatePopup(GetVPanel(), false, showTaskbarIcon,disabled); -} - -void Panel::SetCursor(HCursor cursor) -{ - _cursor = cursor; -} - -HCursor Panel::GetCursor() -{ - return _cursor; -} - -void Panel::SetCursorAlwaysVisible( bool visible ) -{ - surface()->SetCursorAlwaysVisible( visible ); -} - -void Panel::SetMinimumSize(int wide,int tall) -{ - ipanel()->SetMinimumSize(GetVPanel(), wide, tall); -} - -void Panel::GetMinimumSize(int& wide,int &tall) -{ - ipanel()->GetMinimumSize(GetVPanel(), wide, tall); -} - -bool Panel::IsBuildModeEditable() -{ - return true; -} - -void Panel::SetBuildModeEditable(bool state) -{ - if (state) - { - _buildModeFlags |= BUILDMODE_EDITABLE; - } - else - { - _buildModeFlags &= ~BUILDMODE_EDITABLE; - } -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -bool Panel::IsBuildModeDeletable() -{ - return (_buildModeFlags & BUILDMODE_DELETABLE); -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void Panel::SetBuildModeDeletable(bool state) -{ - if (state) - { - _buildModeFlags |= BUILDMODE_DELETABLE; - } - else - { - _buildModeFlags &= ~BUILDMODE_DELETABLE; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool Panel::IsBuildModeActive() -{ - return _buildGroup ? _buildGroup->IsEnabled() : false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::GetClipRect(int& x0,int& y0,int& x1,int& y1) -{ - ipanel()->GetClipRect(GetVPanel(), x0, y0, x1, y1); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int Panel::GetChildCount() -{ - if (ipanel()) - { - return ipanel()->GetChildCount(GetVPanel()); - } - - return 0; -} - -//----------------------------------------------------------------------------- -// Purpose: returns a child by the specified index -//----------------------------------------------------------------------------- -Panel *Panel::GetChild(int index) -{ - // get the child and cast it to a panel - // this assumes that the child is from the same module as the this (precondition) - return ipanel()->GetPanel(ipanel()->GetChild(GetVPanel(), index), GetControlsModuleName()); -} - -CUtlVector< VPANEL > &Panel::GetChildren() -{ - return ipanel()->GetChildren(GetVPanel()); -} - -//----------------------------------------------------------------------------- -// Purpose: moves the key focus back -//----------------------------------------------------------------------------- -bool Panel::RequestFocusPrev(VPANEL panel) -{ - // chain to parent - if (GetVParent()) - { - return ipanel()->RequestFocusPrev(GetVParent(), GetVPanel()); - } - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool Panel::RequestFocusNext(VPANEL panel) -{ - // chain to parent - if (GetVParent()) - { - return ipanel()->RequestFocusNext(GetVParent(), GetVPanel()); - } - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the panel to have the current sub focus -// Input : direction - the direction in which focus travelled to arrive at this panel; forward = 1, back = -1 -//----------------------------------------------------------------------------- -void Panel::RequestFocus(int direction) -{ - // NOTE: This doesn't make any sense if we don't have keyboard input enabled - Assert( ( IsX360() || IsConsoleStylePanel() ) || IsKeyBoardInputEnabled() ); - // ivgui()->DPrintf2("RequestFocus(%s, %s)\n", GetName(), GetClassName()); - OnRequestFocus(GetVPanel(), NULL); -} - -//----------------------------------------------------------------------------- -// Purpose: Called after a panel requests focus to fix up the whole chain -//----------------------------------------------------------------------------- -void Panel::OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel) -{ - CallParentFunction(new KeyValues("OnRequestFocus", "subFocus", subFocus, "defaultPanel", defaultPanel)); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -VPANEL Panel::GetCurrentKeyFocus() -{ - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if the panel has focus -//----------------------------------------------------------------------------- -bool Panel::HasFocus() -{ - if (input()->GetFocus() == GetVPanel()) - { - return true; - } - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetTabPosition(int position) -{ - _tabPosition = position; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int Panel::GetTabPosition() -{ - return _tabPosition; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::InternalFocusChanged(bool lost) -{ - /* - //if focus is gained tell the focusNavGroup about it so its current can be correct - if( (!lost) && (_focusNavGroup!=null) ) - { - _focusNavGroup->setCurrentPanel(this); - } - */ -} - -//----------------------------------------------------------------------------- -// Purpose: Called when a panel loses it's mouse capture -//----------------------------------------------------------------------------- -void Panel::OnMouseCaptureLost() -{ - if (m_pTooltips) - { - m_pTooltips->ResetDelay(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::AddActionSignalTarget(Panel *messageTarget) -{ - HPanel target = ivgui()->PanelToHandle(messageTarget->GetVPanel()); - if (!_actionSignalTargetDar.HasElement(target)) - { - _actionSignalTargetDar.AddElement(target); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::AddActionSignalTarget(VPANEL messageTarget) -{ - HPanel target = ivgui()->PanelToHandle(messageTarget); - if (!_actionSignalTargetDar.HasElement(target)) - { - _actionSignalTargetDar.AddElement(target); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::RemoveActionSignalTarget(Panel *oldTarget) -{ - _actionSignalTargetDar.RemoveElement(ivgui()->PanelToHandle(oldTarget->GetVPanel())); -} - -//----------------------------------------------------------------------------- -// Purpose: Sends a message to all the panels that have requested action signals -//----------------------------------------------------------------------------- -void Panel::PostActionSignal( KeyValues *message ) -{ - if ( m_bIsSilent != true ) - { - // add who it was from the message - message->SetPtr("panel", this); - int i; - for (i = _actionSignalTargetDar.GetCount() - 1; i > 0; i--) - { - VPANEL panel = ivgui()->HandleToPanel(_actionSignalTargetDar[i]); - if (panel) - { - ivgui()->PostMessage(panel, message->MakeCopy(), GetVPanel()); - } - } - - // do this so we can save on one MakeCopy() call - if (i == 0) - { - VPANEL panel = ivgui()->HandleToPanel(_actionSignalTargetDar[i]); - if (panel) - { - ivgui()->PostMessage(panel, message, GetVPanel()); - return; - } - } - } - message->deleteThis(); -} - -void Panel::SetBorder(IBorder *border) -{ - _border = border; - - if (border) - { - int x, y, x2, y2; - border->GetInset(x, y, x2, y2); - ipanel()->SetInset(GetVPanel(), x, y, x2, y2); - - // update our background type based on the bord - SetPaintBackgroundType(border->GetBackgroundType()); - } - else - { - ipanel()->SetInset(GetVPanel(), 0, 0, 0, 0); - } -} - -IBorder *Panel::GetBorder() -{ - return _border; -} - - -void Panel::SetPaintBorderEnabled(bool state) -{ - _flags.SetFlag( PAINT_BORDER_ENABLED, state ); -} - -void Panel::SetPaintBackgroundEnabled(bool state) -{ - _flags.SetFlag( PAINT_BACKGROUND_ENABLED, state ); -} - -void Panel::SetPaintBackgroundType( int type ) -{ - // HACK only 0 through 2 supported for now - m_nPaintBackgroundType = clamp( type, 0, 2 ); -} - -void Panel::SetPaintEnabled(bool state) -{ - _flags.SetFlag( PAINT_ENABLED, state ); -} - -void Panel::SetPostChildPaintEnabled(bool state) -{ - _flags.SetFlag( POST_CHILD_PAINT_ENABLED, state ); -} - -void Panel::GetInset(int& left,int& top,int& right,int& bottom) -{ - ipanel()->GetInset(GetVPanel(), left, top, right, bottom); -} - -void Panel::GetPaintSize(int& wide,int& tall) -{ - GetSize(wide, tall); - if (_border != null) - { - int left,top,right,bottom; - _border->GetInset(left,top,right,bottom); - - wide -= (left+right); - tall -= (top+bottom); - } -} - -int Panel::GetWide() -{ - int wide, tall; - ipanel()->GetSize(GetVPanel(), wide, tall); - return wide; -} - -void Panel::SetWide(int wide) -{ - ipanel()->SetSize(GetVPanel(), wide, GetTall()); -} - -int Panel::GetTall() -{ - int wide, tall; - ipanel()->GetSize(GetVPanel(), wide, tall); - return tall; -} - -void Panel::SetTall(int tall) -{ - ipanel()->SetSize(GetVPanel(), GetWide(), tall); -} - -void Panel::SetBuildGroup(BuildGroup* buildGroup) -{ - //TODO: remove from old group - - Assert(buildGroup != NULL); - - _buildGroup = buildGroup; - - _buildGroup->PanelAdded(this); -} - -bool Panel::IsBuildGroupEnabled() -{ - if ( !_buildGroup.IsValid() ) - return false; - - bool enabled = _buildGroup->IsEnabled(); - if ( enabled ) - return enabled; - - if ( GetParent() && GetParent()->IsBuildGroupEnabled() ) - return true; - - return false; -} - -void Panel::SetBgColor(Color color) -{ - _bgColor = color; -} - -void Panel::SetFgColor(Color color) -{ - _fgColor = color; -} - -Color Panel::GetBgColor() -{ - return _bgColor; -} - -Color Panel::GetFgColor() -{ - return _fgColor; -} - -void Panel::InternalPerformLayout() -{ - // Don't layout if we're still waiting for our scheme to be applied. - // At worst, it leads to crashes, at best it does work that we'll redo as soon as the scheme has been applied. - if ( _flags.IsFlagSet( NEEDS_SCHEME_UPDATE ) ) - return; - - _flags.SetFlag( IN_PERFORM_LAYOUT ); - // make sure the scheme has been applied - _flags.ClearFlag( NEEDS_LAYOUT ); - PerformLayout(); - _flags.ClearFlag( IN_PERFORM_LAYOUT ); -} - -void Panel::PerformLayout() -{ - // this should be overridden to relayout controls -} - -void Panel::InvalidateLayout( bool layoutNow, bool reloadScheme ) -{ - _flags.SetFlag( NEEDS_LAYOUT ); - - if (reloadScheme) - { - // make all our children reload the scheme - _flags.SetFlag( NEEDS_SCHEME_UPDATE ); - - for (int i = 0; i < GetChildCount(); i++) - { - vgui::Panel* panel = GetChild(i); - if( panel ) - { - panel->InvalidateLayout(layoutNow, true); - } - } - - PerformApplySchemeSettings(); - } - - if (layoutNow) - { - InternalPerformLayout(); - Repaint(); - } -} - -bool Panel::IsCursorNone() -{ - HCursor cursor = GetCursor(); - - if (!cursor) - { - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if the cursor is currently over the panel -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Panel::IsCursorOver(void) -{ - int x, y; - input()->GetCursorPos(x, y); - return IsWithin(x, y); -} - -//----------------------------------------------------------------------------- -// Purpose: Called when a panel receives a command message from another panel -//----------------------------------------------------------------------------- -void Panel::OnCommand(const char *command) -{ - if ( !Q_stricmp( "performlayout", command ) ) - { - InvalidateLayout(); - } - else if ( !Q_stricmp( "reloadscheme", command ) ) - { - InvalidateLayout( false, true ); - } - else - { - // if noone else caught this, pass along to the listeners - // (this is useful for generic dialogs - otherwise, commands just get ignored) - KeyValues *msg = new KeyValues( command ); - PostActionSignal( msg ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: panel gained focus message -//----------------------------------------------------------------------------- -void Panel::OnSetFocus() -{ - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: panel lost focus message -//----------------------------------------------------------------------------- -void Panel::OnKillFocus() -{ - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the object up to be deleted next frame -//----------------------------------------------------------------------------- -void Panel::MarkForDeletion() -{ - if ( _flags.IsFlagSet( MARKED_FOR_DELETION ) ) - return; - - _flags.SetFlag( MARKED_FOR_DELETION ); - _flags.ClearFlag( AUTODELETE_ENABLED ); - - if (ivgui()->IsRunning()) - { - ivgui()->MarkPanelForDeletion(GetVPanel()); - } - // direct delete is never safe because even if ivgui is shutdown we manually do RunFrame() - // and we can enter here in a think traverse and then delete from underneath ourselves - /*else - { - delete this; - }*/ -} - -//----------------------------------------------------------------------------- -// Purpose: return true if this object require a perform layout -//----------------------------------------------------------------------------- -bool Panel::IsLayoutInvalid() -{ - return _flags.IsFlagSet( NEEDS_LAYOUT ); -} - - -//----------------------------------------------------------------------------- -// Sets the pin corner + resize mode for resizing panels -//----------------------------------------------------------------------------- -void Panel::SetAutoResize( PinCorner_e pinCorner, AutoResize_e resizeDir, - int nPinOffsetX, int nPinOffsetY, int nUnpinnedCornerOffsetX, int nUnpinnedCornerOffsetY ) -{ - _pinCorner = pinCorner; - _autoResizeDirection = resizeDir; - m_nPinDeltaX = nPinOffsetX; - m_nPinDeltaY = nPinOffsetY; - m_nResizeDeltaX = nUnpinnedCornerOffsetX; - m_nResizeDeltaY = nUnpinnedCornerOffsetY; -} - - -//----------------------------------------------------------------------------- -// Sets the pin corner for non-resizing panels -//----------------------------------------------------------------------------- -void Panel::SetPinCorner( PinCorner_e pinCorner, int nOffsetX, int nOffsetY ) -{ - _pinCorner = pinCorner; - _autoResizeDirection = AUTORESIZE_NO; - m_nPinDeltaX = nOffsetX; - m_nPinDeltaY = nOffsetY; - m_nResizeDeltaX = 0; - m_nResizeDeltaY = 0; -} - - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -Panel::PinCorner_e Panel::GetPinCorner() -{ - return (PinCorner_e)_pinCorner; -} - - -//----------------------------------------------------------------------------- -// Gets the relative offset of the control from the pin corner -//----------------------------------------------------------------------------- -void Panel::GetPinOffset( int &dx, int &dy ) -{ - dx = m_nPinDeltaX; - dy = m_nPinDeltaY; -} - - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -Panel::AutoResize_e Panel::GetAutoResize() -{ - return (AutoResize_e)_autoResizeDirection; -} - - -//----------------------------------------------------------------------------- -// Gets the relative offset of the control from the pin corner -//----------------------------------------------------------------------------- -void Panel::GetResizeOffset( int &dx, int &dy ) -{ - dx = m_nResizeDeltaX; - dy = m_nResizeDeltaY; -} - -//----------------------------------------------------------------------------- -// Tells this panel that it should pin itself to the corner of a specified sibling panel -//----------------------------------------------------------------------------- -void Panel::PinToSibling( const char *pszSibling, PinCorner_e pinOurCorner, PinCorner_e pinSibling ) -{ - _pinCornerToSibling = pinOurCorner; - _pinToSiblingCorner = pinSibling; - - if ( _pinToSibling && pszSibling && !Q_strcmp( _pinToSibling, pszSibling ) ) - return; - - if (_pinToSibling) - { - delete [] _pinToSibling; - _pinToSibling = NULL; - } - - if (pszSibling) - { - int len = Q_strlen(pszSibling) + 1; - _pinToSibling = new char[ len ]; - Q_strncpy( _pinToSibling, pszSibling, len ); - } - m_pinSibling = NULL; - - UpdateSiblingPin(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::UpdateSiblingPin( void ) -{ - if ( !_pinToSibling ) - { - ipanel()->SetSiblingPin(GetVPanel(), NULL); - return; - } - - if ( !m_pinSibling.Get() ) - { - // Resolve our sibling now - m_pinSibling = FindSiblingByName( _pinToSibling ); - } - - if ( m_pinSibling.Get() ) - { - ipanel()->SetSiblingPin( GetVPanel(), m_pinSibling->GetVPanel(), _pinCornerToSibling, _pinToSiblingCorner ); - } - else - { - ipanel()->SetSiblingPin(GetVPanel(), NULL); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::ApplySchemeSettings(IScheme *pScheme) -{ - // get colors - SetFgColor(GetSchemeColor("Panel.FgColor", pScheme)); - SetBgColor(GetSchemeColor("Panel.BgColor", pScheme)); - -#if defined( VGUI_USEDRAGDROP ) - m_clrDragFrame = pScheme->GetColor("DragDrop.DragFrame", Color(255, 255, 255, 192)); - m_clrDropFrame = pScheme->GetColor("DragDrop.DropFrame", Color(150, 255, 150, 255)); - - m_infoFont = pScheme->GetFont( "DefaultVerySmall" ); -#endif - // mark us as no longer needing scheme settings applied - _flags.ClearFlag( NEEDS_SCHEME_UPDATE ); - - if ( IsBuildGroupEnabled() ) - { - _buildGroup->ApplySchemeSettings(pScheme); - return; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Checks to see if the panel needs it's scheme info setup -//----------------------------------------------------------------------------- -void Panel::PerformApplySchemeSettings() -{ - if ( _flags.IsFlagSet( NEEDS_DEFAULT_SETTINGS_APPLIED ) ) - { - InternalInitDefaultValues( GetAnimMap() ); - } - - if ( _flags.IsFlagSet( NEEDS_SCHEME_UPDATE ) ) - { - VPROF( "ApplySchemeSettings" ); - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - AssertOnce( pScheme ); - if ( pScheme ) // this should NEVER be null, but if it is bad things would happen in ApplySchemeSettings... - { - ApplySchemeSettings( pScheme ); - //_needsSchemeUpdate = false; - - ApplyOverridableColors(); - - UpdateSiblingPin(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Loads panel details related to autoresize from the resource info -//----------------------------------------------------------------------------- -#if defined( _DEBUG ) -static Panel *lastWarningParent = 0; -#endif - -void Panel::ApplyAutoResizeSettings(KeyValues *inResourceData) -{ - int x, y; - GetPos(x, y); - - int wide, tall; - GetSize( wide, tall ); - - AutoResize_e autoResize = (AutoResize_e)inResourceData->GetInt( "AutoResize", AUTORESIZE_NO ); - PinCorner_e pinCorner = (PinCorner_e)inResourceData->GetInt( "PinCorner", PIN_TOPLEFT ); - - // By default, measure unpinned corner for the offset - int pw = wide, pt = tall; - if ( GetParent() ) - { - GetParent()->GetSize( pw, pt ); -#if defined( _DEBUG ) - if ( pw == 64 && pt == 24 ) - { - if ( GetParent() != lastWarningParent ) - { - lastWarningParent = GetParent(); - Warning( "Resize parent (panel(%s) -> parent(%s)) not sized yet!!!\n", GetName(), GetParent()->GetName() ); - } - } -#endif - } - - int nPinnedCornerOffsetX = 0, nPinnedCornerOffsetY = 0; - int nUnpinnedCornerOffsetX = 0, nUnpinnedCornerOffsetY = 0; - switch( pinCorner ) - { - case PIN_TOPLEFT: - nPinnedCornerOffsetX = x; - nPinnedCornerOffsetY = y; - nUnpinnedCornerOffsetX = (x + wide) - pw; - nUnpinnedCornerOffsetY = (y + tall) - pt; - break; - - case PIN_TOPRIGHT: - nPinnedCornerOffsetX = (x + wide) - pw; - nPinnedCornerOffsetY = y; - nUnpinnedCornerOffsetX = x; - nUnpinnedCornerOffsetY = (y + tall) - pt; - break; - - case PIN_BOTTOMLEFT: - nPinnedCornerOffsetX = x; - nPinnedCornerOffsetY = (y + tall) - pt; - nUnpinnedCornerOffsetX = (x + wide) - pw; - nUnpinnedCornerOffsetY = y; - break; - - case PIN_BOTTOMRIGHT: - nPinnedCornerOffsetX = (x + wide) - pw; - nPinnedCornerOffsetY = (y + tall) - pt; - nUnpinnedCornerOffsetX = x; - nUnpinnedCornerOffsetY = y; - break; - } - - // Allow specific overrides in the resource file - if ( IsProportional() ) - { - if ( inResourceData->FindKey( "PinnedCornerOffsetX" ) ) - { - nPinnedCornerOffsetX = scheme()->GetProportionalScaledValueEx( GetScheme(), inResourceData->GetInt( "PinnedCornerOffsetX" ) ); - } - if ( inResourceData->FindKey( "PinnedCornerOffsetY" ) ) - { - nPinnedCornerOffsetY = scheme()->GetProportionalScaledValueEx( GetScheme(), inResourceData->GetInt( "PinnedCornerOffsetY" ) ); - } - if ( inResourceData->FindKey( "UnpinnedCornerOffsetX" ) ) - { - nUnpinnedCornerOffsetX = scheme()->GetProportionalScaledValueEx( GetScheme(), inResourceData->GetInt( "UnpinnedCornerOffsetX" ) ); - } - if ( inResourceData->FindKey( "UnpinnedCornerOffsetY" ) ) - { - nUnpinnedCornerOffsetY = scheme()->GetProportionalScaledValueEx( GetScheme(), inResourceData->GetInt( "UnpinnedCornerOffsetY" ) ); - } - } - else - { - nPinnedCornerOffsetX = inResourceData->GetInt( "PinnedCornerOffsetX", nPinnedCornerOffsetX ); - nPinnedCornerOffsetY = inResourceData->GetInt( "PinnedCornerOffsetY", nPinnedCornerOffsetY ); - nUnpinnedCornerOffsetX = inResourceData->GetInt( "UnpinnedCornerOffsetX", nUnpinnedCornerOffsetX ); - nUnpinnedCornerOffsetY = inResourceData->GetInt( "UnpinnedCornerOffsetY", nUnpinnedCornerOffsetY ); - } - - if ( autoResize == AUTORESIZE_NO ) - { - nUnpinnedCornerOffsetX = nUnpinnedCornerOffsetY = 0; - } - - SetAutoResize( pinCorner, autoResize, nPinnedCornerOffsetX, nPinnedCornerOffsetY, nUnpinnedCornerOffsetX, nUnpinnedCornerOffsetY ); -} - -ConVar panel_test_title_safe( "panel_test_title_safe", "0", FCVAR_CHEAT, "Test vgui panel positioning with title safe indentation" ); - - - -//----------------------------------------------------------------------------- -// Purpose: Loads panel details from the resource info -//----------------------------------------------------------------------------- -void Panel::ApplySettings(KeyValues *inResourceData) -{ - // First restore to default values - if ( _flags.IsFlagSet( NEEDS_DEFAULT_SETTINGS_APPLIED ) ) - { - InternalInitDefaultValues( GetAnimMap() ); - } - - // Let PanelAnimationVars auto-retrieve settings (we restore defaults above - // since a script might be missing certain values) - InternalApplySettings( GetAnimMap(), inResourceData ); - - // clear any alignment flags - _buildModeFlags &= ~(BUILDMODE_SAVE_XPOS_RIGHTALIGNED | BUILDMODE_SAVE_XPOS_CENTERALIGNED | BUILDMODE_SAVE_YPOS_BOTTOMALIGNED | BUILDMODE_SAVE_YPOS_CENTERALIGNED | BUILDMODE_SAVE_WIDE_FULL | BUILDMODE_SAVE_TALL_FULL | BUILDMODE_SAVE_PROPORTIONAL_TO_PARENT); - - // get the position - int alignScreenWide, alignScreenTall; // screen dimensions used for pinning in splitscreen - surface()->GetScreenSize( alignScreenWide, alignScreenTall ); - - int screenWide = alignScreenWide; - int screenTall = alignScreenTall; - - // temporarily remove the override to get the fullscreen dimensions - if ( surface()->IsScreenSizeOverrideActive() ) - { - surface()->ForceScreenSizeOverride( false, 0, 0 ); - surface()->GetScreenSize( screenWide, screenTall ); - - // restore the override - surface()->ForceScreenSizeOverride( true, alignScreenWide, alignScreenTall ); - } - - int parentX = 0; - int parentY = 0; - - // flag to cause windows to get screenWide and screenTall from their parents, - // this allows children windows to use fill and right/bottom alignment even - // if their parent does not use the full screen. - if ( inResourceData->GetInt( "proportionalToParent", 0 ) == 1 ) - { - _buildModeFlags |= BUILDMODE_SAVE_PROPORTIONAL_TO_PARENT; - if ( GetParent() != NULL ) - { - GetParent()->GetBounds( parentX, parentY, alignScreenWide, alignScreenTall ); - } - } - - int x, y; - GetPos(x, y); - const char *xstr = inResourceData->GetString( "xpos", NULL ); - const char *ystr = inResourceData->GetString( "ypos", NULL ); - - if (xstr) - { - // look for alignment flags - if (xstr[0] == 'r' || xstr[0] == 'R') - { - _buildModeFlags |= BUILDMODE_SAVE_XPOS_RIGHTALIGNED; - xstr++; - } - else if (xstr[0] == 'c' || xstr[0] == 'C') - { - _buildModeFlags |= BUILDMODE_SAVE_XPOS_CENTERALIGNED; - xstr++; - } - - // get the value - x = atoi(xstr); - // scale the x up to our screen co-ords - if ( IsProportional() ) - { - x = scheme()->GetProportionalScaledValueEx(GetScheme(), x); - } - // now correct the alignment - if (_buildModeFlags & BUILDMODE_SAVE_XPOS_RIGHTALIGNED) - { - x = alignScreenWide - x; - } - else if (_buildModeFlags & BUILDMODE_SAVE_XPOS_CENTERALIGNED) - { - x = (alignScreenWide / 2) + x; - } - } - - if (ystr) - { - // look for alignment flags - if (ystr[0] == 'r' || ystr[0] == 'R') - { - _buildModeFlags |= BUILDMODE_SAVE_YPOS_BOTTOMALIGNED; - ystr++; - } - else if (ystr[0] == 'c' || ystr[0] == 'C') - { - _buildModeFlags |= BUILDMODE_SAVE_YPOS_CENTERALIGNED; - ystr++; - } - y = atoi(ystr); - if (IsProportional()) - { - // scale the y up to our screen co-ords - y = scheme()->GetProportionalScaledValueEx(GetScheme(), y); - } - // now correct the alignment - if (_buildModeFlags & BUILDMODE_SAVE_YPOS_BOTTOMALIGNED) - { - y = alignScreenTall - y; - } - else if (_buildModeFlags & BUILDMODE_SAVE_YPOS_CENTERALIGNED) - { - y = (alignScreenTall / 2) + y; - } - } - - bool bUsesTitleSafeArea = false; - int titleSafeWide = 0; - int titleSafeTall = 0; - - Rect_t excludeEdgeFromTitleSafe; // if a side is set to != 0, don't title safe relative to that edge - excludeEdgeFromTitleSafe.x = 0; - excludeEdgeFromTitleSafe.y = 0; - excludeEdgeFromTitleSafe.width = 0; - excludeEdgeFromTitleSafe.height = 0; - - if ( IsX360() || panel_test_title_safe.GetBool() ) - { - // "usetitlesafe" "1" - required inner 90% - // "usetitlesafe" "2" - suggested inner 85% - - int iUseTitleSafeValue = 0; - if ( inResourceData->FindKey( "usetitlesafe" ) ) - { - iUseTitleSafeValue = inResourceData->GetInt( "usetitlesafe" ); - bUsesTitleSafeArea = ( iUseTitleSafeValue > 0 ); - } - - if( bUsesTitleSafeArea ) - { - titleSafeWide = screenWide * ( iUseTitleSafeValue == 1 ? 0.05f : 0.075f ); - titleSafeTall = screenTall * ( iUseTitleSafeValue == 1 ? 0.05f : 0.075f ); - - // Don't title safe internal boundaries for split screen viewports - int splitX = 0; - int splitY = 0; - vgui::surface()->OffsetAbsPos( splitX, splitY ); - - bool bHorizontalSplit = ( alignScreenTall != screenTall ); - bool bVerticalSplit = ( alignScreenWide != screenWide ); - - if ( bHorizontalSplit ) - { - // top or bottom? - if ( splitY != parentY ) - { - excludeEdgeFromTitleSafe.y = 1; - } - else - { - excludeEdgeFromTitleSafe.height = 1; - } - } - - if ( bVerticalSplit ) - { - // left or right - if ( splitX != parentX ) - { - excludeEdgeFromTitleSafe.x = 1; - } - else - { - excludeEdgeFromTitleSafe.width = 1; - } - } - - if ( _buildModeFlags & BUILDMODE_SAVE_XPOS_RIGHTALIGNED ) - { - if ( !excludeEdgeFromTitleSafe.width ) - { - x -= titleSafeWide; // right edge - } - } - else if (_buildModeFlags & BUILDMODE_SAVE_XPOS_CENTERALIGNED) - { - } - else if ( !excludeEdgeFromTitleSafe.x ) - { - x += titleSafeWide; // left edge - } - - if ( _buildModeFlags & BUILDMODE_SAVE_YPOS_BOTTOMALIGNED ) - { - if ( !excludeEdgeFromTitleSafe.height ) - { - y -= titleSafeTall; // bottom edge - } - } - else if (_buildModeFlags & BUILDMODE_SAVE_YPOS_CENTERALIGNED) - { - } - else if ( !excludeEdgeFromTitleSafe.y ) - { - y += titleSafeTall; // top edge - } - } - } - SetNavUp( inResourceData->GetString("navUp") ); - SetNavDown( inResourceData->GetString("navDown") ); - SetNavLeft( inResourceData->GetString("navLeft") ); - SetNavRight( inResourceData->GetString("navRight") ); - SetNavToRelay( inResourceData->GetString("navToRelay") ); - SetNavActivate( inResourceData->GetString("navActivate") ); - SetNavBack( inResourceData->GetString("navBack") ); - - SetPos(x, y); - - if (inResourceData->FindKey( "zpos" )) - { - SetZPos( inResourceData->GetInt( "zpos" ) ); - } - - // size - int wide, tall; - GetSize( wide, tall ); - - const char *wstr = inResourceData->GetString( "wide", NULL ); - if ( wstr ) - { - if (wstr[0] == 'f' || wstr[0] == 'F') - { - _buildModeFlags |= BUILDMODE_SAVE_WIDE_FULL; - wstr++; - } - wide = atoi(wstr); - if ( IsProportional() ) - { - // scale the width up to our screen co-ords - wide = scheme()->GetProportionalScaledValueEx(GetScheme(), wide); - } - // now correct the alignment - if (_buildModeFlags & BUILDMODE_SAVE_WIDE_FULL) - { - wide = alignScreenWide - wide; - } - } - - // allow tall to be use the "fill" option, set to the height of the parent/screen - wstr = inResourceData->GetString( "tall", NULL ); - if ( wstr ) - { - if (wstr[0] == 'f' || wstr[0] == 'F') - { - _buildModeFlags |= BUILDMODE_SAVE_TALL_FULL; - wstr++; - } - tall = atoi(wstr); - if ( IsProportional() ) - { - // scale the height up to our screen co-ords - tall = scheme()->GetProportionalScaledValueEx(GetScheme(), tall); - } - // now correct the alignment - if (_buildModeFlags & BUILDMODE_SAVE_TALL_FULL) - { - tall = alignScreenTall - tall; - } - } - - if( bUsesTitleSafeArea ) - { - if ( _buildModeFlags & BUILDMODE_SAVE_WIDE_FULL ) - { - if ( !excludeEdgeFromTitleSafe.x ) - wide -= titleSafeWide; - - if ( !excludeEdgeFromTitleSafe.width ) - wide -= titleSafeWide; - } - - if ( _buildModeFlags & BUILDMODE_SAVE_TALL_FULL ) - { - if ( !excludeEdgeFromTitleSafe.y ) - tall -= titleSafeTall; - - if ( !excludeEdgeFromTitleSafe.height ) - tall -= titleSafeTall; - } - } - - SetSize( wide, tall ); - - // NOTE: This has to happen after pos + size is set - ApplyAutoResizeSettings( inResourceData ); - - // only get colors if we're ignoring the scheme - if (inResourceData->GetInt("IgnoreScheme", 0)) - { - PerformApplySchemeSettings(); - } - - // state - int state = inResourceData->GetInt("visible", 1); - if (state == 0) - { - SetVisible(false); - } - else if (state == 1) - { - SetVisible(true); - } - - SetEnabled( inResourceData->GetInt("enabled", true) ); - - bool bMouseEnabled = inResourceData->GetInt( "mouseinputenabled", true ); - if ( !bMouseEnabled ) - { - SetMouseInputEnabled( false ); - } - - // tab order - SetTabPosition(inResourceData->GetInt("tabPosition", 0)); - - const char *tooltip = inResourceData->GetString("tooltiptext", NULL); - if (tooltip && *tooltip) - { - GetTooltip()->SetText(tooltip); - } - - // paint background? - int nPaintBackground = inResourceData->GetInt("paintbackground", -1); - if (nPaintBackground >= 0) - { - SetPaintBackgroundEnabled( nPaintBackground != 0 ); - } - - // paint border? - int nPaintBorder = inResourceData->GetInt("paintborder", -1); - if (nPaintBorder >= 0) - { - SetPaintBorderEnabled( nPaintBorder != 0 ); - } - - // border? - const char *pBorder = inResourceData->GetString( "border", "" ); - if ( *pBorder ) - { - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - SetBorder( pScheme->GetBorder( pBorder ) ); - } - - // check to see if we have a new name assigned - const char *newName = inResourceData->GetString("fieldName", NULL); - if ( newName ) - { - // Only slam the name if the new one differs... - SetName(newName); - } - - // check to see if we need to render to the frame buffer even if - // stereo mode is trying to render all of the ui to a render target - m_bForceStereoRenderToFrameBuffer = inResourceData->GetBool( "ForceStereoRenderToFrameBuffer", false ); - - //============================================================================= - // HPE_BEGIN: - // [pfreese] Support for reading rounded corner flags - //============================================================================= - int roundedCorners = inResourceData->GetInt( "RoundedCorners", -1 ); - if ( roundedCorners >= 0 ) - { - m_roundedCorners = roundedCorners; - } - //============================================================================= - // HPE_END - //============================================================================= - - const char *pszSiblingName = inResourceData->GetString("pin_to_sibling", NULL); - PinCorner_e pinOurCornerToSibling = (PinCorner_e)inResourceData->GetInt( "pin_corner_to_sibling", PIN_TOPLEFT ); - PinCorner_e pinSiblingCorner = (PinCorner_e)inResourceData->GetInt( "pin_to_sibling_corner", PIN_TOPLEFT ); - PinToSibling( pszSiblingName, pinOurCornerToSibling, pinSiblingCorner ); - - - // Allow overriding of colors. Used mostly by HUD elements, where scheme color usage is often undesired. - IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); - for ( int i = 0; i < m_OverridableColorEntries.Count(); i++ ) - { - // Need to ensure the key exists, so we don't overwrite existing colors when it's not set. - if ( inResourceData->FindKey( m_OverridableColorEntries[i].m_pszScriptName, false ) ) - { - // Get the color as a string - test whether it is an actual color or a reference to a scheme color - const char *pColorStr = inResourceData->GetString( m_OverridableColorEntries[i].m_pszScriptName ); - Color &clrDest = m_OverridableColorEntries[i].m_colFromScript; - if ( pColorStr[0] == '.' || isdigit( pColorStr[0] ) ) - { - float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f; - sscanf( pColorStr, "%f %f %f %f", &r, &g, &b, &a ); - clrDest[0] = (unsigned char)r; - clrDest[1] = (unsigned char)g; - clrDest[2] = (unsigned char)b; - clrDest[3] = (unsigned char)a; - } - else - { - // First character wasn't a digit or a decimal - do a scheme color lookup - clrDest = pScheme->GetColor( pColorStr, Color( 255, 255, 255, 255 ) ); - } - - (*m_OverridableColorEntries[i].m_pColor) = m_OverridableColorEntries[i].m_colFromScript; - m_OverridableColorEntries[i].m_bOverridden = true; - } - } - - const char *pKeyboardInputEnabled = inResourceData->GetString( "keyboardinputenabled", NULL ); - if ( pKeyboardInputEnabled && pKeyboardInputEnabled[0] ) - { - SetKeyBoardInputEnabled( atoi( pKeyboardInputEnabled ) ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Saves out a resource description of this panel -//----------------------------------------------------------------------------- -void Panel::GetSettings( KeyValues *outResourceData ) -{ - // control class name (so it can be recreated later if needed) - outResourceData->SetString( "ControlName", GetClassName() ); - - // name - outResourceData->SetString( "fieldName", _panelName ); - - // positioning - int screenWide, screenTall; - surface()->GetScreenSize(screenWide, screenTall); - int x, y; - GetPos( x, y ); - if ( IsProportional() ) - { - x = scheme()->GetProportionalNormalizedValueEx( GetScheme(), x ); - y = scheme()->GetProportionalNormalizedValueEx( GetScheme(), y ); - } - // correct for alignment - if (_buildModeFlags & BUILDMODE_SAVE_XPOS_RIGHTALIGNED) - { - x = screenWide - x; - char xstr[32]; - Q_snprintf(xstr, sizeof( xstr ), "r%d", x); - outResourceData->SetString( "xpos", xstr ); - } - else if (_buildModeFlags & BUILDMODE_SAVE_XPOS_CENTERALIGNED) - { - x = (screenWide / 2) + x; - char xstr[32]; - Q_snprintf(xstr, sizeof( xstr ), "c%d", x); - outResourceData->SetString( "xpos", xstr ); - } - else - { - outResourceData->SetInt( "xpos", x ); - } - if (_buildModeFlags & BUILDMODE_SAVE_YPOS_BOTTOMALIGNED) - { - y = screenTall - y; - char ystr[32]; - Q_snprintf(ystr, sizeof( ystr ), "r%d", y); - outResourceData->SetString( "ypos", ystr ); - } - else if (_buildModeFlags & BUILDMODE_SAVE_YPOS_CENTERALIGNED) - { - y = (screenTall / 2) + y; - char ystr[32]; - Q_snprintf(ystr, sizeof( ystr ), "c%d", y); - outResourceData->SetString( "ypos", ystr ); - } - else - { - outResourceData->SetInt( "ypos", y ); - } - if (m_pTooltips) - { - if (strlen(m_pTooltips->GetText()) > 0) - { - outResourceData->SetString("tooltiptext", m_pTooltips->GetText()); - } - } - int wide, tall; - GetSize( wide, tall ); - if ( IsProportional() ) - { - wide = scheme()->GetProportionalNormalizedValueEx( GetScheme(), wide ); - tall = scheme()->GetProportionalNormalizedValueEx( GetScheme(), tall ); - } - - int z = ipanel()->GetZPos(GetVPanel()); - if (z) - { - outResourceData->SetInt("zpos", z); - } - - // Correct for alignment - if (_buildModeFlags & BUILDMODE_SAVE_WIDE_FULL ) - { - wide = screenWide - wide; - char wstr[32]; - Q_snprintf(wstr, sizeof( wstr ), "f%d", wide); - outResourceData->SetString( "wide", wstr ); - } - else - { - outResourceData->SetInt( "wide", wide ); - } - outResourceData->SetInt( "tall", tall ); - - outResourceData->SetInt("AutoResize", GetAutoResize()); - outResourceData->SetInt("PinCorner", GetPinCorner()); - - //============================================================================= - // HPE_BEGIN: - // [pfreese] Support for writing out rounded corner flags - //============================================================================= - outResourceData->SetInt("RoundedCorners", m_roundedCorners); - //============================================================================= - // HPE_END - //============================================================================= - - outResourceData->SetString( "pin_to_sibling", _pinToSibling ); - outResourceData->SetInt("pin_corner_to_sibling", _pinCornerToSibling ); - outResourceData->SetInt("pin_to_sibling_corner", _pinToSiblingCorner ); - - - // state - outResourceData->SetInt( "visible", IsVisible() ); - outResourceData->SetInt( "enabled", IsEnabled() ); - - outResourceData->SetInt( "tabPosition", GetTabPosition() ); - - for ( int i = 0; i < m_OverridableColorEntries.Count(); i++ ) - { - if ( m_OverridableColorEntries[i].m_bOverridden ) - { - outResourceData->SetColor( m_OverridableColorEntries[i].m_pszScriptName, m_OverridableColorEntries[i].m_colFromScript ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: After applying settings, apply overridable colors. -// Done post apply settings, so that baseclass settings don't stomp -// the script specified override colors. -//----------------------------------------------------------------------------- -void Panel::ApplyOverridableColors( void ) -{ - for ( int i = 0; i < m_OverridableColorEntries.Count(); i++ ) - { - if ( m_OverridableColorEntries[i].m_bOverridden ) - { - (*m_OverridableColorEntries[i].m_pColor) = m_OverridableColorEntries[i].m_colFromScript; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetOverridableColor( Color *pColor, const Color &newColor ) -{ - for ( int i = 0; i < m_OverridableColorEntries.Count(); i++ ) - { - if ( m_OverridableColorEntries[i].m_bOverridden ) - { - if ( m_OverridableColorEntries[i].m_pColor == pColor ) - return; - } - } - - // Didn't find it, or it's not been overridden. - *pColor = newColor; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Color Panel::GetSchemeColor(const char *keyName, IScheme *pScheme) -{ - return pScheme->GetColor(keyName, Color(255, 255, 255, 255)); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Color Panel::GetSchemeColor(const char *keyName, Color defaultColor, IScheme *pScheme) -{ - return pScheme->GetColor(keyName, defaultColor); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns a string description of the panel fields for use in the UI -//----------------------------------------------------------------------------- -const char *Panel::GetDescription( void ) -{ - static const char *panelDescription = "string fieldName, int xpos, int ypos, int wide, int tall, bool visible, bool enabled, int tabPosition, corner pinCorner, autoresize autoResize, string tooltiptext"; - return panelDescription; -} - -//----------------------------------------------------------------------------- -// Purpose: user configuration settings -// this is used for any control details the user wants saved between sessions -// eg. dialog positions, last directory opened, list column width -//----------------------------------------------------------------------------- -void Panel::ApplyUserConfigSettings(KeyValues *userConfig) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: returns user config settings for this control -//----------------------------------------------------------------------------- -void Panel::GetUserConfigSettings(KeyValues *userConfig) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: optimization, return true if this control has any user config settings -//----------------------------------------------------------------------------- -bool Panel::HasUserConfigSettings() -{ - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::InternalInvalidateLayout() -{ - InvalidateLayout(false, false); -} - -//----------------------------------------------------------------------------- -// Purpose: called whenever the panel moves -//----------------------------------------------------------------------------- -void Panel::OnMove() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::InternalMove() -{ - OnMove(); - for(int i=0;iOnMove(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: empty function -//----------------------------------------------------------------------------- -void Panel::OnTick() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: versioning -//----------------------------------------------------------------------------- -void *Panel::QueryInterface(EInterfaceID id) -{ - if (id == ICLIENTPANEL_STANDARD_INTERFACE) - { - return this; - } - - return NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: Map all the base messages to functions -// ordering from most -> least used improves speed -//----------------------------------------------------------------------------- -MessageMapItem_t Panel::m_MessageMap[] = -{ - MAP_MESSAGE_INT( Panel, "RequestFocus", RequestFocus, "direction" ) -}; - -// IMPLEMENT_PANELMAP( Panel, NULL ) -PanelMap_t Panel::m_PanelMap = { Panel::m_MessageMap, ARRAYSIZE(Panel::m_MessageMap), "Panel", NULL }; -PanelMap_t *Panel::GetPanelMap( void ) { return &m_PanelMap; } - -//----------------------------------------------------------------------------- -// Purpose: !! Soon to replace existing prepare panel map -//----------------------------------------------------------------------------- -void PreparePanelMessageMap(PanelMessageMap *panelMap) -{ - // iterate through the class hierarchy message maps - while ( panelMap != NULL && !panelMap->processed ) - { - // hash message map strings into symbols - for (int i = 0; i < panelMap->entries.Count(); i++) - { - MessageMapItem_t *item = &panelMap->entries[i]; - - if (item->name) - { - item->nameSymbol = KeyValuesSystem()->GetSymbolForString(item->name); - } - else - { - item->nameSymbol = INVALID_KEY_SYMBOL; - } - if (item->firstParamName) - { - item->firstParamSymbol = KeyValuesSystem()->GetSymbolForString(item->firstParamName); - } - else - { - item->firstParamSymbol = INVALID_KEY_SYMBOL; - } - if (item->secondParamName) - { - item->secondParamSymbol = KeyValuesSystem()->GetSymbolForString(item->secondParamName); - } - else - { - item->secondParamSymbol = INVALID_KEY_SYMBOL; - } - } - - panelMap->processed = true; - panelMap = panelMap->baseMap; - } -} - - - -//----------------------------------------------------------------------------- -// Purpose: Handles a message -// Dispatches the message to a set of message maps -//----------------------------------------------------------------------------- -void Panel::OnMessage(const KeyValues *params, VPANEL ifromPanel) -{ - PanelMessageMap *panelMap = GetMessageMap(); - bool bFound = false; - int iMessageName = params->GetNameSymbol(); - - if ( !panelMap->processed ) - { - PreparePanelMessageMap( panelMap ); - } - - // iterate through the class hierarchy message maps - for ( ; panelMap != NULL && !bFound; panelMap = panelMap->baseMap ) - { -#if defined( _DEBUG ) -// char const *className = panelMap->pfnClassName(); -// NOTE_UNUSED( className ); -#endif - - // iterate all the entries in the panel map - for ( int i = 0; i < panelMap->entries.Count(); i++ ) - { - MessageMapItem_t *pMap = &panelMap->entries[i]; - - if (iMessageName == pMap->nameSymbol) - { - bFound = true; - - switch (pMap->numParams) - { - case 0: - { - (this->*(pMap->func))(); - break; - } - - case 1: - { - KeyValues *param1 = params->FindKey(pMap->firstParamSymbol); - if (!param1) - { - param1 = const_cast(params); - } - - switch ( pMap->firstParamType ) - { - case DATATYPE_INT: - typedef void (Panel::*MessageFunc_Int_t)(int); - (this->*((MessageFunc_Int_t)pMap->func))( param1->GetInt() ); - break; - - case DATATYPE_UINT64: - typedef void (Panel::*MessageFunc_Uin64_t)(uint64); - (this->*((MessageFunc_Uin64_t)pMap->func))( param1->GetUint64() ); - break; - - case DATATYPE_PTR: - typedef void (Panel::*MessageFunc_Ptr_t)( void * ); - (this->*((MessageFunc_Ptr_t)pMap->func))( param1->GetPtr() ); - break; - - case DATATYPE_HANDLE: - { - typedef void (Panel::*MessageFunc_VPANEL_t)( VPANEL ); - VPANEL vpanel = ivgui()->HandleToPanel( param1->GetInt() ); - (this->*((MessageFunc_VPANEL_t)pMap->func))( vpanel ); - } - break; - - case DATATYPE_FLOAT: - typedef void (Panel::*MessageFunc_Float_t)( float ); - (this->*((MessageFunc_Float_t)pMap->func))( param1->GetFloat() ); - break; - - case DATATYPE_CONSTCHARPTR: - typedef void (Panel::*MessageFunc_CharPtr_t)( const char * ); - (this->*((MessageFunc_CharPtr_t)pMap->func))( param1->GetString() ); - break; - - case DATATYPE_CONSTWCHARPTR: - typedef void (Panel::*MessageFunc_WCharPtr_t)( const wchar_t * ); - (this->*((MessageFunc_WCharPtr_t)pMap->func))( param1->GetWString() ); - break; - - case DATATYPE_KEYVALUES: - typedef void (Panel::*MessageFunc_KeyValues_t)(KeyValues *); - if ( pMap->firstParamName ) - { - (this->*((MessageFunc_KeyValues_t)pMap->func))( (KeyValues *)param1->GetPtr() ); - } - else - { - // no param set, so pass in the whole thing - (this->*((MessageFunc_KeyValues_t)pMap->func))( const_cast(params) ); - } - break; - - default: - Assert(!("No handler for vgui message function")); - break; - } - break; - } - - case 2: - { - KeyValues *param1 = params->FindKey(pMap->firstParamSymbol); - if (!param1) - { - param1 = const_cast(params); - } - KeyValues *param2 = params->FindKey(pMap->secondParamSymbol); - if (!param2) - { - param2 = const_cast(params); - } - - if ( (DATATYPE_INT == pMap->firstParamType) && (DATATYPE_INT == pMap->secondParamType) ) - { - typedef void (Panel::*MessageFunc_IntInt_t)(int, int); - (this->*((MessageFunc_IntInt_t)pMap->func))( param1->GetInt(), param2->GetInt() ); - } - else if ( (DATATYPE_PTR == pMap->firstParamType) && (DATATYPE_INT == pMap->secondParamType) ) - { - typedef void (Panel::*MessageFunc_PtrInt_t)(void *, int); - (this->*((MessageFunc_PtrInt_t)pMap->func))( param1->GetPtr(), param2->GetInt() ); - } - else if ( (DATATYPE_CONSTCHARPTR == pMap->firstParamType) && (DATATYPE_INT == pMap->secondParamType) ) - { - typedef void (Panel::*MessageFunc_ConstCharPtrInt_t)(const char *, int); - (this->*((MessageFunc_ConstCharPtrInt_t)pMap->func))( param1->GetString(), param2->GetInt() ); - } - else if ( (DATATYPE_CONSTCHARPTR == pMap->firstParamType) && (DATATYPE_CONSTCHARPTR == pMap->secondParamType) ) - { - typedef void (Panel::*MessageFunc_ConstCharPtrConstCharPtr_t)(const char *, const char *); - (this->*((MessageFunc_ConstCharPtrConstCharPtr_t)pMap->func))( param1->GetString(), param2->GetString() ); - } - else if ( (DATATYPE_INT == pMap->firstParamType) && (DATATYPE_CONSTCHARPTR == pMap->secondParamType) ) - { - typedef void (Panel::*MessageFunc_IntConstCharPtr_t)(int, const char *); - (this->*((MessageFunc_IntConstCharPtr_t)pMap->func))( param1->GetInt(), param2->GetString() ); - } - else if ( (DATATYPE_PTR == pMap->firstParamType) && (DATATYPE_CONSTCHARPTR == pMap->secondParamType) ) - { - typedef void (Panel::*MessageFunc_PtrConstCharPtr_t)(void *, const char *); - (this->*((MessageFunc_PtrConstCharPtr_t)pMap->func))( param1->GetPtr(), param2->GetString() ); - } - else if ( (DATATYPE_PTR == pMap->firstParamType) && (DATATYPE_CONSTWCHARPTR == pMap->secondParamType) ) - { - typedef void (Panel::*MessageFunc_PtrConstCharPtr_t)(void *, const wchar_t *); - (this->*((MessageFunc_PtrConstCharPtr_t)pMap->func))( param1->GetPtr(), param2->GetWString() ); - } - else if ( (DATATYPE_HANDLE == pMap->firstParamType) && (DATATYPE_CONSTCHARPTR == pMap->secondParamType) ) - { - typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, const char *); - VPANEL vp = ivgui()->HandleToPanel( param1->GetInt() ); - (this->*((MessageFunc_HandleConstCharPtr_t)pMap->func))( vp, param2->GetString() ); - } - else if ( (DATATYPE_HANDLE == pMap->firstParamType) && (DATATYPE_CONSTWCHARPTR == pMap->secondParamType) ) - { - typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, const wchar_t *); - VPANEL vp = ivgui()->HandleToPanel( param1->GetInt() ); - (this->*((MessageFunc_HandleConstCharPtr_t)pMap->func))( vp, param2->GetWString() ); - } - else - { - // the message isn't handled - ivgui()->DPrintf( "Message '%s', sent to '%s', has invalid parameter types\n", params->GetName(), GetName() ); - } - break; - } - - default: - Assert(!("Invalid number of parameters")); - break; - } - - // break the loop - bFound = true; - break; - } - } - } - - if (!bFound) - { - OnOldMessage(const_cast(params), ifromPanel); - } -} - -void Panel::OnOldMessage(KeyValues *params, VPANEL ifromPanel) -{ - bool bFound = false; - // message map dispatch - int iMessageName = params->GetNameSymbol(); - - PanelMap_t *panelMap = GetPanelMap(); - if ( !panelMap->processed ) - { - PreparePanelMap( panelMap ); - } - - // iterate through the class hierarchy message maps - for ( ; panelMap != NULL && !bFound; panelMap = panelMap->baseMap ) - { - MessageMapItem_t *pMessageMap = panelMap->dataDesc; - - for ( int i = 0; i < panelMap->dataNumFields; i++ ) - { - if (iMessageName == pMessageMap[i].nameSymbol) - { - // call the mapped function - switch ( pMessageMap[i].numParams ) - { - case 2: - if ( (DATATYPE_INT == pMessageMap[i].firstParamType) && (DATATYPE_INT == pMessageMap[i].secondParamType) ) - { - typedef void (Panel::*MessageFunc_IntInt_t)(int, int); - (this->*((MessageFunc_IntInt_t)pMessageMap[i].func))( params->GetInt(pMessageMap[i].firstParamName), params->GetInt(pMessageMap[i].secondParamName) ); - } - else if ( (DATATYPE_PTR == pMessageMap[i].firstParamType) && (DATATYPE_INT == pMessageMap[i].secondParamType) ) - { - typedef void (Panel::*MessageFunc_PtrInt_t)(void *, int); - (this->*((MessageFunc_PtrInt_t)pMessageMap[i].func))( params->GetPtr(pMessageMap[i].firstParamName), params->GetInt(pMessageMap[i].secondParamName) ); - } - else if ( (DATATYPE_CONSTCHARPTR == pMessageMap[i].firstParamType) && (DATATYPE_INT == pMessageMap[i].secondParamType) ) - { - typedef void (Panel::*MessageFunc_ConstCharPtrInt_t)(const char *, int); - (this->*((MessageFunc_ConstCharPtrInt_t)pMessageMap[i].func))( params->GetString(pMessageMap[i].firstParamName), params->GetInt(pMessageMap[i].secondParamName) ); - } - else if ( (DATATYPE_CONSTCHARPTR == pMessageMap[i].firstParamType) && (DATATYPE_CONSTCHARPTR == pMessageMap[i].secondParamType) ) - { - typedef void (Panel::*MessageFunc_ConstCharPtrConstCharPtr_t)(const char *, const char *); - (this->*((MessageFunc_ConstCharPtrConstCharPtr_t)pMessageMap[i].func))( params->GetString(pMessageMap[i].firstParamName), params->GetString(pMessageMap[i].secondParamName) ); - } - else if ( (DATATYPE_INT == pMessageMap[i].firstParamType) && (DATATYPE_CONSTCHARPTR == pMessageMap[i].secondParamType) ) - { - typedef void (Panel::*MessageFunc_IntConstCharPtr_t)(int, const char *); - (this->*((MessageFunc_IntConstCharPtr_t)pMessageMap[i].func))( params->GetInt(pMessageMap[i].firstParamName), params->GetString(pMessageMap[i].secondParamName) ); - } - else if ( (DATATYPE_PTR == pMessageMap[i].firstParamType) && (DATATYPE_CONSTCHARPTR == pMessageMap[i].secondParamType) ) - { - typedef void (Panel::*MessageFunc_PtrConstCharPtr_t)(void *, const char *); - (this->*((MessageFunc_PtrConstCharPtr_t)pMessageMap[i].func))( params->GetPtr(pMessageMap[i].firstParamName), params->GetString(pMessageMap[i].secondParamName) ); - } - else if ( (DATATYPE_PTR == pMessageMap[i].firstParamType) && (DATATYPE_CONSTWCHARPTR == pMessageMap[i].secondParamType) ) - { - typedef void (Panel::*MessageFunc_PtrConstCharPtr_t)(void *, const wchar_t *); - (this->*((MessageFunc_PtrConstCharPtr_t)pMessageMap[i].func))( params->GetPtr(pMessageMap[i].firstParamName), params->GetWString(pMessageMap[i].secondParamName) ); - } - else if ( (DATATYPE_HANDLE == pMessageMap[i].firstParamType) && (DATATYPE_CONSTCHARPTR ==pMessageMap[i].secondParamType) ) - { - typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, const char *); - VPANEL vp = ivgui()->HandleToPanel( params->GetInt( pMessageMap[i].firstParamName ) ); - (this->*((MessageFunc_HandleConstCharPtr_t)pMessageMap[i].func))( vp, params->GetString(pMessageMap[i].secondParamName) ); - } - else if ( (DATATYPE_HANDLE == pMessageMap[i].firstParamType) && (DATATYPE_CONSTWCHARPTR == pMessageMap[i].secondParamType) ) - { - typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, const wchar_t *); - VPANEL vp = ivgui()->HandleToPanel( params->GetInt( pMessageMap[i].firstParamName ) ); - (this->*((MessageFunc_HandleConstCharPtr_t)pMessageMap[i].func))( vp, params->GetWString(pMessageMap[i].secondParamName) ); - } - else - { - // the message isn't handled - ivgui()->DPrintf( "Message '%s', sent to '%s', has invalid parameter types\n", params->GetName(), GetName() ); - } - break; - - case 1: - switch ( pMessageMap[i].firstParamType ) - { - case DATATYPE_BOOL: - typedef void (Panel::*MessageFunc_Bool_t)(bool); - (this->*((MessageFunc_Bool_t)pMessageMap[i].func))( (bool)params->GetInt(pMessageMap[i].firstParamName) ); - break; - - case DATATYPE_CONSTCHARPTR: - typedef void (Panel::*MessageFunc_ConstCharPtr_t)(const char *); - (this->*((MessageFunc_ConstCharPtr_t)pMessageMap[i].func))( (const char *)params->GetString(pMessageMap[i].firstParamName) ); - break; - - case DATATYPE_CONSTWCHARPTR: - typedef void (Panel::*MessageFunc_ConstCharPtr_t)(const char *); - (this->*((MessageFunc_ConstCharPtr_t)pMessageMap[i].func))( (const char *)params->GetWString(pMessageMap[i].firstParamName) ); - break; - - case DATATYPE_INT: - typedef void (Panel::*MessageFunc_Int_t)(int); - (this->*((MessageFunc_Int_t)pMessageMap[i].func))( params->GetInt(pMessageMap[i].firstParamName) ); - break; - - case DATATYPE_FLOAT: - typedef void (Panel::*MessageFunc_Float_t)(float); - (this->*((MessageFunc_Float_t)pMessageMap[i].func))( params->GetFloat(pMessageMap[i].firstParamName) ); - break; - - case DATATYPE_PTR: - typedef void (Panel::*MessageFunc_Ptr_t)(void *); - (this->*((MessageFunc_Ptr_t)pMessageMap[i].func))( (void *)params->GetPtr(pMessageMap[i].firstParamName) ); - break; - - case DATATYPE_HANDLE: - { - typedef void (Panel::*MessageFunc_Ptr_t)(void *); - VPANEL vp = ivgui()->HandleToPanel( params->GetInt( pMessageMap[i].firstParamName ) ); - Panel *panel = ipanel()->GetPanel( vp, GetModuleName() ); - (this->*((MessageFunc_Ptr_t)pMessageMap[i].func))( (void *)panel ); - } - break; - - case DATATYPE_KEYVALUES: - typedef void (Panel::*MessageFunc_KeyValues_t)(KeyValues *); - if ( pMessageMap[i].firstParamName ) - { - (this->*((MessageFunc_KeyValues_t)pMessageMap[i].func))( (KeyValues *)params->GetPtr(pMessageMap[i].firstParamName) ); - } - else - { - (this->*((MessageFunc_KeyValues_t)pMessageMap[i].func))( params ); - } - break; - - default: - // the message isn't handled - ivgui()->DPrintf( "Message '%s', sent to '%s', has an invalid parameter type\n", params->GetName(), GetName() ); - break; - } - - break; - - default: - (this->*(pMessageMap[i].func))(); - break; - }; - - // break the loop - bFound = true; - break; - } - } - } - - // message not handled - // debug code - if ( !bFound ) - { - static int s_bDebugMessages = -1; - if ( s_bDebugMessages == -1 ) - { - s_bDebugMessages = CommandLine()->FindParm( "-vguimessages" ) ? 1 : 0; - } - if ( s_bDebugMessages == 1 ) - { - ivgui()->DPrintf( "Message '%s' not handled by panel '%s'\n", params->GetName(), GetName() ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Safe call to get info from child panel by name -//----------------------------------------------------------------------------- -bool Panel::RequestInfoFromChild(const char *childName, KeyValues *outputData) -{ - Panel *panel = FindChildByName(childName); - if (panel) - { - return panel->RequestInfo(outputData); - } - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Posts a message -//----------------------------------------------------------------------------- -void Panel::PostMessage(Panel *target, KeyValues *message, float delay) -{ - ivgui()->PostMessage(target->GetVPanel(), message, GetVPanel(), delay); -} - -void Panel::PostMessage(VPANEL target, KeyValues *message, float delaySeconds) -{ - ivgui()->PostMessage(target, message, GetVPanel(), delaySeconds); -} - -void Panel::PostMessageToAllSiblings( KeyValues *msg, float delaySeconds /*= 0.0f*/ ) -{ - VPANEL parent = GetVParent(); - if ( parent ) - { - VPANEL vpanel = GetVPanel(); - - CUtlVector< VPANEL > &children = ipanel()->GetChildren( parent ); - int nChildCount = children.Count(); - for ( int i = 0; i < nChildCount; ++i ) - { - VPANEL sibling = children[ i ]; - if ( sibling == vpanel ) - continue; - - if ( sibling ) - { - PostMessage( sibling, msg->MakeCopy(), delaySeconds ); - } - } - } - - msg->deleteThis(); -} - -//----------------------------------------------------------------------------- -// Purpose: Safe call to post a message to a child by name -//----------------------------------------------------------------------------- -void Panel::PostMessageToChild(const char *childName, KeyValues *message) -{ - Panel *panel = FindChildByName(childName); - if (panel) - { - ivgui()->PostMessage(panel->GetVPanel(), message, GetVPanel()); - } - else - { - message->deleteThis(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Requests some information from the panel -// Look through the message map for the handler -//----------------------------------------------------------------------------- -bool Panel::RequestInfo( KeyValues *outputData ) -{ - if ( InternalRequestInfo( GetAnimMap(), outputData ) ) - { - return true; - } - - if (GetVParent()) - { - return ipanel()->RequestInfo(GetVParent(), outputData); - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: sets a specified value in the control - inverse of RequestInfo -//----------------------------------------------------------------------------- -bool Panel::SetInfo(KeyValues *inputData) -{ - if ( InternalSetInfo( GetAnimMap(), inputData ) ) - { - return true; - } - - // doesn't chain to parent - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: change the panel's silent mode; if silent, the panel will not post -// any action signals -//----------------------------------------------------------------------------- - -void Panel::SetSilentMode( bool bSilent ) -{ - m_bIsSilent = bSilent; -} - -//----------------------------------------------------------------------------- -// Purpose: mouse events will be send to handler panel instead of this panel -//----------------------------------------------------------------------------- -void Panel::InstallMouseHandler( Panel *pHandler ) -{ - m_hMouseEventHandler = pHandler; -} - -//----------------------------------------------------------------------------- -// Purpose: Prepares the hierarchy panel maps for use (with message maps etc) -//----------------------------------------------------------------------------- -void Panel::PreparePanelMap( PanelMap_t *panelMap ) -{ - // iterate through the class hierarchy message maps - while ( panelMap != NULL && !panelMap->processed ) - { - // fixup cross-dll boundary panel maps - if ( panelMap->baseMap == (PanelMap_t*)0x00000001 ) - { - panelMap->baseMap = &Panel::m_PanelMap; - } - - // hash message map strings into symbols - for (int i = 0; i < panelMap->dataNumFields; i++) - { - MessageMapItem_t *item = &panelMap->dataDesc[i]; - - if (item->name) - { - item->nameSymbol = KeyValuesSystem()->GetSymbolForString(item->name); - } - else - { - item->nameSymbol = INVALID_KEY_SYMBOL; - } - if (item->firstParamName) - { - item->firstParamSymbol = KeyValuesSystem()->GetSymbolForString(item->firstParamName); - } - else - { - item->firstParamSymbol = INVALID_KEY_SYMBOL; - } - if (item->secondParamName) - { - item->secondParamSymbol = KeyValuesSystem()->GetSymbolForString(item->secondParamName); - } - else - { - item->secondParamSymbol = INVALID_KEY_SYMBOL; - } - } - - panelMap->processed = true; - panelMap = panelMap->baseMap; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Called to delete the panel -//----------------------------------------------------------------------------- -void Panel::OnDelete() -{ -#ifdef WIN32 - Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) ); -#endif - delete this; -#ifdef WIN32 - Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) ); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: Panel handle implementation -// Returns a pointer to a valid panel, NULL if the panel has been deleted -//----------------------------------------------------------------------------- -Panel *PHandle::Get() -{ - if (m_iPanelID != INVALID_PANEL) - { - VPANEL panel = ivgui()->HandleToPanel(m_iPanelID); - if (panel) - { - Panel *vguiPanel = ipanel()->GetPanel(panel, GetControlsModuleName()); - return vguiPanel; - } - } - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: sets the smart pointer -//----------------------------------------------------------------------------- -Panel *PHandle::Set(Panel *pent) -{ - if (pent) - { - m_iPanelID = ivgui()->PanelToHandle(pent->GetVPanel()); - } - else - { - m_iPanelID = INVALID_PANEL; - } - return pent; -} - -Panel *PHandle::Set( HPanel hPanel ) -{ - m_iPanelID = hPanel; - return Get(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Returns a handle to a valid panel, NULL if the panel has been deleted -//----------------------------------------------------------------------------- -VPANEL VPanelHandle::Get() -{ - if (m_iPanelID != INVALID_PANEL) - { - if (ivgui()) - { - return ivgui()->HandleToPanel(m_iPanelID); - } - } - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: sets the smart pointer -//----------------------------------------------------------------------------- -VPANEL VPanelHandle::Set(VPANEL pent) -{ - if (pent) - { - m_iPanelID = ivgui()->PanelToHandle(pent); - } - else - { - m_iPanelID = INVALID_PANEL; - } - return pent; -} - -//----------------------------------------------------------------------------- -// Purpose: returns a pointer to the tooltip object associated with the panel -//----------------------------------------------------------------------------- -BaseTooltip *Panel::GetTooltip() -{ - if (!m_pTooltips) - { - m_pTooltips = new TextTooltip(this, NULL); - m_bToolTipOverridden = false; - - if ( IsConsoleStylePanel() ) - { - m_pTooltips->SetEnabled( false ); - } - } - - return m_pTooltips; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetTooltip( BaseTooltip *pToolTip, const char *pszText ) -{ - if ( !m_bToolTipOverridden ) - { - // Remove the one we made, we're being overridden. - delete m_pTooltips; - } - - m_pTooltips = pToolTip; - m_bToolTipOverridden = true; - - if ( _tooltipText ) - { - delete [] _tooltipText; - _tooltipText = NULL; - } - - if ( pszText ) - { - int len = Q_strlen(pszText) + 1; - _tooltipText = new char[ len ]; - Q_strncpy( _tooltipText, pszText, len ); - } -} - -//----------------------------------------------------------------------------- -const char *Panel::GetEffectiveTooltipText() const -{ - if ( _tooltipText ) - { - return _tooltipText; - } - if ( m_pTooltips ) - { - const char *result = m_pTooltips->GetText(); - if ( result ) - { - return result; - } - } - return ""; -} - -//----------------------------------------------------------------------------- -// Purpose: sets the proportional flag on this panel and all it's children -//----------------------------------------------------------------------------- -void Panel::SetProportional(bool state) -{ - // only do something if the state changes - if( state != _flags.IsFlagSet( IS_PROPORTIONAL ) ) - { - _flags.SetFlag( IS_PROPORTIONAL, state ); - - for(int i=0;iSetProportional( IsProportional() ); - } - } - InvalidateLayout(); -} - - -void Panel::SetKeyBoardInputEnabled( bool state ) -{ - ipanel()->SetKeyBoardInputEnabled( GetVPanel(), state ); - for ( int i = 0; i < GetChildCount(); i++ ) - { - Panel *child = GetChild( i ); - if ( !child ) - { - continue; - } - child->SetKeyBoardInputEnabled( state ); - } - - // If turning off keyboard input enable, then make sure - // this panel is not the current key focus of a parent panel - if ( !state ) - { - Panel *pParent = GetParent(); - if ( pParent ) - { - if ( pParent->GetCurrentKeyFocus() == GetVPanel() ) - { - pParent->RequestFocusNext(); - } - } - } -} - -void Panel::SetMouseInputEnabled( bool state ) -{ - ipanel()->SetMouseInputEnabled( GetVPanel(), state ); - /* for(int i=0;iSetMouseInput(state); - }*/ - vgui::surface()->CalculateMouseVisible(); -} - -bool Panel::IsKeyBoardInputEnabled() -{ - return ipanel()->IsKeyBoardInputEnabled( GetVPanel() ); -} - -bool Panel::IsMouseInputEnabled() -{ - return ipanel()->IsMouseInputEnabled( GetVPanel() ); -} - -class CFloatProperty : public vgui::IPanelAnimationPropertyConverter -{ -public: - virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - kv->SetFloat( entry->name(), *(float *)data ); - } - - virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - *(float *)data = kv->GetFloat( entry->name() ); - } - - virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - *(float *)data = atof( entry->defaultvalue() ); - } -}; - -class CProportionalFloatProperty : public vgui::IPanelAnimationPropertyConverter -{ -public: - virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - float f = *(float *)data; - f = scheme()->GetProportionalNormalizedValueEx( panel->GetScheme(), f ); - kv->SetFloat( entry->name(), f ); - } - - virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - float f = kv->GetFloat( entry->name() ); - f = scheme()->GetProportionalScaledValueEx( panel->GetScheme(), f ); - *(float *)data = f; - } - - virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - float f = atof( entry->defaultvalue() ); - f = scheme()->GetProportionalScaledValueEx( panel->GetScheme(), f ); - *(float *)data = f; - } -}; - -class CIntProperty : public vgui::IPanelAnimationPropertyConverter -{ -public: - virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - kv->SetInt( entry->name(), *(int *)data ); - } - - virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - *(int *)data = kv->GetInt( entry->name() ); - } - - virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - *(int *)data = atoi( entry->defaultvalue() ); - } -}; - -class CProportionalIntProperty : public vgui::IPanelAnimationPropertyConverter -{ -public: - virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - int i = *(int *)data; - i = scheme()->GetProportionalNormalizedValueEx( panel->GetScheme(), i ); - kv->SetInt( entry->name(), i ); - } - - virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - int i = kv->GetInt( entry->name() ); - i = scheme()->GetProportionalScaledValueEx( panel->GetScheme(), i ); - *(int *)data = i; - } - virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - int i = atoi( entry->defaultvalue() ); - i = scheme()->GetProportionalScaledValueEx( panel->GetScheme(), i ); - *(int *)data = i; - } -}; - -class CProportionalIntWithScreenspacePropertyX : public vgui::IPanelAnimationPropertyConverter -{ -public: - int ExtractValue( Panel *panel, const char *pszKey ) - { - int iValue = 0; - bool bRightAlign = false; - bool bCenterAlign = false; - if (pszKey[0] == 'r' || pszKey[0] == 'R') - { - bRightAlign = true; - pszKey++; - } - else if (pszKey[0] == 'c' || pszKey[0] == 'C') - { - bCenterAlign = true; - pszKey++; - } - - // get the value - iValue = atoi(pszKey); - iValue = scheme()->GetProportionalScaledValueEx(panel->GetScheme(), iValue); - - int screenSize = GetScreenSize(); - // now correct the alignment - if ( bRightAlign ) - { - iValue = screenSize - iValue; - } - else if ( bCenterAlign ) - { - iValue = (screenSize / 2) + iValue; - } - - return iValue; - } - - virtual int GetScreenSize( void ) - { - int screenWide, screenTall; - surface()->GetScreenSize(screenWide, screenTall); - return screenWide; - } - - virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - // Won't work with this, don't use it. - Assert(0); - } - - virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - *(int *)data = ExtractValue( panel, kv->GetString( entry->name() ) ); - } - virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - *(int *)data = ExtractValue( panel, entry->defaultvalue() ); - } -}; - -class CProportionalIntWithScreenspacePropertyY : public CProportionalIntWithScreenspacePropertyX -{ -public: - virtual int GetScreenSize( void ) - { - int screenWide, screenTall; - surface()->GetScreenSize(screenWide, screenTall); - return screenTall; - } -}; - -class CColorProperty : public vgui::IPanelAnimationPropertyConverter -{ -public: - virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - kv->SetColor( entry->name(), *(Color *)data ); - } - - virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() ); - Assert( scheme ); - if ( scheme ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - - char const *colorName = kv->GetString( entry->name() ); - if ( !colorName || !colorName[0] ) - { - *(Color *)data = kv->GetColor( entry->name() ); - } - else - { - *(Color *)data = scheme->GetColor( colorName, Color( 0, 0, 0, 0 ) ); - } - } - } - - virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) - { - vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() ); - Assert( scheme ); - if ( scheme ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - *(Color *)data = scheme->GetColor( entry->defaultvalue(), Color( 0, 0, 0, 0 ) ); - } - } -}; - -class CBoolProperty : public vgui::IPanelAnimationPropertyConverter -{ -public: - virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - kv->SetInt( entry->name(), *(bool *)data ? 1 : 0 ); - } - - virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - *(bool *)data = kv->GetInt( entry->name() ) ? true : false; - } - - virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - bool b = false; - if ( !stricmp( entry->defaultvalue(), "true" )|| - atoi( entry->defaultvalue() )!= 0 ) - { - b = true; - } - - *(bool *)data = b; - } -}; - -class CStringProperty : public vgui::IPanelAnimationPropertyConverter -{ -public: - virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - kv->SetString( entry->name(), (char *)data ); - } - - virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - strcpy( (char *)data, kv->GetString( entry->name() ) ); - } - - virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - strcpy( ( char * )data, entry->defaultvalue() ); - } -}; - -class CHFontProperty : public vgui::IPanelAnimationPropertyConverter -{ -public: - virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() ); - Assert( scheme ); - if ( scheme ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - char const *fontName = scheme->GetFontName( *(HFont *)data ); - kv->SetString( entry->name(), fontName ); - } - } - - virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() ); - Assert( scheme ); - if ( scheme ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - char const *fontName = kv->GetString( entry->name() ); - *(HFont *)data = scheme->GetFont( fontName, panel->IsProportional() ); - } - } - - virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) - { - vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() ); - Assert( scheme ); - if ( scheme ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - *(HFont *)data = scheme->GetFont( entry->defaultvalue(), panel->IsProportional() ); - } - } -}; - -class CTextureIdProperty : public vgui::IPanelAnimationPropertyConverter -{ -public: - virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - int currentId = *(int *)data; - - // lookup texture name for id - char texturename[ 512 ]; - if ( currentId != -1 && - surface()->DrawGetTextureFile( currentId, texturename, sizeof( texturename ) ) ) - { - kv->SetString( entry->name(), texturename ); - } - else - { - kv->SetString( entry->name(), "" ); - } - } - - virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - - int currentId = -1; - - char const *texturename = kv->GetString( entry->name() ); - if ( texturename && texturename[ 0 ] ) - { - currentId = surface()->DrawGetTextureId( texturename ); - if ( currentId == -1 ) - { - currentId = surface()->CreateNewTextureID(); - } - surface()->DrawSetTextureFile( currentId, texturename, false, true ); - } - - *(int *)data = currentId; - } - - virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) - { - void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); - - int currentId = -1; - - char const *texturename = entry->defaultvalue(); - if ( texturename && texturename[ 0 ] ) - { - currentId = surface()->DrawGetTextureId( texturename ); - if ( currentId == -1 ) - { - currentId = surface()->CreateNewTextureID(); - } - surface()->DrawSetTextureFile( currentId, texturename, false, true ); - } - - *(int *)data = currentId; - } -}; - -static CFloatProperty floatconverter; -static CProportionalFloatProperty p_floatconverter; -static CIntProperty intconverter; -static CProportionalIntProperty p_intconverter; -static CProportionalIntWithScreenspacePropertyX p_screenspace_intconverter_X; -static CProportionalIntWithScreenspacePropertyY p_screenspace_intconverter_Y; -static CColorProperty colorconverter; -static CBoolProperty boolconverter; -static CStringProperty stringconverter; -static CHFontProperty fontconverter; -static CTextureIdProperty textureidconverter; -//static CProportionalXPosProperty xposconverter; -//static CProportionalYPosProperty yposconverter; - -static CUtlDict< IPanelAnimationPropertyConverter *, int > g_AnimationPropertyConverters; - -static IPanelAnimationPropertyConverter *FindConverter( char const *typeName ) -{ - int lookup = g_AnimationPropertyConverters.Find( typeName ); - if ( lookup == g_AnimationPropertyConverters.InvalidIndex() ) - return NULL; - - IPanelAnimationPropertyConverter *converter = g_AnimationPropertyConverters[ lookup ]; - return converter; -} - -void Panel::AddPropertyConverter( char const *typeName, IPanelAnimationPropertyConverter *converter ) -{ - int lookup = g_AnimationPropertyConverters.Find( typeName ); - if ( lookup != g_AnimationPropertyConverters.InvalidIndex() ) - { - Msg( "Already have converter for type %s, ignoring...\n", typeName ); - return; - } - - g_AnimationPropertyConverters.Insert( typeName, converter ); -} - -//----------------------------------------------------------------------------- -// Purpose: Static method to initialize all needed converters -//----------------------------------------------------------------------------- -void Panel::InitPropertyConverters( void ) -{ - static bool initialized = false; - if ( initialized ) - return; - initialized = true; - - AddPropertyConverter( "float", &floatconverter ); - AddPropertyConverter( "int", &intconverter ); - AddPropertyConverter( "Color", &colorconverter ); - //AddPropertyConverter( "vgui::Color", &colorconverter ); - AddPropertyConverter( "bool", &boolconverter ); - AddPropertyConverter( "char", &stringconverter ); - AddPropertyConverter( "string", &stringconverter ); - AddPropertyConverter( "HFont", &fontconverter ); - AddPropertyConverter( "vgui::HFont", &fontconverter ); - - // This is an aliased type for proportional float - AddPropertyConverter( "proportional_float", &p_floatconverter ); - AddPropertyConverter( "proportional_int", &p_intconverter ); - - AddPropertyConverter( "proportional_xpos", &p_screenspace_intconverter_X ); - AddPropertyConverter( "proportional_ypos", &p_screenspace_intconverter_Y ); - - AddPropertyConverter( "textureid", &textureidconverter ); -} - -bool Panel::InternalRequestInfo( PanelAnimationMap *map, KeyValues *outputData ) -{ - if ( !map ) - return false; - - Assert( outputData ); - - char const *name = outputData->GetName(); - - PanelAnimationMapEntry *e = FindPanelAnimationEntry( name, map ); - if ( e ) - { - IPanelAnimationPropertyConverter *converter = FindConverter( e->type() ); - if ( converter ) - { - converter->GetData( this, outputData, e ); - return true; - } - } - - return false; -} - -bool Panel::InternalSetInfo( PanelAnimationMap *map, KeyValues *inputData ) -{ - if ( !map ) - return false; - - Assert( inputData ); - - char const *name = inputData->GetName(); - - PanelAnimationMapEntry *e = FindPanelAnimationEntry( name, map ); - if ( e ) - { - IPanelAnimationPropertyConverter *converter = FindConverter( e->type() ); - if ( converter ) - { - converter->SetData( this, inputData, e ); - return true; - } - } - - return false; -} - -PanelAnimationMapEntry *Panel::FindPanelAnimationEntry( char const *scriptname, PanelAnimationMap *map ) -{ - if ( !map ) - return NULL; - - Assert( scriptname ); - - // Look through mapping for entry - int c = map->entries.Count(); - for ( int i = 0; i < c; i++ ) - { - PanelAnimationMapEntry *e = &map->entries[ i ]; - - if ( !stricmp( e->name(), scriptname ) ) - { - return e; - } - } - - // Recurse - if ( map->baseMap ) - { - return FindPanelAnimationEntry( scriptname, map->baseMap ); - } - - return NULL; -} - -// Recursively invoke settings for PanelAnimationVars -void Panel::InternalApplySettings( PanelAnimationMap *map, KeyValues *inResourceData) -{ - // Loop through keys - KeyValues *kv; - - for ( kv = inResourceData->GetFirstSubKey(); kv; kv = kv->GetNextKey() ) - { - char const *varname = kv->GetName(); - - PanelAnimationMapEntry *entry = FindPanelAnimationEntry( varname, GetAnimMap() ); - if ( entry ) - { - // Set value to value from script - IPanelAnimationPropertyConverter *converter = FindConverter( entry->type() ); - if ( converter ) - { - converter->SetData( this, inResourceData, entry ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets the default values of all CPanelAnimationVars -//----------------------------------------------------------------------------- -void Panel::InternalInitDefaultValues( PanelAnimationMap *map ) -{ - _flags.ClearFlag( NEEDS_DEFAULT_SETTINGS_APPLIED ); - - // Look through mapping for entry - int c = map->entries.Count(); - for ( int i = 0; i < c; i++ ) - { - PanelAnimationMapEntry *e = &map->entries[ i ]; - Assert( e ); - IPanelAnimationPropertyConverter *converter = FindConverter( e->type() ); - if ( !converter ) - continue; - - converter->InitFromDefault( this, e ); - } - - if ( map->baseMap ) - { - InternalInitDefaultValues( map->baseMap ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -int Panel::GetPaintBackgroundType() -{ - return m_nPaintBackgroundType; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : w - -// h - -//----------------------------------------------------------------------------- -void Panel::GetCornerTextureSize( int& w, int& h ) -{ - if ( m_nBgTextureId1 == -1 ) - { - w = h = 0; - return; - } - surface()->DrawGetTextureSize(m_nBgTextureId1, w, h); -} - -//----------------------------------------------------------------------------- -// Purpose: draws a selection box -//----------------------------------------------------------------------------- -void Panel::DrawBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha, bool hollow /*=false*/ ) -{ - if ( m_nBgTextureId1 == -1 || - m_nBgTextureId2 == -1 || - m_nBgTextureId3 == -1 || - m_nBgTextureId4 == -1 ) - { - return; - } - - color[3] *= normalizedAlpha; - - // work out our bounds - int cornerWide, cornerTall; - GetCornerTextureSize( cornerWide, cornerTall ); - - // draw the background in the areas not occupied by the corners - // draw it in three horizontal strips - surface()->DrawSetColor(color); - surface()->DrawFilledRect(x + cornerWide, y, x + wide - cornerWide, y + cornerTall); - if ( !hollow ) - { - surface()->DrawFilledRect(x, y + cornerTall, x + wide, y + tall - cornerTall); - } - else - { - surface()->DrawFilledRect(x, y + cornerTall, x + cornerWide, y + tall - cornerTall); - surface()->DrawFilledRect(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall); - } - surface()->DrawFilledRect(x + cornerWide, y + tall - cornerTall, x + wide - cornerWide, y + tall); - - // draw the corners - - //============================================================================= - // HPE_BEGIN: - // [tj] We now check each individual corner and decide whether to draw it straight or rounded - //============================================================================= - //TOP-LEFT - if (ShouldDrawTopLeftCornerRounded()) - { - surface()->DrawSetTexture(m_nBgTextureId1); - surface()->DrawTexturedRect(x, y, x + cornerWide, y + cornerTall); - } - else - { - surface()->DrawFilledRect(x, y, x + cornerWide, y + cornerTall); - } - - - //TOP-RIGHT - if (ShouldDrawTopRightCornerRounded()) - { - surface()->DrawSetTexture(m_nBgTextureId2); - surface()->DrawTexturedRect(x + wide - cornerWide, y, x + wide, y + cornerTall); - } - else - { - surface()->DrawFilledRect(x + wide - cornerWide, y, x + wide, y + cornerTall); - } - - //BOTTOM-LEFT - if (ShouldDrawBottomLeftCornerRounded()) - { - surface()->DrawSetTexture(m_nBgTextureId4); - surface()->DrawTexturedRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall); - } - else - { - surface()->DrawFilledRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall); - } - - - //BOTTOM-RIGHT - if (ShouldDrawBottomRightCornerRounded()) - { - surface()->DrawSetTexture(m_nBgTextureId3); - surface()->DrawTexturedRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall); - } - else - { - surface()->DrawFilledRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall); - } - //============================================================================= - // HPE_END - //============================================================================= -} - -void Panel::DrawBoxFade(int x, int y, int wide, int tall, Color color, float normalizedAlpha, unsigned int alpha0, unsigned int alpha1, bool bHorizontal, bool hollow /*=false*/ ) -{ - if ( m_nBgTextureId1 == -1 || - m_nBgTextureId2 == -1 || - m_nBgTextureId3 == -1 || - m_nBgTextureId4 == -1 || - surface()->DrawGetAlphaMultiplier() == 0 ) - { - return; - } - - color[3] *= normalizedAlpha; - - // work out our bounds - int cornerWide, cornerTall; - GetCornerTextureSize( cornerWide, cornerTall ); - - if ( !bHorizontal ) - { - // draw the background in the areas not occupied by the corners - // draw it in three horizontal strips - surface()->DrawSetColor(color); - surface()->DrawFilledRectFade(x + cornerWide, y, x + wide - cornerWide, y + cornerTall, alpha0, alpha0, bHorizontal ); - if ( !hollow ) - { - surface()->DrawFilledRectFade(x, y + cornerTall, x + wide, y + tall - cornerTall, alpha0, alpha1, bHorizontal); - } - else - { - surface()->DrawFilledRectFade(x, y + cornerTall, x + cornerWide, y + tall - cornerTall, alpha0, alpha1, bHorizontal); - surface()->DrawFilledRectFade(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall, alpha0, alpha1, bHorizontal); - } - surface()->DrawFilledRectFade(x + cornerWide, y + tall - cornerTall, x + wide - cornerWide, y + tall, alpha1, alpha1, bHorizontal); - } - else - { - // draw the background in the areas not occupied by the corners - // draw it in three horizontal strips - surface()->DrawSetColor(color); - surface()->DrawFilledRectFade(x, y + cornerTall, x + cornerWide, y + tall - cornerTall, alpha0, alpha0, bHorizontal ); - if ( !hollow ) - { - surface()->DrawFilledRectFade(x + cornerWide, y, x + wide - cornerWide, y + tall, alpha0, alpha1, bHorizontal); - } - else - { - // FIXME: Hollow horz version not implemented - //surface()->DrawFilledRectFade(x, y + cornerTall, x + cornerWide, y + tall - cornerTall, alpha0, alpha1, bHorizontal); - //surface()->DrawFilledRectFade(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall, alpha0, alpha1, bHorizontal); - } - surface()->DrawFilledRectFade(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall, alpha1, alpha1, bHorizontal); - } - - float fOldAlpha = color[ 3 ]; - int iAlpha0 = fOldAlpha * ( static_cast( alpha0 ) / 255.0f ); - int iAlpha1 = fOldAlpha * ( static_cast( alpha1 ) / 255.0f ); - - // draw the corners - if ( !bHorizontal ) - { - color[ 3 ] = iAlpha0; - surface()->DrawSetColor( color ); - surface()->DrawSetTexture(m_nBgTextureId1); - surface()->DrawTexturedRect(x, y, x + cornerWide, y + cornerTall); - surface()->DrawSetTexture(m_nBgTextureId2); - surface()->DrawTexturedRect(x + wide - cornerWide, y, x + wide, y + cornerTall); - - color[ 3 ] = iAlpha1; - surface()->DrawSetColor( color ); - surface()->DrawSetTexture(m_nBgTextureId3); - surface()->DrawTexturedRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall); - surface()->DrawSetTexture(m_nBgTextureId4); - surface()->DrawTexturedRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall); - } - else - { - color[ 3 ] = iAlpha0; - surface()->DrawSetColor( color ); - surface()->DrawSetTexture(m_nBgTextureId1); - surface()->DrawTexturedRect(x, y, x + cornerWide, y + cornerTall); - surface()->DrawSetTexture(m_nBgTextureId4); - surface()->DrawTexturedRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall); - - color[ 3 ] = iAlpha1; - surface()->DrawSetColor( color ); - surface()->DrawSetTexture(m_nBgTextureId2); - surface()->DrawTexturedRect(x + wide - cornerWide, y, x + wide, y + cornerTall); - surface()->DrawSetTexture(m_nBgTextureId3); - surface()->DrawTexturedRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : x - -// y - -// wide - -// tall - -// color - -// normalizedAlpha - -//----------------------------------------------------------------------------- -void Panel::DrawHollowBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha ) -{ - DrawBox( x, y, wide, tall, color, normalizedAlpha, true ); -} - -//============================================================================= -// HPE_BEGIN: -// [menglish] Draws a hollow box similar to the already existing draw hollow box function, but takes the indents as params -//============================================================================= - -void Panel::DrawHollowBox( int x, int y, int wide, int tall, Color color, float normalizedAlpha, int cornerWide, int cornerTall ) -{ - if ( m_nBgTextureId1 == -1 || - m_nBgTextureId2 == -1 || - m_nBgTextureId3 == -1 || - m_nBgTextureId4 == -1 ) - { - return; - } - - color[3] *= normalizedAlpha; - - // draw the background in the areas not occupied by the corners - // draw it in three horizontal strips - surface()->DrawSetColor(color); - surface()->DrawFilledRect(x + cornerWide, y, x + wide - cornerWide, y + cornerTall); - surface()->DrawFilledRect(x, y + cornerTall, x + cornerWide, y + tall - cornerTall); - surface()->DrawFilledRect(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall); - surface()->DrawFilledRect(x + cornerWide, y + tall - cornerTall, x + wide - cornerWide, y + tall); - - // draw the corners - surface()->DrawSetTexture(m_nBgTextureId1); - surface()->DrawTexturedRect(x, y, x + cornerWide, y + cornerTall); - surface()->DrawSetTexture(m_nBgTextureId2); - surface()->DrawTexturedRect(x + wide - cornerWide, y, x + wide, y + cornerTall); - surface()->DrawSetTexture(m_nBgTextureId3); - surface()->DrawTexturedRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall); - surface()->DrawSetTexture(m_nBgTextureId4); - surface()->DrawTexturedRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall); -} - -//============================================================================= -// HPE_END -//============================================================================= - -//----------------------------------------------------------------------------- -// Purpose: draws a selection box -//----------------------------------------------------------------------------- -void Panel::DrawTexturedBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha ) -{ - if ( m_nBgTextureId1 == -1 ) - return; - - color[3] *= normalizedAlpha; - - surface()->DrawSetColor( color ); - surface()->DrawSetTexture(m_nBgTextureId1); - surface()->DrawTexturedRect(x, y, x + wide, y + tall); -} - -//----------------------------------------------------------------------------- -// Purpose: Marks this panel as draggable (note that children will chain to their parents to see if any parent is draggable) -// Input : enabled - -//----------------------------------------------------------------------------- -void Panel::SetDragEnabled( bool enabled ) -{ -#if defined( VGUI_USEDRAGDROP ) - // If turning it off, quit dragging if mid-drag - if ( !enabled && - m_pDragDrop->m_bDragging ) - { - OnFinishDragging( false, (MouseCode)-1 ); - } - m_pDragDrop->m_bDragEnabled = enabled; -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Panel::IsDragEnabled() const -{ -#if defined( VGUI_USEDRAGDROP ) - return m_pDragDrop->m_bDragEnabled; -#endif - return false; -} - -void Panel::SetShowDragHelper( bool enabled ) -{ -#if defined( VGUI_USEDRAGDROP ) - m_pDragDrop->m_bShowDragHelper = enabled; -#endif -} - -// Use this to prevent chaining up from a parent which can mess with mouse functionality if you don't want to chain up from a child panel to the best -// draggable parent. -void Panel::SetBlockDragChaining( bool block ) -{ -#if defined( VGUI_USEDRAGDROP ) - m_pDragDrop->m_bPreventChaining = block; -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Panel::IsBlockingDragChaining() const -{ -#if defined( VGUI_USEDRAGDROP ) - return m_pDragDrop->m_bPreventChaining; -#endif - return true; -} - - -//----------------------------------------------------------------------------- -// accessors for m_nDragStartTolerance -//----------------------------------------------------------------------------- -int Panel::GetDragStartTolerance() const -{ -#if defined( VGUI_USEDRAGDROP ) - return m_pDragDrop->m_nDragStartTolerance; -#endif - return 0; -} - -void Panel::SetDragSTartTolerance( int nTolerance ) -{ -#if defined( VGUI_USEDRAGDROP ) - m_pDragDrop->m_nDragStartTolerance = nTolerance; -#endif -} - - -//----------------------------------------------------------------------------- -// Purpose: Marks this panel as droppable ( note that children will chain to their parents to see if any parent is droppable) -// Input : enabled - -//----------------------------------------------------------------------------- -void Panel::SetDropEnabled( bool enabled, float flHoverContextTime /* = 0.0f */ ) -{ -#if defined( VGUI_USEDRAGDROP ) - m_pDragDrop->m_bDropEnabled = enabled; - m_pDragDrop->m_flHoverContextTime = flHoverContextTime; -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Panel::IsDropEnabled() const -{ -#if defined( VGUI_USEDRAGDROP ) - return m_pDragDrop->m_bDropEnabled; -#endif - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Chains up to any parent -// 1) marked DropEnabled; and -// 2) willing to accept the drop payload -// Input : - -// Output : Panel -//----------------------------------------------------------------------------- -Panel *Panel::GetDropTarget( CUtlVector< KeyValues * >& msglist ) -{ -#if defined( VGUI_USEDRAGDROP ) - // Found one - if ( m_pDragDrop->m_bDropEnabled && - IsDroppable( msglist ) ) - { - return this; - } - - // Chain up - if ( GetParent() ) - { - return GetParent()->GetDropTarget( msglist ); - } -#endif - // No luck - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Chains up to first parent marked DragEnabled -// Input : - -// Output : Panel -//----------------------------------------------------------------------------- -Panel *Panel::GetDragPanel() -{ -#if defined( VGUI_USEDRAGDROP ) - // If we encounter a blocker, stop chaining - if ( m_pDragDrop->m_bPreventChaining ) - return NULL; - - if ( m_pDragDrop->m_bDragEnabled ) - return this; - - // Chain up - if ( GetParent() ) - { - return GetParent()->GetDragPanel(); - } -#endif - // No luck - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -void Panel::OnStartDragging() -{ -#if defined( VGUI_USEDRAGDROP ) - // Only left mouse initiates drag/drop. - // FIXME: Revisit? - if ( !input()->IsMouseDown( MOUSE_LEFT ) ) - return; - - if ( !m_pDragDrop->m_bDragEnabled ) - return; - - if ( m_pDragDrop->m_bDragging ) - return; - - g_DragDropCapture = this; - - m_pDragDrop->m_bDragStarted = false; - m_pDragDrop->m_bDragging = true; - input()->GetCursorPos( m_pDragDrop->m_nStartPos[ 0 ], m_pDragDrop->m_nStartPos[ 1 ] ); - m_pDragDrop->m_nLastPos[ 0 ] = m_pDragDrop->m_nStartPos[ 0 ]; - m_pDragDrop->m_nLastPos[ 1 ] = m_pDragDrop->m_nStartPos[ 1 ]; - - OnContinueDragging(); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: Called if drag drop is started but not dropped on top of droppable panel... -// Input : - -//----------------------------------------------------------------------------- -void Panel::OnDragFailed( CUtlVector< KeyValues * >& msglist ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -void Panel::OnFinishDragging( bool mousereleased, MouseCode code, bool abort /*= false*/ ) -{ -#if defined( VGUI_USEDRAGDROP ) - g_DragDropCapture = NULL; - - if ( !m_pDragDrop->m_bDragEnabled ) - return; - - Assert( m_pDragDrop->m_bDragging ); - - if ( !m_pDragDrop->m_bDragging ) - return; - - int x, y; - input()->GetCursorPos( x, y ); - - m_pDragDrop->m_nLastPos[ 0 ] = x; - m_pDragDrop->m_nLastPos[ 1 ] = y; - - if ( s_DragDropHelper.Get() ) - { - s_DragDropHelper->RemovePanel( this ); - } - - m_pDragDrop->m_bDragging = false; - - CUtlVector< KeyValues * >& data = m_pDragDrop->m_DragData; - int c = data.Count(); - - Panel *target = NULL; - bool shouldDrop = false; - - if ( m_pDragDrop->m_bDragStarted ) - { - char cmd[ 256 ]; - Q_strncpy( cmd, "default", sizeof( cmd ) ); - - if ( mousereleased && - m_pDragDrop->m_hCurrentDrop != 0 && - m_pDragDrop->m_hDropContextMenu.Get() ) - { - Menu *menu = m_pDragDrop->m_hDropContextMenu; - - VPANEL hover = menu->IsWithinTraverse( x, y, false ); - if ( hover ) - { - Panel *pHover = ipanel()->GetPanel( hover, GetModuleName() ); - if ( pHover ) - { - // Figure out if it's a menu item... - int c = menu->GetItemCount(); - for ( int i = 0; i < c; ++i ) - { - int id = menu->GetMenuID( i ); - MenuItem *item = menu->GetMenuItem( id ); - if ( item == pHover ) - { - KeyValues *command = item->GetCommand(); - if ( command ) - { - char const *p = command->GetString( "command", "" ); - if ( p && p[ 0 ] ) - { - Q_strncpy( cmd, p, sizeof( cmd ) ); - } - } - } - } - } - } - - delete menu; - m_pDragDrop->m_hDropContextMenu = NULL; - } - - for ( int i = 0 ; i < c; ++i ) - { - KeyValues *msg = data[ i ]; - - msg->SetString( "command", cmd ); - - msg->SetInt( "screenx", x ); - msg->SetInt( "screeny", y ); - } - - target = m_pDragDrop->m_hCurrentDrop.Get(); - if ( target && !abort ) - { - int localmousex = x, localmousey = y; - // Convert screen space coordintes to coordinates relative to drop window - target->ScreenToLocal( localmousex, localmousey ); - - for ( int i = 0 ; i < c; ++i ) - { - KeyValues *msg = data[ i ]; - - msg->SetInt( "x", localmousex ); - msg->SetInt( "y", localmousey ); - } - - shouldDrop = true; - } - - if ( !shouldDrop ) - { - OnDragFailed( data ); - } - } - - m_pDragDrop->m_bDragStarted = false; - m_pDragDrop->m_DragPanels.RemoveAll(); - m_pDragDrop->m_hCurrentDrop = NULL; - - // Copy data ptrs out of data because OnPanelDropped might cause this panel to be deleted - // and our this ptr will be hosed... - CUtlVector< KeyValues * > temp; - for ( int i = 0 ; i < c; ++i ) - { - temp.AddToTail( data[ i ] ); - } - data.RemoveAll(); - - if ( shouldDrop && target ) - { - target->OnPanelDropped( temp ); - } - for ( int i = 0 ; i < c ; ++i ) - { - temp[ i ]->deleteThis(); - } -#endif -} - -void Panel::OnDropContextHoverShow( CUtlVector< KeyValues * >& msglist ) -{ -} - -void Panel::OnDropContextHoverHide( CUtlVector< KeyValues * >& msglist ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *msg - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Panel::IsDroppable( CUtlVector< KeyValues * >& msglist ) -{ - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : startx - -// starty - -// mx - -// my - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Panel::CanStartDragging( int startx, int starty, int mx, int my ) -{ -#if defined( VGUI_USEDRAGDROP ) - if ( IsStartDragWhenMouseExitsPanel() ) - { - ScreenToLocal( mx, my ); - if ( mx < 0 || my < 0 ) - return true; - if ( mx > GetWide() || my > GetTall() ) - return true; - - return false; - } - - int deltax = abs( mx - startx ); - int deltay = abs( my - starty ); - if ( deltax > m_pDragDrop->m_nDragStartTolerance || - deltay > m_pDragDrop->m_nDragStartTolerance ) - { - return true; - } -#endif - return false; -} - -HCursor Panel::GetDropCursor( CUtlVector< KeyValues * >& msglist ) -{ - return dc_arrow; -} - -bool IsSelfDroppable( CUtlVector< KeyValues * > &dragData ) -{ - if ( dragData.Count() == 0 ) - return false; - - KeyValues *pKeyValues( dragData[ 0 ] ); - if ( !pKeyValues ) - return false; - - return pKeyValues->GetInt( "selfDroppable" ) != 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -void Panel::OnContinueDragging() -{ -#if defined( VGUI_USEDRAGDROP ) - if ( !m_pDragDrop->m_bDragEnabled ) - return; - - if ( !m_pDragDrop->m_bDragging ) - return; - - int x, y; - input()->GetCursorPos( x, y ); - - // Update last position - m_pDragDrop->m_nLastPos[ 0 ] = x; - m_pDragDrop->m_nLastPos[ 1 ] = y; - - if ( !m_pDragDrop->m_bDragStarted ) - { - if ( CanStartDragging( m_pDragDrop->m_nStartPos[ 0 ], m_pDragDrop->m_nStartPos[ 1 ], x, y ) ) - { - m_pDragDrop->m_bDragStarted = true; - CreateDragData(); - } - else - { - return; - } - } - - if ( !s_DragDropHelper.Get() && m_pDragDrop->m_bShowDragHelper ) - { - s_DragDropHelper = new CDragDropHelperPanel(); - s_DragDropHelper->SetKeyBoardInputEnabled( false ); - s_DragDropHelper->SetMouseInputEnabled( false ); - Assert( s_DragDropHelper.Get() ); - } - - if ( !s_DragDropHelper.Get() ) - return; - - s_DragDropHelper->AddPanel( this ); - - Assert( m_pDragDrop->m_DragData.Count() ); - - vgui::PHandle oldDrop = m_pDragDrop->m_hCurrentDrop; - - // See what's under that - m_pDragDrop->m_hCurrentDrop = NULL; - - // Search under mouse pos... - Panel *dropTarget = FindDropTargetPanel(); - if ( dropTarget ) - { - dropTarget = dropTarget->GetDropTarget( m_pDragDrop->m_DragData ); - } - - // it's not okay until we find a droppable panel - surface()->SetCursor( dc_no ); - - if ( dropTarget ) - { - if ( dropTarget != this || IsSelfDroppable( m_pDragDrop->m_DragData ) ) - { - m_pDragDrop->m_hCurrentDrop = dropTarget; - surface()->SetCursor( dropTarget->GetDropCursor( m_pDragDrop->m_DragData ) ); - } - } - - if ( m_pDragDrop->m_hCurrentDrop.Get() != oldDrop.Get() ) - { - if ( oldDrop.Get() ) - { - oldDrop->OnPanelExitedDroppablePanel( m_pDragDrop->m_DragData ); - } - - if ( m_pDragDrop->m_hCurrentDrop.Get() ) - { - m_pDragDrop->m_hCurrentDrop->OnPanelEnteredDroppablePanel( m_pDragDrop->m_DragData ); - m_pDragDrop->m_hCurrentDrop->OnDropContextHoverHide( m_pDragDrop->m_DragData ); - - // Reset hover time - m_pDragDrop->m_lDropHoverTime = system()->GetTimeMillis(); - m_pDragDrop->m_bDropMenuShown = false; - } - - // Discard any stale context menu... - if ( m_pDragDrop->m_hDropContextMenu.Get() ) - { - delete m_pDragDrop->m_hDropContextMenu.Get(); - } - } - - if ( m_pDragDrop->m_hCurrentDrop != 0 && - m_pDragDrop->m_hDropContextMenu.Get() ) - { - Menu *menu = m_pDragDrop->m_hDropContextMenu; - - VPANEL hover = menu->IsWithinTraverse( x, y, false ); - if ( hover ) - { - Panel *pHover = ipanel()->GetPanel( hover, GetModuleName() ); - if ( pHover ) - { - // Figure out if it's a menu item... - int c = menu->GetItemCount(); - for ( int i = 0; i < c; ++i ) - { - int id = menu->GetMenuID( i ); - MenuItem *item = menu->GetMenuItem( id ); - if ( item == pHover ) - { - menu->SetCurrentlyHighlightedItem( id ); - } - } - } - } - else - { - menu->ClearCurrentlyHighlightedItem(); - } - } -#endif -} - -#if defined( VGUI_USEDRAGDROP ) -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : DragDrop_t -//----------------------------------------------------------------------------- -DragDrop_t *Panel::GetDragDropInfo() -{ - Assert( m_pDragDrop ); - return m_pDragDrop; -} -#endif - -void Panel::OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles ) -{ - // Nothing here -} - -//----------------------------------------------------------------------------- -// Purpose: Virtual method to allow panels to add to the default values -// Input : *msg - -//----------------------------------------------------------------------------- -void Panel::OnCreateDragData( KeyValues *msg ) -{ - // These values are filled in for you: - // "panel" ptr to panel being dropped - // "screenx", "screeny" - drop cursor pos in screen space - // "x", "y" - drop coordinates relative to this window (the window being dropped upon) -} - -// Called if m_flHoverContextTime was non-zero, allows droppee to preview the drop data and show an appropriate menu -bool Panel::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ) -{ - return false; -} - -void Panel::CreateDragData() -{ -#if defined( VGUI_USEDRAGDROP ) - int i, c; - - if ( m_pDragDrop->m_DragData.Count() ) - { - return; - } - - PHandle h; - h = this; - m_pDragDrop->m_DragPanels.AddToTail( h ); - - CUtlVector< Panel * > temp; - OnGetAdditionalDragPanels( temp ); - c = temp.Count(); - for ( i = 0; i < c; ++i ) - { - h = temp[ i ]; - m_pDragDrop->m_DragPanels.AddToTail( h ); - } - - c = m_pDragDrop->m_DragPanels.Count(); - for ( i = 0 ; i < c; ++i ) - { - Panel *sibling = m_pDragDrop->m_DragPanels[ i ].Get(); - if ( !sibling ) - { - continue; - } - - KeyValues *msg = new KeyValues( "DragDrop" ); - msg->SetPtr( "panel", sibling ); - - sibling->OnCreateDragData( msg ); - - m_pDragDrop->m_DragData.AddToTail( msg ); - } -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : KeyValues -//----------------------------------------------------------------------------- -void Panel::GetDragData( CUtlVector< KeyValues * >& list ) -{ -#if defined( VGUI_USEDRAGDROP ) - int i, c; - - list.RemoveAll(); - - c = m_pDragDrop->m_DragData.Count(); - for ( i = 0 ; i < c; ++i ) - { - list.AddToTail( m_pDragDrop->m_DragData[ i ] ); - } -#endif -} - -#if defined( VGUI_USEDRAGDROP ) -CDragDropHelperPanel::CDragDropHelperPanel() : BaseClass( NULL, "DragDropHelper" ) -{ - SetVisible( true ); - SetPaintEnabled( false ); - SetPaintBackgroundEnabled( false ); - SetMouseInputEnabled( false ); - SetKeyBoardInputEnabled( false ); - // SetCursor( dc_none ); - ipanel()->SetTopmostPopup( GetVPanel(), true ); - int w, h; - surface()->GetScreenSize( w, h ); - SetBounds( 0, 0, w, h ); - - SetPostChildPaintEnabled( true ); - - MakePopup( false ); -} - -VPANEL CDragDropHelperPanel::IsWithinTraverse(int x, int y, bool traversePopups) -{ - return (VPANEL)0; -} - -void CDragDropHelperPanel::PostChildPaint() -{ - int c = m_PaintList.Count(); - for ( int i = c - 1; i >= 0 ; --i ) - { - DragHelperPanel_t& data = m_PaintList[ i ]; - - Panel *panel = data.m_hPanel.Get(); - if ( !panel ) - { - m_PaintList.Remove( i ); - continue; - } - - Panel *dropPanel = panel->GetDragDropInfo()->m_hCurrentDrop.Get(); - if ( panel ) - { - if ( !dropPanel ) - { - panel->OnDraggablePanelPaint(); - } - else - { - CUtlVector< Panel * > temp; - CUtlVector< PHandle >& data = panel->GetDragDropInfo()->m_DragPanels; - CUtlVector< KeyValues * >& msglist = panel->GetDragDropInfo()->m_DragData; - int i, c; - c = data.Count(); - for ( i = 0; i < c ; ++i ) - { - Panel *pPanel = data[ i ].Get(); - if ( pPanel ) - { - temp.AddToTail( pPanel ); - } - } - - dropPanel->OnDroppablePanelPaint( msglist, temp ); - } - } - } - - if ( c == 0 ) - { - MarkForDeletion(); - } -} - -void CDragDropHelperPanel::AddPanel( Panel *current ) -{ - if ( !current ) - return; - - Menu *hover = current->GetDragDropInfo()->m_hDropContextMenu.Get(); - - surface()->MovePopupToFront( GetVPanel() ); - if ( hover && hover->IsPopup() ) - { - surface()->MovePopupToFront( hover->GetVPanel() ); - } - - int c = m_PaintList.Count(); - for ( int i = 0; i < c; ++i ) - { - if ( m_PaintList[ i ].m_hPanel.Get() == current ) - return; - } - - DragHelperPanel_t data; - data.m_hPanel = current; - m_PaintList.AddToTail( data ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *search - -//----------------------------------------------------------------------------- -void CDragDropHelperPanel::RemovePanel( Panel *search ) -{ - int c = m_PaintList.Count(); - for ( int i = c - 1 ; i >= 0; --i ) - { - if ( m_PaintList[ i ].m_hPanel.Get() == search ) - { - m_PaintList.Remove( i ); - return; - } - } -} -#endif -//----------------------------------------------------------------------------- -// Purpose: Enumerates panels under mouse x,y -// Input : panelList - -// x - -// y - -// check - -//----------------------------------------------------------------------------- -void Panel::FindDropTargetPanel_R( CUtlVector< VPANEL >& panelList, int x, int y, VPANEL check ) -{ -#if defined( VGUI_USEDRAGDROP ) - if ( !ipanel()->IsFullyVisible( check ) ) - return; - - if ( ::ShouldHandleInputMessage( check ) && ipanel()->IsWithinTraverse( check, x, y, false ) ) - { - panelList.AddToTail( check ); - } - - CUtlVector< VPANEL > &children = ipanel()->GetChildren( check ); - int childCount = children.Count(); - for ( int i = 0; i < childCount; i++ ) - { - VPANEL child = children[ i ]; - FindDropTargetPanel_R( panelList, x, y, child ); - } -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Panel -//----------------------------------------------------------------------------- -Panel *Panel::FindDropTargetPanel() -{ -#if defined( VGUI_USEDRAGDROP ) - if ( !s_DragDropHelper.Get() ) - return NULL; - - CUtlVector< VPANEL > hits; - - int x, y; - input()->GetCursorPos( x, y ); - - VPANEL embedded = surface()->GetEmbeddedPanel(); - VPANEL helper = s_DragDropHelper.Get()->GetVPanel(); - - if ( surface()->IsCursorVisible() && surface()->IsWithin(x, y) ) - { - // faster version of code below - // checks through each popup in order, top to bottom windows - int c = surface()->GetPopupCount(); - for (int i = c - 1; i >= 0 && hits.Count() == 0; i--) - { - VPANEL popup = surface()->GetPopup(i); - if ( popup == embedded ) - continue; - - // Don't return helper panel!!! - if ( popup == helper ) - continue; - - if ( !ipanel()->IsFullyVisible( popup ) ) - continue; - - FindDropTargetPanel_R( hits, x, y, popup ); - } - - // Check embedded - if ( !hits.Count() ) - { - FindDropTargetPanel_R( hits, x, y, embedded ); - } - } - - // Nothing under mouse... - if ( !hits.Count() ) - return NULL; - - // Return topmost panel under mouse, if it's visible to this .dll - Panel *panel = NULL; - int nCount = hits.Count(); - while ( --nCount >= 0 ) - { - panel = ipanel()->GetPanel( hits[ nCount ], GetModuleName() ); - if ( panel ) - return panel; - } -#endif - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Mouse is on draggable panel and has started moving, but is not over a droppable panel yet -// Input : - -//----------------------------------------------------------------------------- -void Panel::OnDraggablePanelPaint() -{ -#if defined( VGUI_USEDRAGDROP ) - int sw, sh; - GetSize( sw, sh ); - - int x, y; - input()->GetCursorPos( x, y ); - int w, h; - - w = min( sw, 80 ); - h = min( sh, 80 ); - x -= ( w >> 1 ); - y -= ( h >> 1 ); - - surface()->DrawSetColor( m_clrDragFrame ); - surface()->DrawOutlinedRect( x, y, x + w, y + h ); - - if ( m_pDragDrop->m_DragPanels.Count() > 1 ) - { - surface()->DrawSetTextColor( m_clrDragFrame ); - surface()->DrawSetTextFont( m_infoFont ); - surface()->DrawSetTextPos( x + 5, y + 2 ); - - wchar_t sz[ 64 ]; - V_swprintf_safe( sz, L"[ %i ]", m_pDragDrop->m_DragPanels.Count() ); - - surface()->DrawPrintText( sz, wcslen( sz ) ); - } -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: Mouse is now over a droppable panel -// Input : *dragPanel - -//----------------------------------------------------------------------------- -void Panel::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ) -{ -#if defined( VGUI_USEDRAGDROP ) - if ( !dragPanels.Count() ) - return; - - // Convert this panel's bounds to screen space - int w, h; - GetSize( w, h ); - - int x, y; - x = y = 0; - LocalToScreen( x, y ); - - surface()->DrawSetColor( m_clrDropFrame ); - // Draw 2 pixel frame - surface()->DrawOutlinedRect( x, y, x + w, y + h ); - surface()->DrawOutlinedRect( x+1, y+1, x + w-1, y + h-1 ); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Color -//----------------------------------------------------------------------------- -Color Panel::GetDropFrameColor() -{ -#if defined( VGUI_USEDRAGDROP ) - return m_clrDropFrame; -#endif - return Color(0, 0, 0, 0); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Color -//----------------------------------------------------------------------------- -Color Panel::GetDragFrameColor() -{ -#if defined( VGUI_USEDRAGDROP ) - return m_clrDragFrame; -#endif - return Color(0, 0, 0, 0); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *data - -//----------------------------------------------------------------------------- -void Panel::OnPanelDropped( CUtlVector< KeyValues * >& data ) -{ - // Empty. Derived classes would implement handlers here -} - -//----------------------------------------------------------------------------- -// called on droptarget when draggable panel enters droptarget -//----------------------------------------------------------------------------- -void Panel::OnPanelEnteredDroppablePanel( CUtlVector< KeyValues * >& msglist ) -{ - // Empty. Derived classes would implement handlers here -} - -//----------------------------------------------------------------------------- -// called on droptarget when draggable panel exits droptarget -//----------------------------------------------------------------------------- -void Panel::OnPanelExitedDroppablePanel ( CUtlVector< KeyValues * >& msglist ) -{ - // Empty. Derived classes would implement handlers here -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -void Panel::DragDropStartDragging() -{ -#if defined( VGUI_USEDRAGDROP ) - // We somehow missed a mouse release, cancel the previous drag - if ( g_DragDropCapture.Get() ) - { - if ( HasParent( g_DragDropCapture.Get()->GetVPanel() ) ) - return; - - bool started = g_DragDropCapture->GetDragDropInfo()->m_bDragStarted; - g_DragDropCapture->OnFinishDragging( true, (MouseCode)-1 ); - if ( started ) - { - return; - } - } - - // Find actual target panel - Panel *panel = GetDragPanel(); - if ( !panel ) - return; - - DragDrop_t *data = panel->GetDragDropInfo(); - if ( !data ) - return; - - if ( !panel->IsDragEnabled() ) - return; - - if ( data->m_bDragging ) - return; - - panel->OnStartDragging(); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool Panel::IsBeingDragged() -{ -#if defined( VGUI_USEDRAGDROP ) - if ( !g_DragDropCapture.Get() ) - return false; - - if ( g_DragDropCapture.Get() == this ) - return true; - - // If we encounter a blocker, stop chaining - if ( m_pDragDrop->m_bPreventChaining ) - return false; - - // Chain up - if ( GetParent() ) - { - return GetParent()->IsBeingDragged(); - } -#endif - // No luck - return false; -} - -struct srect_t -{ - int x0, y0; - int x1, y1; - - bool IsDegenerate() - { - if ( x1 - x0 <= 0 ) - return true; - if ( y1 - y0 <= 0 ) - return true; - return false; - } -}; - -// Draws a filled rect of specified bounds, but omits the bounds of the skip panel from those bounds -void Panel::FillRectSkippingPanel( const Color &clr, int x, int y, int w, int h, Panel *skipPanel ) -{ - int sx = 0, sy = 0, sw, sh; - skipPanel->GetSize( sw, sh ); - skipPanel->LocalToScreen( sx, sy ); - ScreenToLocal( sx, sy ); - - surface()->DrawSetColor( clr ); - - srect_t r1; - r1.x0 = x; - r1.y0 = y; - r1.x1 = x + w; - r1.y1 = y + h; - - srect_t r2; - r2.x0 = sx; - r2.y0 = sy; - r2.x1 = sx + sw; - r2.y1 = sy + sh; - - int topy = r1.y0; - int bottomy = r1.y1; - - // We'll descend vertically and draw: - // 1 a possible bar across the top - // 2 a possible bar across the bottom - // 3 possible left bar - // 4 possible right bar - - // Room at top? - if ( r2.y0 > r1.y0 ) - { - topy = r2.y0; - - surface()->DrawFilledRect( r1.x0, r1.y0, r1.x1, topy ); - } - - // Room at bottom? - if ( r2.y1 < r1.y1 ) - { - bottomy = r2.y1; - - surface()->DrawFilledRect( r1.x0, bottomy, r1.x1, r1.y1 ); - } - - // Room on left side? - if ( r2.x0 > r1.x0 ) - { - int left = r2.x0; - - surface()->DrawFilledRect( r1.x0, topy, left, bottomy ); - } - - // Room on right side - if ( r2.x1 < r1.x1 ) - { - int right = r2.x1; - - surface()->DrawFilledRect( right, topy, r1.x1, bottomy ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *child - -//----------------------------------------------------------------------------- -void Panel::SetSkipChildDuringPainting( Panel *child ) -{ - m_SkipChild = child; -} - -HPanel Panel::ToHandle() const -{ - return ivgui()->PanelToHandle( _vpanel ); -} -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Panel* Panel::NavigateUp() -{ - Panel *target = GetNavUp(); - if ( target ) - { - NavigateFrom(); - target->m_LastNavDirection = ND_UP; - target->NavigateTo(); - } - - return target; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Panel* Panel::NavigateDown() -{ - Panel *target = GetNavDown(); - if ( target ) - { - NavigateFrom(); - target->m_LastNavDirection = ND_DOWN; - target->NavigateTo(); - } - - return target; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Panel* Panel::NavigateLeft() -{ - Panel *target = GetNavLeft(); - if ( target ) - { - NavigateFrom(); - target->m_LastNavDirection = ND_LEFT; - target->NavigateTo(); - } - return target; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Panel* Panel::NavigateRight() -{ - Panel *target = GetNavRight(); - if ( target ) - { - NavigateFrom(); - target->m_LastNavDirection = ND_RIGHT; - target->NavigateTo(); - } - return target; -} - -Panel* Panel::NavigateActivate() -{ - Panel *target = GetNavActivate(); - if ( target ) - { - NavigateFrom(); - target->m_LastNavDirection = ND_NONE; - target->NavigateTo(); - } - return target; -} - -Panel* Panel::NavigateBack() -{ - Panel *target = GetNavBack(); - if ( target ) - { - NavigateFrom(); - target->m_LastNavDirection = ND_NONE; - target->NavigateTo(); - } - return target; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::NavigateTo() -{ - if ( IsX360() ) - { - RequestFocus( 0 ); - } - - CallParentFunction( new KeyValues( "OnNavigateTo", "panelName", GetName() ) ); - - Panel *target = GetNavToRelay(); - if ( target ) - { - NavigateFrom(); - target->m_LastNavDirection = ND_NONE; - NavigateToChild( target ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::NavigateFrom() -{ - for ( int i = 0; i < GetChildCount(); ++i ) - { - Panel* currentNav = GetChild(i); - if ( currentNav != 0 ) - { - currentNav->NavigateFrom(); - } - } - - CallParentFunction( new KeyValues( "OnNavigateFrom", "panelName", GetName() ) ); - - if ( m_pTooltips ) - { - m_pTooltips->HideTooltip(); - } - - m_LastNavDirection = ND_NONE; -} - -void Panel::NavigateToChild( Panel *pNavigateTo ) -{ - for( int i = 0; i != GetChildCount(); ++i ) - { - vgui::Panel *pChild = GetChild(i); - if( pChild ) - pChild->NavigateFrom(); - } - pNavigateTo->NavigateTo(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Panel* Panel::SetNavUp( Panel* navUp ) -{ - Panel* lastNav = m_NavUp; - m_NavUp = navUp; - - if( navUp ) - m_sNavUpName = navUp->GetName(); - else - m_sNavUpName.Clear(); - - return lastNav; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Panel* Panel::SetNavDown( Panel* navDown ) -{ - Panel* lastNav = m_NavDown; - m_NavDown = navDown; - - if( navDown ) - m_sNavDownName = navDown->GetName(); - else - m_sNavDownName.Clear(); - - return lastNav; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Panel* Panel::SetNavLeft( Panel* navLeft ) -{ - Panel* lastNav = m_NavLeft; - m_NavLeft = navLeft; - - if( navLeft ) - m_sNavLeftName = navLeft->GetName(); - else - m_sNavLeftName.Clear(); - - return lastNav; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Panel* Panel::SetNavRight( Panel* navRight ) -{ - Panel* lastNav = m_NavRight; - m_NavRight = navRight; - - if( navRight ) - m_sNavRightName = navRight->GetName(); - else - m_sNavRightName.Clear(); - - return lastNav; -} - -Panel* Panel::SetNavToRelay( Panel* navToRelay ) -{ - Panel* lastNav = m_NavToRelay; - m_NavToRelay = navToRelay; - - return lastNav; -} - -Panel* Panel::SetNavActivate( Panel* navActivate ) -{ - Panel* lastNav = m_NavActivate; - m_NavActivate = navActivate; - - return lastNav; -} - -Panel* Panel::SetNavBack( Panel* navBack ) -{ - Panel* lastNav = m_NavBack; - m_NavBack = navBack; - - return lastNav; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Panel::NAV_DIRECTION Panel::GetLastNavDirection() -{ - return m_LastNavDirection; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::OnNavigateTo( const char* panelName ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::OnNavigateFrom( const char* panelName ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetNavUp( const char* controlName ) -{ - if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) - { - m_NavUp = NULL; - m_sNavUpName = controlName; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetNavDown( const char* controlName ) -{ - if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) - { - m_NavDown = NULL; - m_sNavDownName = controlName; - } -} -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetNavLeft( const char* controlName ) -{ - if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) - { - m_NavLeft = NULL; - m_sNavLeftName = controlName; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Panel::SetNavRight( const char* controlName ) -{ - if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) - { - m_NavRight = NULL; - m_sNavRightName = controlName; - } -} - -void Panel::SetNavToRelay( const char* controlName ) -{ - if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) - { - m_NavToRelay = NULL; - m_sNavToRelayName = controlName; - } -} - -void Panel::SetNavActivate( const char* controlName ) -{ - if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) - { - m_NavActivate = NULL; - m_sNavActivateName = controlName; - } -} - -void Panel::SetNavBack( const char* controlName ) -{ - if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) - { - m_NavBack = NULL; - m_sNavBackName = controlName; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -vgui::Panel* Panel::GetNavUp( Panel *first ) -{ - if ( !m_NavUp && m_sNavUpName.Length() > 0 ) - { - Panel *pParent = GetParent(); - const char *pName = m_sNavUpName.String(); - while ( pParent && pName[ 0 ] == '<' ) - { - pParent = pParent->GetParent(); - pName++; - } - - if ( !pParent ) - { - return NULL; - } - - Panel *foundPanel = pParent->FindChildByName( pName, true ); - if ( foundPanel != 0 ) - { - m_NavUp = foundPanel; - } - } - - vgui::Panel* nextPanel = m_NavUp; - if( m_NavUp && m_NavUp != first && !m_NavUp->IsVisible() ) - { - Panel *firstPanel = first == NULL ? this : first; - nextPanel = nextPanel->GetNavUp( firstPanel ); - } - - return nextPanel; -} - -vgui::Panel* Panel::GetNavDown( Panel *first ) -{ - if ( !m_NavDown && m_sNavDownName.Length() > 0 ) - { - Panel *pParent = GetParent(); - const char *pName = m_sNavDownName.String(); - while ( pParent && pName[ 0 ] == '<' ) - { - pParent = pParent->GetParent(); - pName++; - } - - if ( !pParent ) - { - return NULL; - } - - Panel* foundPanel = pParent->FindChildByName( pName, true ); - if ( foundPanel != 0 ) - { - m_NavDown = foundPanel->GetPanel(); - } - } - - vgui::Panel* nextPanel = m_NavDown; - if( m_NavDown && m_NavDown != first && !m_NavDown->IsVisible() ) - { - Panel *firstPanel = first == NULL ? this : first; - nextPanel = nextPanel->GetNavDown( firstPanel ); - } - - return nextPanel; -} - -vgui::Panel* Panel::GetNavLeft( Panel *first ) -{ - if ( !m_NavLeft && m_sNavLeftName.Length() > 0 ) - { - Panel *pParent = GetParent(); - const char *pName = m_sNavLeftName.String(); - while ( pParent && pName[ 0 ] == '<' ) - { - pParent = pParent->GetParent(); - pName++; - } - - if ( !pParent ) - { - return NULL; - } - - Panel* foundPanel = pParent->FindChildByName( pName, true ); - if ( foundPanel != 0 ) - { - m_NavLeft = foundPanel->GetPanel(); - } - } - - vgui::Panel* nextPanel = m_NavLeft; - if( m_NavLeft && m_NavLeft != first && !m_NavLeft->IsVisible() ) - { - Panel *firstPanel = first == NULL ? this : first; - nextPanel = nextPanel->GetNavLeft( firstPanel ); - } - - return nextPanel; -} - -vgui::Panel* Panel::GetNavRight( Panel *first ) -{ - if ( !m_NavRight && m_sNavRightName.Length() > 0 ) - { - Panel *pParent = GetParent(); - const char *pName = m_sNavRightName.String(); - while ( pParent && pName[ 0 ] == '<' ) - { - pParent = pParent->GetParent(); - pName++; - } - - if ( !pParent ) - { - return NULL; - } - - Panel* foundPanel = pParent->FindChildByName( pName, true ); - if ( foundPanel != 0 ) - { - m_NavRight = foundPanel->GetPanel(); - } - } - - vgui::Panel* nextPanel = m_NavRight; - if( m_NavRight && m_NavRight != first && !m_NavRight->IsVisible() ) - { - Panel *firstPanel = first == NULL ? this : first; - nextPanel = nextPanel->GetNavRight( firstPanel ); - } - - return nextPanel; -} - -vgui::Panel* Panel::GetNavToRelay( Panel *first ) -{ - if ( !m_NavToRelay && m_sNavToRelayName.Length() > 0 ) - { - Panel *pParent = this; - const char *pName = m_sNavToRelayName.String(); - while ( pParent && pName[ 0 ] == '<' ) - { - pParent = pParent->GetParent(); - pName++; - } - - if ( !pParent ) - { - return NULL; - } - - Panel* foundPanel = pParent->FindChildByName( pName, true ); - if ( foundPanel != 0 ) - { - m_NavToRelay = foundPanel->GetPanel(); - } - } - - vgui::Panel* nextPanel = m_NavToRelay; - if ( m_NavToRelay && m_NavToRelay != first && !m_NavToRelay->IsVisible() ) - { - Panel *firstPanel = first == NULL ? this : first; - nextPanel = nextPanel->GetNavToRelay( firstPanel ); - } - - return nextPanel; -} - -vgui::Panel* Panel::GetNavActivate( Panel *first ) -{ - if ( !m_NavActivate && m_sNavActivateName.Length() > 0 ) - { - Panel *pParent = GetParent(); - const char *pName = m_sNavActivateName.String(); - while ( pParent && pName[ 0 ] == '<' ) - { - pParent = pParent->GetParent(); - pName++; - } - - if ( !pParent ) - { - return NULL; - } - - Panel* foundPanel = pParent->FindChildByName( pName, true ); - if ( foundPanel != 0 ) - { - m_NavActivate = foundPanel->GetPanel(); - } - } - - vgui::Panel* nextPanel = m_NavActivate; - if ( m_NavActivate && m_NavActivate != first && !m_NavActivate->IsVisible() ) - { - Panel *firstPanel = first == NULL ? this : first; - nextPanel = nextPanel->GetNavActivate( firstPanel ); - } - - return nextPanel; -} - -vgui::Panel* Panel::GetNavBack( Panel *first ) -{ - if ( !m_NavBack && m_sNavBackName.Length() > 0 ) - { - Panel *pParent = GetParent(); - const char *pName = m_sNavBackName.String(); - while ( pParent && pName[ 0 ] == '<' ) - { - pParent = pParent->GetParent(); - pName++; - } - - if ( !pParent ) - { - return NULL; - } - - Panel *foundPanel = pParent->FindChildByName( pName ); - if ( foundPanel ) - { - m_NavBack = foundPanel; - } - } - - vgui::Panel* nextPanel = m_NavBack; - if ( m_NavBack && m_NavBack != first && !m_NavBack->IsVisible() ) - { - Panel *firstPanel = first == NULL ? this : first; - nextPanel = nextPanel->GetNavBack( firstPanel ); - } - - return nextPanel; -} - -vgui::Panel* Panel::GetNavUpPanel() -{ - return m_NavUp; -} - -vgui::Panel* Panel::GetNavDownPanel() -{ - return m_NavDown; -} - -vgui::Panel* Panel::GetNavLeftPanel() -{ - return m_NavLeft; -} - -vgui::Panel* Panel::GetNavRightPanel() -{ - return m_NavRight; -} - -vgui::Panel* Panel::GetNavToRelayPanel() -{ - return m_NavToRelay; -} - -vgui::Panel* Panel::GetNavActivatePanel() -{ - return m_NavActivate; -} - -vgui::Panel* Panel::GetNavBackPanel() -{ - return m_NavBack; -} - -void Panel::SetConsoleStylePanel( bool bConsoleStyle ) -{ - m_bIsConsoleStylePanel = bConsoleStyle; -} - -bool Panel::IsConsoleStylePanel() const -{ - return m_bIsConsoleStylePanel; -} - -//----------------------------------------------------------------------------- -// Purpose: Utility class for handling message map allocation -//----------------------------------------------------------------------------- -class CPanelMessageMapDictionary -{ -public: - CPanelMessageMapDictionary() : m_PanelMessageMapPool( sizeof(PanelMessageMap), 32, CUtlMemoryPool::GROW_FAST, "CPanelMessageMapDictionary::m_PanelMessageMapPool" ) - { - m_MessageMaps.RemoveAll(); - } - - PanelMessageMap *FindOrAddPanelMessageMap( char const *className ); - PanelMessageMap *FindPanelMessageMap( char const *className ); -private: - - struct PanelMessageMapDictionaryEntry - { - PanelMessageMap *map; - }; - - char const *StripNamespace( char const *className ); - - CUtlDict< PanelMessageMapDictionaryEntry, int > m_MessageMaps; - CUtlMemoryPool m_PanelMessageMapPool; -}; - - -char const *CPanelMessageMapDictionary::StripNamespace( char const *className ) -{ - if ( !strnicmp( className, "vgui::", 6 ) ) - { - return className + 6; - } - return className; -} - -//----------------------------------------------------------------------------- -// Purpose: Find but don't add mapping -//----------------------------------------------------------------------------- -PanelMessageMap *CPanelMessageMapDictionary::FindPanelMessageMap( char const *className ) -{ - int lookup = m_MessageMaps.Find( StripNamespace( className ) ); - if ( lookup != m_MessageMaps.InvalidIndex() ) - { - return m_MessageMaps[ lookup ].map; - } - return NULL; -} - -#include -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -PanelMessageMap *CPanelMessageMapDictionary::FindOrAddPanelMessageMap( char const *className ) -{ - PanelMessageMap *map = FindPanelMessageMap( className ); - if ( map ) - return map; - - PanelMessageMapDictionaryEntry entry; - // use the alloc in place method of new - entry.map = new (m_PanelMessageMapPool.Alloc(sizeof(PanelMessageMap))) PanelMessageMap; - Construct(entry.map); - m_MessageMaps.Insert( StripNamespace( className ), entry ); - return entry.map; -} -#include - -#if defined( VGUI_USEKEYBINDINGMAPS ) -//----------------------------------------------------------------------------- -// Purpose: Utility class for handling keybinding map allocation -//----------------------------------------------------------------------------- -class CPanelKeyBindingMapDictionary -{ -public: - CPanelKeyBindingMapDictionary() : m_PanelKeyBindingMapPool( sizeof(PanelKeyBindingMap), 32, CUtlMemoryPool::GROW_FAST, "CPanelKeyBindingMapDictionary::m_PanelKeyBindingMapPool" ) - { - m_MessageMaps.RemoveAll(); - } - - PanelKeyBindingMap *FindOrAddPanelKeyBindingMap( char const *className ); - PanelKeyBindingMap *FindPanelKeyBindingMap( char const *className ); -private: - - struct PanelKeyBindingMapDictionaryEntry - { - PanelKeyBindingMap *map; - }; - - char const *StripNamespace( char const *className ); - - CUtlDict< PanelKeyBindingMapDictionaryEntry, int > m_MessageMaps; - CUtlMemoryPool m_PanelKeyBindingMapPool; -}; - - -char const *CPanelKeyBindingMapDictionary::StripNamespace( char const *className ) -{ - if ( !strnicmp( className, "vgui::", 6 ) ) - { - return className + 6; - } - return className; -} - -//----------------------------------------------------------------------------- -// Purpose: Find but don't add mapping -//----------------------------------------------------------------------------- -PanelKeyBindingMap *CPanelKeyBindingMapDictionary::FindPanelKeyBindingMap( char const *className ) -{ - int lookup = m_MessageMaps.Find( StripNamespace( className ) ); - if ( lookup != m_MessageMaps.InvalidIndex() ) - { - return m_MessageMaps[ lookup ].map; - } - return NULL; -} - -#include -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -PanelKeyBindingMap *CPanelKeyBindingMapDictionary::FindOrAddPanelKeyBindingMap( char const *className ) -{ - PanelKeyBindingMap *map = FindPanelKeyBindingMap( className ); - if ( map ) - return map; - - PanelKeyBindingMapDictionaryEntry entry; - // use the alloc in place method of new - entry.map = new (m_PanelKeyBindingMapPool.Alloc(sizeof(PanelKeyBindingMap))) PanelKeyBindingMap; - Construct(entry.map); - m_MessageMaps.Insert( StripNamespace( className ), entry ); - return entry.map; -} - -#include - -CPanelKeyBindingMapDictionary& GetPanelKeyBindingMapDictionary() -{ - static CPanelKeyBindingMapDictionary dictionary; - return dictionary; -} - -#endif // VGUI_USEKEYBINDINGMAPS - -CPanelMessageMapDictionary& GetPanelMessageMapDictionary() -{ - static CPanelMessageMapDictionary dictionary; - return dictionary; -} - -namespace vgui -{ - - //----------------------------------------------------------------------------- - // Purpose: - //----------------------------------------------------------------------------- - PanelMessageMap *FindOrAddPanelMessageMap( char const *className ) - { - return GetPanelMessageMapDictionary().FindOrAddPanelMessageMap( className ); - } - - //----------------------------------------------------------------------------- - // Purpose: Find but don't add mapping - //----------------------------------------------------------------------------- - PanelMessageMap *FindPanelMessageMap( char const *className ) - { - return GetPanelMessageMapDictionary().FindPanelMessageMap( className ); - } - -#if defined( VGUI_USEKEYBINDINGMAPS ) - CPanelKeyBindingMapDictionary& GetPanelKeyBindingMapDictionary() - { - static CPanelKeyBindingMapDictionary dictionary; - return dictionary; - } - //----------------------------------------------------------------------------- - // Purpose: - //----------------------------------------------------------------------------- - PanelKeyBindingMap *FindOrAddPanelKeyBindingMap( char const *className ) - { - return GetPanelKeyBindingMapDictionary().FindOrAddPanelKeyBindingMap( className ); - } - - //----------------------------------------------------------------------------- - // Purpose: Find but don't add mapping - //----------------------------------------------------------------------------- - PanelKeyBindingMap *FindPanelKeyBindingMap( char const *className ) - { - return GetPanelKeyBindingMapDictionary().FindPanelKeyBindingMap( className ); - } -#endif // VGUI_USEKEYBINDINGMAPS - -SortedPanel_t::SortedPanel_t( Panel *panel ) -{ - pPanel = panel; pButton = dynamic_cast< Button* >( panel ); -} - - -void VguiPanelGetSortedChildPanelList( Panel *pParentPanel, void *pSortedPanels ) -{ - CUtlSortVector< SortedPanel_t, CSortedPanelYLess > *pList = reinterpret_cast< CUtlSortVector< SortedPanel_t, CSortedPanelYLess >* >( pSortedPanels ); - - for ( int i = 0; i < pParentPanel->GetChildCount(); i++ ) - { - // perform auto-layout on the child panel - Panel *pPanel = pParentPanel->GetChild( i ); - if ( !pPanel || !pPanel->IsVisible() ) - continue; - - pList->Insert( SortedPanel_t( static_cast< Panel* >( pPanel ) ) ); - } -} - -void VguiPanelGetSortedChildButtonList( Panel *pParentPanel, void *pSortedPanels, char *pchFilter /*= NULL*/, int nFilterType /*= 0*/ ) -{ - CUtlSortVector< SortedPanel_t, CSortedPanelYLess > *pList = reinterpret_cast< CUtlSortVector< SortedPanel_t, CSortedPanelYLess >* >( pSortedPanels ); - - for ( int i = 0; i < pParentPanel->GetChildCount(); i++ ) - { - // perform auto-layout on the child panel - Button *pPanel = dynamic_cast< Button* >( pParentPanel->GetChild( i ) ); - if ( !pPanel || !pPanel->IsVisible() ) - continue; - - if ( pchFilter && pchFilter[ 0 ] != '\0' ) - { - char szBuff[ 128 ]; - pPanel->GetText( szBuff, sizeof( szBuff ) ); - - // Prefix - if ( nFilterType == 0 ) - { - if ( !StringHasPrefix( szBuff, pchFilter ) ) - { - continue; - } - } - // Substring - else if ( nFilterType == 1 ) - { - if ( V_strstr( szBuff, pchFilter ) == NULL ) - { - continue; - } - } - } - - pList->Insert( SortedPanel_t( pPanel ) ); - } -} - -int VguiPanelNavigateSortedChildButtonList( void *pSortedPanels, int nDir ) -{ - CUtlSortVector< SortedPanel_t, CSortedPanelYLess > *pList = reinterpret_cast< CUtlSortVector< SortedPanel_t, CSortedPanelYLess >* >( pSortedPanels ); - - if ( pList->Count() <= 0 ) - return -1; - - if ( nDir != 0 ) - { - int nArmed = -1; - for ( int i = 0; i < pList->Count(); i++ ) - { - if ( (*pList)[ i ].pButton->IsArmed() ) - { - nArmed = i; - break; - } - } - - if ( nArmed == -1 ) - { - (*pList)[ 0 ].pButton->SetArmed( true ); - return 0; - } - else - { - int nNewArmed = clamp( nArmed + nDir, 0, pList->Count() - 1 ); - if ( nNewArmed != nArmed ) - { - (*pList)[ nArmed ].pButton->SetArmed( false ); - } - - (*pList)[ nNewArmed ].pButton->RequestFocus(); - (*pList)[ nNewArmed ].pButton->SetArmed( true ); - - return nNewArmed; - } - } - - return -1; -} - -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include +#include +#include +#include +#include // isdigit() + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "vgui_controls/Menu.h" +#include "vgui_controls/MenuItem.h" + +#include "UtlSortVector.h" + +#include "tier1/utldict.h" +#include "tier1/utlbuffer.h" +#include "mempool.h" +#include "filesystem.h" +#include "tier0/icommandline.h" + +#include "tier0/vprof.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +#define TRIPLE_PRESS_MSEC 300 + + +extern int GetBuildModeDialogCount(); + +static char *CopyString( const char *in ) +{ + if ( !in ) + return NULL; + + int len = strlen( in ); + char *n = new char[ len + 1 ]; + Q_strncpy( n, in, len + 1 ); + return n; +} + +#if defined( VGUI_USEDRAGDROP ) +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +struct vgui::DragDrop_t +{ + DragDrop_t() : + m_bDragEnabled( false ), + m_bShowDragHelper( true ), + m_bDropEnabled( false ), + m_bDragStarted( false ), + m_nDragStartTolerance( 8 ), + m_bDragging( false ), + m_lDropHoverTime( 0 ), + m_bDropMenuShown( false ), + m_bPreventChaining( false ) + { + m_nStartPos[ 0 ] = m_nStartPos[ 1 ] = 0; + m_nLastPos[ 0 ] = m_nLastPos[ 1 ] = 0; + } + + // Drag related data + bool m_bDragEnabled; + bool m_bShowDragHelper; + bool m_bDragging; + bool m_bDragStarted; + // How many pixels the dragged box must move before showing the outline rect... + int m_nDragStartTolerance; + int m_nStartPos[ 2 ]; + int m_nLastPos[ 2 ]; + CUtlVector< KeyValues * > m_DragData; + CUtlVector< PHandle > m_DragPanels; + + // Drop related data + bool m_bDropEnabled; + // A droppable panel can have a hover context menu, which will show up after m_flHoverContextTime of hovering + float m_flHoverContextTime; + + PHandle m_hCurrentDrop; + // Amount of time hovering over current drop target + long m_lDropHoverTime; + bool m_bDropMenuShown; + DHANDLE< Menu > m_hDropContextMenu; + + // Misc data + bool m_bPreventChaining; +}; + +//----------------------------------------------------------------------------- +// Purpose: Helper for painting to the full screen... +//----------------------------------------------------------------------------- +class CDragDropHelperPanel : public Panel +{ + DECLARE_CLASS_SIMPLE( CDragDropHelperPanel, Panel ); +public: + CDragDropHelperPanel(); + + virtual VPANEL IsWithinTraverse(int x, int y, bool traversePopups); + virtual void PostChildPaint(); + + void AddPanel( Panel *current ); + + void RemovePanel( Panel *search ); + +private: + struct DragHelperPanel_t + { + PHandle m_hPanel; + }; + + CUtlVector< DragHelperPanel_t > m_PaintList; +}; + +vgui::DHANDLE< CDragDropHelperPanel > s_DragDropHelper; +#endif + +#if defined( VGUI_USEKEYBINDINGMAPS ) + +BoundKey_t::BoundKey_t(): + isbuiltin( true ), + bindingname( 0 ), + keycode( KEY_NONE ), + modifiers( 0 ) +{ +} + +BoundKey_t::BoundKey_t( const BoundKey_t& src ) +{ + isbuiltin = src.isbuiltin; + bindingname = isbuiltin ? src.bindingname : CopyString( src.bindingname ); + keycode = src.keycode; + modifiers = src.modifiers; +} + +BoundKey_t& BoundKey_t::operator =( const BoundKey_t& src ) +{ + if ( this == &src ) + return *this; + isbuiltin = src.isbuiltin; + bindingname = isbuiltin ? src.bindingname : CopyString( src.bindingname ); + keycode = src.keycode; + modifiers = src.modifiers; + return *this; +} + + +BoundKey_t::~BoundKey_t() +{ + if ( !isbuiltin ) + { + delete[] bindingname; + } +} + +KeyBindingMap_t::KeyBindingMap_t() : + bindingname( 0 ), + func( 0 ), + helpstring( 0 ), + docstring( 0 ), + passive( false ) +{ +} + +KeyBindingMap_t::KeyBindingMap_t( const KeyBindingMap_t& src ) +{ + bindingname = src.bindingname; + helpstring = src.helpstring; + docstring = src.docstring; + + func = src.func; + passive = src.passive; +} + +KeyBindingMap_t::~KeyBindingMap_t() +{ +} + +class CKeyBindingsMgr +{ +public: + + CKeyBindingsMgr() : + m_Bindings( 0, 0, KeyBindingContextHandleLessFunc ), + m_nKeyBindingContexts( 0 ) + { + } + + struct KBContext_t + { + KBContext_t() : + m_KeyBindingsFile( UTL_INVAL_SYMBOL ), + m_KeyBindingsPathID( UTL_INVAL_SYMBOL ) + { + m_Handle = INVALID_KEYBINDINGCONTEXT_HANDLE; + } + + KBContext_t( const KBContext_t& src ) + { + m_Handle = src.m_Handle; + m_KeyBindingsFile = src.m_KeyBindingsFile; + m_KeyBindingsPathID = src.m_KeyBindingsPathID; + int c = src.m_Panels.Count(); + for ( int i = 0; i < c; ++i ) + { + m_Panels.AddToTail( src.m_Panels[ i ] ); + } + } + + KeyBindingContextHandle_t m_Handle; + CUtlSymbol m_KeyBindingsFile; + CUtlSymbol m_KeyBindingsPathID; + CUtlVector< Panel * > m_Panels; + }; + + static bool KeyBindingContextHandleLessFunc( const KBContext_t& lhs, const KBContext_t& rhs ) + { + return lhs.m_Handle < rhs.m_Handle; + } + + KeyBindingContextHandle_t CreateContext( char const *filename, char const *pathID ) + { + KBContext_t entry; + + entry.m_Handle = (KeyBindingContextHandle_t)++m_nKeyBindingContexts; + entry.m_KeyBindingsFile = filename; + if ( pathID ) + { + entry.m_KeyBindingsPathID = pathID; + } + else + { + entry.m_KeyBindingsPathID = UTL_INVAL_SYMBOL; + } + + m_Bindings.Insert( entry ); + + return entry.m_Handle; + } + + void AddPanelToContext( KeyBindingContextHandle_t handle, Panel *panel ) + { + if ( !panel->GetName() || !panel->GetName()[ 0 ] ) + { + Warning( "Can't add Keybindings Context for unnamed panels\n" ); + return; + } + + KBContext_t *entry = Find( handle ); + Assert( entry ); + if ( entry ) + { + int idx = entry->m_Panels.Find( panel ); + if ( idx == entry->m_Panels.InvalidIndex() ) + { + entry->m_Panels.AddToTail( panel ); + } + } + } + + void OnPanelDeleted( KeyBindingContextHandle_t handle, Panel *panel ) + { + KBContext_t *kb = Find( handle ); + if ( kb ) + { + kb->m_Panels.FindAndRemove( panel ); + } + } + + KBContext_t *Find( KeyBindingContextHandle_t handle ) + { + KBContext_t search; + search.m_Handle = handle; + int idx = m_Bindings.Find( search ); + if ( idx == m_Bindings.InvalidIndex() ) + { + return NULL; + } + return &m_Bindings[ idx ]; + } + + char const *GetKeyBindingsFile( KeyBindingContextHandle_t handle ) + { + KBContext_t *kb = Find( handle ); + if ( kb ) + { + return kb->m_KeyBindingsFile.String(); + } + Assert( 0 ); + return ""; + } + + char const *GetKeyBindingsFilePathID( KeyBindingContextHandle_t handle ) + { + KBContext_t *kb = Find( handle ); + if ( kb ) + { + return kb->m_KeyBindingsPathID.String(); + } + Assert( 0 ); + return NULL; + } + + int GetPanelsWithKeyBindingsCount( KeyBindingContextHandle_t handle ) + { + KBContext_t *kb = Find( handle ); + if ( kb ) + { + return kb->m_Panels.Count(); + } + Assert( 0 ); + return 0; + } + + //----------------------------------------------------------------------------- + // Purpose: static method + // Input : index - + // Output : Panel + //----------------------------------------------------------------------------- + Panel *GetPanelWithKeyBindings( KeyBindingContextHandle_t handle, int index ) + { + KBContext_t *kb = Find( handle ); + if ( kb ) + { + Assert( index >= 0 && index < kb->m_Panels.Count() ); + return kb->m_Panels[ index ]; + } + Assert( 0 ); + return 0; + } + + CUtlRBTree< KBContext_t, int > m_Bindings; + int m_nKeyBindingContexts; +}; + +static CKeyBindingsMgr g_KBMgr; + +//----------------------------------------------------------------------------- +// Purpose: Static method to allocate a context +// Input : - +// Output : KeyBindingContextHandle_t +//----------------------------------------------------------------------------- +KeyBindingContextHandle_t Panel::CreateKeyBindingsContext( char const *filename, char const *pathID /*=0*/ ) +{ + return g_KBMgr.CreateContext( filename, pathID ); +} + + +//----------------------------------------------------------------------------- +// Purpose: static method +// Input : - +// Output : int +//----------------------------------------------------------------------------- +int Panel::GetPanelsWithKeyBindingsCount( KeyBindingContextHandle_t handle ) +{ + return g_KBMgr.GetPanelsWithKeyBindingsCount( handle ); +} + +//----------------------------------------------------------------------------- +// Purpose: static method +// Input : index - +// Output : Panel +//----------------------------------------------------------------------------- +Panel *Panel::GetPanelWithKeyBindings( KeyBindingContextHandle_t handle, int index ) +{ + return g_KBMgr.GetPanelWithKeyBindings( handle, index ); +} + + +//----------------------------------------------------------------------------- +// Returns the number of keybindings +//----------------------------------------------------------------------------- +int Panel::GetKeyMappingCount( ) +{ + int nCount = 0; + PanelKeyBindingMap *map = GetKBMap(); + while ( map ) + { + nCount += map->entries.Count(); + map = map->baseMap; + } + return nCount; +} + + +//----------------------------------------------------------------------------- +// Purpose: static method. Reverts key bindings for all registered panels (panels with keybindings actually +// loaded from file +// Input : - +//----------------------------------------------------------------------------- +void Panel::RevertKeyBindings( KeyBindingContextHandle_t handle ) +{ + int c = GetPanelsWithKeyBindingsCount( handle ); + for ( int i = 0; i < c; ++i ) + { + Panel *kbPanel = GetPanelWithKeyBindings( handle, i ); + Assert( kbPanel ); + kbPanel->RevertKeyBindingsToDefault(); + } +} + +static void BufPrint( CUtlBuffer& buf, int level, char const *fmt, ... ) +{ + char string[ 2048 ]; + va_list argptr; + va_start( argptr, fmt ); + _vsnprintf( string, sizeof( string ) - 1, fmt, argptr ); + va_end( argptr ); + string[ sizeof( string ) - 1 ] = 0; + + while ( --level >= 0 ) + { + buf.Printf( " " ); + } + buf.Printf( "%s", string ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : handle - +//----------------------------------------------------------------------------- +void Panel::SaveKeyBindings( KeyBindingContextHandle_t handle ) +{ + char const *filename = g_KBMgr.GetKeyBindingsFile( handle ); + char const *pathID = g_KBMgr.GetKeyBindingsFilePathID( handle ); + + SaveKeyBindingsToFile( handle, filename, pathID ); +} + +//----------------------------------------------------------------------------- +// Purpose: static method. Saves key binding files out for all keybindings +// Input : - +//----------------------------------------------------------------------------- +void Panel::SaveKeyBindingsToFile( KeyBindingContextHandle_t handle, char const *filename, char const *pathID /*= 0*/ ) +{ + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + + BufPrint( buf, 0, "keybindings\n" ); + BufPrint( buf, 0, "{\n" ); + + int c = GetPanelsWithKeyBindingsCount( handle ); + for ( int i = 0; i < c; ++i ) + { + Panel *kbPanel = GetPanelWithKeyBindings( handle, i ); + Assert( kbPanel ); + if ( !kbPanel ) + continue; + + Assert( kbPanel->GetName() ); + Assert( kbPanel->GetName()[ 0 ] ); + + if ( !kbPanel->GetName() || !kbPanel->GetName()[ 0 ] ) + continue; + + BufPrint( buf, 1, "\"%s\"\n", kbPanel->GetName() ); + BufPrint( buf, 1, "{\n" ); + + kbPanel->SaveKeyBindingsToBuffer( 2, buf ); + + BufPrint( buf, 1, "}\n" ); + } + + BufPrint( buf, 0, "}\n" ); + + if ( g_pFullFileSystem->FileExists( filename, pathID ) && + !g_pFullFileSystem->IsFileWritable( filename, pathID ) ) + { + Warning( "Panel::SaveKeyBindings '%s' is read-only!!!\n", filename ); + } + + FileHandle_t h = g_pFullFileSystem->Open( filename, "wb", pathID ); + if ( FILESYSTEM_INVALID_HANDLE != h ) + { + g_pFullFileSystem->Write( buf.Base(), buf.TellPut(), h ); + g_pFullFileSystem->Close( h ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : handle - +// *panelOfInterest - +//----------------------------------------------------------------------------- +void Panel::LoadKeyBindingsForOnePanel( KeyBindingContextHandle_t handle, Panel *panelOfInterest ) +{ + if ( !panelOfInterest ) + return; + if ( !panelOfInterest->GetName() ) + return; + if ( !panelOfInterest->GetName()[ 0 ] ) + return; + + char const *filename = g_KBMgr.GetKeyBindingsFile( handle ); + char const *pathID = g_KBMgr.GetKeyBindingsFilePathID( handle ); + + KeyValues *kv = new KeyValues( "keybindings" ); + if ( kv->LoadFromFile( g_pFullFileSystem, filename, pathID ) ) + { + int c = GetPanelsWithKeyBindingsCount( handle ); + for ( int i = 0; i < c; ++i ) + { + Panel *kbPanel = GetPanelWithKeyBindings( handle, i ); + Assert( kbPanel ); + + char const *panelName = kbPanel->GetName(); + if ( !panelName ) + { + continue; + } + + if ( Q_stricmp( panelOfInterest->GetName(), panelName ) ) + continue; + + KeyValues *subKey = kv->FindKey( panelName, false ); + if ( !subKey ) + { + Warning( "Panel::ReloadKeyBindings: Can't find entry for panel '%s'\n", panelName ); + continue; + } + + kbPanel->ParseKeyBindings( subKey ); + } + } + kv->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: static method. Loads all key bindings again +// Input : - +//----------------------------------------------------------------------------- + +void Panel::ReloadKeyBindings( KeyBindingContextHandle_t handle ) +{ + char const *filename = g_KBMgr.GetKeyBindingsFile( handle ); + char const *pathID = g_KBMgr.GetKeyBindingsFilePathID( handle ); + + KeyValues *kv = new KeyValues( "keybindings" ); + if ( kv->LoadFromFile( g_pFullFileSystem, filename, pathID ) ) + { + int c = GetPanelsWithKeyBindingsCount( handle ); + for ( int i = 0; i < c; ++i ) + { + Panel *kbPanel = GetPanelWithKeyBindings( handle, i ); + Assert( kbPanel ); + + char const *panelName = kbPanel->GetName(); + if ( !panelName ) + { + continue; + } + + KeyValues *subKey = kv->FindKey( panelName, false ); + if ( !subKey ) + { + Warning( "Panel::ReloadKeyBindings: Can't find entry for panel '%s'\n", panelName ); + continue; + } + + kbPanel->ParseKeyBindings( subKey ); + } + } + kv->deleteThis(); +} +#endif // VGUI_USEKEYBINDINGMAPS + +DECLARE_BUILD_FACTORY( Panel ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Panel::Panel() +{ + Init(0, 0, 64, 24); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Panel::Panel(Panel *parent) +{ + Init(0, 0, 64, 24); + SetParent(parent); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Panel::Panel(Panel *parent, const char *panelName) +{ + Init(0, 0, 64, 24); + SetName(panelName); + SetParent(parent); + SetBuildModeEditable(true); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Panel::Panel( Panel *parent, const char *panelName, HScheme scheme ) +{ + Init(0, 0, 64, 24); + SetName(panelName); + SetParent(parent); + SetBuildModeEditable(true); + SetScheme( scheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: Setup +//----------------------------------------------------------------------------- +void Panel::Init( int x, int y, int wide, int tall ) +{ + _panelName = NULL; + _tooltipText = NULL; + _pinToSibling = NULL; + m_hMouseEventHandler = NULL; + _pinCornerToSibling = PIN_TOPLEFT; + _pinToSiblingCorner = PIN_TOPLEFT; + + // get ourselves an internal panel + _vpanel = ivgui()->AllocPanel(); + ipanel()->Init(_vpanel, this); + + SetPos(x, y); + SetSize(wide, tall); + _flags.SetFlag( NEEDS_LAYOUT | NEEDS_SCHEME_UPDATE | NEEDS_DEFAULT_SETTINGS_APPLIED ); + _flags.SetFlag( AUTODELETE_ENABLED | PAINT_BORDER_ENABLED | PAINT_BACKGROUND_ENABLED | PAINT_ENABLED ); +#if defined( VGUI_USEKEYBINDINGMAPS ) + _flags.SetFlag( ALLOW_CHAIN_KEYBINDING_TO_PARENT ); +#endif + m_nPinDeltaX = m_nPinDeltaY = 0; + m_nResizeDeltaX = m_nResizeDeltaY = 0; + _autoResizeDirection = AUTORESIZE_NO; + _pinCorner = PIN_TOPLEFT; + _cursor = dc_arrow; + _border = NULL; + _buildGroup = UTLHANDLE_INVALID; + _tabPosition = 0; + m_iScheme = 0; + m_bIsSilent = false; + m_bParentNeedsCursorMoveEvents = false; + + _buildModeFlags = 0; // not editable or deletable in buildmode dialog by default + + m_pTooltips = NULL; + m_bToolTipOverridden = false; + + m_flAlpha = 255.0f; + m_nPaintBackgroundType = 0; + + //============================================================================= + // HPE_BEGIN: + // [tj] Default to rounding all corners (for draw style 2) + //============================================================================= + m_roundedCorners = PANEL_ROUND_CORNER_ALL; + //============================================================================= + // HPE_END + //============================================================================= + + m_nBgTextureId1 = -1; + m_nBgTextureId2 = -1; + m_nBgTextureId3 = -1; + m_nBgTextureId4 = -1; +#if defined( VGUI_USEDRAGDROP ) + m_pDragDrop = new DragDrop_t; + +#endif + + m_lLastDoublePressTime = 0L; + +#if defined( VGUI_USEKEYBINDINGMAPS ) + m_hKeyBindingsContext = INVALID_KEYBINDINGCONTEXT_HANDLE; +#endif + + REGISTER_COLOR_AS_OVERRIDABLE( _fgColor, "fgcolor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _bgColor, "bgcolor_override" ); + + m_bIsConsoleStylePanel = false; + m_NavUp = NULL; + m_NavDown = NULL; + m_NavLeft = NULL; + m_NavRight = NULL; + m_NavToRelay = NULL; + m_NavActivate = NULL; + m_NavBack = NULL; + m_sNavUpName = NULL; + m_sNavDownName = NULL; + m_sNavLeftName = NULL; + m_sNavRightName = NULL; + m_sNavToRelayName = NULL; + m_sNavActivateName = NULL; + m_sNavBackName = NULL; + + m_PassUnhandledInput = true; + m_LastNavDirection = ND_NONE; + m_bWorldPositionCurrentFrame = false; + m_bForceStereoRenderToFrameBuffer = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +Panel::~Panel() +{ + // @note Tom Bui: only cleanup if we've created it + if ( !m_bToolTipOverridden ) + { + if ( m_pTooltips ) + { + delete m_pTooltips; + } + } +#if defined( VGUI_USEKEYBINDINGMAPS ) + if ( IsValidKeyBindingsContext() ) + { + g_KBMgr.OnPanelDeleted( m_hKeyBindingsContext, this ); + } +#endif // VGUI_USEKEYBINDINGMAPS +#if defined( VGUI_USEDRAGDROP ) + if ( m_pDragDrop->m_bDragging ) + { + OnFinishDragging( false, (MouseCode)-1 ); + } +#endif // VGUI_USEDRAGDROP + + _flags.ClearFlag( AUTODELETE_ENABLED ); + _flags.SetFlag( MARKED_FOR_DELETION ); + + // remove panel from any list + SetParent((VPANEL)NULL); + + // Stop our children from pointing at us, and delete them if possible + while (ipanel()->GetChildCount(GetVPanel())) + { + VPANEL child = ipanel()->GetChild(GetVPanel(), 0); + if (ipanel()->IsAutoDeleteSet(child)) + { + ipanel()->DeletePanel(child); + } + else + { + ipanel()->SetParent(child, NULL); + } + } + + // delete VPanel + ivgui()->FreePanel(_vpanel); + // free our name + delete [] _panelName; + + if ( _tooltipText && _tooltipText[0] ) + { + delete [] _tooltipText; + } + + delete [] _pinToSibling; + + _vpanel = NULL; +#if defined( VGUI_USEDRAGDROP ) + delete m_pDragDrop; +#endif // VGUI_USEDRAGDROP +} + +//----------------------------------------------------------------------------- +// Purpose: fully construct this panel so its ready for use right now (i.e fonts loaded, colors set, default label text set, ...) +//----------------------------------------------------------------------------- +void Panel::MakeReadyForUse() +{ +// PerformApplySchemeSettings(); + UpdateSiblingPin(); + surface()->SolveTraverse( GetVPanel(), true ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetName( const char *panelName ) +{ + // No change? + if ( _panelName && + panelName && + !Q_strcmp( _panelName, panelName ) ) + { + return; + } + + if (_panelName) + { + delete [] _panelName; + _panelName = NULL; + } + + if (panelName) + { + int len = Q_strlen(panelName) + 1; + _panelName = new char[ len ]; + Q_strncpy( _panelName, panelName, len ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns the given name of the panel +//----------------------------------------------------------------------------- +const char *Panel::GetName() +{ + if (_panelName) + return _panelName; + + return ""; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the name of the module that this instance of panel was compiled into +//----------------------------------------------------------------------------- +const char *Panel::GetModuleName() +{ + return vgui::GetControlsModuleName(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the classname of the panel (as specified in the panelmaps) +//----------------------------------------------------------------------------- +const char *Panel::GetClassName() +{ + // loop up the panel map name + PanelMessageMap *panelMap = GetMessageMap(); + if ( panelMap ) + { + return panelMap->pfnClassName(); + } + + return "Panel"; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetPos(int x, int y) +{ + if (!CommandLine()->FindParm("-hushasserts")) + { + Assert( abs(x) < 32768 && abs(y) < 32768 ); + } + ipanel()->SetPos(GetVPanel(), x, y); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::GetPos(int &x, int &y) +{ + ipanel()->GetPos(GetVPanel(), x, y); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetSize(int wide, int tall) +{ + Assert( abs(wide) < 32768 && abs(tall) < 32768 ); + ipanel()->SetSize(GetVPanel(), wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::GetSize(int &wide, int &tall) +{ + ipanel()->GetSize(GetVPanel(), wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetBounds(int x, int y, int wide, int tall) +{ + SetPos(x,y); + SetSize(wide,tall); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::GetBounds(int &x, int &y, int &wide, int &tall) +{ + GetPos(x, y); + GetSize(wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: returns safe handle to parent +//----------------------------------------------------------------------------- +VPANEL Panel::GetVParent() +{ + if ( ipanel() ) + { + return ipanel()->GetParent(GetVPanel()); + } + + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to a controls version of a Panel pointer +//----------------------------------------------------------------------------- +Panel *Panel::GetParent() +{ + // get the parent and convert it to a Panel * + // this is OK, the hierarchy is guaranteed to be all from the same module, except for the root node + // the root node always returns NULL when a GetParent() is done so everything is OK + if ( ipanel() ) + { + VPANEL parent = ipanel()->GetParent(GetVPanel()); + if (parent) + { + Panel *pParent = ipanel()->GetPanel(parent, GetControlsModuleName()); + Assert(!pParent || !strcmp(pParent->GetModuleName(), GetControlsModuleName())); + return pParent; + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Screen size change notification handler +//----------------------------------------------------------------------------- +void Panel::OnScreenSizeChanged(int nOldWide, int nOldTall) +{ + // post to all children + for (int i = 0; i < ipanel()->GetChildCount(GetVPanel()); i++) + { + VPANEL child = ipanel()->GetChild(GetVPanel(), i); + PostMessage(child, new KeyValues("OnScreenSizeChanged", "oldwide", nOldWide, "oldtall", nOldTall), NULL); + } + + // make any currently fullsize window stay fullsize + int x, y, wide, tall; + GetBounds(x, y, wide, tall); + int screenWide, screenTall; + surface()->GetScreenSize(screenWide, screenTall); + if (x == 0 && y == 0 && nOldWide == wide && tall == nOldTall) + { + // fullsize + surface()->GetScreenSize(wide, tall); + SetBounds(0, 0, wide, tall); + } + + // panel needs to re-get it's scheme settings + _flags.SetFlag( NEEDS_SCHEME_UPDATE ); + + // invalidate our settings + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetVisible(bool state) +{ + ipanel()->SetVisible(GetVPanel(), state); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool Panel::IsVisible() +{ + if (ipanel()) + { + return ipanel()->IsVisible(GetVPanel()); + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetEnabled(bool state) +{ + if (state != ipanel()->IsEnabled( GetVPanel())) + { + ipanel()->SetEnabled(GetVPanel(), state); + InvalidateLayout(false); + Repaint(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool Panel::IsEnabled() +{ + return ipanel()->IsEnabled(GetVPanel()); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool Panel::IsPopup() +{ + return ipanel()->IsPopup(GetVPanel()); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::Repaint() +{ + _flags.SetFlag( NEEDS_REPAINT ); + if (surface()) + { + surface()->Invalidate(GetVPanel()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::Think() +{ + if (IsVisible()) + { + // update any tooltips + if (m_pTooltips) + { + m_pTooltips->PerformLayout(); + } + if ( _flags.IsFlagSet( NEEDS_LAYOUT ) ) + { + InternalPerformLayout(); + } + } + + OnThink(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::PaintTraverse( bool repaint, bool allowForce ) +{ + if ( m_bWorldPositionCurrentFrame ) + { + surface()->SolveTraverse( GetVPanel() ); + } + + if ( !IsVisible() ) + { + return; + } + + float oldAlphaMultiplier = surface()->DrawGetAlphaMultiplier(); + float newAlphaMultiplier = oldAlphaMultiplier * m_flAlpha * 1.0f/255.0f; + + if ( IsXbox() && !newAlphaMultiplier ) + { + // xbox optimization not suitable for pc + // xbox panels are compliant and can early out and not traverse their children + // when they have no opacity + return; + } + + if ( !repaint && + allowForce && + _flags.IsFlagSet( NEEDS_REPAINT ) ) + { + repaint = true; + _flags.ClearFlag( NEEDS_REPAINT ); + } + + VPANEL vpanel = GetVPanel(); + + bool bPushedViewport = false; + if( GetForceStereoRenderToFrameBuffer() ) + { + CMatRenderContextPtr pRenderContext( materials ); + if( pRenderContext->GetRenderTarget() ) + { + surface()->PushFullscreenViewport(); + bPushedViewport = true; + } + } + + int clipRect[4]; + ipanel()->GetClipRect( vpanel, clipRect[0], clipRect[1], clipRect[2], clipRect[3] ); + if ( ( clipRect[2] <= clipRect[0] ) || ( clipRect[3] <= clipRect[1] ) ) + { + repaint = false; + } + + // set global alpha + surface()->DrawSetAlphaMultiplier( newAlphaMultiplier ); + + bool bBorderPaintFirst = _border ? _border->PaintFirst() : false; + + // draw the border first if requested to + if ( bBorderPaintFirst && repaint && _flags.IsFlagSet( PAINT_BORDER_ENABLED ) && ( _border != null ) ) + { + // Paint the border over the background with no inset + surface()->PushMakeCurrent( vpanel, false ); + PaintBorder(); + surface()->PopMakeCurrent( vpanel ); + } + + if ( repaint ) + { + // draw the background with no inset + if ( _flags.IsFlagSet( PAINT_BACKGROUND_ENABLED ) ) + { + surface()->PushMakeCurrent( vpanel, false ); + PaintBackground(); + surface()->PopMakeCurrent( vpanel ); + } + + // draw the front of the panel with the inset + if ( _flags.IsFlagSet( PAINT_ENABLED ) ) + { + surface()->PushMakeCurrent( vpanel, true ); + Paint(); + surface()->PopMakeCurrent( vpanel ); + } + } + + // traverse and paint all our children + CUtlVector< VPANEL > &children = ipanel()->GetChildren( vpanel ); + int childCount = children.Count(); + for (int i = 0; i < childCount; i++) + { + VPANEL child = children[ i ]; + bool bVisible = ipanel()->IsVisible( child ); + + if ( surface()->ShouldPaintChildPanel( child ) ) + { + if ( bVisible ) + { + ipanel()->PaintTraverse( child, repaint, allowForce ); + } + } + else + { + // Invalidate the child panel so that it gets redrawn + surface()->Invalidate( child ); + + // keep traversing the tree, just don't allow anyone to paint after here + if ( bVisible ) + { + ipanel()->PaintTraverse( child, false, false ); + } + } + } + + // draw the border last + if ( repaint ) + { + if ( !bBorderPaintFirst && _flags.IsFlagSet( PAINT_BORDER_ENABLED ) && ( _border != null ) ) + { + // Paint the border over the background with no inset + surface()->PushMakeCurrent( vpanel, false ); + PaintBorder(); + surface()->PopMakeCurrent( vpanel ); + } + +#ifdef _DEBUG + // IsBuildGroupEnabled recurses up all the parents and ends up being very expensive as it wanders all over memory + if ( GetBuildModeDialogCount() && IsBuildGroupEnabled() ) //&& HasFocus() ) + { + // outline all selected panels + CUtlVector *controlGroup = _buildGroup->GetControlGroup(); + for (int i=0; i < controlGroup->Size(); ++i) + { + // outline all selected panels + CUtlVector *controlGroup = _buildGroup->GetControlGroup(); + for (int i=0; i < controlGroup->Size(); ++i) + { + surface()->PushMakeCurrent( ((*controlGroup)[i].Get())->GetVPanel(), false ); + ((*controlGroup)[i].Get())->PaintBuildOverlay(); + surface()->PopMakeCurrent( ((*controlGroup)[i].Get())->GetVPanel() ); + } + + _buildGroup->DrawRulers(); + } + } +#endif + + // All of our children have painted, etc, now allow painting in top of them + if ( _flags.IsFlagSet( POST_CHILD_PAINT_ENABLED ) ) + { + surface()->PushMakeCurrent( vpanel, false ); + PostChildPaint(); + surface()->PopMakeCurrent( vpanel ); + } + } + + surface()->DrawSetAlphaMultiplier( oldAlphaMultiplier ); + + surface()->SwapBuffers( vpanel ); + + if( bPushedViewport ) + { + surface()->PopFullscreenViewport(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::PaintBorder() +{ + _border->Paint(GetVPanel()); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::PaintBackground() +{ + int wide, tall; + GetSize( wide, tall ); + if ( m_SkipChild.Get() && m_SkipChild->IsVisible() ) + { + if ( GetPaintBackgroundType() == 2 ) + { + int cornerWide, cornerTall; + GetCornerTextureSize( cornerWide, cornerTall ); + + Color col = GetBgColor(); + DrawHollowBox( 0, 0, wide, tall, col, 1.0f ); + + wide -= 2 * cornerWide; + tall -= 2 * cornerTall; + + FillRectSkippingPanel( GetBgColor(), cornerWide, cornerTall, wide, tall, m_SkipChild.Get() ); + } + else + { + FillRectSkippingPanel( GetBgColor(), 0, 0, wide, tall, m_SkipChild.Get() ); + } + } + else + { + Color col = GetBgColor(); + + switch ( m_nPaintBackgroundType ) + { + default: + case 0: + { + surface()->DrawSetColor(col); + surface()->DrawFilledRect(0, 0, wide, tall); + } + break; + case 1: + { + DrawTexturedBox( 0, 0, wide, tall, col, 1.0f ); + } + break; + case 2: + { + DrawBox( 0, 0, wide, tall, col, 1.0f ); + } + break; + case 3: + { + DrawBoxFade( 0, 0, wide, tall, col, 1.0f, 255, 0, true ); + } + break; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::Paint() +{ + // empty on purpose + // PaintBackground is painted and default behavior is for Paint to do nothing +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::PostChildPaint() +{ + // Empty on purpose + // This is called if _postChildPaintEnabled is true and allows painting to + // continue on the surface after all of the panel's children have painted + // themselves. Allows drawing an overlay on top of the children, etc. +} + +//----------------------------------------------------------------------------- +// Purpose: Draws a black rectangle around the panel. +//----------------------------------------------------------------------------- +void Panel::PaintBuildOverlay() +{ + int wide,tall; + GetSize(wide,tall); + surface()->DrawSetColor(0, 0, 0, 255); + + surface()->DrawFilledRect(0,0,wide,2); //top + surface()->DrawFilledRect(0,tall-2,wide,tall); //bottom + surface()->DrawFilledRect(0,2,2,tall-2); //left + surface()->DrawFilledRect(wide-2,2,wide,tall-2); //right +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the panel's draw code will fully cover it's area +//----------------------------------------------------------------------------- +bool Panel::IsOpaque() +{ + // FIXME: Add code to account for the 'SkipChild' functionality in Frame + if ( IsVisible() && _flags.IsFlagSet( PAINT_BACKGROUND_ENABLED ) && ( _bgColor[3] == 255 ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the settings are aligned to the right of the screen +//----------------------------------------------------------------------------- +bool Panel::IsRightAligned() +{ + return (_buildModeFlags & BUILDMODE_SAVE_XPOS_RIGHTALIGNED); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the settings are aligned to the bottom of the screen +//----------------------------------------------------------------------------- +bool Panel::IsBottomAligned() +{ + return (_buildModeFlags & BUILDMODE_SAVE_YPOS_BOTTOMALIGNED); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the parent +//----------------------------------------------------------------------------- +void Panel::SetParent(Panel *newParent) +{ + // Assert that the parent is from the same module as the child + // FIXME: !!! work out how to handle this properly! + // Assert(!newParent || !strcmp(newParent->GetModuleName(), GetControlsModuleName())); + + if (newParent) + { + SetParent(newParent->GetVPanel()); + } + else + { + SetParent((VPANEL)NULL); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetParent(VPANEL newParent) +{ + if (newParent) + { + ipanel()->SetParent(GetVPanel(), newParent); + } + else + { + ipanel()->SetParent(GetVPanel(), NULL); + } + + if (GetVParent() && !IsPopup()) + { + SetProportional(ipanel()->IsProportional(GetVParent())); + + // most of the time KBInput == parents kbinput + if (ipanel()->IsKeyBoardInputEnabled(GetVParent()) != IsKeyBoardInputEnabled()) + { + SetKeyBoardInputEnabled(ipanel()->IsKeyBoardInputEnabled(GetVParent())); + } + + if (ipanel()->IsMouseInputEnabled(GetVParent()) != IsMouseInputEnabled()) + { + SetMouseInputEnabled(ipanel()->IsMouseInputEnabled(GetVParent())); + } + } + + UpdateSiblingPin(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::OnChildAdded(VPANEL child) +{ + Assert( !_flags.IsFlagSet( IN_PERFORM_LAYOUT ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: default message handler +//----------------------------------------------------------------------------- +void Panel::OnSizeChanged(int newWide, int newTall) +{ + InvalidateLayout(); // our size changed so force us to layout again +} + +//----------------------------------------------------------------------------- +// Purpose: sets Z ordering - lower numbers are always behind higher z's +//----------------------------------------------------------------------------- +void Panel::SetZPos(int z) +{ + ipanel()->SetZPos(GetVPanel(), z); +} + +//----------------------------------------------------------------------------- +// Purpose: sets Z ordering - lower numbers are always behind higher z's +//----------------------------------------------------------------------------- +int Panel::GetZPos() +{ + return ( ipanel()->GetZPos( GetVPanel() ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: sets alpha modifier for panel and all child panels [0..255] +//----------------------------------------------------------------------------- +void Panel::SetAlpha(int alpha) +{ + m_flAlpha = alpha; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +int Panel::GetAlpha() +{ + return (int)m_flAlpha; +} + +//----------------------------------------------------------------------------- +// Purpose: Moves the panel to the front of the z-order +//----------------------------------------------------------------------------- +void Panel::MoveToFront(void) +{ + // FIXME: only use ipanel() as per src branch? + if (IsPopup()) + { + surface()->BringToFront(GetVPanel()); + } + else + { + ipanel()->MoveToFront(GetVPanel()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Iterates up the hierarchy looking for a particular parent +//----------------------------------------------------------------------------- +bool Panel::HasParent(VPANEL potentialParent) +{ + if (!potentialParent) + return false; + + return ipanel()->HasParent(GetVPanel(), potentialParent); +} + +//----------------------------------------------------------------------------- +// Purpose: Finds the index of a child panel by string name +// Output : int - -1 if no panel of that name is found +//----------------------------------------------------------------------------- +int Panel::FindChildIndexByName(const char *childName) +{ + for (int i = 0; i < GetChildCount(); i++) + { + Panel *pChild = GetChild(i); + if (!pChild) + continue; + + if (!stricmp(pChild->GetName(), childName)) + { + return i; + } + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Finds a child panel by string name +// Output : Panel * - NULL if no panel of that name is found +//----------------------------------------------------------------------------- +Panel *Panel::FindChildByName(const char *childName, bool recurseDown) +{ + for (int i = 0; i < GetChildCount(); i++) + { + Panel *pChild = GetChild(i); + if (!pChild) + continue; + + if (!V_stricmp(pChild->GetName(), childName)) + { + return pChild; + } + + if (recurseDown) + { + Panel *panel = pChild->FindChildByName(childName, recurseDown); + if ( panel ) + { + return panel; + } + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Finds a sibling panel by name +//----------------------------------------------------------------------------- +Panel *Panel::FindSiblingByName(const char *siblingName) +{ + if ( !GetVParent() ) + return NULL; + + int siblingCount = ipanel()->GetChildCount(GetVParent()); + for (int i = 0; i < siblingCount; i++) + { + VPANEL sibling = ipanel()->GetChild(GetVParent(), i); + Panel *panel = ipanel()->GetPanel(sibling, GetControlsModuleName()); + if (!stricmp(panel->GetName(), siblingName)) + { + return panel; + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Dispatches immediately a message to the parent +//----------------------------------------------------------------------------- +void Panel::CallParentFunction(KeyValues *message) +{ + if (GetVParent()) + { + ipanel()->SendMessage(GetVParent(), message, GetVPanel()); + } + if (message) + { + message->deleteThis(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: if set to true, panel automatically frees itself when parent is deleted +//----------------------------------------------------------------------------- +void Panel::SetAutoDelete( bool state ) +{ + _flags.SetFlag( AUTODELETE_ENABLED, state ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool Panel::IsAutoDeleteSet() +{ + return _flags.IsFlagSet( AUTODELETE_ENABLED ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Just calls 'delete this' +//----------------------------------------------------------------------------- +void Panel::DeletePanel() +{ + // Avoid re-entrancy + _flags.SetFlag( MARKED_FOR_DELETION ); + _flags.ClearFlag( AUTODELETE_ENABLED ); + delete this; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +HScheme Panel::GetScheme() +{ + if (m_iScheme) + { + return m_iScheme; // return our internal scheme + } + + if (GetVParent()) // recurse down the heirarchy + { + return ipanel()->GetScheme(GetVParent()); + } + + return scheme()->GetDefaultScheme(); +} + +//----------------------------------------------------------------------------- +// Purpose: set the scheme to render this panel with by name +//----------------------------------------------------------------------------- +void Panel::SetScheme(const char *tag) +{ + if (strlen(tag) > 0 && scheme()->GetScheme(tag)) // check the scheme exists + { + SetScheme(scheme()->GetScheme(tag)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: set the scheme to render this panel with +//----------------------------------------------------------------------------- +void Panel::SetScheme(HScheme scheme) +{ + if (scheme != m_iScheme) + { + m_iScheme = scheme; + + // This will cause the new scheme to be applied at a later point +// InvalidateLayout( false, true ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the char of this panels hotkey +//----------------------------------------------------------------------------- +Panel *Panel::HasHotkey(wchar_t key) +{ + return NULL; +} + +#if defined( VGUI_USEDRAGDROP ) +static vgui::PHandle g_DragDropCapture; +#endif // VGUI_USEDRAGDROP + +void Panel::InternalCursorMoved(int x, int y) +{ +#if defined( VGUI_USEDRAGDROP ) + if ( g_DragDropCapture.Get() ) + { + bool started = g_DragDropCapture->GetDragDropInfo()->m_bDragStarted; + + g_DragDropCapture->OnContinueDragging(); + + if ( started ) + { + bool isEscapeKeyDown = input()->IsKeyDown( KEY_ESCAPE ); + if ( isEscapeKeyDown ) + { + g_DragDropCapture->OnFinishDragging( true, (MouseCode)-1, true ); + } + return; + } + } +#endif // VGUI_USEDRAGDROP + + if ( !ShouldHandleInputMessage() ) + return; + + if ( IsCursorNone() ) + return; + + if ( !IsMouseInputEnabled() ) + { + return; + } + + if (IsBuildGroupEnabled()) + { + if ( _buildGroup->CursorMoved(x, y, this) ) + { + return; + } + } + + if (m_pTooltips) + { + if ( _tooltipText ) + { + m_pTooltips->SetText( _tooltipText ); + } + m_pTooltips->ShowTooltip(this); + } + + ScreenToLocal(x, y); + + OnCursorMoved(x, y); +} + +void Panel::InternalCursorEntered() +{ + if (IsCursorNone() || !IsMouseInputEnabled()) + return; + + if (IsBuildGroupEnabled()) + return; + + if (m_pTooltips) + { + m_pTooltips->ResetDelay(); + + if ( _tooltipText ) + { + m_pTooltips->SetText( _tooltipText ); + } + m_pTooltips->ShowTooltip(this); + } + + OnCursorEntered(); +} + +void Panel::InternalCursorExited() +{ + if (IsCursorNone() || !IsMouseInputEnabled()) + return; + + if (IsBuildGroupEnabled()) + return; + + if (m_pTooltips) + { + m_pTooltips->HideTooltip(); + } + + OnCursorExited(); +} + +bool Panel::IsChildOfSurfaceModalPanel() +{ + VPANEL appModalPanel = input()->GetAppModalSurface(); + if ( !appModalPanel ) + return true; + + if ( ipanel()->HasParent( GetVPanel(), appModalPanel ) ) + return true; + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Panel::IsChildOfModalSubTree() +{ + VPANEL subTree = input()->GetModalSubTree(); + if ( !subTree ) + return true; + + if ( HasParent( subTree ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Checks to see if message is being subverted due to modal subtree logic +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +static bool ShouldHandleInputMessage( VPANEL p ) +{ + // If there is not modal subtree, then always handle the msg + if ( !input()->GetModalSubTree() ) + return true; + + // What state are we in? + bool bChildOfModal = false; + VPANEL subTree = input()->GetModalSubTree(); + if ( !subTree ) + { + bChildOfModal = true; + } + else if ( ipanel()->HasParent( p, subTree ) ) + { + bChildOfModal = true; + } + + if ( input()->ShouldModalSubTreeReceiveMessages() ) + return bChildOfModal; + + return !bChildOfModal; +} + +bool Panel::ShouldHandleInputMessage() +{ + return ::ShouldHandleInputMessage( GetVPanel() ); +} + +void Panel::InternalMousePressed(int code) +{ + long curtime = system()->GetTimeMillis(); + if ( IsTriplePressAllowed() ) + { + long elapsed = curtime - m_lLastDoublePressTime; + if ( elapsed < TRIPLE_PRESS_MSEC ) + { + InternalMouseTriplePressed( code ); + return; + } + } + + // The menu system passively watches for mouse released messages so it + // can clear any open menus if the release is somewhere other than on a menu + Menu::OnInternalMousePressed( this, (MouseCode)code ); + + if ( !ShouldHandleInputMessage() ) + return; + + if ( IsCursorNone() ) + return; + + if ( !IsMouseInputEnabled()) + { +#if defined( VGUI_USEDRAGDROP ) + DragDropStartDragging(); +#endif + return; + } + + if (IsBuildGroupEnabled()) + { + if ( _buildGroup->MousePressed((MouseCode)code, this) ) + { + return; + } + } + + Panel *pMouseHandler = m_hMouseEventHandler.Get(); + if ( pMouseHandler ) + { + pMouseHandler->OnMousePressed( (MouseCode)code ); + } + else + { + OnMousePressed( (MouseCode)code ); + } + +#if defined( VGUI_USEDRAGDROP ) + DragDropStartDragging(); +#endif +} + +void Panel::InternalMouseDoublePressed(int code) +{ + m_lLastDoublePressTime = system()->GetTimeMillis(); + + if ( !ShouldHandleInputMessage() ) + return; + + if ( IsCursorNone() ) + return; + + if ( !IsMouseInputEnabled()) + { + return; + } + + if (IsBuildGroupEnabled()) + { + if ( _buildGroup->MouseDoublePressed((MouseCode)code, this) ) + { + return; + } + } + + Panel *pMouseHandler = m_hMouseEventHandler.Get(); + if ( pMouseHandler ) + { + pMouseHandler->OnMouseDoublePressed( (MouseCode)code ); + } + else + { + OnMouseDoublePressed( (MouseCode)code ); + } +} + +#if defined( VGUI_USEDRAGDROP ) +void Panel::SetStartDragWhenMouseExitsPanel( bool state ) +{ + _flags.SetFlag( DRAG_REQUIRES_PANEL_EXIT, state ); +} + +bool Panel::IsStartDragWhenMouseExitsPanel() const +{ + return _flags.IsFlagSet( DRAG_REQUIRES_PANEL_EXIT ); +} +#endif // VGUI_USEDRAGDROP + +void Panel::SetTriplePressAllowed( bool state ) +{ + _flags.SetFlag( TRIPLE_PRESS_ALLOWED, state ); +} + +bool Panel::IsTriplePressAllowed() const +{ + return _flags.IsFlagSet( TRIPLE_PRESS_ALLOWED ); +} + +void Panel::InternalMouseTriplePressed( int code ) +{ + Assert( IsTriplePressAllowed() ); + m_lLastDoublePressTime = 0L; + + if ( !ShouldHandleInputMessage() ) + return; + + if ( IsCursorNone() ) + return; + + if ( !IsMouseInputEnabled()) + { +#if defined( VGUI_USEDRAGDROP ) + DragDropStartDragging(); +#endif + return; + } + + if (IsBuildGroupEnabled()) + { + return; + } + + OnMouseTriplePressed((MouseCode)code); +#if defined( VGUI_USEDRAGDROP ) + DragDropStartDragging(); +#endif +} + +void Panel::InternalMouseReleased(int code) +{ +#if defined( VGUI_USEDRAGDROP ) + if ( g_DragDropCapture.Get() ) + { + bool started = g_DragDropCapture->GetDragDropInfo()->m_bDragStarted; + g_DragDropCapture->OnFinishDragging( true, (MouseCode)code ); + if ( started ) + { + return; + } + } +#endif + + if ( !ShouldHandleInputMessage() ) + return; + + if ( IsCursorNone() ) + return; + + if ( !IsMouseInputEnabled()) + { + return; + } + + if (IsBuildGroupEnabled()) + { + if ( _buildGroup->MouseReleased((MouseCode)code, this) ) + { + return; + } + } + + OnMouseReleased((MouseCode)code); +} + +void Panel::InternalMouseWheeled(int delta) +{ + if (IsBuildGroupEnabled() || !IsMouseInputEnabled()) + { + return; + } + + if ( !ShouldHandleInputMessage() ) + return; + + OnMouseWheeled(delta); +} + +void Panel::InternalKeyCodePressed(int code) +{ + if ( !ShouldHandleInputMessage() ) + return; + + if (IsKeyBoardInputEnabled()) + { + OnKeyCodePressed((KeyCode)code); + } + else + { + CallParentFunction(new KeyValues("KeyCodePressed", "code", code)); + } +} + +#if defined( VGUI_USEKEYBINDINGMAPS ) +//----------------------------------------------------------------------------- +// Purpose: +// Input : *bindingName - +// keycode - +// modifiers - +//----------------------------------------------------------------------------- +void Panel::AddKeyBinding( char const *bindingName, int keycode, int modifiers ) +{ + PanelKeyBindingMap *map = LookupMapForBinding( bindingName ); + if ( !map ) + { + Assert( 0 ); + return; + } + + BoundKey_t kb; + kb.isbuiltin = false; + kb.bindingname = CopyString( bindingName ); + kb.keycode = keycode; + kb.modifiers = modifiers; + + map->boundkeys.AddToTail( kb ); +} + +KeyBindingMap_t *Panel::LookupBinding( char const *bindingName ) +{ + PanelKeyBindingMap *map = GetKBMap(); + while( map ) + { + int c = map->entries.Count(); + for( int i = 0; i < c ; ++i ) + { + KeyBindingMap_t *binding = &map->entries[ i ]; + if ( !Q_stricmp( binding->bindingname, bindingName ) ) + return binding; + } + + map = map->baseMap; + } + + return NULL; +} + +PanelKeyBindingMap *Panel::LookupMapForBinding( char const *bindingName ) +{ + PanelKeyBindingMap *map = GetKBMap(); + while( map ) + { + int c = map->entries.Count(); + for( int i = 0; i < c ; ++i ) + { + KeyBindingMap_t *binding = &map->entries[ i ]; + if ( !Q_stricmp( binding->bindingname, bindingName ) ) + return map; + } + + map = map->baseMap; + } + + return NULL; +} + +KeyBindingMap_t *Panel::LookupBindingByKeyCode( KeyCode code, int modifiers ) +{ + PanelKeyBindingMap *map = GetKBMap(); + while( map ) + { + int c = map->boundkeys.Count(); + for( int i = 0; i < c ; ++i ) + { + BoundKey_t *kb = &map->boundkeys[ i ]; + if ( kb->keycode == code && kb->modifiers == modifiers ) + { + KeyBindingMap_t *binding = LookupBinding( kb->bindingname ); + Assert( binding ); + if ( binding ) + { + return binding; + } + } + } + + map = map->baseMap; + } + + return NULL; +} + +BoundKey_t *Panel::LookupDefaultKey( char const *bindingName ) +{ + PanelKeyBindingMap *map = GetKBMap(); + while( map ) + { + int c = map->defaultkeys.Count(); + for( int i = 0; i < c ; ++i ) + { + BoundKey_t *kb = &map->defaultkeys[ i ]; + if ( !Q_stricmp( kb->bindingname, bindingName ) ) + { + return kb; + } + } + + map = map->baseMap; + } + return NULL; +} + +void Panel::LookupBoundKeys( char const *bindingName, CUtlVector< BoundKey_t * >& list ) +{ + PanelKeyBindingMap *map = GetKBMap(); + while( map ) + { + int c = map->boundkeys.Count(); + for( int i = 0; i < c ; ++i ) + { + BoundKey_t *kb = &map->boundkeys[ i ]; + if ( !Q_stricmp( kb->bindingname, bindingName ) ) + { + list.AddToTail( kb ); + } + } + + map = map->baseMap; + } +} + +void Panel::RevertKeyBindingsToDefault() +{ + PanelKeyBindingMap *map = GetKBMap(); + while( map ) + { + map->boundkeys.RemoveAll(); + map->boundkeys = map->defaultkeys; + + map = map->baseMap; + } +} + +void Panel::RemoveAllKeyBindings() +{ + PanelKeyBindingMap *map = GetKBMap(); + while( map ) + { + map->boundkeys.RemoveAll(); + map = map->baseMap; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void Panel::ReloadKeyBindings() +{ + RevertKeyBindingsToDefault(); + LoadKeyBindingsForOnePanel( GetKeyBindingsContext(), this ); +} + +#define MAKE_STRING( x ) #x +#define KEY_NAME( str, disp ) { KEY_##str, MAKE_STRING( KEY_##str ), disp } + +struct KeyNames_t +{ + KeyCode code; + char const *string; + char const *displaystring; +}; + +static KeyNames_t g_KeyNames[] = +{ +KEY_NAME( NONE, "None" ), +KEY_NAME( 0, "0" ), +KEY_NAME( 1, "1" ), +KEY_NAME( 2, "2" ), +KEY_NAME( 3, "3" ), +KEY_NAME( 4, "4" ), +KEY_NAME( 5, "5" ), +KEY_NAME( 6, "6" ), +KEY_NAME( 7, "7" ), +KEY_NAME( 8, "8" ), +KEY_NAME( 9, "9" ), +KEY_NAME( A, "A" ), +KEY_NAME( B, "B" ), +KEY_NAME( C, "C" ), +KEY_NAME( D, "D" ), +KEY_NAME( E, "E" ), +KEY_NAME( F, "F" ), +KEY_NAME( G, "G" ), +KEY_NAME( H, "H" ), +KEY_NAME( I, "I" ), +KEY_NAME( J, "J" ), +KEY_NAME( K, "K" ), +KEY_NAME( L, "L" ), +KEY_NAME( M, "M" ), +KEY_NAME( N, "N" ), +KEY_NAME( O, "O" ), +KEY_NAME( P, "P" ), +KEY_NAME( Q, "Q" ), +KEY_NAME( R, "R" ), +KEY_NAME( S, "S" ), +KEY_NAME( T, "T" ), +KEY_NAME( U, "U" ), +KEY_NAME( V, "V" ), +KEY_NAME( W, "W" ), +KEY_NAME( X, "X" ), +KEY_NAME( Y, "Y" ), +KEY_NAME( Z, "Z" ), +KEY_NAME( PAD_0, "Key Pad 0" ), +KEY_NAME( PAD_1, "Key Pad 1" ), +KEY_NAME( PAD_2, "Key Pad 2" ), +KEY_NAME( PAD_3, "Key Pad 3" ), +KEY_NAME( PAD_4, "Key Pad 4" ), +KEY_NAME( PAD_5, "Key Pad 5" ), +KEY_NAME( PAD_6, "Key Pad 6" ), +KEY_NAME( PAD_7, "Key Pad 7" ), +KEY_NAME( PAD_8, "Key Pad 8" ), +KEY_NAME( PAD_9, "Key Pad 9" ), +KEY_NAME( PAD_DIVIDE, "Key Pad /" ), +KEY_NAME( PAD_MULTIPLY, "Key Pad *" ), +KEY_NAME( PAD_MINUS, "Key Pad -" ), +KEY_NAME( PAD_PLUS, "Key Pad +" ), +KEY_NAME( PAD_ENTER, "Key Pad Enter" ), +KEY_NAME( PAD_DECIMAL, "Key Pad ." ), +KEY_NAME( LBRACKET, "[" ), +KEY_NAME( RBRACKET, "]" ), +KEY_NAME( SEMICOLON, "," ), +KEY_NAME( APOSTROPHE, "'" ), +KEY_NAME( BACKQUOTE, "`" ), +KEY_NAME( COMMA, "," ), +KEY_NAME( PERIOD, "." ), +KEY_NAME( SLASH, "/" ), +KEY_NAME( BACKSLASH, "\\" ), +KEY_NAME( MINUS, "-" ), +KEY_NAME( EQUAL, "=" ), +KEY_NAME( ENTER, "Enter" ), +KEY_NAME( SPACE, "Space" ), +KEY_NAME( BACKSPACE, "Backspace" ), +KEY_NAME( TAB, "Tab" ), +KEY_NAME( CAPSLOCK, "Caps Lock" ), +KEY_NAME( NUMLOCK, "Num Lock" ), +KEY_NAME( ESCAPE, "Escape" ), +KEY_NAME( SCROLLLOCK, "Scroll Lock" ), +KEY_NAME( INSERT, "Ins" ), +KEY_NAME( DELETE, "Del" ), +KEY_NAME( HOME, "Home" ), +KEY_NAME( END, "End" ), +KEY_NAME( PAGEUP, "PgUp" ), +KEY_NAME( PAGEDOWN, "PgDn" ), +KEY_NAME( BREAK, "Break" ), +KEY_NAME( LSHIFT, "Shift" ), +KEY_NAME( RSHIFT, "Shift" ), +KEY_NAME( LALT, "Alt" ), +KEY_NAME( RALT, "Alt" ), +KEY_NAME( LCONTROL, "Ctrl" ), +KEY_NAME( RCONTROL, "Ctrl" ), +KEY_NAME( LWIN, "Windows" ), +KEY_NAME( RWIN, "Windows" ), +KEY_NAME( APP, "App" ), +KEY_NAME( UP, "Up" ), +KEY_NAME( LEFT, "Left" ), +KEY_NAME( DOWN, "Down" ), +KEY_NAME( RIGHT, "Right" ), +KEY_NAME( F1, "F1" ), +KEY_NAME( F2, "F2" ), +KEY_NAME( F3, "F3" ), +KEY_NAME( F4, "F4" ), +KEY_NAME( F5, "F5" ), +KEY_NAME( F6, "F6" ), +KEY_NAME( F7, "F7" ), +KEY_NAME( F8, "F8" ), +KEY_NAME( F9, "F9" ), +KEY_NAME( F10, "F10" ), +KEY_NAME( F11, "F11" ), +KEY_NAME( F12, "F12" ), +KEY_NAME( CAPSLOCKTOGGLE, "Caps Lock Toggle" ), +KEY_NAME( NUMLOCKTOGGLE, "Num Lock Toggle" ), +KEY_NAME( SCROLLLOCKTOGGLE, "Scroll Lock Toggle" ), +}; + +char const *Panel::KeyCodeToString( KeyCode code ) +{ + int c = ARRAYSIZE( g_KeyNames ); + for ( int i = 0; i < c ; ++i ) + { + if ( g_KeyNames[ i ].code == code ) + return g_KeyNames[ i ].string; + } + + return ""; +} + +wchar_t const *Panel::KeyCodeToDisplayString( KeyCode code ) +{ + int c = ARRAYSIZE( g_KeyNames ); + for ( int i = 0; i < c ; ++i ) + { + if ( g_KeyNames[ i ].code == code ) + { + char const *str = g_KeyNames[ i ].displaystring; + wchar_t *wstr = g_pVGuiLocalize->Find( str ); + if ( wstr ) + { + return wstr; + } + + static wchar_t buf[ 64 ]; + g_pVGuiLocalize->ConvertANSIToUnicode( str, buf, sizeof( buf ) ); + return buf; + } + } + + return L""; +} + +static void AddModifierToString( char const *modifiername, char *buf, size_t bufsize ) +{ + char add[ 32 ]; + if ( Q_strlen( buf ) > 0 ) + { + Q_snprintf( add, sizeof( add ), "+%s", modifiername ); + } + else + { + Q_strncpy( add, modifiername, sizeof( add ) ); + } + + Q_strncat( buf, add, bufsize, COPY_ALL_CHARACTERS ); + +} + +wchar_t const *Panel::KeyCodeModifiersToDisplayString( KeyCode code, int modifiers ) +{ + char sz[ 256 ]; + sz[ 0 ] = 0; + + if ( modifiers & MODIFIER_SHIFT ) + { + AddModifierToString( "Shift", sz, sizeof( sz ) ); + } + if ( modifiers & MODIFIER_CONTROL ) + { + AddModifierToString( "Ctrl", sz, sizeof( sz ) ); + } + if ( modifiers & MODIFIER_ALT ) + { + AddModifierToString( "Alt", sz, sizeof( sz ) ); + } + + if ( Q_strlen( sz ) > 0 ) + { + Q_strncat( sz, "+", sizeof( sz ), COPY_ALL_CHARACTERS ); + } + + static wchar_t unicode[ 256 ]; + V_swprintf_safe( unicode, L"%S%s", sz, Panel::KeyCodeToDisplayString( (KeyCode)code ) ); + return unicode; +} + +KeyCode Panel::StringToKeyCode( char const *str ) +{ + int c = ARRAYSIZE( g_KeyNames ); + for ( int i = 0; i < c ; ++i ) + { + if ( !Q_stricmp( str, g_KeyNames[ i ].string ) ) + return g_KeyNames[ i ].code; + } + + return KEY_NONE; +} + +static void WriteKeyBindingToBuffer( CUtlBuffer& buf, int level, const BoundKey_t& binding ) +{ + BufPrint( buf, level, "\"keycode\"\t\"%s\"\n", Panel::KeyCodeToString( (KeyCode)binding.keycode ) ); + if ( binding.modifiers & MODIFIER_SHIFT ) + { + BufPrint( buf, level, "\"shift\"\t\"1\"\n" ); + } + if ( binding.modifiers & MODIFIER_CONTROL ) + { + BufPrint( buf, level, "\"ctrl\"\t\"1\"\n" ); + } + if ( binding.modifiers & MODIFIER_ALT ) + { + BufPrint( buf, level, "\"alt\"\t\"1\"\n" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *filename - +// *pathID - +//----------------------------------------------------------------------------- +void Panel::SaveKeyBindingsToBuffer( int level, CUtlBuffer& buf ) +{ + Assert( IsValidKeyBindingsContext() ); + + Assert( buf.IsText() ); + + PanelKeyBindingMap *map = GetKBMap(); + while( map ) + { + int c = map->boundkeys.Count(); + for( int i = 0; i < c ; ++i ) + { + const BoundKey_t& binding = map->boundkeys[ i ]; + + // Spew to file + BufPrint( buf, level, "\"%s\"\n", binding.bindingname ); + BufPrint( buf, level, "{\n" ); + + WriteKeyBindingToBuffer( buf, level + 1, binding ); + + BufPrint( buf, level, "}\n" ); + } + + map = map->baseMap; + } +} + +bool Panel::ParseKeyBindings( KeyValues *kv ) +{ + Assert( IsValidKeyBindingsContext() ); + if ( !IsValidKeyBindingsContext() ) + return false; + + // To have KB the panel must have a name + Assert( GetName() && GetName()[ 0 ] ); + if ( !GetName() || !GetName()[ 0 ] ) + return false; + + bool success = false; + + g_KBMgr.AddPanelToContext( GetKeyBindingsContext(), this ); + + RemoveAllKeyBindings(); + + // Walk through bindings + for ( KeyValues *binding = kv->GetFirstSubKey(); binding != NULL; binding = binding->GetNextKey() ) + { + char const *bindingName = binding->GetName(); + if ( !bindingName || !bindingName[ 0 ] ) + continue; + + KeyBindingMap_t *b = LookupBinding( bindingName ); + if ( b ) + { + success = true; + const char *keycode = binding->GetString( "keycode", "" ); + int modifiers = 0; + if ( binding->GetInt( "shift", 0 ) != 0 ) + { + modifiers |= MODIFIER_SHIFT; + } + if ( binding->GetInt( "ctrl", 0 ) != 0 ) + { + modifiers |= MODIFIER_CONTROL; + } + if ( binding->GetInt( "alt", 0 ) != 0 ) + { + modifiers |= MODIFIER_ALT; + } + + KeyBindingMap_t *bound = LookupBindingByKeyCode( StringToKeyCode( keycode ), modifiers ); + if ( !bound ) + { + AddKeyBinding( bindingName, StringToKeyCode( keycode ), modifiers ); + } + } + else + { + Warning( "KeyBinding for panel '%s' contained unknown binding '%s'\n", GetName() ? GetName() : "???", bindingName ); + } + } + + // Now for each binding which is currently "unbound" to any key, use the default binding + PanelKeyBindingMap *map = GetKBMap(); + while( map ) + { + int c = map->entries.Count(); + for( int i = 0; i < c ; ++i ) + { + KeyBindingMap_t *binding = &map->entries[ i ]; + + // See if there is a bound key + CUtlVector< BoundKey_t * > list; + LookupBoundKeys( binding->bindingname, list ); + if ( list.Count() == 0 ) + { + // Assign the default binding to this key + BoundKey_t *defaultKey = LookupDefaultKey( binding->bindingname ); + if ( defaultKey ) + { + KeyBindingMap_t *alreadyBound = LookupBindingByKeyCode( (KeyCode)defaultKey->keycode, defaultKey->modifiers ); + if ( alreadyBound ) + { + Warning( "No binding for '%s', defautl key already bound to '%s'\n", binding->bindingname, alreadyBound->bindingname ); + } + else + { + AddKeyBinding( defaultKey->bindingname, defaultKey->keycode, defaultKey->modifiers ); + } + } + } + } + + map = map->baseMap; + } + + return success; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : handle - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +void Panel::SetKeyBindingsContext( KeyBindingContextHandle_t handle ) +{ + Assert( !IsValidKeyBindingsContext() || handle == GetKeyBindingsContext() ); + g_KBMgr.AddPanelToContext( handle, this ); + m_hKeyBindingsContext = handle; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : KeyBindingContextHandle_t +//----------------------------------------------------------------------------- +KeyBindingContextHandle_t Panel::GetKeyBindingsContext() const +{ + return m_hKeyBindingsContext; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Panel::IsValidKeyBindingsContext() const +{ + return GetKeyBindingsContext() != INVALID_KEYBINDINGCONTEXT_HANDLE; +} + +char const *Panel::GetKeyBindingsFile() const +{ + Assert( IsValidKeyBindingsContext() ); + return g_KBMgr.GetKeyBindingsFile( GetKeyBindingsContext() ); +} + +char const *Panel::GetKeyBindingsFilePathID() const +{ + Assert( IsValidKeyBindingsContext() ); + return g_KBMgr.GetKeyBindingsFilePathID( GetKeyBindingsContext() ); +} + +void Panel::EditKeyBindings() +{ + Assert( 0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Set this to false to disallow IsKeyRebound chaining to GetParent() Panels... +// Input : state - +//----------------------------------------------------------------------------- +void Panel::SetAllowKeyBindingChainToParent( bool state ) +{ + _flags.SetFlag( ALLOW_CHAIN_KEYBINDING_TO_PARENT, state ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Panel::IsKeyBindingChainToParentAllowed() const +{ + return _flags.IsFlagSet( ALLOW_CHAIN_KEYBINDING_TO_PARENT ); +} + +bool Panel::IsKeyOverridden( KeyCode code, int modifiers ) +{ + // By default assume all keys should pass through binding system + return false; +} + +bool Panel::IsKeyRebound( KeyCode code, int modifiers ) +{ + if ( IsKeyBoardInputEnabled() ) + { + KeyBindingMap_t* binding = LookupBindingByKeyCode( code, modifiers ); + // Only dispatch if we're part of the current modal subtree + if ( binding && IsChildOfSurfaceModalPanel() ) + { + // Found match, post message to panel + if ( binding->func ) + { + // dispatch the func + (this->*binding->func)(); + } + else + { + Assert( 0 ); + } + + if ( !binding->passive ) + { + // Exit this function... + return true; + } + } + } + + // Chain to parent + Panel* pParent = GetParent(); + if ( IsKeyBindingChainToParentAllowed() && pParent && !IsKeyOverridden( code, modifiers ) ) + return pParent->IsKeyRebound( code, modifiers ); + + // No suitable binding found + return false; +} + +static bool s_bSuppressRebindChecks = false; +#endif // VGUI_USEKEYBINDINGMAPS + +void Panel::InternalKeyCodeTyped( int code ) +{ + if ( !ShouldHandleInputMessage() ) + { + input()->OnKeyCodeUnhandled( code ); + return; + } + + if (IsKeyBoardInputEnabled()) + { + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + + int modifiers = 0; + if ( shift ) + { + modifiers |= MODIFIER_SHIFT; + } + if ( ctrl ) + { + modifiers |= MODIFIER_CONTROL; + } + if ( alt ) + { + modifiers |= MODIFIER_ALT; + } + + // Things in build mode don't have accelerators + if (IsBuildGroupEnabled()) + { + _buildGroup->KeyCodeTyped((KeyCode)code, this); + return; + } + + if ( !s_bSuppressRebindChecks && IsKeyRebound( (KeyCode)code, modifiers ) ) + { + return; + } + + bool oldVal = s_bSuppressRebindChecks; + s_bSuppressRebindChecks = true; + OnKeyCodeTyped((KeyCode)code); + s_bSuppressRebindChecks = oldVal; + } + else + { + if ( GetVPanel() == surface()->GetEmbeddedPanel() ) + { + input()->OnKeyCodeUnhandled( code ); + } + CallParentFunction(new KeyValues("KeyCodeTyped", "code", code)); + } +} + +void Panel::InternalKeyTyped(int unichar) +{ + if ( !ShouldHandleInputMessage() ) + return; + + if (IsKeyBoardInputEnabled()) + { + if ( IsBuildGroupEnabled() ) + { + if ( _buildGroup->KeyTyped( (wchar_t)unichar, this ) ) + { + return; + } + } + + OnKeyTyped((wchar_t)unichar); + } + else + { + CallParentFunction(new KeyValues("KeyTyped", "unichar", unichar)); + } +} + +void Panel::InternalKeyCodeReleased(int code) +{ + if ( !ShouldHandleInputMessage() ) + return; + + if (IsKeyBoardInputEnabled()) + { + if (IsBuildGroupEnabled()) + { + if ( _buildGroup->KeyCodeReleased((KeyCode)code, this) ) + { + return; + } + } + + OnKeyCodeReleased((KeyCode)code); + } + else + { + CallParentFunction(new KeyValues("KeyCodeReleased", "code", code)); + } +} + +void Panel::InternalKeyFocusTicked() +{ + if (IsBuildGroupEnabled()) + return; + + OnKeyFocusTicked(); +} + +void Panel::InternalMouseFocusTicked() +{ + if (IsBuildGroupEnabled()) + { + // must repaint so the numbers will be accurate + if (_buildGroup->HasRulersOn()) + { + PaintTraverse(true); + } + return; + } + + // update cursor + InternalSetCursor(); + OnMouseFocusTicked(); +} + + +void Panel::InternalSetCursor() +{ + bool visible = IsVisible(); + + if (visible) + { +#if defined( VGUI_USEDRAGDROP ) + // Drag drop is overriding cursor? + if ( m_pDragDrop->m_bDragging || + g_DragDropCapture.Get() != NULL ) + return; +#endif + // chain up and make sure all our parents are also visible + VPANEL p = GetVParent(); + while (p) + { + visible &= ipanel()->IsVisible(p); + p = ipanel()->GetParent(p); + } + + // only change the cursor if this panel is visible, and if its part of the main VGUI tree + if (visible && HasParent(surface()->GetEmbeddedPanel())) + { + HCursor cursor = GetCursor(); + + if (IsBuildGroupEnabled()) + { + cursor = _buildGroup->GetCursor(this); + } + + if (input()->GetCursorOveride()) + { + cursor = input()->GetCursorOveride(); + } + + surface()->SetCursor(cursor); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called every frame the panel is visible, designed to be overridden +//----------------------------------------------------------------------------- +void Panel::OnThink() +{ +#if defined( VGUI_USEDRAGDROP ) + if ( IsPC() && + m_pDragDrop->m_bDragEnabled && + m_pDragDrop->m_bDragging && + m_pDragDrop->m_bDragStarted ) + { + bool isEscapeKeyDown = input()->IsKeyDown( KEY_ESCAPE ); + if ( isEscapeKeyDown ) + { + OnContinueDragging(); + OnFinishDragging( true, (MouseCode)-1, true ); + return; + } + + if ( m_pDragDrop->m_hCurrentDrop != 0 ) + { + if ( !input()->IsMouseDown( MOUSE_LEFT ) ) + { + OnContinueDragging(); + OnFinishDragging( true, (MouseCode)-1 ); + return; + } + + // allow the cursor to change based upon things like changing keystate, etc. + surface()->SetCursor( m_pDragDrop->m_hCurrentDrop->GetDropCursor( m_pDragDrop->m_DragData ) ); + + if ( !m_pDragDrop->m_bDropMenuShown ) + { + // See if the hover time has gotten larger + float hoverSeconds = ( system()->GetTimeMillis() - m_pDragDrop->m_lDropHoverTime ) * 0.001f; + DragDrop_t *dropInfo = m_pDragDrop->m_hCurrentDrop->GetDragDropInfo(); + + if ( dropInfo->m_flHoverContextTime != 0.0f ) + { + if ( hoverSeconds >= dropInfo->m_flHoverContextTime ) + { + m_pDragDrop->m_bDropMenuShown = true; + + CUtlVector< KeyValues * > data; + + GetDragData( data ); + + int x, y; + input()->GetCursorPos( x, y ); + + if ( m_pDragDrop->m_hDropContextMenu.Get() ) + { + delete m_pDragDrop->m_hDropContextMenu.Get(); + } + + Menu *menu = new Menu( m_pDragDrop->m_hCurrentDrop.Get(), "DropContext" ); + + bool useMenu = m_pDragDrop->m_hCurrentDrop->GetDropContextMenu( menu, data ); + if ( useMenu ) + { + m_pDragDrop->m_hDropContextMenu = menu; + + menu->SetPos( x, y ); + menu->SetVisible( true ); + menu->MakePopup(); + surface()->MovePopupToFront( menu->GetVPanel() ); + if ( menu->GetItemCount() > 0 ) + { + int id = menu->GetMenuID( 0 ); + menu->SetCurrentlyHighlightedItem( id ); + MenuItem *item = menu->GetMenuItem( id ); + item->SetArmed( true ); + } + } + else + { + delete menu; + } + + m_pDragDrop->m_hCurrentDrop->OnDropContextHoverShow( data ); + } + } + } + } + } +#endif +} + +// input messages handlers (designed for override) +void Panel::OnCursorMoved(int x, int y) +{ + if( ParentNeedsCursorMoveEvents() ) + { + // figure out x and y in parent space + int thisX, thisY; + ipanel()->GetPos( GetVPanel(), thisX, thisY ); + CallParentFunction( new KeyValues( "OnCursorMoved", "x", x + thisX, "y", y + thisY ) ); + } +} + +void Panel::OnCursorEntered() +{ +} + +void Panel::OnCursorExited() +{ +} + +void Panel::OnMousePressed(MouseCode code) +{ +} + +void Panel::OnMouseDoublePressed(MouseCode code) +{ +} + +void Panel::OnMouseTriplePressed(MouseCode code) +{ +} + +void Panel::OnMouseReleased(MouseCode code) +{ +} + +void Panel::OnMouseWheeled(int delta) +{ + CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); +} + +// base implementation forwards Key messages to the Panel's parent - override to 'swallow' the input +void Panel::OnKeyCodePressed(KeyCode code) +{ + static ConVarRef vgui_nav_lock( "vgui_nav_lock" ); + + bool handled = false; + switch( GetBaseButtonCode( code ) ) + { + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case KEY_XSTICK2_UP: + case KEY_UP: + if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateUp() ) + { + vgui_nav_lock.SetValue( 1 ); + vgui::surface()->PlaySound( "UI/menu_focus.wav" ); + handled = true; + } + break; + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case KEY_XSTICK2_DOWN: + case KEY_DOWN: + if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateDown() ) + { + vgui_nav_lock.SetValue( 1 ); + vgui::surface()->PlaySound( "UI/menu_focus.wav" ); + handled = true; + } + break; + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case KEY_XSTICK2_LEFT: + case KEY_LEFT: + if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateLeft() ) + { + vgui_nav_lock.SetValue( 1 ); + vgui::surface()->PlaySound( "UI/menu_focus.wav" ); + handled = true; + } + break; + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + case KEY_XSTICK2_RIGHT: + case KEY_RIGHT: + if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateRight() ) + { + vgui_nav_lock.SetValue( 1 ); + vgui::surface()->PlaySound( "UI/menu_focus.wav" ); + handled = true; + } + break; + case KEY_XBUTTON_B: + if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateBack() ) + { + vgui_nav_lock.SetValue( 1 ); + vgui::surface()->PlaySound( "UI/menu_focus.wav" ); + handled = true; + } + break; + } + + if( !handled && !m_PassUnhandledInput ) + return; + + CallParentFunction(new KeyValues("KeyCodePressed", "code", code)); +} + +void Panel::OnKeyCodeTyped(KeyCode keycode) +{ + vgui::KeyCode code = GetBaseButtonCode( keycode ); + + // handle focus change + if ( IsX360() || IsConsoleStylePanel() ) + { + // eat these typed codes, will get handled in OnKeyCodePressed + switch ( code ) + { + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case KEY_XSTICK2_UP: + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case KEY_XSTICK2_DOWN: + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case KEY_XSTICK2_LEFT: + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + case KEY_XSTICK2_RIGHT: + case KEY_XBUTTON_A: + case KEY_XBUTTON_B: + case KEY_XBUTTON_X: + case KEY_XBUTTON_Y: + case KEY_XBUTTON_LEFT_SHOULDER: + case KEY_XBUTTON_RIGHT_SHOULDER: + case KEY_XBUTTON_BACK: + case KEY_XBUTTON_START: + case KEY_XBUTTON_STICK1: + case KEY_XBUTTON_STICK2: + case KEY_XBUTTON_LTRIGGER: + case KEY_XBUTTON_RTRIGGER: + + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + return; + } + + // legacy handling - need to re-enable for older apps? + /* + if ( code == KEY_XSTICK1_RIGHT || code == KEY_XBUTTON_RIGHT ) + { + RequestFocusNext(); + return; + } + else if ( code == KEY_XSTICK1_LEFT || code == KEY_XBUTTON_LEFT ) + { + RequestFocusPrev(); + return; + } + */ + } + + if (code == KEY_TAB) + { + bool bShiftDown = input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT); + + if ( IsConsoleStylePanel() ) + { + if ( bShiftDown ) + { + NavigateUp(); + } + else + { + NavigateDown(); + } + } + else + { + // if shift is down goto previous tab position, otherwise goto next + if ( bShiftDown ) + { + RequestFocusPrev(); + } + else + { + RequestFocusNext(); + } + } + } + else + { + // forward up + if ( GetVPanel() == surface()->GetEmbeddedPanel() ) + { + input()->OnKeyCodeUnhandled( keycode ); + } + CallParentFunction(new KeyValues("KeyCodeTyped", "code", keycode)); + } +} + +void Panel::OnKeyTyped(wchar_t unichar) +{ + CallParentFunction(new KeyValues("KeyTyped", "unichar", unichar)); +} + +void Panel::OnKeyCodeReleased(KeyCode code) +{ + CallParentFunction(new KeyValues("KeyCodeReleased", "code", code)); +} + +void Panel::OnKeyFocusTicked() +{ + CallParentFunction(new KeyValues("KeyFocusTicked")); +} + +void Panel::OnMouseFocusTicked() +{ + CallParentFunction(new KeyValues("OnMouseFocusTicked")); +} + +bool Panel::IsWithin(int x,int y) +{ + // check against our clip rect + int clipRect[4]; + ipanel()->GetClipRect(GetVPanel(), clipRect[0], clipRect[1], clipRect[2], clipRect[3]); + + if (x < clipRect[0]) + { + return false; + } + + if (y < clipRect[1]) + { + return false; + } + + if (x >= clipRect[2]) + { + return false; + } + + if (y >= clipRect[3]) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: determines which is the topmost panel under the coordinates (x, y) +//----------------------------------------------------------------------------- +VPANEL Panel::IsWithinTraverse(int x, int y, bool traversePopups) +{ + // if this one is not visible, its children won't be either + // also if it doesn't want mouse input its children can't get it either + if (!IsVisible() || !IsMouseInputEnabled()) + return NULL; + + if (traversePopups) + { + // check popups first + int i; + CUtlVector< VPANEL > &children = ipanel()->GetChildren( GetVPanel() ); + int childCount = children.Count(); + for (i = childCount - 1; i >= 0; i--) + { + VPANEL panel = children[ i ]; + if (ipanel()->IsPopup(panel)) + { + panel = ipanel()->IsWithinTraverse(panel, x, y, true); + if (panel != null) + { + return panel; + } + } + } + + // check children recursive, if you find one, just return first one + // this checks in backwards order so the last child drawn for this panel is chosen which + // coincides to how it would be visibly displayed + for (i = childCount - 1; i >= 0; i--) + { + VPANEL panel = children[ i ]; + // we've already checked popups so ignore + if (!ipanel()->IsPopup(panel)) + { + panel = ipanel()->IsWithinTraverse(panel, x, y, true); + if (panel != 0) + { + return panel; + } + } + } + + // check ourself + if ( !IsMouseInputDisabledForThisPanel() && IsWithin(x, y) ) + { + return GetVPanel(); + } + } + else + { + // since we're not checking popups, it must be within us, so we can check ourself first + if (IsWithin(x, y)) + { + // check children recursive, if you find one, just return first one + // this checks in backwards order so the last child drawn for this panel is chosen which + // coincides to how it would be visibly displayed + CUtlVector< VPANEL > &children = ipanel()->GetChildren( GetVPanel() ); + int childCount = children.Count(); + for (int i = childCount - 1; i >= 0; i--) + { + VPANEL panel = children[ i ]; + // ignore popups + if (!ipanel()->IsPopup(panel)) + { + panel = ipanel()->IsWithinTraverse(panel, x, y, false); + if (panel != 0) + { + return panel; + } + } + } + + // not a child, must be us + if ( !IsMouseInputDisabledForThisPanel() ) + return GetVPanel(); + } + } + + return NULL; +} + +void Panel::LocalToScreen(int& x,int& y) +{ + int px, py; + ipanel()->GetAbsPos(GetVPanel(), px, py); + + x = x + px; + y = y + py; +} + +void Panel::ScreenToLocal(int& x,int& y) +{ + int px, py; + ipanel()->GetAbsPos(GetVPanel(), px, py); + + x = x - px; + y = y - py; +} + +void Panel::ParentLocalToScreen(int &x, int &y) +{ + int px, py; + ipanel()->GetAbsPos(GetVParent(), px, py); + + x = x + px; + y = y + py; +} + +void Panel::MakePopup(bool showTaskbarIcon,bool disabled) +{ + surface()->CreatePopup(GetVPanel(), false, showTaskbarIcon,disabled); +} + +void Panel::SetCursor(HCursor cursor) +{ + _cursor = cursor; +} + +HCursor Panel::GetCursor() +{ + return _cursor; +} + +void Panel::SetCursorAlwaysVisible( bool visible ) +{ + surface()->SetCursorAlwaysVisible( visible ); +} + +void Panel::SetMinimumSize(int wide,int tall) +{ + ipanel()->SetMinimumSize(GetVPanel(), wide, tall); +} + +void Panel::GetMinimumSize(int& wide,int &tall) +{ + ipanel()->GetMinimumSize(GetVPanel(), wide, tall); +} + +bool Panel::IsBuildModeEditable() +{ + return true; +} + +void Panel::SetBuildModeEditable(bool state) +{ + if (state) + { + _buildModeFlags |= BUILDMODE_EDITABLE; + } + else + { + _buildModeFlags &= ~BUILDMODE_EDITABLE; + } +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool Panel::IsBuildModeDeletable() +{ + return (_buildModeFlags & BUILDMODE_DELETABLE); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void Panel::SetBuildModeDeletable(bool state) +{ + if (state) + { + _buildModeFlags |= BUILDMODE_DELETABLE; + } + else + { + _buildModeFlags &= ~BUILDMODE_DELETABLE; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool Panel::IsBuildModeActive() +{ + return _buildGroup ? _buildGroup->IsEnabled() : false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::GetClipRect(int& x0,int& y0,int& x1,int& y1) +{ + ipanel()->GetClipRect(GetVPanel(), x0, y0, x1, y1); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int Panel::GetChildCount() +{ + if (ipanel()) + { + return ipanel()->GetChildCount(GetVPanel()); + } + + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: returns a child by the specified index +//----------------------------------------------------------------------------- +Panel *Panel::GetChild(int index) +{ + // get the child and cast it to a panel + // this assumes that the child is from the same module as the this (precondition) + return ipanel()->GetPanel(ipanel()->GetChild(GetVPanel(), index), GetControlsModuleName()); +} + +CUtlVector< VPANEL > &Panel::GetChildren() +{ + return ipanel()->GetChildren(GetVPanel()); +} + +//----------------------------------------------------------------------------- +// Purpose: moves the key focus back +//----------------------------------------------------------------------------- +bool Panel::RequestFocusPrev(VPANEL panel) +{ + // chain to parent + if (GetVParent()) + { + return ipanel()->RequestFocusPrev(GetVParent(), GetVPanel()); + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool Panel::RequestFocusNext(VPANEL panel) +{ + // chain to parent + if (GetVParent()) + { + return ipanel()->RequestFocusNext(GetVParent(), GetVPanel()); + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the panel to have the current sub focus +// Input : direction - the direction in which focus travelled to arrive at this panel; forward = 1, back = -1 +//----------------------------------------------------------------------------- +void Panel::RequestFocus(int direction) +{ + // NOTE: This doesn't make any sense if we don't have keyboard input enabled + Assert( ( IsX360() || IsConsoleStylePanel() ) || IsKeyBoardInputEnabled() ); + // ivgui()->DPrintf2("RequestFocus(%s, %s)\n", GetName(), GetClassName()); + OnRequestFocus(GetVPanel(), NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: Called after a panel requests focus to fix up the whole chain +//----------------------------------------------------------------------------- +void Panel::OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel) +{ + CallParentFunction(new KeyValues("OnRequestFocus", "subFocus", subFocus, "defaultPanel", defaultPanel)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPANEL Panel::GetCurrentKeyFocus() +{ + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the panel has focus +//----------------------------------------------------------------------------- +bool Panel::HasFocus() +{ + if (input()->GetFocus() == GetVPanel()) + { + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetTabPosition(int position) +{ + _tabPosition = position; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int Panel::GetTabPosition() +{ + return _tabPosition; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::InternalFocusChanged(bool lost) +{ + /* + //if focus is gained tell the focusNavGroup about it so its current can be correct + if( (!lost) && (_focusNavGroup!=null) ) + { + _focusNavGroup->setCurrentPanel(this); + } + */ +} + +//----------------------------------------------------------------------------- +// Purpose: Called when a panel loses it's mouse capture +//----------------------------------------------------------------------------- +void Panel::OnMouseCaptureLost() +{ + if (m_pTooltips) + { + m_pTooltips->ResetDelay(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::AddActionSignalTarget(Panel *messageTarget) +{ + HPanel target = ivgui()->PanelToHandle(messageTarget->GetVPanel()); + if (!_actionSignalTargetDar.HasElement(target)) + { + _actionSignalTargetDar.AddElement(target); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::AddActionSignalTarget(VPANEL messageTarget) +{ + HPanel target = ivgui()->PanelToHandle(messageTarget); + if (!_actionSignalTargetDar.HasElement(target)) + { + _actionSignalTargetDar.AddElement(target); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::RemoveActionSignalTarget(Panel *oldTarget) +{ + _actionSignalTargetDar.RemoveElement(ivgui()->PanelToHandle(oldTarget->GetVPanel())); +} + +//----------------------------------------------------------------------------- +// Purpose: Sends a message to all the panels that have requested action signals +//----------------------------------------------------------------------------- +void Panel::PostActionSignal( KeyValues *message ) +{ + if ( m_bIsSilent != true ) + { + // add who it was from the message + message->SetPtr("panel", this); + int i; + for (i = _actionSignalTargetDar.GetCount() - 1; i > 0; i--) + { + VPANEL panel = ivgui()->HandleToPanel(_actionSignalTargetDar[i]); + if (panel) + { + ivgui()->PostMessage(panel, message->MakeCopy(), GetVPanel()); + } + } + + // do this so we can save on one MakeCopy() call + if (i == 0) + { + VPANEL panel = ivgui()->HandleToPanel(_actionSignalTargetDar[i]); + if (panel) + { + ivgui()->PostMessage(panel, message, GetVPanel()); + return; + } + } + } + message->deleteThis(); +} + +void Panel::SetBorder(IBorder *border) +{ + _border = border; + + if (border) + { + int x, y, x2, y2; + border->GetInset(x, y, x2, y2); + ipanel()->SetInset(GetVPanel(), x, y, x2, y2); + + // update our background type based on the bord + SetPaintBackgroundType(border->GetBackgroundType()); + } + else + { + ipanel()->SetInset(GetVPanel(), 0, 0, 0, 0); + } +} + +IBorder *Panel::GetBorder() +{ + return _border; +} + + +void Panel::SetPaintBorderEnabled(bool state) +{ + _flags.SetFlag( PAINT_BORDER_ENABLED, state ); +} + +void Panel::SetPaintBackgroundEnabled(bool state) +{ + _flags.SetFlag( PAINT_BACKGROUND_ENABLED, state ); +} + +void Panel::SetPaintBackgroundType( int type ) +{ + // HACK only 0 through 2 supported for now + m_nPaintBackgroundType = clamp( type, 0, 2 ); +} + +void Panel::SetPaintEnabled(bool state) +{ + _flags.SetFlag( PAINT_ENABLED, state ); +} + +void Panel::SetPostChildPaintEnabled(bool state) +{ + _flags.SetFlag( POST_CHILD_PAINT_ENABLED, state ); +} + +void Panel::GetInset(int& left,int& top,int& right,int& bottom) +{ + ipanel()->GetInset(GetVPanel(), left, top, right, bottom); +} + +void Panel::GetPaintSize(int& wide,int& tall) +{ + GetSize(wide, tall); + if (_border != null) + { + int left,top,right,bottom; + _border->GetInset(left,top,right,bottom); + + wide -= (left+right); + tall -= (top+bottom); + } +} + +int Panel::GetWide() +{ + int wide, tall; + ipanel()->GetSize(GetVPanel(), wide, tall); + return wide; +} + +void Panel::SetWide(int wide) +{ + ipanel()->SetSize(GetVPanel(), wide, GetTall()); +} + +int Panel::GetTall() +{ + int wide, tall; + ipanel()->GetSize(GetVPanel(), wide, tall); + return tall; +} + +void Panel::SetTall(int tall) +{ + ipanel()->SetSize(GetVPanel(), GetWide(), tall); +} + +void Panel::SetBuildGroup(BuildGroup* buildGroup) +{ + //TODO: remove from old group + + Assert(buildGroup != NULL); + + _buildGroup = buildGroup; + + _buildGroup->PanelAdded(this); +} + +bool Panel::IsBuildGroupEnabled() +{ + if ( !_buildGroup.IsValid() ) + return false; + + bool enabled = _buildGroup->IsEnabled(); + if ( enabled ) + return enabled; + + if ( GetParent() && GetParent()->IsBuildGroupEnabled() ) + return true; + + return false; +} + +void Panel::SetBgColor(Color color) +{ + _bgColor = color; +} + +void Panel::SetFgColor(Color color) +{ + _fgColor = color; +} + +Color Panel::GetBgColor() +{ + return _bgColor; +} + +Color Panel::GetFgColor() +{ + return _fgColor; +} + +void Panel::InternalPerformLayout() +{ + // Don't layout if we're still waiting for our scheme to be applied. + // At worst, it leads to crashes, at best it does work that we'll redo as soon as the scheme has been applied. + if ( _flags.IsFlagSet( NEEDS_SCHEME_UPDATE ) ) + return; + + _flags.SetFlag( IN_PERFORM_LAYOUT ); + // make sure the scheme has been applied + _flags.ClearFlag( NEEDS_LAYOUT ); + PerformLayout(); + _flags.ClearFlag( IN_PERFORM_LAYOUT ); +} + +void Panel::PerformLayout() +{ + // this should be overridden to relayout controls +} + +void Panel::InvalidateLayout( bool layoutNow, bool reloadScheme ) +{ + _flags.SetFlag( NEEDS_LAYOUT ); + + if (reloadScheme) + { + // make all our children reload the scheme + _flags.SetFlag( NEEDS_SCHEME_UPDATE ); + + for (int i = 0; i < GetChildCount(); i++) + { + vgui::Panel* panel = GetChild(i); + if( panel ) + { + panel->InvalidateLayout(layoutNow, true); + } + } + + PerformApplySchemeSettings(); + } + + if (layoutNow) + { + InternalPerformLayout(); + Repaint(); + } +} + +bool Panel::IsCursorNone() +{ + HCursor cursor = GetCursor(); + + if (!cursor) + { + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the cursor is currently over the panel +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Panel::IsCursorOver(void) +{ + int x, y; + input()->GetCursorPos(x, y); + return IsWithin(x, y); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when a panel receives a command message from another panel +//----------------------------------------------------------------------------- +void Panel::OnCommand(const char *command) +{ + if ( !Q_stricmp( "performlayout", command ) ) + { + InvalidateLayout(); + } + else if ( !Q_stricmp( "reloadscheme", command ) ) + { + InvalidateLayout( false, true ); + } + else + { + // if noone else caught this, pass along to the listeners + // (this is useful for generic dialogs - otherwise, commands just get ignored) + KeyValues *msg = new KeyValues( command ); + PostActionSignal( msg ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: panel gained focus message +//----------------------------------------------------------------------------- +void Panel::OnSetFocus() +{ + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: panel lost focus message +//----------------------------------------------------------------------------- +void Panel::OnKillFocus() +{ + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the object up to be deleted next frame +//----------------------------------------------------------------------------- +void Panel::MarkForDeletion() +{ + if ( _flags.IsFlagSet( MARKED_FOR_DELETION ) ) + return; + + _flags.SetFlag( MARKED_FOR_DELETION ); + _flags.ClearFlag( AUTODELETE_ENABLED ); + + if (ivgui()->IsRunning()) + { + ivgui()->MarkPanelForDeletion(GetVPanel()); + } + // direct delete is never safe because even if ivgui is shutdown we manually do RunFrame() + // and we can enter here in a think traverse and then delete from underneath ourselves + /*else + { + delete this; + }*/ +} + +//----------------------------------------------------------------------------- +// Purpose: return true if this object require a perform layout +//----------------------------------------------------------------------------- +bool Panel::IsLayoutInvalid() +{ + return _flags.IsFlagSet( NEEDS_LAYOUT ); +} + + +//----------------------------------------------------------------------------- +// Sets the pin corner + resize mode for resizing panels +//----------------------------------------------------------------------------- +void Panel::SetAutoResize( PinCorner_e pinCorner, AutoResize_e resizeDir, + int nPinOffsetX, int nPinOffsetY, int nUnpinnedCornerOffsetX, int nUnpinnedCornerOffsetY ) +{ + _pinCorner = pinCorner; + _autoResizeDirection = resizeDir; + m_nPinDeltaX = nPinOffsetX; + m_nPinDeltaY = nPinOffsetY; + m_nResizeDeltaX = nUnpinnedCornerOffsetX; + m_nResizeDeltaY = nUnpinnedCornerOffsetY; +} + + +//----------------------------------------------------------------------------- +// Sets the pin corner for non-resizing panels +//----------------------------------------------------------------------------- +void Panel::SetPinCorner( PinCorner_e pinCorner, int nOffsetX, int nOffsetY ) +{ + _pinCorner = pinCorner; + _autoResizeDirection = AUTORESIZE_NO; + m_nPinDeltaX = nOffsetX; + m_nPinDeltaY = nOffsetY; + m_nResizeDeltaX = 0; + m_nResizeDeltaY = 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +Panel::PinCorner_e Panel::GetPinCorner() +{ + return (PinCorner_e)_pinCorner; +} + + +//----------------------------------------------------------------------------- +// Gets the relative offset of the control from the pin corner +//----------------------------------------------------------------------------- +void Panel::GetPinOffset( int &dx, int &dy ) +{ + dx = m_nPinDeltaX; + dy = m_nPinDeltaY; +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +Panel::AutoResize_e Panel::GetAutoResize() +{ + return (AutoResize_e)_autoResizeDirection; +} + + +//----------------------------------------------------------------------------- +// Gets the relative offset of the control from the pin corner +//----------------------------------------------------------------------------- +void Panel::GetResizeOffset( int &dx, int &dy ) +{ + dx = m_nResizeDeltaX; + dy = m_nResizeDeltaY; +} + +//----------------------------------------------------------------------------- +// Tells this panel that it should pin itself to the corner of a specified sibling panel +//----------------------------------------------------------------------------- +void Panel::PinToSibling( const char *pszSibling, PinCorner_e pinOurCorner, PinCorner_e pinSibling ) +{ + _pinCornerToSibling = pinOurCorner; + _pinToSiblingCorner = pinSibling; + + if ( _pinToSibling && pszSibling && !Q_strcmp( _pinToSibling, pszSibling ) ) + return; + + if (_pinToSibling) + { + delete [] _pinToSibling; + _pinToSibling = NULL; + } + + if (pszSibling) + { + int len = Q_strlen(pszSibling) + 1; + _pinToSibling = new char[ len ]; + Q_strncpy( _pinToSibling, pszSibling, len ); + } + m_pinSibling = NULL; + + UpdateSiblingPin(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::UpdateSiblingPin( void ) +{ + if ( !_pinToSibling ) + { + ipanel()->SetSiblingPin(GetVPanel(), NULL); + return; + } + + if ( !m_pinSibling.Get() ) + { + // Resolve our sibling now + m_pinSibling = FindSiblingByName( _pinToSibling ); + } + + if ( m_pinSibling.Get() ) + { + ipanel()->SetSiblingPin( GetVPanel(), m_pinSibling->GetVPanel(), _pinCornerToSibling, _pinToSiblingCorner ); + } + else + { + ipanel()->SetSiblingPin(GetVPanel(), NULL); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::ApplySchemeSettings(IScheme *pScheme) +{ + // get colors + SetFgColor(GetSchemeColor("Panel.FgColor", pScheme)); + SetBgColor(GetSchemeColor("Panel.BgColor", pScheme)); + +#if defined( VGUI_USEDRAGDROP ) + m_clrDragFrame = pScheme->GetColor("DragDrop.DragFrame", Color(255, 255, 255, 192)); + m_clrDropFrame = pScheme->GetColor("DragDrop.DropFrame", Color(150, 255, 150, 255)); + + m_infoFont = pScheme->GetFont( "DefaultVerySmall" ); +#endif + // mark us as no longer needing scheme settings applied + _flags.ClearFlag( NEEDS_SCHEME_UPDATE ); + + if ( IsBuildGroupEnabled() ) + { + _buildGroup->ApplySchemeSettings(pScheme); + return; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Checks to see if the panel needs it's scheme info setup +//----------------------------------------------------------------------------- +void Panel::PerformApplySchemeSettings() +{ + if ( _flags.IsFlagSet( NEEDS_DEFAULT_SETTINGS_APPLIED ) ) + { + InternalInitDefaultValues( GetAnimMap() ); + } + + if ( _flags.IsFlagSet( NEEDS_SCHEME_UPDATE ) ) + { + VPROF( "ApplySchemeSettings" ); + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + AssertOnce( pScheme ); + if ( pScheme ) // this should NEVER be null, but if it is bad things would happen in ApplySchemeSettings... + { + ApplySchemeSettings( pScheme ); + //_needsSchemeUpdate = false; + + ApplyOverridableColors(); + + UpdateSiblingPin(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Loads panel details related to autoresize from the resource info +//----------------------------------------------------------------------------- +#if defined( _DEBUG ) +static Panel *lastWarningParent = 0; +#endif + +void Panel::ApplyAutoResizeSettings(KeyValues *inResourceData) +{ + int x, y; + GetPos(x, y); + + int wide, tall; + GetSize( wide, tall ); + + AutoResize_e autoResize = (AutoResize_e)inResourceData->GetInt( "AutoResize", AUTORESIZE_NO ); + PinCorner_e pinCorner = (PinCorner_e)inResourceData->GetInt( "PinCorner", PIN_TOPLEFT ); + + // By default, measure unpinned corner for the offset + int pw = wide, pt = tall; + if ( GetParent() ) + { + GetParent()->GetSize( pw, pt ); +#if defined( _DEBUG ) + if ( pw == 64 && pt == 24 ) + { + if ( GetParent() != lastWarningParent ) + { + lastWarningParent = GetParent(); + Warning( "Resize parent (panel(%s) -> parent(%s)) not sized yet!!!\n", GetName(), GetParent()->GetName() ); + } + } +#endif + } + + int nPinnedCornerOffsetX = 0, nPinnedCornerOffsetY = 0; + int nUnpinnedCornerOffsetX = 0, nUnpinnedCornerOffsetY = 0; + switch( pinCorner ) + { + case PIN_TOPLEFT: + nPinnedCornerOffsetX = x; + nPinnedCornerOffsetY = y; + nUnpinnedCornerOffsetX = (x + wide) - pw; + nUnpinnedCornerOffsetY = (y + tall) - pt; + break; + + case PIN_TOPRIGHT: + nPinnedCornerOffsetX = (x + wide) - pw; + nPinnedCornerOffsetY = y; + nUnpinnedCornerOffsetX = x; + nUnpinnedCornerOffsetY = (y + tall) - pt; + break; + + case PIN_BOTTOMLEFT: + nPinnedCornerOffsetX = x; + nPinnedCornerOffsetY = (y + tall) - pt; + nUnpinnedCornerOffsetX = (x + wide) - pw; + nUnpinnedCornerOffsetY = y; + break; + + case PIN_BOTTOMRIGHT: + nPinnedCornerOffsetX = (x + wide) - pw; + nPinnedCornerOffsetY = (y + tall) - pt; + nUnpinnedCornerOffsetX = x; + nUnpinnedCornerOffsetY = y; + break; + } + + // Allow specific overrides in the resource file + if ( IsProportional() ) + { + if ( inResourceData->FindKey( "PinnedCornerOffsetX" ) ) + { + nPinnedCornerOffsetX = scheme()->GetProportionalScaledValueEx( GetScheme(), inResourceData->GetInt( "PinnedCornerOffsetX" ) ); + } + if ( inResourceData->FindKey( "PinnedCornerOffsetY" ) ) + { + nPinnedCornerOffsetY = scheme()->GetProportionalScaledValueEx( GetScheme(), inResourceData->GetInt( "PinnedCornerOffsetY" ) ); + } + if ( inResourceData->FindKey( "UnpinnedCornerOffsetX" ) ) + { + nUnpinnedCornerOffsetX = scheme()->GetProportionalScaledValueEx( GetScheme(), inResourceData->GetInt( "UnpinnedCornerOffsetX" ) ); + } + if ( inResourceData->FindKey( "UnpinnedCornerOffsetY" ) ) + { + nUnpinnedCornerOffsetY = scheme()->GetProportionalScaledValueEx( GetScheme(), inResourceData->GetInt( "UnpinnedCornerOffsetY" ) ); + } + } + else + { + nPinnedCornerOffsetX = inResourceData->GetInt( "PinnedCornerOffsetX", nPinnedCornerOffsetX ); + nPinnedCornerOffsetY = inResourceData->GetInt( "PinnedCornerOffsetY", nPinnedCornerOffsetY ); + nUnpinnedCornerOffsetX = inResourceData->GetInt( "UnpinnedCornerOffsetX", nUnpinnedCornerOffsetX ); + nUnpinnedCornerOffsetY = inResourceData->GetInt( "UnpinnedCornerOffsetY", nUnpinnedCornerOffsetY ); + } + + if ( autoResize == AUTORESIZE_NO ) + { + nUnpinnedCornerOffsetX = nUnpinnedCornerOffsetY = 0; + } + + SetAutoResize( pinCorner, autoResize, nPinnedCornerOffsetX, nPinnedCornerOffsetY, nUnpinnedCornerOffsetX, nUnpinnedCornerOffsetY ); +} + +ConVar panel_test_title_safe( "panel_test_title_safe", "0", FCVAR_CHEAT, "Test vgui panel positioning with title safe indentation" ); + + + +//----------------------------------------------------------------------------- +// Purpose: Loads panel details from the resource info +//----------------------------------------------------------------------------- +void Panel::ApplySettings(KeyValues *inResourceData) +{ + // First restore to default values + if ( _flags.IsFlagSet( NEEDS_DEFAULT_SETTINGS_APPLIED ) ) + { + InternalInitDefaultValues( GetAnimMap() ); + } + + // Let PanelAnimationVars auto-retrieve settings (we restore defaults above + // since a script might be missing certain values) + InternalApplySettings( GetAnimMap(), inResourceData ); + + // clear any alignment flags + _buildModeFlags &= ~(BUILDMODE_SAVE_XPOS_RIGHTALIGNED | BUILDMODE_SAVE_XPOS_CENTERALIGNED | BUILDMODE_SAVE_YPOS_BOTTOMALIGNED | BUILDMODE_SAVE_YPOS_CENTERALIGNED | BUILDMODE_SAVE_WIDE_FULL | BUILDMODE_SAVE_TALL_FULL | BUILDMODE_SAVE_PROPORTIONAL_TO_PARENT); + + // get the position + int alignScreenWide, alignScreenTall; // screen dimensions used for pinning in splitscreen + surface()->GetScreenSize( alignScreenWide, alignScreenTall ); + + int screenWide = alignScreenWide; + int screenTall = alignScreenTall; + + // temporarily remove the override to get the fullscreen dimensions + if ( surface()->IsScreenSizeOverrideActive() ) + { + surface()->ForceScreenSizeOverride( false, 0, 0 ); + surface()->GetScreenSize( screenWide, screenTall ); + + // restore the override + surface()->ForceScreenSizeOverride( true, alignScreenWide, alignScreenTall ); + } + + int parentX = 0; + int parentY = 0; + + // flag to cause windows to get screenWide and screenTall from their parents, + // this allows children windows to use fill and right/bottom alignment even + // if their parent does not use the full screen. + if ( inResourceData->GetInt( "proportionalToParent", 0 ) == 1 ) + { + _buildModeFlags |= BUILDMODE_SAVE_PROPORTIONAL_TO_PARENT; + if ( GetParent() != NULL ) + { + GetParent()->GetBounds( parentX, parentY, alignScreenWide, alignScreenTall ); + } + } + + int x, y; + GetPos(x, y); + const char *xstr = inResourceData->GetString( "xpos", NULL ); + const char *ystr = inResourceData->GetString( "ypos", NULL ); + + if (xstr) + { + // look for alignment flags + if (xstr[0] == 'r' || xstr[0] == 'R') + { + _buildModeFlags |= BUILDMODE_SAVE_XPOS_RIGHTALIGNED; + xstr++; + } + else if (xstr[0] == 'c' || xstr[0] == 'C') + { + _buildModeFlags |= BUILDMODE_SAVE_XPOS_CENTERALIGNED; + xstr++; + } + + // get the value + x = atoi(xstr); + // scale the x up to our screen co-ords + if ( IsProportional() ) + { + x = scheme()->GetProportionalScaledValueEx(GetScheme(), x); + } + // now correct the alignment + if (_buildModeFlags & BUILDMODE_SAVE_XPOS_RIGHTALIGNED) + { + x = alignScreenWide - x; + } + else if (_buildModeFlags & BUILDMODE_SAVE_XPOS_CENTERALIGNED) + { + x = (alignScreenWide / 2) + x; + } + } + + if (ystr) + { + // look for alignment flags + if (ystr[0] == 'r' || ystr[0] == 'R') + { + _buildModeFlags |= BUILDMODE_SAVE_YPOS_BOTTOMALIGNED; + ystr++; + } + else if (ystr[0] == 'c' || ystr[0] == 'C') + { + _buildModeFlags |= BUILDMODE_SAVE_YPOS_CENTERALIGNED; + ystr++; + } + y = atoi(ystr); + if (IsProportional()) + { + // scale the y up to our screen co-ords + y = scheme()->GetProportionalScaledValueEx(GetScheme(), y); + } + // now correct the alignment + if (_buildModeFlags & BUILDMODE_SAVE_YPOS_BOTTOMALIGNED) + { + y = alignScreenTall - y; + } + else if (_buildModeFlags & BUILDMODE_SAVE_YPOS_CENTERALIGNED) + { + y = (alignScreenTall / 2) + y; + } + } + + bool bUsesTitleSafeArea = false; + int titleSafeWide = 0; + int titleSafeTall = 0; + + Rect_t excludeEdgeFromTitleSafe; // if a side is set to != 0, don't title safe relative to that edge + excludeEdgeFromTitleSafe.x = 0; + excludeEdgeFromTitleSafe.y = 0; + excludeEdgeFromTitleSafe.width = 0; + excludeEdgeFromTitleSafe.height = 0; + + if ( IsX360() || panel_test_title_safe.GetBool() ) + { + // "usetitlesafe" "1" - required inner 90% + // "usetitlesafe" "2" - suggested inner 85% + + int iUseTitleSafeValue = 0; + if ( inResourceData->FindKey( "usetitlesafe" ) ) + { + iUseTitleSafeValue = inResourceData->GetInt( "usetitlesafe" ); + bUsesTitleSafeArea = ( iUseTitleSafeValue > 0 ); + } + + if( bUsesTitleSafeArea ) + { + titleSafeWide = screenWide * ( iUseTitleSafeValue == 1 ? 0.05f : 0.075f ); + titleSafeTall = screenTall * ( iUseTitleSafeValue == 1 ? 0.05f : 0.075f ); + + // Don't title safe internal boundaries for split screen viewports + int splitX = 0; + int splitY = 0; + vgui::surface()->OffsetAbsPos( splitX, splitY ); + + bool bHorizontalSplit = ( alignScreenTall != screenTall ); + bool bVerticalSplit = ( alignScreenWide != screenWide ); + + if ( bHorizontalSplit ) + { + // top or bottom? + if ( splitY != parentY ) + { + excludeEdgeFromTitleSafe.y = 1; + } + else + { + excludeEdgeFromTitleSafe.height = 1; + } + } + + if ( bVerticalSplit ) + { + // left or right + if ( splitX != parentX ) + { + excludeEdgeFromTitleSafe.x = 1; + } + else + { + excludeEdgeFromTitleSafe.width = 1; + } + } + + if ( _buildModeFlags & BUILDMODE_SAVE_XPOS_RIGHTALIGNED ) + { + if ( !excludeEdgeFromTitleSafe.width ) + { + x -= titleSafeWide; // right edge + } + } + else if (_buildModeFlags & BUILDMODE_SAVE_XPOS_CENTERALIGNED) + { + } + else if ( !excludeEdgeFromTitleSafe.x ) + { + x += titleSafeWide; // left edge + } + + if ( _buildModeFlags & BUILDMODE_SAVE_YPOS_BOTTOMALIGNED ) + { + if ( !excludeEdgeFromTitleSafe.height ) + { + y -= titleSafeTall; // bottom edge + } + } + else if (_buildModeFlags & BUILDMODE_SAVE_YPOS_CENTERALIGNED) + { + } + else if ( !excludeEdgeFromTitleSafe.y ) + { + y += titleSafeTall; // top edge + } + } + } + SetNavUp( inResourceData->GetString("navUp") ); + SetNavDown( inResourceData->GetString("navDown") ); + SetNavLeft( inResourceData->GetString("navLeft") ); + SetNavRight( inResourceData->GetString("navRight") ); + SetNavToRelay( inResourceData->GetString("navToRelay") ); + SetNavActivate( inResourceData->GetString("navActivate") ); + SetNavBack( inResourceData->GetString("navBack") ); + + SetPos(x, y); + + if (inResourceData->FindKey( "zpos" )) + { + SetZPos( inResourceData->GetInt( "zpos" ) ); + } + + // size + int wide, tall; + GetSize( wide, tall ); + + const char *wstr = inResourceData->GetString( "wide", NULL ); + if ( wstr ) + { + if (wstr[0] == 'f' || wstr[0] == 'F') + { + _buildModeFlags |= BUILDMODE_SAVE_WIDE_FULL; + wstr++; + } + wide = atoi(wstr); + if ( IsProportional() ) + { + // scale the width up to our screen co-ords + wide = scheme()->GetProportionalScaledValueEx(GetScheme(), wide); + } + // now correct the alignment + if (_buildModeFlags & BUILDMODE_SAVE_WIDE_FULL) + { + wide = alignScreenWide - wide; + } + } + + // allow tall to be use the "fill" option, set to the height of the parent/screen + wstr = inResourceData->GetString( "tall", NULL ); + if ( wstr ) + { + if (wstr[0] == 'f' || wstr[0] == 'F') + { + _buildModeFlags |= BUILDMODE_SAVE_TALL_FULL; + wstr++; + } + tall = atoi(wstr); + if ( IsProportional() ) + { + // scale the height up to our screen co-ords + tall = scheme()->GetProportionalScaledValueEx(GetScheme(), tall); + } + // now correct the alignment + if (_buildModeFlags & BUILDMODE_SAVE_TALL_FULL) + { + tall = alignScreenTall - tall; + } + } + + if( bUsesTitleSafeArea ) + { + if ( _buildModeFlags & BUILDMODE_SAVE_WIDE_FULL ) + { + if ( !excludeEdgeFromTitleSafe.x ) + wide -= titleSafeWide; + + if ( !excludeEdgeFromTitleSafe.width ) + wide -= titleSafeWide; + } + + if ( _buildModeFlags & BUILDMODE_SAVE_TALL_FULL ) + { + if ( !excludeEdgeFromTitleSafe.y ) + tall -= titleSafeTall; + + if ( !excludeEdgeFromTitleSafe.height ) + tall -= titleSafeTall; + } + } + + SetSize( wide, tall ); + + // NOTE: This has to happen after pos + size is set + ApplyAutoResizeSettings( inResourceData ); + + // only get colors if we're ignoring the scheme + if (inResourceData->GetInt("IgnoreScheme", 0)) + { + PerformApplySchemeSettings(); + } + + // state + int state = inResourceData->GetInt("visible", 1); + if (state == 0) + { + SetVisible(false); + } + else if (state == 1) + { + SetVisible(true); + } + + SetEnabled( inResourceData->GetInt("enabled", true) ); + + bool bMouseEnabled = inResourceData->GetInt( "mouseinputenabled", true ); + if ( !bMouseEnabled ) + { + SetMouseInputEnabled( false ); + } + + // tab order + SetTabPosition(inResourceData->GetInt("tabPosition", 0)); + + const char *tooltip = inResourceData->GetString("tooltiptext", NULL); + if (tooltip && *tooltip) + { + GetTooltip()->SetText(tooltip); + } + + // paint background? + int nPaintBackground = inResourceData->GetInt("paintbackground", -1); + if (nPaintBackground >= 0) + { + SetPaintBackgroundEnabled( nPaintBackground != 0 ); + } + + // paint border? + int nPaintBorder = inResourceData->GetInt("paintborder", -1); + if (nPaintBorder >= 0) + { + SetPaintBorderEnabled( nPaintBorder != 0 ); + } + + // border? + const char *pBorder = inResourceData->GetString( "border", "" ); + if ( *pBorder ) + { + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + SetBorder( pScheme->GetBorder( pBorder ) ); + } + + // check to see if we have a new name assigned + const char *newName = inResourceData->GetString("fieldName", NULL); + if ( newName ) + { + // Only slam the name if the new one differs... + SetName(newName); + } + + // check to see if we need to render to the frame buffer even if + // stereo mode is trying to render all of the ui to a render target + m_bForceStereoRenderToFrameBuffer = inResourceData->GetBool( "ForceStereoRenderToFrameBuffer", false ); + + //============================================================================= + // HPE_BEGIN: + // [pfreese] Support for reading rounded corner flags + //============================================================================= + int roundedCorners = inResourceData->GetInt( "RoundedCorners", -1 ); + if ( roundedCorners >= 0 ) + { + m_roundedCorners = roundedCorners; + } + //============================================================================= + // HPE_END + //============================================================================= + + const char *pszSiblingName = inResourceData->GetString("pin_to_sibling", NULL); + PinCorner_e pinOurCornerToSibling = (PinCorner_e)inResourceData->GetInt( "pin_corner_to_sibling", PIN_TOPLEFT ); + PinCorner_e pinSiblingCorner = (PinCorner_e)inResourceData->GetInt( "pin_to_sibling_corner", PIN_TOPLEFT ); + PinToSibling( pszSiblingName, pinOurCornerToSibling, pinSiblingCorner ); + + + // Allow overriding of colors. Used mostly by HUD elements, where scheme color usage is often undesired. + IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); + for ( int i = 0; i < m_OverridableColorEntries.Count(); i++ ) + { + // Need to ensure the key exists, so we don't overwrite existing colors when it's not set. + if ( inResourceData->FindKey( m_OverridableColorEntries[i].m_pszScriptName, false ) ) + { + // Get the color as a string - test whether it is an actual color or a reference to a scheme color + const char *pColorStr = inResourceData->GetString( m_OverridableColorEntries[i].m_pszScriptName ); + Color &clrDest = m_OverridableColorEntries[i].m_colFromScript; + if ( pColorStr[0] == '.' || isdigit( pColorStr[0] ) ) + { + float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f; + sscanf( pColorStr, "%f %f %f %f", &r, &g, &b, &a ); + clrDest[0] = (unsigned char)r; + clrDest[1] = (unsigned char)g; + clrDest[2] = (unsigned char)b; + clrDest[3] = (unsigned char)a; + } + else + { + // First character wasn't a digit or a decimal - do a scheme color lookup + clrDest = pScheme->GetColor( pColorStr, Color( 255, 255, 255, 255 ) ); + } + + (*m_OverridableColorEntries[i].m_pColor) = m_OverridableColorEntries[i].m_colFromScript; + m_OverridableColorEntries[i].m_bOverridden = true; + } + } + + const char *pKeyboardInputEnabled = inResourceData->GetString( "keyboardinputenabled", NULL ); + if ( pKeyboardInputEnabled && pKeyboardInputEnabled[0] ) + { + SetKeyBoardInputEnabled( atoi( pKeyboardInputEnabled ) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Saves out a resource description of this panel +//----------------------------------------------------------------------------- +void Panel::GetSettings( KeyValues *outResourceData ) +{ + // control class name (so it can be recreated later if needed) + outResourceData->SetString( "ControlName", GetClassName() ); + + // name + outResourceData->SetString( "fieldName", _panelName ); + + // positioning + int screenWide, screenTall; + surface()->GetScreenSize(screenWide, screenTall); + int x, y; + GetPos( x, y ); + if ( IsProportional() ) + { + x = scheme()->GetProportionalNormalizedValueEx( GetScheme(), x ); + y = scheme()->GetProportionalNormalizedValueEx( GetScheme(), y ); + } + // correct for alignment + if (_buildModeFlags & BUILDMODE_SAVE_XPOS_RIGHTALIGNED) + { + x = screenWide - x; + char xstr[32]; + Q_snprintf(xstr, sizeof( xstr ), "r%d", x); + outResourceData->SetString( "xpos", xstr ); + } + else if (_buildModeFlags & BUILDMODE_SAVE_XPOS_CENTERALIGNED) + { + x = (screenWide / 2) + x; + char xstr[32]; + Q_snprintf(xstr, sizeof( xstr ), "c%d", x); + outResourceData->SetString( "xpos", xstr ); + } + else + { + outResourceData->SetInt( "xpos", x ); + } + if (_buildModeFlags & BUILDMODE_SAVE_YPOS_BOTTOMALIGNED) + { + y = screenTall - y; + char ystr[32]; + Q_snprintf(ystr, sizeof( ystr ), "r%d", y); + outResourceData->SetString( "ypos", ystr ); + } + else if (_buildModeFlags & BUILDMODE_SAVE_YPOS_CENTERALIGNED) + { + y = (screenTall / 2) + y; + char ystr[32]; + Q_snprintf(ystr, sizeof( ystr ), "c%d", y); + outResourceData->SetString( "ypos", ystr ); + } + else + { + outResourceData->SetInt( "ypos", y ); + } + if (m_pTooltips) + { + if (strlen(m_pTooltips->GetText()) > 0) + { + outResourceData->SetString("tooltiptext", m_pTooltips->GetText()); + } + } + int wide, tall; + GetSize( wide, tall ); + if ( IsProportional() ) + { + wide = scheme()->GetProportionalNormalizedValueEx( GetScheme(), wide ); + tall = scheme()->GetProportionalNormalizedValueEx( GetScheme(), tall ); + } + + int z = ipanel()->GetZPos(GetVPanel()); + if (z) + { + outResourceData->SetInt("zpos", z); + } + + // Correct for alignment + if (_buildModeFlags & BUILDMODE_SAVE_WIDE_FULL ) + { + wide = screenWide - wide; + char wstr[32]; + Q_snprintf(wstr, sizeof( wstr ), "f%d", wide); + outResourceData->SetString( "wide", wstr ); + } + else + { + outResourceData->SetInt( "wide", wide ); + } + outResourceData->SetInt( "tall", tall ); + + outResourceData->SetInt("AutoResize", GetAutoResize()); + outResourceData->SetInt("PinCorner", GetPinCorner()); + + //============================================================================= + // HPE_BEGIN: + // [pfreese] Support for writing out rounded corner flags + //============================================================================= + outResourceData->SetInt("RoundedCorners", m_roundedCorners); + //============================================================================= + // HPE_END + //============================================================================= + + outResourceData->SetString( "pin_to_sibling", _pinToSibling ); + outResourceData->SetInt("pin_corner_to_sibling", _pinCornerToSibling ); + outResourceData->SetInt("pin_to_sibling_corner", _pinToSiblingCorner ); + + + // state + outResourceData->SetInt( "visible", IsVisible() ); + outResourceData->SetInt( "enabled", IsEnabled() ); + + outResourceData->SetInt( "tabPosition", GetTabPosition() ); + + for ( int i = 0; i < m_OverridableColorEntries.Count(); i++ ) + { + if ( m_OverridableColorEntries[i].m_bOverridden ) + { + outResourceData->SetColor( m_OverridableColorEntries[i].m_pszScriptName, m_OverridableColorEntries[i].m_colFromScript ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: After applying settings, apply overridable colors. +// Done post apply settings, so that baseclass settings don't stomp +// the script specified override colors. +//----------------------------------------------------------------------------- +void Panel::ApplyOverridableColors( void ) +{ + for ( int i = 0; i < m_OverridableColorEntries.Count(); i++ ) + { + if ( m_OverridableColorEntries[i].m_bOverridden ) + { + (*m_OverridableColorEntries[i].m_pColor) = m_OverridableColorEntries[i].m_colFromScript; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetOverridableColor( Color *pColor, const Color &newColor ) +{ + for ( int i = 0; i < m_OverridableColorEntries.Count(); i++ ) + { + if ( m_OverridableColorEntries[i].m_bOverridden ) + { + if ( m_OverridableColorEntries[i].m_pColor == pColor ) + return; + } + } + + // Didn't find it, or it's not been overridden. + *pColor = newColor; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Color Panel::GetSchemeColor(const char *keyName, IScheme *pScheme) +{ + return pScheme->GetColor(keyName, Color(255, 255, 255, 255)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Color Panel::GetSchemeColor(const char *keyName, Color defaultColor, IScheme *pScheme) +{ + return pScheme->GetColor(keyName, defaultColor); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a string description of the panel fields for use in the UI +//----------------------------------------------------------------------------- +const char *Panel::GetDescription( void ) +{ + static const char *panelDescription = "string fieldName, int xpos, int ypos, int wide, int tall, bool visible, bool enabled, int tabPosition, corner pinCorner, autoresize autoResize, string tooltiptext"; + return panelDescription; +} + +//----------------------------------------------------------------------------- +// Purpose: user configuration settings +// this is used for any control details the user wants saved between sessions +// eg. dialog positions, last directory opened, list column width +//----------------------------------------------------------------------------- +void Panel::ApplyUserConfigSettings(KeyValues *userConfig) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: returns user config settings for this control +//----------------------------------------------------------------------------- +void Panel::GetUserConfigSettings(KeyValues *userConfig) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: optimization, return true if this control has any user config settings +//----------------------------------------------------------------------------- +bool Panel::HasUserConfigSettings() +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::InternalInvalidateLayout() +{ + InvalidateLayout(false, false); +} + +//----------------------------------------------------------------------------- +// Purpose: called whenever the panel moves +//----------------------------------------------------------------------------- +void Panel::OnMove() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::InternalMove() +{ + OnMove(); + for(int i=0;iOnMove(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: empty function +//----------------------------------------------------------------------------- +void Panel::OnTick() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: versioning +//----------------------------------------------------------------------------- +void *Panel::QueryInterface(EInterfaceID id) +{ + if (id == ICLIENTPANEL_STANDARD_INTERFACE) + { + return this; + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Map all the base messages to functions +// ordering from most -> least used improves speed +//----------------------------------------------------------------------------- +MessageMapItem_t Panel::m_MessageMap[] = +{ + MAP_MESSAGE_INT( Panel, "RequestFocus", RequestFocus, "direction" ) +}; + +// IMPLEMENT_PANELMAP( Panel, NULL ) +PanelMap_t Panel::m_PanelMap = { Panel::m_MessageMap, ARRAYSIZE(Panel::m_MessageMap), "Panel", NULL }; +PanelMap_t *Panel::GetPanelMap( void ) { return &m_PanelMap; } + +//----------------------------------------------------------------------------- +// Purpose: !! Soon to replace existing prepare panel map +//----------------------------------------------------------------------------- +void PreparePanelMessageMap(PanelMessageMap *panelMap) +{ + // iterate through the class hierarchy message maps + while ( panelMap != NULL && !panelMap->processed ) + { + // hash message map strings into symbols + for (int i = 0; i < panelMap->entries.Count(); i++) + { + MessageMapItem_t *item = &panelMap->entries[i]; + + if (item->name) + { + item->nameSymbol = KeyValuesSystem()->GetSymbolForString(item->name); + } + else + { + item->nameSymbol = INVALID_KEY_SYMBOL; + } + if (item->firstParamName) + { + item->firstParamSymbol = KeyValuesSystem()->GetSymbolForString(item->firstParamName); + } + else + { + item->firstParamSymbol = INVALID_KEY_SYMBOL; + } + if (item->secondParamName) + { + item->secondParamSymbol = KeyValuesSystem()->GetSymbolForString(item->secondParamName); + } + else + { + item->secondParamSymbol = INVALID_KEY_SYMBOL; + } + } + + panelMap->processed = true; + panelMap = panelMap->baseMap; + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: Handles a message +// Dispatches the message to a set of message maps +//----------------------------------------------------------------------------- +void Panel::OnMessage(const KeyValues *params, VPANEL ifromPanel) +{ + PanelMessageMap *panelMap = GetMessageMap(); + bool bFound = false; + int iMessageName = params->GetNameSymbol(); + + if ( !panelMap->processed ) + { + PreparePanelMessageMap( panelMap ); + } + + // iterate through the class hierarchy message maps + for ( ; panelMap != NULL && !bFound; panelMap = panelMap->baseMap ) + { +#if defined( _DEBUG ) +// char const *className = panelMap->pfnClassName(); +// NOTE_UNUSED( className ); +#endif + + // iterate all the entries in the panel map + for ( int i = 0; i < panelMap->entries.Count(); i++ ) + { + MessageMapItem_t *pMap = &panelMap->entries[i]; + + if (iMessageName == pMap->nameSymbol) + { + bFound = true; + + switch (pMap->numParams) + { + case 0: + { + (this->*(pMap->func))(); + break; + } + + case 1: + { + KeyValues *param1 = params->FindKey(pMap->firstParamSymbol); + if (!param1) + { + param1 = const_cast(params); + } + + switch ( pMap->firstParamType ) + { + case DATATYPE_INT: + typedef void (Panel::*MessageFunc_Int_t)(int); + (this->*((MessageFunc_Int_t)pMap->func))( param1->GetInt() ); + break; + + case DATATYPE_UINT64: + typedef void (Panel::*MessageFunc_Uin64_t)(uint64); + (this->*((MessageFunc_Uin64_t)pMap->func))( param1->GetUint64() ); + break; + + case DATATYPE_PTR: + typedef void (Panel::*MessageFunc_Ptr_t)( void * ); + (this->*((MessageFunc_Ptr_t)pMap->func))( param1->GetPtr() ); + break; + + case DATATYPE_HANDLE: + { + typedef void (Panel::*MessageFunc_VPANEL_t)( VPANEL ); + VPANEL vpanel = ivgui()->HandleToPanel( param1->GetInt() ); + (this->*((MessageFunc_VPANEL_t)pMap->func))( vpanel ); + } + break; + + case DATATYPE_FLOAT: + typedef void (Panel::*MessageFunc_Float_t)( float ); + (this->*((MessageFunc_Float_t)pMap->func))( param1->GetFloat() ); + break; + + case DATATYPE_CONSTCHARPTR: + typedef void (Panel::*MessageFunc_CharPtr_t)( const char * ); + (this->*((MessageFunc_CharPtr_t)pMap->func))( param1->GetString() ); + break; + + case DATATYPE_CONSTWCHARPTR: + typedef void (Panel::*MessageFunc_WCharPtr_t)( const wchar_t * ); + (this->*((MessageFunc_WCharPtr_t)pMap->func))( param1->GetWString() ); + break; + + case DATATYPE_KEYVALUES: + typedef void (Panel::*MessageFunc_KeyValues_t)(KeyValues *); + if ( pMap->firstParamName ) + { + (this->*((MessageFunc_KeyValues_t)pMap->func))( (KeyValues *)param1->GetPtr() ); + } + else + { + // no param set, so pass in the whole thing + (this->*((MessageFunc_KeyValues_t)pMap->func))( const_cast(params) ); + } + break; + + default: + Assert(!("No handler for vgui message function")); + break; + } + break; + } + + case 2: + { + KeyValues *param1 = params->FindKey(pMap->firstParamSymbol); + if (!param1) + { + param1 = const_cast(params); + } + KeyValues *param2 = params->FindKey(pMap->secondParamSymbol); + if (!param2) + { + param2 = const_cast(params); + } + + if ( (DATATYPE_INT == pMap->firstParamType) && (DATATYPE_INT == pMap->secondParamType) ) + { + typedef void (Panel::*MessageFunc_IntInt_t)(int, int); + (this->*((MessageFunc_IntInt_t)pMap->func))( param1->GetInt(), param2->GetInt() ); + } + else if ( (DATATYPE_PTR == pMap->firstParamType) && (DATATYPE_INT == pMap->secondParamType) ) + { + typedef void (Panel::*MessageFunc_PtrInt_t)(void *, int); + (this->*((MessageFunc_PtrInt_t)pMap->func))( param1->GetPtr(), param2->GetInt() ); + } + else if ( (DATATYPE_CONSTCHARPTR == pMap->firstParamType) && (DATATYPE_INT == pMap->secondParamType) ) + { + typedef void (Panel::*MessageFunc_ConstCharPtrInt_t)(const char *, int); + (this->*((MessageFunc_ConstCharPtrInt_t)pMap->func))( param1->GetString(), param2->GetInt() ); + } + else if ( (DATATYPE_CONSTCHARPTR == pMap->firstParamType) && (DATATYPE_CONSTCHARPTR == pMap->secondParamType) ) + { + typedef void (Panel::*MessageFunc_ConstCharPtrConstCharPtr_t)(const char *, const char *); + (this->*((MessageFunc_ConstCharPtrConstCharPtr_t)pMap->func))( param1->GetString(), param2->GetString() ); + } + else if ( (DATATYPE_INT == pMap->firstParamType) && (DATATYPE_CONSTCHARPTR == pMap->secondParamType) ) + { + typedef void (Panel::*MessageFunc_IntConstCharPtr_t)(int, const char *); + (this->*((MessageFunc_IntConstCharPtr_t)pMap->func))( param1->GetInt(), param2->GetString() ); + } + else if ( (DATATYPE_PTR == pMap->firstParamType) && (DATATYPE_CONSTCHARPTR == pMap->secondParamType) ) + { + typedef void (Panel::*MessageFunc_PtrConstCharPtr_t)(void *, const char *); + (this->*((MessageFunc_PtrConstCharPtr_t)pMap->func))( param1->GetPtr(), param2->GetString() ); + } + else if ( (DATATYPE_PTR == pMap->firstParamType) && (DATATYPE_CONSTWCHARPTR == pMap->secondParamType) ) + { + typedef void (Panel::*MessageFunc_PtrConstCharPtr_t)(void *, const wchar_t *); + (this->*((MessageFunc_PtrConstCharPtr_t)pMap->func))( param1->GetPtr(), param2->GetWString() ); + } + else if ( (DATATYPE_HANDLE == pMap->firstParamType) && (DATATYPE_CONSTCHARPTR == pMap->secondParamType) ) + { + typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, const char *); + VPANEL vp = ivgui()->HandleToPanel( param1->GetInt() ); + (this->*((MessageFunc_HandleConstCharPtr_t)pMap->func))( vp, param2->GetString() ); + } + else if ( (DATATYPE_HANDLE == pMap->firstParamType) && (DATATYPE_CONSTWCHARPTR == pMap->secondParamType) ) + { + typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, const wchar_t *); + VPANEL vp = ivgui()->HandleToPanel( param1->GetInt() ); + (this->*((MessageFunc_HandleConstCharPtr_t)pMap->func))( vp, param2->GetWString() ); + } + else + { + // the message isn't handled + ivgui()->DPrintf( "Message '%s', sent to '%s', has invalid parameter types\n", params->GetName(), GetName() ); + } + break; + } + + default: + Assert(!("Invalid number of parameters")); + break; + } + + // break the loop + bFound = true; + break; + } + } + } + + if (!bFound) + { + OnOldMessage(const_cast(params), ifromPanel); + } +} + +void Panel::OnOldMessage(KeyValues *params, VPANEL ifromPanel) +{ + bool bFound = false; + // message map dispatch + int iMessageName = params->GetNameSymbol(); + + PanelMap_t *panelMap = GetPanelMap(); + if ( !panelMap->processed ) + { + PreparePanelMap( panelMap ); + } + + // iterate through the class hierarchy message maps + for ( ; panelMap != NULL && !bFound; panelMap = panelMap->baseMap ) + { + MessageMapItem_t *pMessageMap = panelMap->dataDesc; + + for ( int i = 0; i < panelMap->dataNumFields; i++ ) + { + if (iMessageName == pMessageMap[i].nameSymbol) + { + // call the mapped function + switch ( pMessageMap[i].numParams ) + { + case 2: + if ( (DATATYPE_INT == pMessageMap[i].firstParamType) && (DATATYPE_INT == pMessageMap[i].secondParamType) ) + { + typedef void (Panel::*MessageFunc_IntInt_t)(int, int); + (this->*((MessageFunc_IntInt_t)pMessageMap[i].func))( params->GetInt(pMessageMap[i].firstParamName), params->GetInt(pMessageMap[i].secondParamName) ); + } + else if ( (DATATYPE_PTR == pMessageMap[i].firstParamType) && (DATATYPE_INT == pMessageMap[i].secondParamType) ) + { + typedef void (Panel::*MessageFunc_PtrInt_t)(void *, int); + (this->*((MessageFunc_PtrInt_t)pMessageMap[i].func))( params->GetPtr(pMessageMap[i].firstParamName), params->GetInt(pMessageMap[i].secondParamName) ); + } + else if ( (DATATYPE_CONSTCHARPTR == pMessageMap[i].firstParamType) && (DATATYPE_INT == pMessageMap[i].secondParamType) ) + { + typedef void (Panel::*MessageFunc_ConstCharPtrInt_t)(const char *, int); + (this->*((MessageFunc_ConstCharPtrInt_t)pMessageMap[i].func))( params->GetString(pMessageMap[i].firstParamName), params->GetInt(pMessageMap[i].secondParamName) ); + } + else if ( (DATATYPE_CONSTCHARPTR == pMessageMap[i].firstParamType) && (DATATYPE_CONSTCHARPTR == pMessageMap[i].secondParamType) ) + { + typedef void (Panel::*MessageFunc_ConstCharPtrConstCharPtr_t)(const char *, const char *); + (this->*((MessageFunc_ConstCharPtrConstCharPtr_t)pMessageMap[i].func))( params->GetString(pMessageMap[i].firstParamName), params->GetString(pMessageMap[i].secondParamName) ); + } + else if ( (DATATYPE_INT == pMessageMap[i].firstParamType) && (DATATYPE_CONSTCHARPTR == pMessageMap[i].secondParamType) ) + { + typedef void (Panel::*MessageFunc_IntConstCharPtr_t)(int, const char *); + (this->*((MessageFunc_IntConstCharPtr_t)pMessageMap[i].func))( params->GetInt(pMessageMap[i].firstParamName), params->GetString(pMessageMap[i].secondParamName) ); + } + else if ( (DATATYPE_PTR == pMessageMap[i].firstParamType) && (DATATYPE_CONSTCHARPTR == pMessageMap[i].secondParamType) ) + { + typedef void (Panel::*MessageFunc_PtrConstCharPtr_t)(void *, const char *); + (this->*((MessageFunc_PtrConstCharPtr_t)pMessageMap[i].func))( params->GetPtr(pMessageMap[i].firstParamName), params->GetString(pMessageMap[i].secondParamName) ); + } + else if ( (DATATYPE_PTR == pMessageMap[i].firstParamType) && (DATATYPE_CONSTWCHARPTR == pMessageMap[i].secondParamType) ) + { + typedef void (Panel::*MessageFunc_PtrConstCharPtr_t)(void *, const wchar_t *); + (this->*((MessageFunc_PtrConstCharPtr_t)pMessageMap[i].func))( params->GetPtr(pMessageMap[i].firstParamName), params->GetWString(pMessageMap[i].secondParamName) ); + } + else if ( (DATATYPE_HANDLE == pMessageMap[i].firstParamType) && (DATATYPE_CONSTCHARPTR ==pMessageMap[i].secondParamType) ) + { + typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, const char *); + VPANEL vp = ivgui()->HandleToPanel( params->GetInt( pMessageMap[i].firstParamName ) ); + (this->*((MessageFunc_HandleConstCharPtr_t)pMessageMap[i].func))( vp, params->GetString(pMessageMap[i].secondParamName) ); + } + else if ( (DATATYPE_HANDLE == pMessageMap[i].firstParamType) && (DATATYPE_CONSTWCHARPTR == pMessageMap[i].secondParamType) ) + { + typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, const wchar_t *); + VPANEL vp = ivgui()->HandleToPanel( params->GetInt( pMessageMap[i].firstParamName ) ); + (this->*((MessageFunc_HandleConstCharPtr_t)pMessageMap[i].func))( vp, params->GetWString(pMessageMap[i].secondParamName) ); + } + else + { + // the message isn't handled + ivgui()->DPrintf( "Message '%s', sent to '%s', has invalid parameter types\n", params->GetName(), GetName() ); + } + break; + + case 1: + switch ( pMessageMap[i].firstParamType ) + { + case DATATYPE_BOOL: + typedef void (Panel::*MessageFunc_Bool_t)(bool); + (this->*((MessageFunc_Bool_t)pMessageMap[i].func))( (bool)params->GetInt(pMessageMap[i].firstParamName) ); + break; + + case DATATYPE_CONSTCHARPTR: + typedef void (Panel::*MessageFunc_ConstCharPtr_t)(const char *); + (this->*((MessageFunc_ConstCharPtr_t)pMessageMap[i].func))( (const char *)params->GetString(pMessageMap[i].firstParamName) ); + break; + + case DATATYPE_CONSTWCHARPTR: + typedef void (Panel::*MessageFunc_ConstCharPtr_t)(const char *); + (this->*((MessageFunc_ConstCharPtr_t)pMessageMap[i].func))( (const char *)params->GetWString(pMessageMap[i].firstParamName) ); + break; + + case DATATYPE_INT: + typedef void (Panel::*MessageFunc_Int_t)(int); + (this->*((MessageFunc_Int_t)pMessageMap[i].func))( params->GetInt(pMessageMap[i].firstParamName) ); + break; + + case DATATYPE_FLOAT: + typedef void (Panel::*MessageFunc_Float_t)(float); + (this->*((MessageFunc_Float_t)pMessageMap[i].func))( params->GetFloat(pMessageMap[i].firstParamName) ); + break; + + case DATATYPE_PTR: + typedef void (Panel::*MessageFunc_Ptr_t)(void *); + (this->*((MessageFunc_Ptr_t)pMessageMap[i].func))( (void *)params->GetPtr(pMessageMap[i].firstParamName) ); + break; + + case DATATYPE_HANDLE: + { + typedef void (Panel::*MessageFunc_Ptr_t)(void *); + VPANEL vp = ivgui()->HandleToPanel( params->GetInt( pMessageMap[i].firstParamName ) ); + Panel *panel = ipanel()->GetPanel( vp, GetModuleName() ); + (this->*((MessageFunc_Ptr_t)pMessageMap[i].func))( (void *)panel ); + } + break; + + case DATATYPE_KEYVALUES: + typedef void (Panel::*MessageFunc_KeyValues_t)(KeyValues *); + if ( pMessageMap[i].firstParamName ) + { + (this->*((MessageFunc_KeyValues_t)pMessageMap[i].func))( (KeyValues *)params->GetPtr(pMessageMap[i].firstParamName) ); + } + else + { + (this->*((MessageFunc_KeyValues_t)pMessageMap[i].func))( params ); + } + break; + + default: + // the message isn't handled + ivgui()->DPrintf( "Message '%s', sent to '%s', has an invalid parameter type\n", params->GetName(), GetName() ); + break; + } + + break; + + default: + (this->*(pMessageMap[i].func))(); + break; + }; + + // break the loop + bFound = true; + break; + } + } + } + + // message not handled + // debug code + if ( !bFound ) + { + static int s_bDebugMessages = -1; + if ( s_bDebugMessages == -1 ) + { + s_bDebugMessages = CommandLine()->FindParm( "-vguimessages" ) ? 1 : 0; + } + if ( s_bDebugMessages == 1 ) + { + ivgui()->DPrintf( "Message '%s' not handled by panel '%s'\n", params->GetName(), GetName() ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Safe call to get info from child panel by name +//----------------------------------------------------------------------------- +bool Panel::RequestInfoFromChild(const char *childName, KeyValues *outputData) +{ + Panel *panel = FindChildByName(childName); + if (panel) + { + return panel->RequestInfo(outputData); + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Posts a message +//----------------------------------------------------------------------------- +void Panel::PostMessage(Panel *target, KeyValues *message, float delay) +{ + ivgui()->PostMessage(target->GetVPanel(), message, GetVPanel(), delay); +} + +void Panel::PostMessage(VPANEL target, KeyValues *message, float delaySeconds) +{ + ivgui()->PostMessage(target, message, GetVPanel(), delaySeconds); +} + +void Panel::PostMessageToAllSiblings( KeyValues *msg, float delaySeconds /*= 0.0f*/ ) +{ + VPANEL parent = GetVParent(); + if ( parent ) + { + VPANEL vpanel = GetVPanel(); + + CUtlVector< VPANEL > &children = ipanel()->GetChildren( parent ); + int nChildCount = children.Count(); + for ( int i = 0; i < nChildCount; ++i ) + { + VPANEL sibling = children[ i ]; + if ( sibling == vpanel ) + continue; + + if ( sibling ) + { + PostMessage( sibling, msg->MakeCopy(), delaySeconds ); + } + } + } + + msg->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: Safe call to post a message to a child by name +//----------------------------------------------------------------------------- +void Panel::PostMessageToChild(const char *childName, KeyValues *message) +{ + Panel *panel = FindChildByName(childName); + if (panel) + { + ivgui()->PostMessage(panel->GetVPanel(), message, GetVPanel()); + } + else + { + message->deleteThis(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Requests some information from the panel +// Look through the message map for the handler +//----------------------------------------------------------------------------- +bool Panel::RequestInfo( KeyValues *outputData ) +{ + if ( InternalRequestInfo( GetAnimMap(), outputData ) ) + { + return true; + } + + if (GetVParent()) + { + return ipanel()->RequestInfo(GetVParent(), outputData); + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: sets a specified value in the control - inverse of RequestInfo +//----------------------------------------------------------------------------- +bool Panel::SetInfo(KeyValues *inputData) +{ + if ( InternalSetInfo( GetAnimMap(), inputData ) ) + { + return true; + } + + // doesn't chain to parent + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: change the panel's silent mode; if silent, the panel will not post +// any action signals +//----------------------------------------------------------------------------- + +void Panel::SetSilentMode( bool bSilent ) +{ + m_bIsSilent = bSilent; +} + +//----------------------------------------------------------------------------- +// Purpose: mouse events will be send to handler panel instead of this panel +//----------------------------------------------------------------------------- +void Panel::InstallMouseHandler( Panel *pHandler ) +{ + m_hMouseEventHandler = pHandler; +} + +//----------------------------------------------------------------------------- +// Purpose: Prepares the hierarchy panel maps for use (with message maps etc) +//----------------------------------------------------------------------------- +void Panel::PreparePanelMap( PanelMap_t *panelMap ) +{ + // iterate through the class hierarchy message maps + while ( panelMap != NULL && !panelMap->processed ) + { + // fixup cross-dll boundary panel maps + if ( panelMap->baseMap == (PanelMap_t*)0x00000001 ) + { + panelMap->baseMap = &Panel::m_PanelMap; + } + + // hash message map strings into symbols + for (int i = 0; i < panelMap->dataNumFields; i++) + { + MessageMapItem_t *item = &panelMap->dataDesc[i]; + + if (item->name) + { + item->nameSymbol = KeyValuesSystem()->GetSymbolForString(item->name); + } + else + { + item->nameSymbol = INVALID_KEY_SYMBOL; + } + if (item->firstParamName) + { + item->firstParamSymbol = KeyValuesSystem()->GetSymbolForString(item->firstParamName); + } + else + { + item->firstParamSymbol = INVALID_KEY_SYMBOL; + } + if (item->secondParamName) + { + item->secondParamSymbol = KeyValuesSystem()->GetSymbolForString(item->secondParamName); + } + else + { + item->secondParamSymbol = INVALID_KEY_SYMBOL; + } + } + + panelMap->processed = true; + panelMap = panelMap->baseMap; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called to delete the panel +//----------------------------------------------------------------------------- +void Panel::OnDelete() +{ +#ifdef WIN32 + Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) ); +#endif + delete this; +#ifdef WIN32 + Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Panel handle implementation +// Returns a pointer to a valid panel, NULL if the panel has been deleted +//----------------------------------------------------------------------------- +Panel *PHandle::Get() +{ + if (m_iPanelID != INVALID_PANEL) + { + VPANEL panel = ivgui()->HandleToPanel(m_iPanelID); + if (panel) + { + Panel *vguiPanel = ipanel()->GetPanel(panel, GetControlsModuleName()); + return vguiPanel; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: sets the smart pointer +//----------------------------------------------------------------------------- +Panel *PHandle::Set(Panel *pent) +{ + if (pent) + { + m_iPanelID = ivgui()->PanelToHandle(pent->GetVPanel()); + } + else + { + m_iPanelID = INVALID_PANEL; + } + return pent; +} + +Panel *PHandle::Set( HPanel hPanel ) +{ + m_iPanelID = hPanel; + return Get(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a handle to a valid panel, NULL if the panel has been deleted +//----------------------------------------------------------------------------- +VPANEL VPanelHandle::Get() +{ + if (m_iPanelID != INVALID_PANEL) + { + if (ivgui()) + { + return ivgui()->HandleToPanel(m_iPanelID); + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: sets the smart pointer +//----------------------------------------------------------------------------- +VPANEL VPanelHandle::Set(VPANEL pent) +{ + if (pent) + { + m_iPanelID = ivgui()->PanelToHandle(pent); + } + else + { + m_iPanelID = INVALID_PANEL; + } + return pent; +} + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to the tooltip object associated with the panel +//----------------------------------------------------------------------------- +BaseTooltip *Panel::GetTooltip() +{ + if (!m_pTooltips) + { + m_pTooltips = new TextTooltip(this, NULL); + m_bToolTipOverridden = false; + + if ( IsConsoleStylePanel() ) + { + m_pTooltips->SetEnabled( false ); + } + } + + return m_pTooltips; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetTooltip( BaseTooltip *pToolTip, const char *pszText ) +{ + if ( !m_bToolTipOverridden ) + { + // Remove the one we made, we're being overridden. + delete m_pTooltips; + } + + m_pTooltips = pToolTip; + m_bToolTipOverridden = true; + + if ( _tooltipText ) + { + delete [] _tooltipText; + _tooltipText = NULL; + } + + if ( pszText ) + { + int len = Q_strlen(pszText) + 1; + _tooltipText = new char[ len ]; + Q_strncpy( _tooltipText, pszText, len ); + } +} + +//----------------------------------------------------------------------------- +const char *Panel::GetEffectiveTooltipText() const +{ + if ( _tooltipText ) + { + return _tooltipText; + } + if ( m_pTooltips ) + { + const char *result = m_pTooltips->GetText(); + if ( result ) + { + return result; + } + } + return ""; +} + +//----------------------------------------------------------------------------- +// Purpose: sets the proportional flag on this panel and all it's children +//----------------------------------------------------------------------------- +void Panel::SetProportional(bool state) +{ + // only do something if the state changes + if( state != _flags.IsFlagSet( IS_PROPORTIONAL ) ) + { + _flags.SetFlag( IS_PROPORTIONAL, state ); + + for(int i=0;iSetProportional( IsProportional() ); + } + } + InvalidateLayout(); +} + + +void Panel::SetKeyBoardInputEnabled( bool state ) +{ + ipanel()->SetKeyBoardInputEnabled( GetVPanel(), state ); + for ( int i = 0; i < GetChildCount(); i++ ) + { + Panel *child = GetChild( i ); + if ( !child ) + { + continue; + } + child->SetKeyBoardInputEnabled( state ); + } + + // If turning off keyboard input enable, then make sure + // this panel is not the current key focus of a parent panel + if ( !state ) + { + Panel *pParent = GetParent(); + if ( pParent ) + { + if ( pParent->GetCurrentKeyFocus() == GetVPanel() ) + { + pParent->RequestFocusNext(); + } + } + } +} + +void Panel::SetMouseInputEnabled( bool state ) +{ + ipanel()->SetMouseInputEnabled( GetVPanel(), state ); + /* for(int i=0;iSetMouseInput(state); + }*/ + vgui::surface()->CalculateMouseVisible(); +} + +bool Panel::IsKeyBoardInputEnabled() +{ + return ipanel()->IsKeyBoardInputEnabled( GetVPanel() ); +} + +bool Panel::IsMouseInputEnabled() +{ + return ipanel()->IsMouseInputEnabled( GetVPanel() ); +} + +class CFloatProperty : public vgui::IPanelAnimationPropertyConverter +{ +public: + virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + kv->SetFloat( entry->name(), *(float *)data ); + } + + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + *(float *)data = kv->GetFloat( entry->name() ); + } + + virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + *(float *)data = atof( entry->defaultvalue() ); + } +}; + +class CProportionalFloatProperty : public vgui::IPanelAnimationPropertyConverter +{ +public: + virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + float f = *(float *)data; + f = scheme()->GetProportionalNormalizedValueEx( panel->GetScheme(), f ); + kv->SetFloat( entry->name(), f ); + } + + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + float f = kv->GetFloat( entry->name() ); + f = scheme()->GetProportionalScaledValueEx( panel->GetScheme(), f ); + *(float *)data = f; + } + + virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + float f = atof( entry->defaultvalue() ); + f = scheme()->GetProportionalScaledValueEx( panel->GetScheme(), f ); + *(float *)data = f; + } +}; + +class CIntProperty : public vgui::IPanelAnimationPropertyConverter +{ +public: + virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + kv->SetInt( entry->name(), *(int *)data ); + } + + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + *(int *)data = kv->GetInt( entry->name() ); + } + + virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + *(int *)data = atoi( entry->defaultvalue() ); + } +}; + +class CProportionalIntProperty : public vgui::IPanelAnimationPropertyConverter +{ +public: + virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + int i = *(int *)data; + i = scheme()->GetProportionalNormalizedValueEx( panel->GetScheme(), i ); + kv->SetInt( entry->name(), i ); + } + + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + int i = kv->GetInt( entry->name() ); + i = scheme()->GetProportionalScaledValueEx( panel->GetScheme(), i ); + *(int *)data = i; + } + virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + int i = atoi( entry->defaultvalue() ); + i = scheme()->GetProportionalScaledValueEx( panel->GetScheme(), i ); + *(int *)data = i; + } +}; + +class CProportionalIntWithScreenspacePropertyX : public vgui::IPanelAnimationPropertyConverter +{ +public: + int ExtractValue( Panel *panel, const char *pszKey ) + { + int iValue = 0; + bool bRightAlign = false; + bool bCenterAlign = false; + if (pszKey[0] == 'r' || pszKey[0] == 'R') + { + bRightAlign = true; + pszKey++; + } + else if (pszKey[0] == 'c' || pszKey[0] == 'C') + { + bCenterAlign = true; + pszKey++; + } + + // get the value + iValue = atoi(pszKey); + iValue = scheme()->GetProportionalScaledValueEx(panel->GetScheme(), iValue); + + int screenSize = GetScreenSize(); + // now correct the alignment + if ( bRightAlign ) + { + iValue = screenSize - iValue; + } + else if ( bCenterAlign ) + { + iValue = (screenSize / 2) + iValue; + } + + return iValue; + } + + virtual int GetScreenSize( void ) + { + int screenWide, screenTall; + surface()->GetScreenSize(screenWide, screenTall); + return screenWide; + } + + virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + // Won't work with this, don't use it. + Assert(0); + } + + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + *(int *)data = ExtractValue( panel, kv->GetString( entry->name() ) ); + } + virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + *(int *)data = ExtractValue( panel, entry->defaultvalue() ); + } +}; + +class CProportionalIntWithScreenspacePropertyY : public CProportionalIntWithScreenspacePropertyX +{ +public: + virtual int GetScreenSize( void ) + { + int screenWide, screenTall; + surface()->GetScreenSize(screenWide, screenTall); + return screenTall; + } +}; + +class CColorProperty : public vgui::IPanelAnimationPropertyConverter +{ +public: + virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + kv->SetColor( entry->name(), *(Color *)data ); + } + + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() ); + Assert( scheme ); + if ( scheme ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + + char const *colorName = kv->GetString( entry->name() ); + if ( !colorName || !colorName[0] ) + { + *(Color *)data = kv->GetColor( entry->name() ); + } + else + { + *(Color *)data = scheme->GetColor( colorName, Color( 0, 0, 0, 0 ) ); + } + } + } + + virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) + { + vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() ); + Assert( scheme ); + if ( scheme ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + *(Color *)data = scheme->GetColor( entry->defaultvalue(), Color( 0, 0, 0, 0 ) ); + } + } +}; + +class CBoolProperty : public vgui::IPanelAnimationPropertyConverter +{ +public: + virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + kv->SetInt( entry->name(), *(bool *)data ? 1 : 0 ); + } + + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + *(bool *)data = kv->GetInt( entry->name() ) ? true : false; + } + + virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + bool b = false; + if ( !stricmp( entry->defaultvalue(), "true" )|| + atoi( entry->defaultvalue() )!= 0 ) + { + b = true; + } + + *(bool *)data = b; + } +}; + +class CStringProperty : public vgui::IPanelAnimationPropertyConverter +{ +public: + virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + kv->SetString( entry->name(), (char *)data ); + } + + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + strcpy( (char *)data, kv->GetString( entry->name() ) ); + } + + virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + strcpy( ( char * )data, entry->defaultvalue() ); + } +}; + +class CHFontProperty : public vgui::IPanelAnimationPropertyConverter +{ +public: + virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() ); + Assert( scheme ); + if ( scheme ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + char const *fontName = scheme->GetFontName( *(HFont *)data ); + kv->SetString( entry->name(), fontName ); + } + } + + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() ); + Assert( scheme ); + if ( scheme ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + char const *fontName = kv->GetString( entry->name() ); + *(HFont *)data = scheme->GetFont( fontName, panel->IsProportional() ); + } + } + + virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) + { + vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() ); + Assert( scheme ); + if ( scheme ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + *(HFont *)data = scheme->GetFont( entry->defaultvalue(), panel->IsProportional() ); + } + } +}; + +class CTextureIdProperty : public vgui::IPanelAnimationPropertyConverter +{ +public: + virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + int currentId = *(int *)data; + + // lookup texture name for id + char texturename[ 512 ]; + if ( currentId != -1 && + surface()->DrawGetTextureFile( currentId, texturename, sizeof( texturename ) ) ) + { + kv->SetString( entry->name(), texturename ); + } + else + { + kv->SetString( entry->name(), "" ); + } + } + + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + + int currentId = -1; + + char const *texturename = kv->GetString( entry->name() ); + if ( texturename && texturename[ 0 ] ) + { + currentId = surface()->DrawGetTextureId( texturename ); + if ( currentId == -1 ) + { + currentId = surface()->CreateNewTextureID(); + } + surface()->DrawSetTextureFile( currentId, texturename, false, true ); + } + + *(int *)data = currentId; + } + + virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) + { + void *data = ( void * )( (*entry->m_pfnLookup)( panel ) ); + + int currentId = -1; + + char const *texturename = entry->defaultvalue(); + if ( texturename && texturename[ 0 ] ) + { + currentId = surface()->DrawGetTextureId( texturename ); + if ( currentId == -1 ) + { + currentId = surface()->CreateNewTextureID(); + } + surface()->DrawSetTextureFile( currentId, texturename, false, true ); + } + + *(int *)data = currentId; + } +}; + +static CFloatProperty floatconverter; +static CProportionalFloatProperty p_floatconverter; +static CIntProperty intconverter; +static CProportionalIntProperty p_intconverter; +static CProportionalIntWithScreenspacePropertyX p_screenspace_intconverter_X; +static CProportionalIntWithScreenspacePropertyY p_screenspace_intconverter_Y; +static CColorProperty colorconverter; +static CBoolProperty boolconverter; +static CStringProperty stringconverter; +static CHFontProperty fontconverter; +static CTextureIdProperty textureidconverter; +//static CProportionalXPosProperty xposconverter; +//static CProportionalYPosProperty yposconverter; + +static CUtlDict< IPanelAnimationPropertyConverter *, int > g_AnimationPropertyConverters; + +static IPanelAnimationPropertyConverter *FindConverter( char const *typeName ) +{ + int lookup = g_AnimationPropertyConverters.Find( typeName ); + if ( lookup == g_AnimationPropertyConverters.InvalidIndex() ) + return NULL; + + IPanelAnimationPropertyConverter *converter = g_AnimationPropertyConverters[ lookup ]; + return converter; +} + +void Panel::AddPropertyConverter( char const *typeName, IPanelAnimationPropertyConverter *converter ) +{ + int lookup = g_AnimationPropertyConverters.Find( typeName ); + if ( lookup != g_AnimationPropertyConverters.InvalidIndex() ) + { + Msg( "Already have converter for type %s, ignoring...\n", typeName ); + return; + } + + g_AnimationPropertyConverters.Insert( typeName, converter ); +} + +//----------------------------------------------------------------------------- +// Purpose: Static method to initialize all needed converters +//----------------------------------------------------------------------------- +void Panel::InitPropertyConverters( void ) +{ + static bool initialized = false; + if ( initialized ) + return; + initialized = true; + + AddPropertyConverter( "float", &floatconverter ); + AddPropertyConverter( "int", &intconverter ); + AddPropertyConverter( "Color", &colorconverter ); + //AddPropertyConverter( "vgui::Color", &colorconverter ); + AddPropertyConverter( "bool", &boolconverter ); + AddPropertyConverter( "char", &stringconverter ); + AddPropertyConverter( "string", &stringconverter ); + AddPropertyConverter( "HFont", &fontconverter ); + AddPropertyConverter( "vgui::HFont", &fontconverter ); + + // This is an aliased type for proportional float + AddPropertyConverter( "proportional_float", &p_floatconverter ); + AddPropertyConverter( "proportional_int", &p_intconverter ); + + AddPropertyConverter( "proportional_xpos", &p_screenspace_intconverter_X ); + AddPropertyConverter( "proportional_ypos", &p_screenspace_intconverter_Y ); + + AddPropertyConverter( "textureid", &textureidconverter ); +} + +bool Panel::InternalRequestInfo( PanelAnimationMap *map, KeyValues *outputData ) +{ + if ( !map ) + return false; + + Assert( outputData ); + + char const *name = outputData->GetName(); + + PanelAnimationMapEntry *e = FindPanelAnimationEntry( name, map ); + if ( e ) + { + IPanelAnimationPropertyConverter *converter = FindConverter( e->type() ); + if ( converter ) + { + converter->GetData( this, outputData, e ); + return true; + } + } + + return false; +} + +bool Panel::InternalSetInfo( PanelAnimationMap *map, KeyValues *inputData ) +{ + if ( !map ) + return false; + + Assert( inputData ); + + char const *name = inputData->GetName(); + + PanelAnimationMapEntry *e = FindPanelAnimationEntry( name, map ); + if ( e ) + { + IPanelAnimationPropertyConverter *converter = FindConverter( e->type() ); + if ( converter ) + { + converter->SetData( this, inputData, e ); + return true; + } + } + + return false; +} + +PanelAnimationMapEntry *Panel::FindPanelAnimationEntry( char const *scriptname, PanelAnimationMap *map ) +{ + if ( !map ) + return NULL; + + Assert( scriptname ); + + // Look through mapping for entry + int c = map->entries.Count(); + for ( int i = 0; i < c; i++ ) + { + PanelAnimationMapEntry *e = &map->entries[ i ]; + + if ( !stricmp( e->name(), scriptname ) ) + { + return e; + } + } + + // Recurse + if ( map->baseMap ) + { + return FindPanelAnimationEntry( scriptname, map->baseMap ); + } + + return NULL; +} + +// Recursively invoke settings for PanelAnimationVars +void Panel::InternalApplySettings( PanelAnimationMap *map, KeyValues *inResourceData) +{ + // Loop through keys + KeyValues *kv; + + for ( kv = inResourceData->GetFirstSubKey(); kv; kv = kv->GetNextKey() ) + { + char const *varname = kv->GetName(); + + PanelAnimationMapEntry *entry = FindPanelAnimationEntry( varname, GetAnimMap() ); + if ( entry ) + { + // Set value to value from script + IPanelAnimationPropertyConverter *converter = FindConverter( entry->type() ); + if ( converter ) + { + converter->SetData( this, inResourceData, entry ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets the default values of all CPanelAnimationVars +//----------------------------------------------------------------------------- +void Panel::InternalInitDefaultValues( PanelAnimationMap *map ) +{ + _flags.ClearFlag( NEEDS_DEFAULT_SETTINGS_APPLIED ); + + // Look through mapping for entry + int c = map->entries.Count(); + for ( int i = 0; i < c; i++ ) + { + PanelAnimationMapEntry *e = &map->entries[ i ]; + Assert( e ); + IPanelAnimationPropertyConverter *converter = FindConverter( e->type() ); + if ( !converter ) + continue; + + converter->InitFromDefault( this, e ); + } + + if ( map->baseMap ) + { + InternalInitDefaultValues( map->baseMap ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +int Panel::GetPaintBackgroundType() +{ + return m_nPaintBackgroundType; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : w - +// h - +//----------------------------------------------------------------------------- +void Panel::GetCornerTextureSize( int& w, int& h ) +{ + if ( m_nBgTextureId1 == -1 ) + { + w = h = 0; + return; + } + surface()->DrawGetTextureSize(m_nBgTextureId1, w, h); +} + +//----------------------------------------------------------------------------- +// Purpose: draws a selection box +//----------------------------------------------------------------------------- +void Panel::DrawBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha, bool hollow /*=false*/ ) +{ + if ( m_nBgTextureId1 == -1 || + m_nBgTextureId2 == -1 || + m_nBgTextureId3 == -1 || + m_nBgTextureId4 == -1 ) + { + return; + } + + color[3] *= normalizedAlpha; + + // work out our bounds + int cornerWide, cornerTall; + GetCornerTextureSize( cornerWide, cornerTall ); + + // draw the background in the areas not occupied by the corners + // draw it in three horizontal strips + surface()->DrawSetColor(color); + surface()->DrawFilledRect(x + cornerWide, y, x + wide - cornerWide, y + cornerTall); + if ( !hollow ) + { + surface()->DrawFilledRect(x, y + cornerTall, x + wide, y + tall - cornerTall); + } + else + { + surface()->DrawFilledRect(x, y + cornerTall, x + cornerWide, y + tall - cornerTall); + surface()->DrawFilledRect(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall); + } + surface()->DrawFilledRect(x + cornerWide, y + tall - cornerTall, x + wide - cornerWide, y + tall); + + // draw the corners + + //============================================================================= + // HPE_BEGIN: + // [tj] We now check each individual corner and decide whether to draw it straight or rounded + //============================================================================= + //TOP-LEFT + if (ShouldDrawTopLeftCornerRounded()) + { + surface()->DrawSetTexture(m_nBgTextureId1); + surface()->DrawTexturedRect(x, y, x + cornerWide, y + cornerTall); + } + else + { + surface()->DrawFilledRect(x, y, x + cornerWide, y + cornerTall); + } + + + //TOP-RIGHT + if (ShouldDrawTopRightCornerRounded()) + { + surface()->DrawSetTexture(m_nBgTextureId2); + surface()->DrawTexturedRect(x + wide - cornerWide, y, x + wide, y + cornerTall); + } + else + { + surface()->DrawFilledRect(x + wide - cornerWide, y, x + wide, y + cornerTall); + } + + //BOTTOM-LEFT + if (ShouldDrawBottomLeftCornerRounded()) + { + surface()->DrawSetTexture(m_nBgTextureId4); + surface()->DrawTexturedRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall); + } + else + { + surface()->DrawFilledRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall); + } + + + //BOTTOM-RIGHT + if (ShouldDrawBottomRightCornerRounded()) + { + surface()->DrawSetTexture(m_nBgTextureId3); + surface()->DrawTexturedRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall); + } + else + { + surface()->DrawFilledRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall); + } + //============================================================================= + // HPE_END + //============================================================================= +} + +void Panel::DrawBoxFade(int x, int y, int wide, int tall, Color color, float normalizedAlpha, unsigned int alpha0, unsigned int alpha1, bool bHorizontal, bool hollow /*=false*/ ) +{ + if ( m_nBgTextureId1 == -1 || + m_nBgTextureId2 == -1 || + m_nBgTextureId3 == -1 || + m_nBgTextureId4 == -1 || + surface()->DrawGetAlphaMultiplier() == 0 ) + { + return; + } + + color[3] *= normalizedAlpha; + + // work out our bounds + int cornerWide, cornerTall; + GetCornerTextureSize( cornerWide, cornerTall ); + + if ( !bHorizontal ) + { + // draw the background in the areas not occupied by the corners + // draw it in three horizontal strips + surface()->DrawSetColor(color); + surface()->DrawFilledRectFade(x + cornerWide, y, x + wide - cornerWide, y + cornerTall, alpha0, alpha0, bHorizontal ); + if ( !hollow ) + { + surface()->DrawFilledRectFade(x, y + cornerTall, x + wide, y + tall - cornerTall, alpha0, alpha1, bHorizontal); + } + else + { + surface()->DrawFilledRectFade(x, y + cornerTall, x + cornerWide, y + tall - cornerTall, alpha0, alpha1, bHorizontal); + surface()->DrawFilledRectFade(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall, alpha0, alpha1, bHorizontal); + } + surface()->DrawFilledRectFade(x + cornerWide, y + tall - cornerTall, x + wide - cornerWide, y + tall, alpha1, alpha1, bHorizontal); + } + else + { + // draw the background in the areas not occupied by the corners + // draw it in three horizontal strips + surface()->DrawSetColor(color); + surface()->DrawFilledRectFade(x, y + cornerTall, x + cornerWide, y + tall - cornerTall, alpha0, alpha0, bHorizontal ); + if ( !hollow ) + { + surface()->DrawFilledRectFade(x + cornerWide, y, x + wide - cornerWide, y + tall, alpha0, alpha1, bHorizontal); + } + else + { + // FIXME: Hollow horz version not implemented + //surface()->DrawFilledRectFade(x, y + cornerTall, x + cornerWide, y + tall - cornerTall, alpha0, alpha1, bHorizontal); + //surface()->DrawFilledRectFade(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall, alpha0, alpha1, bHorizontal); + } + surface()->DrawFilledRectFade(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall, alpha1, alpha1, bHorizontal); + } + + float fOldAlpha = color[ 3 ]; + int iAlpha0 = fOldAlpha * ( static_cast( alpha0 ) / 255.0f ); + int iAlpha1 = fOldAlpha * ( static_cast( alpha1 ) / 255.0f ); + + // draw the corners + if ( !bHorizontal ) + { + color[ 3 ] = iAlpha0; + surface()->DrawSetColor( color ); + surface()->DrawSetTexture(m_nBgTextureId1); + surface()->DrawTexturedRect(x, y, x + cornerWide, y + cornerTall); + surface()->DrawSetTexture(m_nBgTextureId2); + surface()->DrawTexturedRect(x + wide - cornerWide, y, x + wide, y + cornerTall); + + color[ 3 ] = iAlpha1; + surface()->DrawSetColor( color ); + surface()->DrawSetTexture(m_nBgTextureId3); + surface()->DrawTexturedRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall); + surface()->DrawSetTexture(m_nBgTextureId4); + surface()->DrawTexturedRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall); + } + else + { + color[ 3 ] = iAlpha0; + surface()->DrawSetColor( color ); + surface()->DrawSetTexture(m_nBgTextureId1); + surface()->DrawTexturedRect(x, y, x + cornerWide, y + cornerTall); + surface()->DrawSetTexture(m_nBgTextureId4); + surface()->DrawTexturedRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall); + + color[ 3 ] = iAlpha1; + surface()->DrawSetColor( color ); + surface()->DrawSetTexture(m_nBgTextureId2); + surface()->DrawTexturedRect(x + wide - cornerWide, y, x + wide, y + cornerTall); + surface()->DrawSetTexture(m_nBgTextureId3); + surface()->DrawTexturedRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : x - +// y - +// wide - +// tall - +// color - +// normalizedAlpha - +//----------------------------------------------------------------------------- +void Panel::DrawHollowBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha ) +{ + DrawBox( x, y, wide, tall, color, normalizedAlpha, true ); +} + +//============================================================================= +// HPE_BEGIN: +// [menglish] Draws a hollow box similar to the already existing draw hollow box function, but takes the indents as params +//============================================================================= + +void Panel::DrawHollowBox( int x, int y, int wide, int tall, Color color, float normalizedAlpha, int cornerWide, int cornerTall ) +{ + if ( m_nBgTextureId1 == -1 || + m_nBgTextureId2 == -1 || + m_nBgTextureId3 == -1 || + m_nBgTextureId4 == -1 ) + { + return; + } + + color[3] *= normalizedAlpha; + + // draw the background in the areas not occupied by the corners + // draw it in three horizontal strips + surface()->DrawSetColor(color); + surface()->DrawFilledRect(x + cornerWide, y, x + wide - cornerWide, y + cornerTall); + surface()->DrawFilledRect(x, y + cornerTall, x + cornerWide, y + tall - cornerTall); + surface()->DrawFilledRect(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall); + surface()->DrawFilledRect(x + cornerWide, y + tall - cornerTall, x + wide - cornerWide, y + tall); + + // draw the corners + surface()->DrawSetTexture(m_nBgTextureId1); + surface()->DrawTexturedRect(x, y, x + cornerWide, y + cornerTall); + surface()->DrawSetTexture(m_nBgTextureId2); + surface()->DrawTexturedRect(x + wide - cornerWide, y, x + wide, y + cornerTall); + surface()->DrawSetTexture(m_nBgTextureId3); + surface()->DrawTexturedRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall); + surface()->DrawSetTexture(m_nBgTextureId4); + surface()->DrawTexturedRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall); +} + +//============================================================================= +// HPE_END +//============================================================================= + +//----------------------------------------------------------------------------- +// Purpose: draws a selection box +//----------------------------------------------------------------------------- +void Panel::DrawTexturedBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha ) +{ + if ( m_nBgTextureId1 == -1 ) + return; + + color[3] *= normalizedAlpha; + + surface()->DrawSetColor( color ); + surface()->DrawSetTexture(m_nBgTextureId1); + surface()->DrawTexturedRect(x, y, x + wide, y + tall); +} + +//----------------------------------------------------------------------------- +// Purpose: Marks this panel as draggable (note that children will chain to their parents to see if any parent is draggable) +// Input : enabled - +//----------------------------------------------------------------------------- +void Panel::SetDragEnabled( bool enabled ) +{ +#if defined( VGUI_USEDRAGDROP ) + // If turning it off, quit dragging if mid-drag + if ( !enabled && + m_pDragDrop->m_bDragging ) + { + OnFinishDragging( false, (MouseCode)-1 ); + } + m_pDragDrop->m_bDragEnabled = enabled; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Panel::IsDragEnabled() const +{ +#if defined( VGUI_USEDRAGDROP ) + return m_pDragDrop->m_bDragEnabled; +#endif + return false; +} + +void Panel::SetShowDragHelper( bool enabled ) +{ +#if defined( VGUI_USEDRAGDROP ) + m_pDragDrop->m_bShowDragHelper = enabled; +#endif +} + +// Use this to prevent chaining up from a parent which can mess with mouse functionality if you don't want to chain up from a child panel to the best +// draggable parent. +void Panel::SetBlockDragChaining( bool block ) +{ +#if defined( VGUI_USEDRAGDROP ) + m_pDragDrop->m_bPreventChaining = block; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Panel::IsBlockingDragChaining() const +{ +#if defined( VGUI_USEDRAGDROP ) + return m_pDragDrop->m_bPreventChaining; +#endif + return true; +} + + +//----------------------------------------------------------------------------- +// accessors for m_nDragStartTolerance +//----------------------------------------------------------------------------- +int Panel::GetDragStartTolerance() const +{ +#if defined( VGUI_USEDRAGDROP ) + return m_pDragDrop->m_nDragStartTolerance; +#endif + return 0; +} + +void Panel::SetDragSTartTolerance( int nTolerance ) +{ +#if defined( VGUI_USEDRAGDROP ) + m_pDragDrop->m_nDragStartTolerance = nTolerance; +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: Marks this panel as droppable ( note that children will chain to their parents to see if any parent is droppable) +// Input : enabled - +//----------------------------------------------------------------------------- +void Panel::SetDropEnabled( bool enabled, float flHoverContextTime /* = 0.0f */ ) +{ +#if defined( VGUI_USEDRAGDROP ) + m_pDragDrop->m_bDropEnabled = enabled; + m_pDragDrop->m_flHoverContextTime = flHoverContextTime; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Panel::IsDropEnabled() const +{ +#if defined( VGUI_USEDRAGDROP ) + return m_pDragDrop->m_bDropEnabled; +#endif + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Chains up to any parent +// 1) marked DropEnabled; and +// 2) willing to accept the drop payload +// Input : - +// Output : Panel +//----------------------------------------------------------------------------- +Panel *Panel::GetDropTarget( CUtlVector< KeyValues * >& msglist ) +{ +#if defined( VGUI_USEDRAGDROP ) + // Found one + if ( m_pDragDrop->m_bDropEnabled && + IsDroppable( msglist ) ) + { + return this; + } + + // Chain up + if ( GetParent() ) + { + return GetParent()->GetDropTarget( msglist ); + } +#endif + // No luck + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Chains up to first parent marked DragEnabled +// Input : - +// Output : Panel +//----------------------------------------------------------------------------- +Panel *Panel::GetDragPanel() +{ +#if defined( VGUI_USEDRAGDROP ) + // If we encounter a blocker, stop chaining + if ( m_pDragDrop->m_bPreventChaining ) + return NULL; + + if ( m_pDragDrop->m_bDragEnabled ) + return this; + + // Chain up + if ( GetParent() ) + { + return GetParent()->GetDragPanel(); + } +#endif + // No luck + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void Panel::OnStartDragging() +{ +#if defined( VGUI_USEDRAGDROP ) + // Only left mouse initiates drag/drop. + // FIXME: Revisit? + if ( !input()->IsMouseDown( MOUSE_LEFT ) ) + return; + + if ( !m_pDragDrop->m_bDragEnabled ) + return; + + if ( m_pDragDrop->m_bDragging ) + return; + + g_DragDropCapture = this; + + m_pDragDrop->m_bDragStarted = false; + m_pDragDrop->m_bDragging = true; + input()->GetCursorPos( m_pDragDrop->m_nStartPos[ 0 ], m_pDragDrop->m_nStartPos[ 1 ] ); + m_pDragDrop->m_nLastPos[ 0 ] = m_pDragDrop->m_nStartPos[ 0 ]; + m_pDragDrop->m_nLastPos[ 1 ] = m_pDragDrop->m_nStartPos[ 1 ]; + + OnContinueDragging(); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Called if drag drop is started but not dropped on top of droppable panel... +// Input : - +//----------------------------------------------------------------------------- +void Panel::OnDragFailed( CUtlVector< KeyValues * >& msglist ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void Panel::OnFinishDragging( bool mousereleased, MouseCode code, bool abort /*= false*/ ) +{ +#if defined( VGUI_USEDRAGDROP ) + g_DragDropCapture = NULL; + + if ( !m_pDragDrop->m_bDragEnabled ) + return; + + Assert( m_pDragDrop->m_bDragging ); + + if ( !m_pDragDrop->m_bDragging ) + return; + + int x, y; + input()->GetCursorPos( x, y ); + + m_pDragDrop->m_nLastPos[ 0 ] = x; + m_pDragDrop->m_nLastPos[ 1 ] = y; + + if ( s_DragDropHelper.Get() ) + { + s_DragDropHelper->RemovePanel( this ); + } + + m_pDragDrop->m_bDragging = false; + + CUtlVector< KeyValues * >& data = m_pDragDrop->m_DragData; + int c = data.Count(); + + Panel *target = NULL; + bool shouldDrop = false; + + if ( m_pDragDrop->m_bDragStarted ) + { + char cmd[ 256 ]; + Q_strncpy( cmd, "default", sizeof( cmd ) ); + + if ( mousereleased && + m_pDragDrop->m_hCurrentDrop != 0 && + m_pDragDrop->m_hDropContextMenu.Get() ) + { + Menu *menu = m_pDragDrop->m_hDropContextMenu; + + VPANEL hover = menu->IsWithinTraverse( x, y, false ); + if ( hover ) + { + Panel *pHover = ipanel()->GetPanel( hover, GetModuleName() ); + if ( pHover ) + { + // Figure out if it's a menu item... + int c = menu->GetItemCount(); + for ( int i = 0; i < c; ++i ) + { + int id = menu->GetMenuID( i ); + MenuItem *item = menu->GetMenuItem( id ); + if ( item == pHover ) + { + KeyValues *command = item->GetCommand(); + if ( command ) + { + char const *p = command->GetString( "command", "" ); + if ( p && p[ 0 ] ) + { + Q_strncpy( cmd, p, sizeof( cmd ) ); + } + } + } + } + } + } + + delete menu; + m_pDragDrop->m_hDropContextMenu = NULL; + } + + for ( int i = 0 ; i < c; ++i ) + { + KeyValues *msg = data[ i ]; + + msg->SetString( "command", cmd ); + + msg->SetInt( "screenx", x ); + msg->SetInt( "screeny", y ); + } + + target = m_pDragDrop->m_hCurrentDrop.Get(); + if ( target && !abort ) + { + int localmousex = x, localmousey = y; + // Convert screen space coordintes to coordinates relative to drop window + target->ScreenToLocal( localmousex, localmousey ); + + for ( int i = 0 ; i < c; ++i ) + { + KeyValues *msg = data[ i ]; + + msg->SetInt( "x", localmousex ); + msg->SetInt( "y", localmousey ); + } + + shouldDrop = true; + } + + if ( !shouldDrop ) + { + OnDragFailed( data ); + } + } + + m_pDragDrop->m_bDragStarted = false; + m_pDragDrop->m_DragPanels.RemoveAll(); + m_pDragDrop->m_hCurrentDrop = NULL; + + // Copy data ptrs out of data because OnPanelDropped might cause this panel to be deleted + // and our this ptr will be hosed... + CUtlVector< KeyValues * > temp; + for ( int i = 0 ; i < c; ++i ) + { + temp.AddToTail( data[ i ] ); + } + data.RemoveAll(); + + if ( shouldDrop && target ) + { + target->OnPanelDropped( temp ); + } + for ( int i = 0 ; i < c ; ++i ) + { + temp[ i ]->deleteThis(); + } +#endif +} + +void Panel::OnDropContextHoverShow( CUtlVector< KeyValues * >& msglist ) +{ +} + +void Panel::OnDropContextHoverHide( CUtlVector< KeyValues * >& msglist ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *msg - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Panel::IsDroppable( CUtlVector< KeyValues * >& msglist ) +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : startx - +// starty - +// mx - +// my - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Panel::CanStartDragging( int startx, int starty, int mx, int my ) +{ +#if defined( VGUI_USEDRAGDROP ) + if ( IsStartDragWhenMouseExitsPanel() ) + { + ScreenToLocal( mx, my ); + if ( mx < 0 || my < 0 ) + return true; + if ( mx > GetWide() || my > GetTall() ) + return true; + + return false; + } + + int deltax = abs( mx - startx ); + int deltay = abs( my - starty ); + if ( deltax > m_pDragDrop->m_nDragStartTolerance || + deltay > m_pDragDrop->m_nDragStartTolerance ) + { + return true; + } +#endif + return false; +} + +HCursor Panel::GetDropCursor( CUtlVector< KeyValues * >& msglist ) +{ + return dc_arrow; +} + +bool IsSelfDroppable( CUtlVector< KeyValues * > &dragData ) +{ + if ( dragData.Count() == 0 ) + return false; + + KeyValues *pKeyValues( dragData[ 0 ] ); + if ( !pKeyValues ) + return false; + + return pKeyValues->GetInt( "selfDroppable" ) != 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void Panel::OnContinueDragging() +{ +#if defined( VGUI_USEDRAGDROP ) + if ( !m_pDragDrop->m_bDragEnabled ) + return; + + if ( !m_pDragDrop->m_bDragging ) + return; + + int x, y; + input()->GetCursorPos( x, y ); + + // Update last position + m_pDragDrop->m_nLastPos[ 0 ] = x; + m_pDragDrop->m_nLastPos[ 1 ] = y; + + if ( !m_pDragDrop->m_bDragStarted ) + { + if ( CanStartDragging( m_pDragDrop->m_nStartPos[ 0 ], m_pDragDrop->m_nStartPos[ 1 ], x, y ) ) + { + m_pDragDrop->m_bDragStarted = true; + CreateDragData(); + } + else + { + return; + } + } + + if ( !s_DragDropHelper.Get() && m_pDragDrop->m_bShowDragHelper ) + { + s_DragDropHelper = new CDragDropHelperPanel(); + s_DragDropHelper->SetKeyBoardInputEnabled( false ); + s_DragDropHelper->SetMouseInputEnabled( false ); + Assert( s_DragDropHelper.Get() ); + } + + if ( !s_DragDropHelper.Get() ) + return; + + s_DragDropHelper->AddPanel( this ); + + Assert( m_pDragDrop->m_DragData.Count() ); + + vgui::PHandle oldDrop = m_pDragDrop->m_hCurrentDrop; + + // See what's under that + m_pDragDrop->m_hCurrentDrop = NULL; + + // Search under mouse pos... + Panel *dropTarget = FindDropTargetPanel(); + if ( dropTarget ) + { + dropTarget = dropTarget->GetDropTarget( m_pDragDrop->m_DragData ); + } + + // it's not okay until we find a droppable panel + surface()->SetCursor( dc_no ); + + if ( dropTarget ) + { + if ( dropTarget != this || IsSelfDroppable( m_pDragDrop->m_DragData ) ) + { + m_pDragDrop->m_hCurrentDrop = dropTarget; + surface()->SetCursor( dropTarget->GetDropCursor( m_pDragDrop->m_DragData ) ); + } + } + + if ( m_pDragDrop->m_hCurrentDrop.Get() != oldDrop.Get() ) + { + if ( oldDrop.Get() ) + { + oldDrop->OnPanelExitedDroppablePanel( m_pDragDrop->m_DragData ); + } + + if ( m_pDragDrop->m_hCurrentDrop.Get() ) + { + m_pDragDrop->m_hCurrentDrop->OnPanelEnteredDroppablePanel( m_pDragDrop->m_DragData ); + m_pDragDrop->m_hCurrentDrop->OnDropContextHoverHide( m_pDragDrop->m_DragData ); + + // Reset hover time + m_pDragDrop->m_lDropHoverTime = system()->GetTimeMillis(); + m_pDragDrop->m_bDropMenuShown = false; + } + + // Discard any stale context menu... + if ( m_pDragDrop->m_hDropContextMenu.Get() ) + { + delete m_pDragDrop->m_hDropContextMenu.Get(); + } + } + + if ( m_pDragDrop->m_hCurrentDrop != 0 && + m_pDragDrop->m_hDropContextMenu.Get() ) + { + Menu *menu = m_pDragDrop->m_hDropContextMenu; + + VPANEL hover = menu->IsWithinTraverse( x, y, false ); + if ( hover ) + { + Panel *pHover = ipanel()->GetPanel( hover, GetModuleName() ); + if ( pHover ) + { + // Figure out if it's a menu item... + int c = menu->GetItemCount(); + for ( int i = 0; i < c; ++i ) + { + int id = menu->GetMenuID( i ); + MenuItem *item = menu->GetMenuItem( id ); + if ( item == pHover ) + { + menu->SetCurrentlyHighlightedItem( id ); + } + } + } + } + else + { + menu->ClearCurrentlyHighlightedItem(); + } + } +#endif +} + +#if defined( VGUI_USEDRAGDROP ) +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : DragDrop_t +//----------------------------------------------------------------------------- +DragDrop_t *Panel::GetDragDropInfo() +{ + Assert( m_pDragDrop ); + return m_pDragDrop; +} +#endif + +void Panel::OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles ) +{ + // Nothing here +} + +//----------------------------------------------------------------------------- +// Purpose: Virtual method to allow panels to add to the default values +// Input : *msg - +//----------------------------------------------------------------------------- +void Panel::OnCreateDragData( KeyValues *msg ) +{ + // These values are filled in for you: + // "panel" ptr to panel being dropped + // "screenx", "screeny" - drop cursor pos in screen space + // "x", "y" - drop coordinates relative to this window (the window being dropped upon) +} + +// Called if m_flHoverContextTime was non-zero, allows droppee to preview the drop data and show an appropriate menu +bool Panel::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ) +{ + return false; +} + +void Panel::CreateDragData() +{ +#if defined( VGUI_USEDRAGDROP ) + int i, c; + + if ( m_pDragDrop->m_DragData.Count() ) + { + return; + } + + PHandle h; + h = this; + m_pDragDrop->m_DragPanels.AddToTail( h ); + + CUtlVector< Panel * > temp; + OnGetAdditionalDragPanels( temp ); + c = temp.Count(); + for ( i = 0; i < c; ++i ) + { + h = temp[ i ]; + m_pDragDrop->m_DragPanels.AddToTail( h ); + } + + c = m_pDragDrop->m_DragPanels.Count(); + for ( i = 0 ; i < c; ++i ) + { + Panel *sibling = m_pDragDrop->m_DragPanels[ i ].Get(); + if ( !sibling ) + { + continue; + } + + KeyValues *msg = new KeyValues( "DragDrop" ); + msg->SetPtr( "panel", sibling ); + + sibling->OnCreateDragData( msg ); + + m_pDragDrop->m_DragData.AddToTail( msg ); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : KeyValues +//----------------------------------------------------------------------------- +void Panel::GetDragData( CUtlVector< KeyValues * >& list ) +{ +#if defined( VGUI_USEDRAGDROP ) + int i, c; + + list.RemoveAll(); + + c = m_pDragDrop->m_DragData.Count(); + for ( i = 0 ; i < c; ++i ) + { + list.AddToTail( m_pDragDrop->m_DragData[ i ] ); + } +#endif +} + +#if defined( VGUI_USEDRAGDROP ) +CDragDropHelperPanel::CDragDropHelperPanel() : BaseClass( NULL, "DragDropHelper" ) +{ + SetVisible( true ); + SetPaintEnabled( false ); + SetPaintBackgroundEnabled( false ); + SetMouseInputEnabled( false ); + SetKeyBoardInputEnabled( false ); + // SetCursor( dc_none ); + ipanel()->SetTopmostPopup( GetVPanel(), true ); + int w, h; + surface()->GetScreenSize( w, h ); + SetBounds( 0, 0, w, h ); + + SetPostChildPaintEnabled( true ); + + MakePopup( false ); +} + +VPANEL CDragDropHelperPanel::IsWithinTraverse(int x, int y, bool traversePopups) +{ + return (VPANEL)0; +} + +void CDragDropHelperPanel::PostChildPaint() +{ + int c = m_PaintList.Count(); + for ( int i = c - 1; i >= 0 ; --i ) + { + DragHelperPanel_t& data = m_PaintList[ i ]; + + Panel *panel = data.m_hPanel.Get(); + if ( !panel ) + { + m_PaintList.Remove( i ); + continue; + } + + Panel *dropPanel = panel->GetDragDropInfo()->m_hCurrentDrop.Get(); + if ( panel ) + { + if ( !dropPanel ) + { + panel->OnDraggablePanelPaint(); + } + else + { + CUtlVector< Panel * > temp; + CUtlVector< PHandle >& data = panel->GetDragDropInfo()->m_DragPanels; + CUtlVector< KeyValues * >& msglist = panel->GetDragDropInfo()->m_DragData; + int i, c; + c = data.Count(); + for ( i = 0; i < c ; ++i ) + { + Panel *pPanel = data[ i ].Get(); + if ( pPanel ) + { + temp.AddToTail( pPanel ); + } + } + + dropPanel->OnDroppablePanelPaint( msglist, temp ); + } + } + } + + if ( c == 0 ) + { + MarkForDeletion(); + } +} + +void CDragDropHelperPanel::AddPanel( Panel *current ) +{ + if ( !current ) + return; + + Menu *hover = current->GetDragDropInfo()->m_hDropContextMenu.Get(); + + surface()->MovePopupToFront( GetVPanel() ); + if ( hover && hover->IsPopup() ) + { + surface()->MovePopupToFront( hover->GetVPanel() ); + } + + int c = m_PaintList.Count(); + for ( int i = 0; i < c; ++i ) + { + if ( m_PaintList[ i ].m_hPanel.Get() == current ) + return; + } + + DragHelperPanel_t data; + data.m_hPanel = current; + m_PaintList.AddToTail( data ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *search - +//----------------------------------------------------------------------------- +void CDragDropHelperPanel::RemovePanel( Panel *search ) +{ + int c = m_PaintList.Count(); + for ( int i = c - 1 ; i >= 0; --i ) + { + if ( m_PaintList[ i ].m_hPanel.Get() == search ) + { + m_PaintList.Remove( i ); + return; + } + } +} +#endif +//----------------------------------------------------------------------------- +// Purpose: Enumerates panels under mouse x,y +// Input : panelList - +// x - +// y - +// check - +//----------------------------------------------------------------------------- +void Panel::FindDropTargetPanel_R( CUtlVector< VPANEL >& panelList, int x, int y, VPANEL check ) +{ +#if defined( VGUI_USEDRAGDROP ) + if ( !ipanel()->IsFullyVisible( check ) ) + return; + + if ( ::ShouldHandleInputMessage( check ) && ipanel()->IsWithinTraverse( check, x, y, false ) ) + { + panelList.AddToTail( check ); + } + + CUtlVector< VPANEL > &children = ipanel()->GetChildren( check ); + int childCount = children.Count(); + for ( int i = 0; i < childCount; i++ ) + { + VPANEL child = children[ i ]; + FindDropTargetPanel_R( panelList, x, y, child ); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Panel +//----------------------------------------------------------------------------- +Panel *Panel::FindDropTargetPanel() +{ +#if defined( VGUI_USEDRAGDROP ) + if ( !s_DragDropHelper.Get() ) + return NULL; + + CUtlVector< VPANEL > hits; + + int x, y; + input()->GetCursorPos( x, y ); + + VPANEL embedded = surface()->GetEmbeddedPanel(); + VPANEL helper = s_DragDropHelper.Get()->GetVPanel(); + + if ( surface()->IsCursorVisible() && surface()->IsWithin(x, y) ) + { + // faster version of code below + // checks through each popup in order, top to bottom windows + int c = surface()->GetPopupCount(); + for (int i = c - 1; i >= 0 && hits.Count() == 0; i--) + { + VPANEL popup = surface()->GetPopup(i); + if ( popup == embedded ) + continue; + + // Don't return helper panel!!! + if ( popup == helper ) + continue; + + if ( !ipanel()->IsFullyVisible( popup ) ) + continue; + + FindDropTargetPanel_R( hits, x, y, popup ); + } + + // Check embedded + if ( !hits.Count() ) + { + FindDropTargetPanel_R( hits, x, y, embedded ); + } + } + + // Nothing under mouse... + if ( !hits.Count() ) + return NULL; + + // Return topmost panel under mouse, if it's visible to this .dll + Panel *panel = NULL; + int nCount = hits.Count(); + while ( --nCount >= 0 ) + { + panel = ipanel()->GetPanel( hits[ nCount ], GetModuleName() ); + if ( panel ) + return panel; + } +#endif + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Mouse is on draggable panel and has started moving, but is not over a droppable panel yet +// Input : - +//----------------------------------------------------------------------------- +void Panel::OnDraggablePanelPaint() +{ +#if defined( VGUI_USEDRAGDROP ) + int sw, sh; + GetSize( sw, sh ); + + int x, y; + input()->GetCursorPos( x, y ); + int w, h; + + w = min( sw, 80 ); + h = min( sh, 80 ); + x -= ( w >> 1 ); + y -= ( h >> 1 ); + + surface()->DrawSetColor( m_clrDragFrame ); + surface()->DrawOutlinedRect( x, y, x + w, y + h ); + + if ( m_pDragDrop->m_DragPanels.Count() > 1 ) + { + surface()->DrawSetTextColor( m_clrDragFrame ); + surface()->DrawSetTextFont( m_infoFont ); + surface()->DrawSetTextPos( x + 5, y + 2 ); + + wchar_t sz[ 64 ]; + V_swprintf_safe( sz, L"[ %i ]", m_pDragDrop->m_DragPanels.Count() ); + + surface()->DrawPrintText( sz, wcslen( sz ) ); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Mouse is now over a droppable panel +// Input : *dragPanel - +//----------------------------------------------------------------------------- +void Panel::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ) +{ +#if defined( VGUI_USEDRAGDROP ) + if ( !dragPanels.Count() ) + return; + + // Convert this panel's bounds to screen space + int w, h; + GetSize( w, h ); + + int x, y; + x = y = 0; + LocalToScreen( x, y ); + + surface()->DrawSetColor( m_clrDropFrame ); + // Draw 2 pixel frame + surface()->DrawOutlinedRect( x, y, x + w, y + h ); + surface()->DrawOutlinedRect( x+1, y+1, x + w-1, y + h-1 ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Color +//----------------------------------------------------------------------------- +Color Panel::GetDropFrameColor() +{ +#if defined( VGUI_USEDRAGDROP ) + return m_clrDropFrame; +#endif + return Color(0, 0, 0, 0); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Color +//----------------------------------------------------------------------------- +Color Panel::GetDragFrameColor() +{ +#if defined( VGUI_USEDRAGDROP ) + return m_clrDragFrame; +#endif + return Color(0, 0, 0, 0); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *data - +//----------------------------------------------------------------------------- +void Panel::OnPanelDropped( CUtlVector< KeyValues * >& data ) +{ + // Empty. Derived classes would implement handlers here +} + +//----------------------------------------------------------------------------- +// called on droptarget when draggable panel enters droptarget +//----------------------------------------------------------------------------- +void Panel::OnPanelEnteredDroppablePanel( CUtlVector< KeyValues * >& msglist ) +{ + // Empty. Derived classes would implement handlers here +} + +//----------------------------------------------------------------------------- +// called on droptarget when draggable panel exits droptarget +//----------------------------------------------------------------------------- +void Panel::OnPanelExitedDroppablePanel ( CUtlVector< KeyValues * >& msglist ) +{ + // Empty. Derived classes would implement handlers here +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void Panel::DragDropStartDragging() +{ +#if defined( VGUI_USEDRAGDROP ) + // We somehow missed a mouse release, cancel the previous drag + if ( g_DragDropCapture.Get() ) + { + if ( HasParent( g_DragDropCapture.Get()->GetVPanel() ) ) + return; + + bool started = g_DragDropCapture->GetDragDropInfo()->m_bDragStarted; + g_DragDropCapture->OnFinishDragging( true, (MouseCode)-1 ); + if ( started ) + { + return; + } + } + + // Find actual target panel + Panel *panel = GetDragPanel(); + if ( !panel ) + return; + + DragDrop_t *data = panel->GetDragDropInfo(); + if ( !data ) + return; + + if ( !panel->IsDragEnabled() ) + return; + + if ( data->m_bDragging ) + return; + + panel->OnStartDragging(); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Panel::IsBeingDragged() +{ +#if defined( VGUI_USEDRAGDROP ) + if ( !g_DragDropCapture.Get() ) + return false; + + if ( g_DragDropCapture.Get() == this ) + return true; + + // If we encounter a blocker, stop chaining + if ( m_pDragDrop->m_bPreventChaining ) + return false; + + // Chain up + if ( GetParent() ) + { + return GetParent()->IsBeingDragged(); + } +#endif + // No luck + return false; +} + +struct srect_t +{ + int x0, y0; + int x1, y1; + + bool IsDegenerate() + { + if ( x1 - x0 <= 0 ) + return true; + if ( y1 - y0 <= 0 ) + return true; + return false; + } +}; + +// Draws a filled rect of specified bounds, but omits the bounds of the skip panel from those bounds +void Panel::FillRectSkippingPanel( const Color &clr, int x, int y, int w, int h, Panel *skipPanel ) +{ + int sx = 0, sy = 0, sw, sh; + skipPanel->GetSize( sw, sh ); + skipPanel->LocalToScreen( sx, sy ); + ScreenToLocal( sx, sy ); + + surface()->DrawSetColor( clr ); + + srect_t r1; + r1.x0 = x; + r1.y0 = y; + r1.x1 = x + w; + r1.y1 = y + h; + + srect_t r2; + r2.x0 = sx; + r2.y0 = sy; + r2.x1 = sx + sw; + r2.y1 = sy + sh; + + int topy = r1.y0; + int bottomy = r1.y1; + + // We'll descend vertically and draw: + // 1 a possible bar across the top + // 2 a possible bar across the bottom + // 3 possible left bar + // 4 possible right bar + + // Room at top? + if ( r2.y0 > r1.y0 ) + { + topy = r2.y0; + + surface()->DrawFilledRect( r1.x0, r1.y0, r1.x1, topy ); + } + + // Room at bottom? + if ( r2.y1 < r1.y1 ) + { + bottomy = r2.y1; + + surface()->DrawFilledRect( r1.x0, bottomy, r1.x1, r1.y1 ); + } + + // Room on left side? + if ( r2.x0 > r1.x0 ) + { + int left = r2.x0; + + surface()->DrawFilledRect( r1.x0, topy, left, bottomy ); + } + + // Room on right side + if ( r2.x1 < r1.x1 ) + { + int right = r2.x1; + + surface()->DrawFilledRect( right, topy, r1.x1, bottomy ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *child - +//----------------------------------------------------------------------------- +void Panel::SetSkipChildDuringPainting( Panel *child ) +{ + m_SkipChild = child; +} + +HPanel Panel::ToHandle() const +{ + return ivgui()->PanelToHandle( _vpanel ); +} +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Panel* Panel::NavigateUp() +{ + Panel *target = GetNavUp(); + if ( target ) + { + NavigateFrom(); + target->m_LastNavDirection = ND_UP; + target->NavigateTo(); + } + + return target; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Panel* Panel::NavigateDown() +{ + Panel *target = GetNavDown(); + if ( target ) + { + NavigateFrom(); + target->m_LastNavDirection = ND_DOWN; + target->NavigateTo(); + } + + return target; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Panel* Panel::NavigateLeft() +{ + Panel *target = GetNavLeft(); + if ( target ) + { + NavigateFrom(); + target->m_LastNavDirection = ND_LEFT; + target->NavigateTo(); + } + return target; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Panel* Panel::NavigateRight() +{ + Panel *target = GetNavRight(); + if ( target ) + { + NavigateFrom(); + target->m_LastNavDirection = ND_RIGHT; + target->NavigateTo(); + } + return target; +} + +Panel* Panel::NavigateActivate() +{ + Panel *target = GetNavActivate(); + if ( target ) + { + NavigateFrom(); + target->m_LastNavDirection = ND_NONE; + target->NavigateTo(); + } + return target; +} + +Panel* Panel::NavigateBack() +{ + Panel *target = GetNavBack(); + if ( target ) + { + NavigateFrom(); + target->m_LastNavDirection = ND_NONE; + target->NavigateTo(); + } + return target; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::NavigateTo() +{ + if ( IsX360() ) + { + RequestFocus( 0 ); + } + + CallParentFunction( new KeyValues( "OnNavigateTo", "panelName", GetName() ) ); + + Panel *target = GetNavToRelay(); + if ( target ) + { + NavigateFrom(); + target->m_LastNavDirection = ND_NONE; + NavigateToChild( target ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::NavigateFrom() +{ + for ( int i = 0; i < GetChildCount(); ++i ) + { + Panel* currentNav = GetChild(i); + if ( currentNav != 0 ) + { + currentNav->NavigateFrom(); + } + } + + CallParentFunction( new KeyValues( "OnNavigateFrom", "panelName", GetName() ) ); + + if ( m_pTooltips ) + { + m_pTooltips->HideTooltip(); + } + + m_LastNavDirection = ND_NONE; +} + +void Panel::NavigateToChild( Panel *pNavigateTo ) +{ + for( int i = 0; i != GetChildCount(); ++i ) + { + vgui::Panel *pChild = GetChild(i); + if( pChild ) + pChild->NavigateFrom(); + } + pNavigateTo->NavigateTo(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Panel* Panel::SetNavUp( Panel* navUp ) +{ + Panel* lastNav = m_NavUp; + m_NavUp = navUp; + + if( navUp ) + m_sNavUpName = navUp->GetName(); + else + m_sNavUpName.Clear(); + + return lastNav; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Panel* Panel::SetNavDown( Panel* navDown ) +{ + Panel* lastNav = m_NavDown; + m_NavDown = navDown; + + if( navDown ) + m_sNavDownName = navDown->GetName(); + else + m_sNavDownName.Clear(); + + return lastNav; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Panel* Panel::SetNavLeft( Panel* navLeft ) +{ + Panel* lastNav = m_NavLeft; + m_NavLeft = navLeft; + + if( navLeft ) + m_sNavLeftName = navLeft->GetName(); + else + m_sNavLeftName.Clear(); + + return lastNav; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Panel* Panel::SetNavRight( Panel* navRight ) +{ + Panel* lastNav = m_NavRight; + m_NavRight = navRight; + + if( navRight ) + m_sNavRightName = navRight->GetName(); + else + m_sNavRightName.Clear(); + + return lastNav; +} + +Panel* Panel::SetNavToRelay( Panel* navToRelay ) +{ + Panel* lastNav = m_NavToRelay; + m_NavToRelay = navToRelay; + + return lastNav; +} + +Panel* Panel::SetNavActivate( Panel* navActivate ) +{ + Panel* lastNav = m_NavActivate; + m_NavActivate = navActivate; + + return lastNav; +} + +Panel* Panel::SetNavBack( Panel* navBack ) +{ + Panel* lastNav = m_NavBack; + m_NavBack = navBack; + + return lastNav; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Panel::NAV_DIRECTION Panel::GetLastNavDirection() +{ + return m_LastNavDirection; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::OnNavigateTo( const char* panelName ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::OnNavigateFrom( const char* panelName ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetNavUp( const char* controlName ) +{ + if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) + { + m_NavUp = NULL; + m_sNavUpName = controlName; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetNavDown( const char* controlName ) +{ + if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) + { + m_NavDown = NULL; + m_sNavDownName = controlName; + } +} +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetNavLeft( const char* controlName ) +{ + if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) + { + m_NavLeft = NULL; + m_sNavLeftName = controlName; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Panel::SetNavRight( const char* controlName ) +{ + if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) + { + m_NavRight = NULL; + m_sNavRightName = controlName; + } +} + +void Panel::SetNavToRelay( const char* controlName ) +{ + if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) + { + m_NavToRelay = NULL; + m_sNavToRelayName = controlName; + } +} + +void Panel::SetNavActivate( const char* controlName ) +{ + if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) + { + m_NavActivate = NULL; + m_sNavActivateName = controlName; + } +} + +void Panel::SetNavBack( const char* controlName ) +{ + if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 ) + { + m_NavBack = NULL; + m_sNavBackName = controlName; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +vgui::Panel* Panel::GetNavUp( Panel *first ) +{ + if ( !m_NavUp && m_sNavUpName.Length() > 0 ) + { + Panel *pParent = GetParent(); + const char *pName = m_sNavUpName.String(); + while ( pParent && pName[ 0 ] == '<' ) + { + pParent = pParent->GetParent(); + pName++; + } + + if ( !pParent ) + { + return NULL; + } + + Panel *foundPanel = pParent->FindChildByName( pName, true ); + if ( foundPanel != 0 ) + { + m_NavUp = foundPanel; + } + } + + vgui::Panel* nextPanel = m_NavUp; + if( m_NavUp && m_NavUp != first && !m_NavUp->IsVisible() ) + { + Panel *firstPanel = first == NULL ? this : first; + nextPanel = nextPanel->GetNavUp( firstPanel ); + } + + return nextPanel; +} + +vgui::Panel* Panel::GetNavDown( Panel *first ) +{ + if ( !m_NavDown && m_sNavDownName.Length() > 0 ) + { + Panel *pParent = GetParent(); + const char *pName = m_sNavDownName.String(); + while ( pParent && pName[ 0 ] == '<' ) + { + pParent = pParent->GetParent(); + pName++; + } + + if ( !pParent ) + { + return NULL; + } + + Panel* foundPanel = pParent->FindChildByName( pName, true ); + if ( foundPanel != 0 ) + { + m_NavDown = foundPanel->GetPanel(); + } + } + + vgui::Panel* nextPanel = m_NavDown; + if( m_NavDown && m_NavDown != first && !m_NavDown->IsVisible() ) + { + Panel *firstPanel = first == NULL ? this : first; + nextPanel = nextPanel->GetNavDown( firstPanel ); + } + + return nextPanel; +} + +vgui::Panel* Panel::GetNavLeft( Panel *first ) +{ + if ( !m_NavLeft && m_sNavLeftName.Length() > 0 ) + { + Panel *pParent = GetParent(); + const char *pName = m_sNavLeftName.String(); + while ( pParent && pName[ 0 ] == '<' ) + { + pParent = pParent->GetParent(); + pName++; + } + + if ( !pParent ) + { + return NULL; + } + + Panel* foundPanel = pParent->FindChildByName( pName, true ); + if ( foundPanel != 0 ) + { + m_NavLeft = foundPanel->GetPanel(); + } + } + + vgui::Panel* nextPanel = m_NavLeft; + if( m_NavLeft && m_NavLeft != first && !m_NavLeft->IsVisible() ) + { + Panel *firstPanel = first == NULL ? this : first; + nextPanel = nextPanel->GetNavLeft( firstPanel ); + } + + return nextPanel; +} + +vgui::Panel* Panel::GetNavRight( Panel *first ) +{ + if ( !m_NavRight && m_sNavRightName.Length() > 0 ) + { + Panel *pParent = GetParent(); + const char *pName = m_sNavRightName.String(); + while ( pParent && pName[ 0 ] == '<' ) + { + pParent = pParent->GetParent(); + pName++; + } + + if ( !pParent ) + { + return NULL; + } + + Panel* foundPanel = pParent->FindChildByName( pName, true ); + if ( foundPanel != 0 ) + { + m_NavRight = foundPanel->GetPanel(); + } + } + + vgui::Panel* nextPanel = m_NavRight; + if( m_NavRight && m_NavRight != first && !m_NavRight->IsVisible() ) + { + Panel *firstPanel = first == NULL ? this : first; + nextPanel = nextPanel->GetNavRight( firstPanel ); + } + + return nextPanel; +} + +vgui::Panel* Panel::GetNavToRelay( Panel *first ) +{ + if ( !m_NavToRelay && m_sNavToRelayName.Length() > 0 ) + { + Panel *pParent = this; + const char *pName = m_sNavToRelayName.String(); + while ( pParent && pName[ 0 ] == '<' ) + { + pParent = pParent->GetParent(); + pName++; + } + + if ( !pParent ) + { + return NULL; + } + + Panel* foundPanel = pParent->FindChildByName( pName, true ); + if ( foundPanel != 0 ) + { + m_NavToRelay = foundPanel->GetPanel(); + } + } + + vgui::Panel* nextPanel = m_NavToRelay; + if ( m_NavToRelay && m_NavToRelay != first && !m_NavToRelay->IsVisible() ) + { + Panel *firstPanel = first == NULL ? this : first; + nextPanel = nextPanel->GetNavToRelay( firstPanel ); + } + + return nextPanel; +} + +vgui::Panel* Panel::GetNavActivate( Panel *first ) +{ + if ( !m_NavActivate && m_sNavActivateName.Length() > 0 ) + { + Panel *pParent = GetParent(); + const char *pName = m_sNavActivateName.String(); + while ( pParent && pName[ 0 ] == '<' ) + { + pParent = pParent->GetParent(); + pName++; + } + + if ( !pParent ) + { + return NULL; + } + + Panel* foundPanel = pParent->FindChildByName( pName, true ); + if ( foundPanel != 0 ) + { + m_NavActivate = foundPanel->GetPanel(); + } + } + + vgui::Panel* nextPanel = m_NavActivate; + if ( m_NavActivate && m_NavActivate != first && !m_NavActivate->IsVisible() ) + { + Panel *firstPanel = first == NULL ? this : first; + nextPanel = nextPanel->GetNavActivate( firstPanel ); + } + + return nextPanel; +} + +vgui::Panel* Panel::GetNavBack( Panel *first ) +{ + if ( !m_NavBack && m_sNavBackName.Length() > 0 ) + { + Panel *pParent = GetParent(); + const char *pName = m_sNavBackName.String(); + while ( pParent && pName[ 0 ] == '<' ) + { + pParent = pParent->GetParent(); + pName++; + } + + if ( !pParent ) + { + return NULL; + } + + Panel *foundPanel = pParent->FindChildByName( pName ); + if ( foundPanel ) + { + m_NavBack = foundPanel; + } + } + + vgui::Panel* nextPanel = m_NavBack; + if ( m_NavBack && m_NavBack != first && !m_NavBack->IsVisible() ) + { + Panel *firstPanel = first == NULL ? this : first; + nextPanel = nextPanel->GetNavBack( firstPanel ); + } + + return nextPanel; +} + +vgui::Panel* Panel::GetNavUpPanel() +{ + return m_NavUp; +} + +vgui::Panel* Panel::GetNavDownPanel() +{ + return m_NavDown; +} + +vgui::Panel* Panel::GetNavLeftPanel() +{ + return m_NavLeft; +} + +vgui::Panel* Panel::GetNavRightPanel() +{ + return m_NavRight; +} + +vgui::Panel* Panel::GetNavToRelayPanel() +{ + return m_NavToRelay; +} + +vgui::Panel* Panel::GetNavActivatePanel() +{ + return m_NavActivate; +} + +vgui::Panel* Panel::GetNavBackPanel() +{ + return m_NavBack; +} + +void Panel::SetConsoleStylePanel( bool bConsoleStyle ) +{ + m_bIsConsoleStylePanel = bConsoleStyle; +} + +bool Panel::IsConsoleStylePanel() const +{ + return m_bIsConsoleStylePanel; +} + +//----------------------------------------------------------------------------- +// Purpose: Utility class for handling message map allocation +//----------------------------------------------------------------------------- +class CPanelMessageMapDictionary +{ +public: + CPanelMessageMapDictionary() : m_PanelMessageMapPool( sizeof(PanelMessageMap), 32, CUtlMemoryPool::GROW_FAST, "CPanelMessageMapDictionary::m_PanelMessageMapPool" ) + { + m_MessageMaps.RemoveAll(); + } + + PanelMessageMap *FindOrAddPanelMessageMap( char const *className ); + PanelMessageMap *FindPanelMessageMap( char const *className ); +private: + + struct PanelMessageMapDictionaryEntry + { + PanelMessageMap *map; + }; + + char const *StripNamespace( char const *className ); + + CUtlDict< PanelMessageMapDictionaryEntry, int > m_MessageMaps; + CUtlMemoryPool m_PanelMessageMapPool; +}; + + +char const *CPanelMessageMapDictionary::StripNamespace( char const *className ) +{ + if ( !strnicmp( className, "vgui::", 6 ) ) + { + return className + 6; + } + return className; +} + +//----------------------------------------------------------------------------- +// Purpose: Find but don't add mapping +//----------------------------------------------------------------------------- +PanelMessageMap *CPanelMessageMapDictionary::FindPanelMessageMap( char const *className ) +{ + int lookup = m_MessageMaps.Find( StripNamespace( className ) ); + if ( lookup != m_MessageMaps.InvalidIndex() ) + { + return m_MessageMaps[ lookup ].map; + } + return NULL; +} + +#include +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +PanelMessageMap *CPanelMessageMapDictionary::FindOrAddPanelMessageMap( char const *className ) +{ + PanelMessageMap *map = FindPanelMessageMap( className ); + if ( map ) + return map; + + PanelMessageMapDictionaryEntry entry; + // use the alloc in place method of new + entry.map = new (m_PanelMessageMapPool.Alloc(sizeof(PanelMessageMap))) PanelMessageMap; + Construct(entry.map); + m_MessageMaps.Insert( StripNamespace( className ), entry ); + return entry.map; +} +#include + +#if defined( VGUI_USEKEYBINDINGMAPS ) +//----------------------------------------------------------------------------- +// Purpose: Utility class for handling keybinding map allocation +//----------------------------------------------------------------------------- +class CPanelKeyBindingMapDictionary +{ +public: + CPanelKeyBindingMapDictionary() : m_PanelKeyBindingMapPool( sizeof(PanelKeyBindingMap), 32, CUtlMemoryPool::GROW_FAST, "CPanelKeyBindingMapDictionary::m_PanelKeyBindingMapPool" ) + { + m_MessageMaps.RemoveAll(); + } + + PanelKeyBindingMap *FindOrAddPanelKeyBindingMap( char const *className ); + PanelKeyBindingMap *FindPanelKeyBindingMap( char const *className ); +private: + + struct PanelKeyBindingMapDictionaryEntry + { + PanelKeyBindingMap *map; + }; + + char const *StripNamespace( char const *className ); + + CUtlDict< PanelKeyBindingMapDictionaryEntry, int > m_MessageMaps; + CUtlMemoryPool m_PanelKeyBindingMapPool; +}; + + +char const *CPanelKeyBindingMapDictionary::StripNamespace( char const *className ) +{ + if ( !strnicmp( className, "vgui::", 6 ) ) + { + return className + 6; + } + return className; +} + +//----------------------------------------------------------------------------- +// Purpose: Find but don't add mapping +//----------------------------------------------------------------------------- +PanelKeyBindingMap *CPanelKeyBindingMapDictionary::FindPanelKeyBindingMap( char const *className ) +{ + int lookup = m_MessageMaps.Find( StripNamespace( className ) ); + if ( lookup != m_MessageMaps.InvalidIndex() ) + { + return m_MessageMaps[ lookup ].map; + } + return NULL; +} + +#include +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +PanelKeyBindingMap *CPanelKeyBindingMapDictionary::FindOrAddPanelKeyBindingMap( char const *className ) +{ + PanelKeyBindingMap *map = FindPanelKeyBindingMap( className ); + if ( map ) + return map; + + PanelKeyBindingMapDictionaryEntry entry; + // use the alloc in place method of new + entry.map = new (m_PanelKeyBindingMapPool.Alloc(sizeof(PanelKeyBindingMap))) PanelKeyBindingMap; + Construct(entry.map); + m_MessageMaps.Insert( StripNamespace( className ), entry ); + return entry.map; +} + +#include + +CPanelKeyBindingMapDictionary& GetPanelKeyBindingMapDictionary() +{ + static CPanelKeyBindingMapDictionary dictionary; + return dictionary; +} + +#endif // VGUI_USEKEYBINDINGMAPS + +CPanelMessageMapDictionary& GetPanelMessageMapDictionary() +{ + static CPanelMessageMapDictionary dictionary; + return dictionary; +} + +namespace vgui +{ + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + PanelMessageMap *FindOrAddPanelMessageMap( char const *className ) + { + return GetPanelMessageMapDictionary().FindOrAddPanelMessageMap( className ); + } + + //----------------------------------------------------------------------------- + // Purpose: Find but don't add mapping + //----------------------------------------------------------------------------- + PanelMessageMap *FindPanelMessageMap( char const *className ) + { + return GetPanelMessageMapDictionary().FindPanelMessageMap( className ); + } + +#if defined( VGUI_USEKEYBINDINGMAPS ) + CPanelKeyBindingMapDictionary& GetPanelKeyBindingMapDictionary() + { + static CPanelKeyBindingMapDictionary dictionary; + return dictionary; + } + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + PanelKeyBindingMap *FindOrAddPanelKeyBindingMap( char const *className ) + { + return GetPanelKeyBindingMapDictionary().FindOrAddPanelKeyBindingMap( className ); + } + + //----------------------------------------------------------------------------- + // Purpose: Find but don't add mapping + //----------------------------------------------------------------------------- + PanelKeyBindingMap *FindPanelKeyBindingMap( char const *className ) + { + return GetPanelKeyBindingMapDictionary().FindPanelKeyBindingMap( className ); + } +#endif // VGUI_USEKEYBINDINGMAPS + +SortedPanel_t::SortedPanel_t( Panel *panel ) +{ + pPanel = panel; pButton = dynamic_cast< Button* >( panel ); +} + + +void VguiPanelGetSortedChildPanelList( Panel *pParentPanel, void *pSortedPanels ) +{ + CUtlSortVector< SortedPanel_t, CSortedPanelYLess > *pList = reinterpret_cast< CUtlSortVector< SortedPanel_t, CSortedPanelYLess >* >( pSortedPanels ); + + for ( int i = 0; i < pParentPanel->GetChildCount(); i++ ) + { + // perform auto-layout on the child panel + Panel *pPanel = pParentPanel->GetChild( i ); + if ( !pPanel || !pPanel->IsVisible() ) + continue; + + pList->Insert( SortedPanel_t( static_cast< Panel* >( pPanel ) ) ); + } +} + +void VguiPanelGetSortedChildButtonList( Panel *pParentPanel, void *pSortedPanels, char *pchFilter /*= NULL*/, int nFilterType /*= 0*/ ) +{ + CUtlSortVector< SortedPanel_t, CSortedPanelYLess > *pList = reinterpret_cast< CUtlSortVector< SortedPanel_t, CSortedPanelYLess >* >( pSortedPanels ); + + for ( int i = 0; i < pParentPanel->GetChildCount(); i++ ) + { + // perform auto-layout on the child panel + Button *pPanel = dynamic_cast< Button* >( pParentPanel->GetChild( i ) ); + if ( !pPanel || !pPanel->IsVisible() ) + continue; + + if ( pchFilter && pchFilter[ 0 ] != '\0' ) + { + char szBuff[ 128 ]; + pPanel->GetText( szBuff, sizeof( szBuff ) ); + + // Prefix + if ( nFilterType == 0 ) + { + if ( !StringHasPrefix( szBuff, pchFilter ) ) + { + continue; + } + } + // Substring + else if ( nFilterType == 1 ) + { + if ( V_strstr( szBuff, pchFilter ) == NULL ) + { + continue; + } + } + } + + pList->Insert( SortedPanel_t( pPanel ) ); + } +} + +int VguiPanelNavigateSortedChildButtonList( void *pSortedPanels, int nDir ) +{ + CUtlSortVector< SortedPanel_t, CSortedPanelYLess > *pList = reinterpret_cast< CUtlSortVector< SortedPanel_t, CSortedPanelYLess >* >( pSortedPanels ); + + if ( pList->Count() <= 0 ) + return -1; + + if ( nDir != 0 ) + { + int nArmed = -1; + for ( int i = 0; i < pList->Count(); i++ ) + { + if ( (*pList)[ i ].pButton->IsArmed() ) + { + nArmed = i; + break; + } + } + + if ( nArmed == -1 ) + { + (*pList)[ 0 ].pButton->SetArmed( true ); + return 0; + } + else + { + int nNewArmed = clamp( nArmed + nDir, 0, pList->Count() - 1 ); + if ( nNewArmed != nArmed ) + { + (*pList)[ nArmed ].pButton->SetArmed( false ); + } + + (*pList)[ nNewArmed ].pButton->RequestFocus(); + (*pList)[ nNewArmed ].pButton->SetArmed( true ); + + return nNewArmed; + } + } + + return -1; +} + +} diff --git a/mp/src/vgui2/vgui_controls/PanelListPanel.cpp b/mp/src/vgui2/vgui_controls/PanelListPanel.cpp index 5ef40966..51fe8ab9 100644 --- a/mp/src/vgui2/vgui_controls/PanelListPanel.cpp +++ b/mp/src/vgui2/vgui_controls/PanelListPanel.cpp @@ -1,473 +1,473 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "vgui/MouseCode.h" -#include "vgui/IInput.h" -#include "vgui/IScheme.h" -#include "vgui/ISurface.h" - -#include "vgui_controls/EditablePanel.h" -#include "vgui_controls/ScrollBar.h" -#include "vgui_controls/Label.h" -#include "vgui_controls/Button.h" -#include "vgui_controls/Controls.h" -#include "vgui_controls/PanelListPanel.h" - -#include "KeyValues.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -PanelListPanel::PanelListPanel( vgui::Panel *parent, char const *panelName ) : EditablePanel( parent, panelName ) -{ - SetBounds( 0, 0, 100, 100 ); - - m_vbar = new ScrollBar(this, "PanelListPanelVScroll", true); - m_vbar->SetVisible(false); - m_vbar->AddActionSignalTarget( this ); - - m_pPanelEmbedded = new EditablePanel(this, "PanelListEmbedded"); - m_pPanelEmbedded->SetBounds(0, 0, 20, 20); - m_pPanelEmbedded->SetPaintBackgroundEnabled( false ); - m_pPanelEmbedded->SetPaintBorderEnabled(false); - - m_iFirstColumnWidth = 100; // default width - m_iNumColumns = 1; // 1 column by default - - if ( IsProportional() ) - { - m_iDefaultHeight = scheme()->GetProportionalScaledValueEx( GetScheme(), DEFAULT_HEIGHT ); - m_iPanelBuffer = scheme()->GetProportionalScaledValueEx( GetScheme(), PANELBUFFER ); - } - else - { - m_iDefaultHeight = DEFAULT_HEIGHT; - m_iPanelBuffer = PANELBUFFER; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -PanelListPanel::~PanelListPanel() -{ - // free data from table - DeleteAllItems(); -} - -void PanelListPanel::SetVerticalBufferPixels( int buffer ) -{ - m_iPanelBuffer = buffer; - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: counts the total vertical pixels -//----------------------------------------------------------------------------- -int PanelListPanel::ComputeVPixelsNeeded() -{ - int iCurrentItem = 0; - int iLargestH = 0; - - int pixels = 0; - for ( int i = 0; i < m_SortedItems.Count(); i++ ) - { - Panel *panel = m_DataItems[ m_SortedItems[i] ].panel; - if ( !panel ) - continue; - - if ( panel->IsLayoutInvalid() ) - { - panel->InvalidateLayout( true ); - } - - int iCurrentColumn = iCurrentItem % m_iNumColumns; - - int w, h; - panel->GetSize( w, h ); - - if ( iLargestH < h ) - iLargestH = h; - - if ( iCurrentColumn == 0 ) - pixels += m_iPanelBuffer; // add in buffer. between rows. - - if ( iCurrentColumn >= m_iNumColumns - 1 ) - { - pixels += iLargestH; - iLargestH = 0; - } - - iCurrentItem++; - } - - // Add in remaining largest height - pixels += iLargestH; - - pixels += m_iPanelBuffer; // add in buffer below last item - - return pixels; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the panel to use to render a cell -//----------------------------------------------------------------------------- -Panel *PanelListPanel::GetCellRenderer( int row ) -{ - if ( !m_SortedItems.IsValidIndex(row) ) - return NULL; - - Panel *panel = m_DataItems[ m_SortedItems[row] ].panel; - return panel; -} - -//----------------------------------------------------------------------------- -// Purpose: adds an item to the view -// data->GetName() is used to uniquely identify an item -// data sub items are matched against column header name to be used in the table -//----------------------------------------------------------------------------- -int PanelListPanel::AddItem( Panel *labelPanel, Panel *panel) -{ - Assert(panel); - - if ( labelPanel ) - { - labelPanel->SetParent( m_pPanelEmbedded ); - } - - panel->SetParent( m_pPanelEmbedded ); - - int itemID = m_DataItems.AddToTail(); - DATAITEM &newitem = m_DataItems[itemID]; - newitem.labelPanel = labelPanel; - newitem.panel = panel; - m_SortedItems.AddToTail(itemID); - - InvalidateLayout(); - return itemID; -} - -//----------------------------------------------------------------------------- -// Purpose: iteration accessor -//----------------------------------------------------------------------------- -int PanelListPanel::GetItemCount() const -{ - return m_DataItems.Count(); -} - -int PanelListPanel::GetItemIDFromRow( int nRow ) const -{ - if ( nRow < 0 || nRow >= GetItemCount() ) - return m_DataItems.InvalidIndex(); - return m_SortedItems[ nRow ]; -} - - -//----------------------------------------------------------------------------- -// Iteration. Use these until they return InvalidItemID to iterate all the items. -//----------------------------------------------------------------------------- -int PanelListPanel::FirstItem() const -{ - return m_DataItems.Head(); -} - -int PanelListPanel::NextItem( int nItemID ) const -{ - return m_DataItems.Next( nItemID ); -} - -int PanelListPanel::InvalidItemID() const -{ - return m_DataItems.InvalidIndex( ); -} - - -//----------------------------------------------------------------------------- -// Purpose: returns label panel for this itemID -//----------------------------------------------------------------------------- -Panel *PanelListPanel::GetItemLabel(int itemID) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return NULL; - - return m_DataItems[itemID].labelPanel; -} - -//----------------------------------------------------------------------------- -// Purpose: returns label panel for this itemID -//----------------------------------------------------------------------------- -Panel *PanelListPanel::GetItemPanel(int itemID) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return NULL; - - return m_DataItems[itemID].panel; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PanelListPanel::RemoveItem(int itemID) -{ - if ( !m_DataItems.IsValidIndex(itemID) ) - return; - - DATAITEM &item = m_DataItems[itemID]; - if ( item.panel ) - { - item.panel->MarkForDeletion(); - } - if ( item.labelPanel ) - { - item.labelPanel->MarkForDeletion(); - } - - m_DataItems.Remove(itemID); - m_SortedItems.FindAndRemove(itemID); - - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: clears and deletes all the memory used by the data items -//----------------------------------------------------------------------------- -void PanelListPanel::DeleteAllItems() -{ - FOR_EACH_LL( m_DataItems, i ) - { - if ( m_DataItems[i].panel ) - { - delete m_DataItems[i].panel; - } - } - - m_DataItems.RemoveAll(); - m_SortedItems.RemoveAll(); - - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: clears and deletes all the memory used by the data items -//----------------------------------------------------------------------------- -void PanelListPanel::RemoveAll() -{ - m_DataItems.RemoveAll(); - m_SortedItems.RemoveAll(); - - // move the scrollbar to the top of the list - m_vbar->SetValue(0); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PanelListPanel::OnSizeChanged(int wide, int tall) -{ - BaseClass::OnSizeChanged(wide, tall); - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: relayouts out the panel after any internal changes -//----------------------------------------------------------------------------- -void PanelListPanel::PerformLayout() -{ - int wide, tall; - GetSize( wide, tall ); - - int vpixels = ComputeVPixelsNeeded(); - - m_vbar->SetRange( 0, vpixels ); - m_vbar->SetRangeWindow( tall ); - m_vbar->SetButtonPressedScrollValue( tall / 4 ); // standard height of labels/buttons etc. - - m_vbar->SetPos( wide - m_vbar->GetWide() - 2, 0 ); - m_vbar->SetSize( m_vbar->GetWide(), tall - 2 ); - - int top = m_vbar->GetValue(); - - m_pPanelEmbedded->SetPos( 0, -top ); - m_pPanelEmbedded->SetSize( wide - m_vbar->GetWide(), vpixels ); // scrollbar will sit on top (zpos set explicitly) - - bool bScrollbarVisible = true; - // If we're supposed to automatically hide the scrollbar when unnecessary, check it now - if ( m_bAutoHideScrollbar ) - { - bScrollbarVisible = (m_pPanelEmbedded->GetTall() > tall); - } - m_vbar->SetVisible( bScrollbarVisible ); - - // Now lay out the controls on the embedded panel - int y = 0; - int h = 0; - int totalh = 0; - - int xpos = m_iFirstColumnWidth + m_iPanelBuffer; - int iColumnWidth = ( wide - xpos - m_vbar->GetWide() - 12 ) / m_iNumColumns; - - for ( int i = 0; i < m_SortedItems.Count(); i++ ) - { - int iCurrentColumn = i % m_iNumColumns; - - // add in a little buffer between panels - if ( iCurrentColumn == 0 ) - y += m_iPanelBuffer; - - DATAITEM &item = m_DataItems[ m_SortedItems[i] ]; - - if ( h < item.panel->GetTall() ) - h = item.panel->GetTall(); - - if ( item.labelPanel ) - { - item.labelPanel->SetBounds( 0, y, m_iFirstColumnWidth, item.panel->GetTall() ); - } - - item.panel->SetBounds( xpos + iCurrentColumn * iColumnWidth, y, iColumnWidth, item.panel->GetTall() ); - - if ( iCurrentColumn >= m_iNumColumns - 1 ) - { - y += h; - totalh += h; - - h = 0; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: scheme settings -//----------------------------------------------------------------------------- -void PanelListPanel::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); - SetBgColor(GetSchemeColor("ListPanel.BgColor", GetBgColor(), pScheme)); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PanelListPanel::OnSliderMoved( int position ) -{ - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PanelListPanel::MoveScrollBarToTop() -{ - m_vbar->SetValue(0); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PanelListPanel::SetFirstColumnWidth( int width ) -{ - m_iFirstColumnWidth = width; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -int PanelListPanel::GetFirstColumnWidth() -{ - return m_iFirstColumnWidth; -} - -void PanelListPanel::SetNumColumns( int iNumColumns ) -{ - m_iNumColumns = iNumColumns; -} - -int PanelListPanel::GetNumColumns( void ) -{ - return m_iNumColumns; -} - -//----------------------------------------------------------------------------- -// Purpose: moves the scrollbar with the mousewheel -//----------------------------------------------------------------------------- -void PanelListPanel::OnMouseWheeled(int delta) -{ - int val = m_vbar->GetValue(); - val -= (delta * DEFAULT_HEIGHT); - m_vbar->SetValue(val); -} - -//----------------------------------------------------------------------------- -// Purpose: selection handler -//----------------------------------------------------------------------------- -void PanelListPanel::SetSelectedPanel( Panel *panel ) -{ - if ( panel != m_hSelectedItem ) - { - // notify the panels of the selection change - if ( m_hSelectedItem ) - { - PostMessage( m_hSelectedItem.Get(), new KeyValues("PanelSelected", "state", 0) ); - } - if ( panel ) - { - PostMessage( panel, new KeyValues("PanelSelected", "state", 1) ); - } - m_hSelectedItem = panel; - } -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -Panel *PanelListPanel::GetSelectedPanel() -{ - return m_hSelectedItem; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PanelListPanel::ScrollToItem( int itemNumber ) -{ - if (!m_vbar->IsVisible()) - { - return; - } - - DATAITEM& item = m_DataItems[ m_SortedItems[ itemNumber ] ]; - if ( !item.panel ) - return; - - int x, y; - item.panel->GetPos( x, y ); - int lx, ly; - lx = x; - ly = y; - m_pPanelEmbedded->LocalToScreen( lx, ly ); - ScreenToLocal( lx, ly ); - - int h = item.panel->GetTall(); - - if ( ly >= 0 && ly + h < GetTall() ) - return; - - m_vbar->SetValue( y ); - InvalidateLayout(); -} - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "vgui/MouseCode.h" +#include "vgui/IInput.h" +#include "vgui/IScheme.h" +#include "vgui/ISurface.h" + +#include "vgui_controls/EditablePanel.h" +#include "vgui_controls/ScrollBar.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/Controls.h" +#include "vgui_controls/PanelListPanel.h" + +#include "KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +PanelListPanel::PanelListPanel( vgui::Panel *parent, char const *panelName ) : EditablePanel( parent, panelName ) +{ + SetBounds( 0, 0, 100, 100 ); + + m_vbar = new ScrollBar(this, "PanelListPanelVScroll", true); + m_vbar->SetVisible(false); + m_vbar->AddActionSignalTarget( this ); + + m_pPanelEmbedded = new EditablePanel(this, "PanelListEmbedded"); + m_pPanelEmbedded->SetBounds(0, 0, 20, 20); + m_pPanelEmbedded->SetPaintBackgroundEnabled( false ); + m_pPanelEmbedded->SetPaintBorderEnabled(false); + + m_iFirstColumnWidth = 100; // default width + m_iNumColumns = 1; // 1 column by default + + if ( IsProportional() ) + { + m_iDefaultHeight = scheme()->GetProportionalScaledValueEx( GetScheme(), DEFAULT_HEIGHT ); + m_iPanelBuffer = scheme()->GetProportionalScaledValueEx( GetScheme(), PANELBUFFER ); + } + else + { + m_iDefaultHeight = DEFAULT_HEIGHT; + m_iPanelBuffer = PANELBUFFER; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +PanelListPanel::~PanelListPanel() +{ + // free data from table + DeleteAllItems(); +} + +void PanelListPanel::SetVerticalBufferPixels( int buffer ) +{ + m_iPanelBuffer = buffer; + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: counts the total vertical pixels +//----------------------------------------------------------------------------- +int PanelListPanel::ComputeVPixelsNeeded() +{ + int iCurrentItem = 0; + int iLargestH = 0; + + int pixels = 0; + for ( int i = 0; i < m_SortedItems.Count(); i++ ) + { + Panel *panel = m_DataItems[ m_SortedItems[i] ].panel; + if ( !panel ) + continue; + + if ( panel->IsLayoutInvalid() ) + { + panel->InvalidateLayout( true ); + } + + int iCurrentColumn = iCurrentItem % m_iNumColumns; + + int w, h; + panel->GetSize( w, h ); + + if ( iLargestH < h ) + iLargestH = h; + + if ( iCurrentColumn == 0 ) + pixels += m_iPanelBuffer; // add in buffer. between rows. + + if ( iCurrentColumn >= m_iNumColumns - 1 ) + { + pixels += iLargestH; + iLargestH = 0; + } + + iCurrentItem++; + } + + // Add in remaining largest height + pixels += iLargestH; + + pixels += m_iPanelBuffer; // add in buffer below last item + + return pixels; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the panel to use to render a cell +//----------------------------------------------------------------------------- +Panel *PanelListPanel::GetCellRenderer( int row ) +{ + if ( !m_SortedItems.IsValidIndex(row) ) + return NULL; + + Panel *panel = m_DataItems[ m_SortedItems[row] ].panel; + return panel; +} + +//----------------------------------------------------------------------------- +// Purpose: adds an item to the view +// data->GetName() is used to uniquely identify an item +// data sub items are matched against column header name to be used in the table +//----------------------------------------------------------------------------- +int PanelListPanel::AddItem( Panel *labelPanel, Panel *panel) +{ + Assert(panel); + + if ( labelPanel ) + { + labelPanel->SetParent( m_pPanelEmbedded ); + } + + panel->SetParent( m_pPanelEmbedded ); + + int itemID = m_DataItems.AddToTail(); + DATAITEM &newitem = m_DataItems[itemID]; + newitem.labelPanel = labelPanel; + newitem.panel = panel; + m_SortedItems.AddToTail(itemID); + + InvalidateLayout(); + return itemID; +} + +//----------------------------------------------------------------------------- +// Purpose: iteration accessor +//----------------------------------------------------------------------------- +int PanelListPanel::GetItemCount() const +{ + return m_DataItems.Count(); +} + +int PanelListPanel::GetItemIDFromRow( int nRow ) const +{ + if ( nRow < 0 || nRow >= GetItemCount() ) + return m_DataItems.InvalidIndex(); + return m_SortedItems[ nRow ]; +} + + +//----------------------------------------------------------------------------- +// Iteration. Use these until they return InvalidItemID to iterate all the items. +//----------------------------------------------------------------------------- +int PanelListPanel::FirstItem() const +{ + return m_DataItems.Head(); +} + +int PanelListPanel::NextItem( int nItemID ) const +{ + return m_DataItems.Next( nItemID ); +} + +int PanelListPanel::InvalidItemID() const +{ + return m_DataItems.InvalidIndex( ); +} + + +//----------------------------------------------------------------------------- +// Purpose: returns label panel for this itemID +//----------------------------------------------------------------------------- +Panel *PanelListPanel::GetItemLabel(int itemID) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return NULL; + + return m_DataItems[itemID].labelPanel; +} + +//----------------------------------------------------------------------------- +// Purpose: returns label panel for this itemID +//----------------------------------------------------------------------------- +Panel *PanelListPanel::GetItemPanel(int itemID) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return NULL; + + return m_DataItems[itemID].panel; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PanelListPanel::RemoveItem(int itemID) +{ + if ( !m_DataItems.IsValidIndex(itemID) ) + return; + + DATAITEM &item = m_DataItems[itemID]; + if ( item.panel ) + { + item.panel->MarkForDeletion(); + } + if ( item.labelPanel ) + { + item.labelPanel->MarkForDeletion(); + } + + m_DataItems.Remove(itemID); + m_SortedItems.FindAndRemove(itemID); + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: clears and deletes all the memory used by the data items +//----------------------------------------------------------------------------- +void PanelListPanel::DeleteAllItems() +{ + FOR_EACH_LL( m_DataItems, i ) + { + if ( m_DataItems[i].panel ) + { + delete m_DataItems[i].panel; + } + } + + m_DataItems.RemoveAll(); + m_SortedItems.RemoveAll(); + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: clears and deletes all the memory used by the data items +//----------------------------------------------------------------------------- +void PanelListPanel::RemoveAll() +{ + m_DataItems.RemoveAll(); + m_SortedItems.RemoveAll(); + + // move the scrollbar to the top of the list + m_vbar->SetValue(0); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PanelListPanel::OnSizeChanged(int wide, int tall) +{ + BaseClass::OnSizeChanged(wide, tall); + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: relayouts out the panel after any internal changes +//----------------------------------------------------------------------------- +void PanelListPanel::PerformLayout() +{ + int wide, tall; + GetSize( wide, tall ); + + int vpixels = ComputeVPixelsNeeded(); + + m_vbar->SetRange( 0, vpixels ); + m_vbar->SetRangeWindow( tall ); + m_vbar->SetButtonPressedScrollValue( tall / 4 ); // standard height of labels/buttons etc. + + m_vbar->SetPos( wide - m_vbar->GetWide() - 2, 0 ); + m_vbar->SetSize( m_vbar->GetWide(), tall - 2 ); + + int top = m_vbar->GetValue(); + + m_pPanelEmbedded->SetPos( 0, -top ); + m_pPanelEmbedded->SetSize( wide - m_vbar->GetWide(), vpixels ); // scrollbar will sit on top (zpos set explicitly) + + bool bScrollbarVisible = true; + // If we're supposed to automatically hide the scrollbar when unnecessary, check it now + if ( m_bAutoHideScrollbar ) + { + bScrollbarVisible = (m_pPanelEmbedded->GetTall() > tall); + } + m_vbar->SetVisible( bScrollbarVisible ); + + // Now lay out the controls on the embedded panel + int y = 0; + int h = 0; + int totalh = 0; + + int xpos = m_iFirstColumnWidth + m_iPanelBuffer; + int iColumnWidth = ( wide - xpos - m_vbar->GetWide() - 12 ) / m_iNumColumns; + + for ( int i = 0; i < m_SortedItems.Count(); i++ ) + { + int iCurrentColumn = i % m_iNumColumns; + + // add in a little buffer between panels + if ( iCurrentColumn == 0 ) + y += m_iPanelBuffer; + + DATAITEM &item = m_DataItems[ m_SortedItems[i] ]; + + if ( h < item.panel->GetTall() ) + h = item.panel->GetTall(); + + if ( item.labelPanel ) + { + item.labelPanel->SetBounds( 0, y, m_iFirstColumnWidth, item.panel->GetTall() ); + } + + item.panel->SetBounds( xpos + iCurrentColumn * iColumnWidth, y, iColumnWidth, item.panel->GetTall() ); + + if ( iCurrentColumn >= m_iNumColumns - 1 ) + { + y += h; + totalh += h; + + h = 0; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: scheme settings +//----------------------------------------------------------------------------- +void PanelListPanel::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); + SetBgColor(GetSchemeColor("ListPanel.BgColor", GetBgColor(), pScheme)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PanelListPanel::OnSliderMoved( int position ) +{ + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PanelListPanel::MoveScrollBarToTop() +{ + m_vbar->SetValue(0); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PanelListPanel::SetFirstColumnWidth( int width ) +{ + m_iFirstColumnWidth = width; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +int PanelListPanel::GetFirstColumnWidth() +{ + return m_iFirstColumnWidth; +} + +void PanelListPanel::SetNumColumns( int iNumColumns ) +{ + m_iNumColumns = iNumColumns; +} + +int PanelListPanel::GetNumColumns( void ) +{ + return m_iNumColumns; +} + +//----------------------------------------------------------------------------- +// Purpose: moves the scrollbar with the mousewheel +//----------------------------------------------------------------------------- +void PanelListPanel::OnMouseWheeled(int delta) +{ + int val = m_vbar->GetValue(); + val -= (delta * DEFAULT_HEIGHT); + m_vbar->SetValue(val); +} + +//----------------------------------------------------------------------------- +// Purpose: selection handler +//----------------------------------------------------------------------------- +void PanelListPanel::SetSelectedPanel( Panel *panel ) +{ + if ( panel != m_hSelectedItem ) + { + // notify the panels of the selection change + if ( m_hSelectedItem ) + { + PostMessage( m_hSelectedItem.Get(), new KeyValues("PanelSelected", "state", 0) ); + } + if ( panel ) + { + PostMessage( panel, new KeyValues("PanelSelected", "state", 1) ); + } + m_hSelectedItem = panel; + } +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +Panel *PanelListPanel::GetSelectedPanel() +{ + return m_hSelectedItem; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PanelListPanel::ScrollToItem( int itemNumber ) +{ + if (!m_vbar->IsVisible()) + { + return; + } + + DATAITEM& item = m_DataItems[ m_SortedItems[ itemNumber ] ]; + if ( !item.panel ) + return; + + int x, y; + item.panel->GetPos( x, y ); + int lx, ly; + lx = x; + ly = y; + m_pPanelEmbedded->LocalToScreen( lx, ly ); + ScreenToLocal( lx, ly ); + + int h = item.panel->GetTall(); + + if ( ly >= 0 && ly + h < GetTall() ) + return; + + m_vbar->SetValue( y ); + InvalidateLayout(); +} + + diff --git a/mp/src/vgui2/vgui_controls/PerforceFileExplorer.cpp b/mp/src/vgui2/vgui_controls/PerforceFileExplorer.cpp index de5cf9d1..130599bb 100644 --- a/mp/src/vgui2/vgui_controls/PerforceFileExplorer.cpp +++ b/mp/src/vgui2/vgui_controls/PerforceFileExplorer.cpp @@ -1,278 +1,278 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: Contains a list of files, determines their perforce status -// -// $NoKeywords: $ -//===========================================================================// - -#include -#include -#include -#include -#include -#include "tier1/KeyValues.h" -#include "vgui/ISystem.h" -#include "filesystem.h" -#include -#include "p4lib/ip4.h" -#include "tier2/tier2.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - - -using namespace vgui; - - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -PerforceFileExplorer::PerforceFileExplorer( Panel *pParent, const char *pPanelName ) : - BaseClass( pParent, pPanelName ) -{ - m_pFileList = new PerforceFileList( this, "PerforceFileList" ); - - // Get the list of available drives and put them in a menu here. - // Start with the directory we are in. - m_pFullPathCombo = new ComboBox( this, "FullPathCombo", 8, false ); - m_pFullPathCombo->GetTooltip()->SetTooltipFormatToSingleLine(); - - char pFullPath[MAX_PATH]; - g_pFullFileSystem->GetCurrentDirectory( pFullPath, sizeof(pFullPath) ); - SetCurrentDirectory( pFullPath ); - - m_pFullPathCombo->AddActionSignalTarget( this ); - - m_pFolderUpButton = new Button(this, "FolderUpButton", "", this); - m_pFolderUpButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_Up" ); - m_pFolderUpButton->SetCommand( new KeyValues( "FolderUp" ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -PerforceFileExplorer::~PerforceFileExplorer() -{ -} - - -//----------------------------------------------------------------------------- -// Inherited from Frame -//----------------------------------------------------------------------------- -void PerforceFileExplorer::ApplySchemeSettings( IScheme *pScheme ) -{ - BaseClass::ApplySchemeSettings( pScheme ); - m_pFolderUpButton->AddImage( scheme()->GetImage( "resource/icon_folderup", false), -3 ); -} - - -//----------------------------------------------------------------------------- -// Inherited from Frame -//----------------------------------------------------------------------------- -void PerforceFileExplorer::PerformLayout() -{ - BaseClass::PerformLayout(); - - int x, y, w, h; - GetClientArea( x, y, w, h ); - - m_pFullPathCombo->SetBounds( x, y + 6, w - 30, 24 ); - m_pFolderUpButton->SetBounds( x + w - 24, y + 6, 24, 24 ); - - m_pFileList->SetBounds( x, y + 36, w, h - 36 ); -} - - -//----------------------------------------------------------------------------- -// Sets the current directory -//----------------------------------------------------------------------------- -void PerforceFileExplorer::SetCurrentDirectory( const char *pFullPath ) -{ - if ( !pFullPath ) - return; - - while ( isspace( *pFullPath ) ) - { - ++pFullPath; - } - - if ( !pFullPath[0] ) - return; - - m_CurrentDirectory = pFullPath; - m_CurrentDirectory.StripTrailingSlash(); - Q_FixSlashes( m_CurrentDirectory.Get() ); - - PopulateFileList(); - PopulateDriveList(); - - char pCurrentDirectory[ MAX_PATH ]; - m_pFullPathCombo->GetText( pCurrentDirectory, sizeof(pCurrentDirectory) ); - if ( Q_stricmp( m_CurrentDirectory.Get(), pCurrentDirectory ) ) - { - char pNewDirectory[ MAX_PATH ]; - Q_snprintf( pNewDirectory, sizeof(pNewDirectory), "%s\\", m_CurrentDirectory.Get() ); - m_pFullPathCombo->SetText( pNewDirectory ); - m_pFullPathCombo->GetTooltip()->SetText( pNewDirectory ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PerforceFileExplorer::PopulateDriveList() -{ - char pFullPath[MAX_PATH * 4]; - char pSubDirPath[MAX_PATH * 4]; - Q_strncpy( pFullPath, m_CurrentDirectory.Get(), sizeof( pFullPath ) ); - Q_strncpy( pSubDirPath, m_CurrentDirectory.Get(), sizeof( pSubDirPath ) ); - - m_pFullPathCombo->DeleteAllItems(); - - // populate the drive list - char buf[512]; - int len = system()->GetAvailableDrives(buf, 512); - char *pBuf = buf; - for (int i=0; i < len / 4; i++) - { - m_pFullPathCombo->AddItem(pBuf, NULL); - - // is this our drive - add all subdirectories - if ( !_strnicmp( pBuf, pFullPath, 2 ) ) - { - int indent = 0; - char *pData = pFullPath; - while (*pData) - { - if (*pData == '\\') - { - if (indent > 0) - { - memset(pSubDirPath, ' ', indent); - memcpy(pSubDirPath+indent, pFullPath, pData-pFullPath+1); - pSubDirPath[indent+pData-pFullPath+1] = 0; - - m_pFullPathCombo->AddItem( pSubDirPath, NULL ); - } - indent += 2; - } - pData++; - } - } - pBuf += 4; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Fill the filelist with the names of all the files in the current directory -//----------------------------------------------------------------------------- -void PerforceFileExplorer::PopulateFileList() -{ - // clear the current list - m_pFileList->RemoveAllFiles(); - - // Create filter string - char pFullFoundPath[MAX_PATH]; - char pFilter[MAX_PATH+3]; - Q_snprintf( pFilter, sizeof(pFilter), "%s\\*.*", m_CurrentDirectory.Get() ); - - // Find all files on disk - FileFindHandle_t h; - const char *pFileName = g_pFullFileSystem->FindFirstEx( pFilter, NULL, &h ); - for ( ; pFileName; pFileName = g_pFullFileSystem->FindNext( h ) ) - { - if ( !Q_stricmp( pFileName, ".." ) || !Q_stricmp( pFileName, "." ) ) - continue; - - if ( !Q_IsAbsolutePath( pFileName ) ) - { - Q_snprintf( pFullFoundPath, sizeof(pFullFoundPath), "%s\\%s", m_CurrentDirectory.Get(), pFileName ); - pFileName = pFullFoundPath; - } - - int nItemID = m_pFileList->AddFile( pFileName, true ); - m_pFileList->RefreshPerforceState( nItemID, true, NULL ); - } - g_pFullFileSystem->FindClose( h ); - - // Now find all files in perforce - CUtlVector &fileList = p4->GetFileList( m_CurrentDirectory ); - int nCount = fileList.Count(); - for ( int i = 0; i < nCount; ++i ) - { - const char *pFileName = p4->String( fileList[i].m_sLocalFile ); - if ( !pFileName[0] ) - continue; - - int nItemID = m_pFileList->FindFile( pFileName ); - bool bFileExists = true; - if ( nItemID == m_pFileList->InvalidItemID() ) - { - // If it didn't find it, the file must not exist - // since it already would have added it above - bFileExists = false; - nItemID = m_pFileList->AddFile( pFileName, false, fileList[i].m_bDir ); - } - m_pFileList->RefreshPerforceState( nItemID, bFileExists, &fileList[i] ); - } - - m_pFileList->SortList(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Handle an item in the Drive combo box being selected -//----------------------------------------------------------------------------- -void PerforceFileExplorer::OnTextChanged( KeyValues *kv ) -{ - Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); - - // first check which control had its text changed! - if ( pPanel == m_pFullPathCombo ) - { - char pCurrentDirectory[ MAX_PATH ]; - m_pFullPathCombo->GetText( pCurrentDirectory, sizeof(pCurrentDirectory) ); - SetCurrentDirectory( pCurrentDirectory ); - return; - } -} - - -//----------------------------------------------------------------------------- -// Called when the file list was doubleclicked -//----------------------------------------------------------------------------- -void PerforceFileExplorer::OnItemDoubleClicked() -{ - if ( m_pFileList->GetSelectedItemsCount() != 1 ) - return; - - int nItemID = m_pFileList->GetSelectedItem( 0 ); - if ( m_pFileList->IsDirectoryItem( nItemID ) ) - { - const char *pDirectoryName = m_pFileList->GetFile( nItemID ); - SetCurrentDirectory( pDirectoryName ); - } -} - - -//----------------------------------------------------------------------------- -// Called when the folder up button was hit -//----------------------------------------------------------------------------- -void PerforceFileExplorer::OnFolderUp() -{ - char pUpDirectory[MAX_PATH]; - Q_strncpy( pUpDirectory, m_CurrentDirectory.Get(), sizeof(pUpDirectory) ); - Q_StripLastDir( pUpDirectory, sizeof(pUpDirectory) ); - Q_StripTrailingSlash( pUpDirectory ); - - // This occurs at the root directory - if ( !Q_stricmp( pUpDirectory, "." ) ) - return; - SetCurrentDirectory( pUpDirectory ); -} - - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Contains a list of files, determines their perforce status +// +// $NoKeywords: $ +//===========================================================================// + +#include +#include +#include +#include +#include +#include "tier1/KeyValues.h" +#include "vgui/ISystem.h" +#include "filesystem.h" +#include +#include "p4lib/ip4.h" +#include "tier2/tier2.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +PerforceFileExplorer::PerforceFileExplorer( Panel *pParent, const char *pPanelName ) : + BaseClass( pParent, pPanelName ) +{ + m_pFileList = new PerforceFileList( this, "PerforceFileList" ); + + // Get the list of available drives and put them in a menu here. + // Start with the directory we are in. + m_pFullPathCombo = new ComboBox( this, "FullPathCombo", 8, false ); + m_pFullPathCombo->GetTooltip()->SetTooltipFormatToSingleLine(); + + char pFullPath[MAX_PATH]; + g_pFullFileSystem->GetCurrentDirectory( pFullPath, sizeof(pFullPath) ); + SetCurrentDirectory( pFullPath ); + + m_pFullPathCombo->AddActionSignalTarget( this ); + + m_pFolderUpButton = new Button(this, "FolderUpButton", "", this); + m_pFolderUpButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_Up" ); + m_pFolderUpButton->SetCommand( new KeyValues( "FolderUp" ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +PerforceFileExplorer::~PerforceFileExplorer() +{ +} + + +//----------------------------------------------------------------------------- +// Inherited from Frame +//----------------------------------------------------------------------------- +void PerforceFileExplorer::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + m_pFolderUpButton->AddImage( scheme()->GetImage( "resource/icon_folderup", false), -3 ); +} + + +//----------------------------------------------------------------------------- +// Inherited from Frame +//----------------------------------------------------------------------------- +void PerforceFileExplorer::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y, w, h; + GetClientArea( x, y, w, h ); + + m_pFullPathCombo->SetBounds( x, y + 6, w - 30, 24 ); + m_pFolderUpButton->SetBounds( x + w - 24, y + 6, 24, 24 ); + + m_pFileList->SetBounds( x, y + 36, w, h - 36 ); +} + + +//----------------------------------------------------------------------------- +// Sets the current directory +//----------------------------------------------------------------------------- +void PerforceFileExplorer::SetCurrentDirectory( const char *pFullPath ) +{ + if ( !pFullPath ) + return; + + while ( isspace( *pFullPath ) ) + { + ++pFullPath; + } + + if ( !pFullPath[0] ) + return; + + m_CurrentDirectory = pFullPath; + m_CurrentDirectory.StripTrailingSlash(); + Q_FixSlashes( m_CurrentDirectory.Get() ); + + PopulateFileList(); + PopulateDriveList(); + + char pCurrentDirectory[ MAX_PATH ]; + m_pFullPathCombo->GetText( pCurrentDirectory, sizeof(pCurrentDirectory) ); + if ( Q_stricmp( m_CurrentDirectory.Get(), pCurrentDirectory ) ) + { + char pNewDirectory[ MAX_PATH ]; + Q_snprintf( pNewDirectory, sizeof(pNewDirectory), "%s\\", m_CurrentDirectory.Get() ); + m_pFullPathCombo->SetText( pNewDirectory ); + m_pFullPathCombo->GetTooltip()->SetText( pNewDirectory ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PerforceFileExplorer::PopulateDriveList() +{ + char pFullPath[MAX_PATH * 4]; + char pSubDirPath[MAX_PATH * 4]; + Q_strncpy( pFullPath, m_CurrentDirectory.Get(), sizeof( pFullPath ) ); + Q_strncpy( pSubDirPath, m_CurrentDirectory.Get(), sizeof( pSubDirPath ) ); + + m_pFullPathCombo->DeleteAllItems(); + + // populate the drive list + char buf[512]; + int len = system()->GetAvailableDrives(buf, 512); + char *pBuf = buf; + for (int i=0; i < len / 4; i++) + { + m_pFullPathCombo->AddItem(pBuf, NULL); + + // is this our drive - add all subdirectories + if ( !_strnicmp( pBuf, pFullPath, 2 ) ) + { + int indent = 0; + char *pData = pFullPath; + while (*pData) + { + if (*pData == '\\') + { + if (indent > 0) + { + memset(pSubDirPath, ' ', indent); + memcpy(pSubDirPath+indent, pFullPath, pData-pFullPath+1); + pSubDirPath[indent+pData-pFullPath+1] = 0; + + m_pFullPathCombo->AddItem( pSubDirPath, NULL ); + } + indent += 2; + } + pData++; + } + } + pBuf += 4; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Fill the filelist with the names of all the files in the current directory +//----------------------------------------------------------------------------- +void PerforceFileExplorer::PopulateFileList() +{ + // clear the current list + m_pFileList->RemoveAllFiles(); + + // Create filter string + char pFullFoundPath[MAX_PATH]; + char pFilter[MAX_PATH+3]; + Q_snprintf( pFilter, sizeof(pFilter), "%s\\*.*", m_CurrentDirectory.Get() ); + + // Find all files on disk + FileFindHandle_t h; + const char *pFileName = g_pFullFileSystem->FindFirstEx( pFilter, NULL, &h ); + for ( ; pFileName; pFileName = g_pFullFileSystem->FindNext( h ) ) + { + if ( !Q_stricmp( pFileName, ".." ) || !Q_stricmp( pFileName, "." ) ) + continue; + + if ( !Q_IsAbsolutePath( pFileName ) ) + { + Q_snprintf( pFullFoundPath, sizeof(pFullFoundPath), "%s\\%s", m_CurrentDirectory.Get(), pFileName ); + pFileName = pFullFoundPath; + } + + int nItemID = m_pFileList->AddFile( pFileName, true ); + m_pFileList->RefreshPerforceState( nItemID, true, NULL ); + } + g_pFullFileSystem->FindClose( h ); + + // Now find all files in perforce + CUtlVector &fileList = p4->GetFileList( m_CurrentDirectory ); + int nCount = fileList.Count(); + for ( int i = 0; i < nCount; ++i ) + { + const char *pFileName = p4->String( fileList[i].m_sLocalFile ); + if ( !pFileName[0] ) + continue; + + int nItemID = m_pFileList->FindFile( pFileName ); + bool bFileExists = true; + if ( nItemID == m_pFileList->InvalidItemID() ) + { + // If it didn't find it, the file must not exist + // since it already would have added it above + bFileExists = false; + nItemID = m_pFileList->AddFile( pFileName, false, fileList[i].m_bDir ); + } + m_pFileList->RefreshPerforceState( nItemID, bFileExists, &fileList[i] ); + } + + m_pFileList->SortList(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Handle an item in the Drive combo box being selected +//----------------------------------------------------------------------------- +void PerforceFileExplorer::OnTextChanged( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + + // first check which control had its text changed! + if ( pPanel == m_pFullPathCombo ) + { + char pCurrentDirectory[ MAX_PATH ]; + m_pFullPathCombo->GetText( pCurrentDirectory, sizeof(pCurrentDirectory) ); + SetCurrentDirectory( pCurrentDirectory ); + return; + } +} + + +//----------------------------------------------------------------------------- +// Called when the file list was doubleclicked +//----------------------------------------------------------------------------- +void PerforceFileExplorer::OnItemDoubleClicked() +{ + if ( m_pFileList->GetSelectedItemsCount() != 1 ) + return; + + int nItemID = m_pFileList->GetSelectedItem( 0 ); + if ( m_pFileList->IsDirectoryItem( nItemID ) ) + { + const char *pDirectoryName = m_pFileList->GetFile( nItemID ); + SetCurrentDirectory( pDirectoryName ); + } +} + + +//----------------------------------------------------------------------------- +// Called when the folder up button was hit +//----------------------------------------------------------------------------- +void PerforceFileExplorer::OnFolderUp() +{ + char pUpDirectory[MAX_PATH]; + Q_strncpy( pUpDirectory, m_CurrentDirectory.Get(), sizeof(pUpDirectory) ); + Q_StripLastDir( pUpDirectory, sizeof(pUpDirectory) ); + Q_StripTrailingSlash( pUpDirectory ); + + // This occurs at the root directory + if ( !Q_stricmp( pUpDirectory, "." ) ) + return; + SetCurrentDirectory( pUpDirectory ); +} + + + diff --git a/mp/src/vgui2/vgui_controls/PerforceFileList.cpp b/mp/src/vgui2/vgui_controls/PerforceFileList.cpp index 618f4443..de7fa32a 100644 --- a/mp/src/vgui2/vgui_controls/PerforceFileList.cpp +++ b/mp/src/vgui2/vgui_controls/PerforceFileList.cpp @@ -1,570 +1,570 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: Contains a list of files, determines their perforce status -// -// $NoKeywords: $ -//===========================================================================// - -#include -#include -#include -#include -#include "tier1/KeyValues.h" -#include -#include "filesystem.h" -#include "p4lib/ip4.h" -#include "tier2/tier2.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - - -using namespace vgui; - - -static int ListFileNameSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) -{ - NOTE_UNUSED( pPanel ); - - bool dir1 = item1.kv->GetInt("directory") == 1; - bool dir2 = item2.kv->GetInt("directory") == 1; - - // if they're both not directories of files, return if dir1 is a directory (before files) - if ( dir1 != dir2 ) - { - return dir1 ? -1 : 1; - } - - const char *string1 = item1.kv->GetString("text"); - const char *string2 = item2.kv->GetString("text"); - - // YWB: Mimic windows behavior where filenames starting with numbers are sorted based on numeric part - int num1 = Q_atoi( string1 ); - int num2 = Q_atoi( string2 ); - - if ( num1 != 0 && - num2 != 0 ) - { - if ( num1 < num2 ) - return -1; - else if ( num1 > num2 ) - return 1; - } - - // Push numbers before everything else - if ( num1 != 0 ) - { - return -1; - } - - // Push numbers before everything else - if ( num2 != 0 ) - { - return 1; - } - - return Q_stricmp( string1, string2 ); -} - -static int ListBaseStringSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName ) -{ - bool dir1 = item1.kv->GetInt("directory") == 1; - bool dir2 = item2.kv->GetInt("directory") == 1; - - // if they're both not directories of files, return if dir1 is a directory (before files) - if (dir1 != dir2) - { - return -1; - } - - const char *string1 = item1.kv->GetString(fieldName); - const char *string2 = item2.kv->GetString(fieldName); - int cval = Q_stricmp(string1, string2); - if ( cval == 0 ) - { - // Use filename to break ties - return ListFileNameSortFunc( pPanel, item1, item2 ); - } - - return cval; -} - -static int ListBaseIntegerSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName ) -{ - bool dir1 = item1.kv->GetInt("directory") == 1; - bool dir2 = item2.kv->GetInt("directory") == 1; - - // if they're both not directories of files, return if dir1 is a directory (before files) - if (dir1 != dir2) - { - return -1; - } - - int i1 = item1.kv->GetInt(fieldName); - int i2 = item2.kv->GetInt(fieldName); - if ( i1 == i2 ) - { - // Use filename to break ties - return ListFileNameSortFunc( pPanel, item1, item2 ); - } - - return ( i1 < i2 ) ? -1 : 1; -} - -static int ListFileSizeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) -{ - return ListBaseIntegerSortFunc( pPanel, item1, item2, "filesizeint" ); -} - -static int ListFileAttributesSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) -{ - return ListBaseStringSortFunc( pPanel, item1, item2, "attributes" ); -} - -static int ListFileTypeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) -{ - return ListBaseStringSortFunc( pPanel, item1, item2, "type" ); -} - - -//----------------------------------------------------------------------------- -// Dictionary of start dir contexts -//----------------------------------------------------------------------------- -struct ColumnInfo_t -{ - char const *columnName; - char const *columnText; - int startingWidth; - int minWidth; - int maxWidth; - int flags; - SortFunc *pfnSort; - Label::Alignment alignment; -}; - -static ColumnInfo_t g_ColInfo[] = -{ - { "text", "#PerforceFileList_Col_Name", 175, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileNameSortFunc , Label::a_west }, - { "type", "#PerforceFileList_Col_Type", 150, 20, 10000, 0, &ListFileTypeSortFunc , Label::a_west }, - { "in_perforce", "#PerforceFileList_Col_InPerforce", 50, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileAttributesSortFunc , Label::a_west }, - { "synched", "#PerforceFileList_Col_Synched", 50, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileAttributesSortFunc , Label::a_west }, - { "checked_out", "#PerforceFileList_Col_Checked_Out", 50, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileAttributesSortFunc , Label::a_west }, - { "attributes", "#PerforceFileList_Col_Attributes", 50, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileAttributesSortFunc , Label::a_west }, -}; - - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -PerforceFileList::PerforceFileList( Panel *pParent, const char *pPanelName ) : - BaseClass( pParent, pPanelName ) -{ - SetMultiselectEnabled( false ); - m_bShowDeletedFiles = false; - - // list panel - for ( int i = 0; i < ARRAYSIZE( g_ColInfo ); ++i ) - { - const ColumnInfo_t& info = g_ColInfo[ i ]; - - AddColumnHeader( i, info.columnName, info.columnText, info.startingWidth, info.minWidth, info.maxWidth, info.flags ); - SetSortFunc( i, info.pfnSort ); - SetColumnTextAlignment( i, info.alignment ); - } - - SetSortColumn( 0 ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -PerforceFileList::~PerforceFileList() -{ -} - - -//----------------------------------------------------------------------------- -// Purpose: Apply scheme settings -//----------------------------------------------------------------------------- -void PerforceFileList::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings( pScheme ); - - ImageList *pImageList = new ImageList( false ); - pImageList->AddImage( scheme()->GetImage( "resource/icon_file", false ) ); - pImageList->AddImage( scheme()->GetImage( "resource/icon_folder", false ) ); - pImageList->AddImage( scheme()->GetImage( "resource/icon_folder_selected", false ) ); - - SetImageList( pImageList, true ); -} - - -//----------------------------------------------------------------------------- -// Toggle showing deleted files or not -//----------------------------------------------------------------------------- -void PerforceFileList::ShowDeletedFiles( bool bShowDeletedFiles ) -{ - if ( m_bShowDeletedFiles != bShowDeletedFiles ) - { - m_bShowDeletedFiles = bShowDeletedFiles; - - for ( int i = FirstItem(); i != InvalidItemID(); i = NextItem( i ) ) - { - KeyValues *pKeyValues = GetItem( i ); - if ( !pKeyValues->GetInt( "deleted", 0 ) ) - continue; - - SetItemVisible( i, m_bShowDeletedFiles ); - } - } -} - - -//----------------------------------------------------------------------------- -// Add a directory to the directory list, returns client spec -//----------------------------------------------------------------------------- -void PerforceFileList::AddItemToDirectoryList( const char *pFullPath, int nItemID, bool bIsDirectory ) -{ - char pDirectoryBuf[MAX_PATH]; - Q_ExtractFilePath( pFullPath, pDirectoryBuf, sizeof(pDirectoryBuf) ); - Q_StripTrailingSlash( pDirectoryBuf ); - pFullPath = pDirectoryBuf; - - DirectoryInfo_t *pInfo; - UtlSymId_t i = m_Directories.Find( pFullPath ); - if ( i != m_Directories.InvalidIndex() ) - { - pInfo = &m_Directories[i]; - } - else - { - char pClientSpec[MAX_PATH]; - if ( !p4->GetClientSpecForDirectory( pFullPath, pClientSpec, sizeof(pClientSpec) ) ) - { - pClientSpec[0] = 0; - } - - pInfo = &m_Directories[ pFullPath ]; - pInfo->m_ClientSpec = pClientSpec; - } - - pInfo->m_ItemIDs.AddToTail( nItemID ); -} - - -//----------------------------------------------------------------------------- -// Add a file to the file list. -//----------------------------------------------------------------------------- -int PerforceFileList::AddFileToFileList( const char *pFullPath, bool bExistsOnDisk ) -{ - bool bIsFileWriteable = bExistsOnDisk ? g_pFullFileSystem->IsFileWritable( pFullPath, NULL ) : true; - - // add the file to the list - KeyValues *kv = new KeyValues("item"); - - const char *pRelativePath = Q_UnqualifiedFileName( pFullPath ); - kv->SetString( "text", pRelativePath ); - kv->SetString( "fullpath", pFullPath ); - kv->SetInt( "image", 1 ); - - IImage *pImage = surface()->GetIconImageForFullPath( pFullPath ); - if ( pImage ) - { - kv->SetPtr( "iconImage", (void *)pImage ); - } - - kv->SetInt( "imageSelected", 1 ); - kv->SetInt( "directory", 0 ); - - // These are computed by Refresh - kv->SetInt( "in_perforce", 0 ); - kv->SetInt( "synched", 0 ); - kv->SetInt( "checked_out", 0 ); - kv->SetInt( "deleted", 0 ); - - wchar_t pFileType[ 80 ]; - g_pFullFileSystem->GetFileTypeForFullPath( pFullPath, pFileType, sizeof( pFileType ) ); - - kv->SetWString( "type", pFileType ); - kv->SetString( "attributes", bIsFileWriteable ? "" : "R" ); - - int nItemID = AddItem( kv, 0, false, false ); - kv->deleteThis(); - - AddItemToDirectoryList( pFullPath, nItemID, false ); - return nItemID; -} - - -//----------------------------------------------------------------------------- -// Add a directory to the file list. -//----------------------------------------------------------------------------- -int PerforceFileList::AddDirectoryToFileList( const char *pFullPath, bool bExistsOnDisk ) -{ - KeyValues *kv = new KeyValues("item"); - - const char *pRelativePath = Q_UnqualifiedFileName( pFullPath ); - kv->SetString( "text", pRelativePath ); - kv->SetString( "fullpath", pFullPath ); - kv->SetPtr( "iconImage", (void *)NULL ); - kv->SetInt( "image", 2 ); - kv->SetInt( "imageSelected", 3 ); - kv->SetInt( "directory", 1 ); - - // These are computed by Refresh - kv->SetInt( "in_perforce", 0 ); - kv->SetInt( "synched", 0 ); - kv->SetInt( "checked_out", 0 ); - kv->SetInt( "deleted", 0 ); - - kv->SetString( "type", "#PerforceFileList_FileType_Folder" ); - kv->SetString( "attributes", "D" ); - - int nItemID = AddItem(kv, 0, false, false); - kv->deleteThis(); - - AddItemToDirectoryList( pFullPath, nItemID, true ); - return nItemID; -} - - -//----------------------------------------------------------------------------- -// Add a file or directory to the file list. -//----------------------------------------------------------------------------- -int PerforceFileList::AddFile( const char *pFullPath, int nFileExists, int nIsDirectory ) -{ - if ( !pFullPath ) - return InvalidItemID(); - - if ( !Q_IsAbsolutePath( pFullPath ) ) - { - Warning( "Absolute paths required for PerforceFileList::AddFile!\n" - "\"%s\" is not an abolute path", pFullPath ); - return InvalidItemID(); - } - - char pFixedPath[MAX_PATH]; - Q_strncpy( pFixedPath, pFullPath, sizeof(pFixedPath) ); - Q_FixSlashes( pFixedPath ); - - // Check to see if the file is on disk - int nItemID = -1; - bool bFileExists, bIsDirectory; - if ( nFileExists < 0 ) - { - bFileExists = g_pFullFileSystem->FileExists( pFixedPath ) ; - } - else - { - bFileExists = ( nFileExists != 0 ); - } - - if ( nIsDirectory < 0 ) - { - if ( bFileExists ) - { - bIsDirectory = g_pFullFileSystem->IsDirectory( pFixedPath ); - } - else - { - int nLen = Q_strlen( pFixedPath ); - bIsDirectory = ( pFixedPath[nLen-1] == CORRECT_PATH_SEPARATOR ); - } - } - else - { - bIsDirectory = ( nIsDirectory != 0 ); - } - - if ( bIsDirectory ) - { - nItemID = AddDirectoryToFileList( pFixedPath, bFileExists ); - } - else - { - nItemID = AddFileToFileList( pFixedPath, bFileExists ); - } - - return nItemID; -} - - -//----------------------------------------------------------------------------- -// Remove all files from the list -//----------------------------------------------------------------------------- -void PerforceFileList::RemoveAllFiles() -{ - RemoveAll(); - m_Directories.Clear(); -} - - -//----------------------------------------------------------------------------- -// Finds a file in the p4 list -//----------------------------------------------------------------------------- -static P4File_t *FindFileInPerforceList( const char *pFileName, CUtlVector &fileList, bool *pFound ) -{ - int nCount = fileList.Count(); - for ( int i = 0; i < nCount; ++i ) - { - if ( pFound[i] ) - continue; - - const char *pPerforceFileName = p4->String( fileList[i].m_sLocalFile ); - if ( !Q_stricmp( pPerforceFileName, pFileName ) ) - { - pFound[i] = true; - return &fileList[i]; - } - } - return NULL; -} - - -//----------------------------------------------------------------------------- -// Refresh perforce information -//----------------------------------------------------------------------------- -void PerforceFileList::RefreshPerforceState( int nItemID, bool bFileExists, P4File_t *pFileInfo ) -{ - KeyValues *kv = GetItem( nItemID ); - - bool bIsSynched = false; - bool bIsFileInPerforce = (pFileInfo != NULL); - if ( bIsFileInPerforce ) - { - if ( pFileInfo->m_bDeleted != bFileExists ) - { - bIsSynched = ( pFileInfo->m_bDeleted || ( pFileInfo->m_iHeadRevision == pFileInfo->m_iHaveRevision ) ); - } - } - else - { - bIsSynched = !bFileExists; - } - - bool bIsDeleted = bIsFileInPerforce && !bFileExists && pFileInfo->m_bDeleted; - - kv->SetInt( "in_perforce", bIsFileInPerforce ); - kv->SetInt( "synched", bIsSynched ); - kv->SetInt( "checked_out", bIsFileInPerforce && ( pFileInfo->m_eOpenState != P4FILE_UNOPENED ) ); - kv->SetInt( "deleted", bIsDeleted ); - - if ( bIsDeleted ) - { - SetItemVisible( nItemID, m_bShowDeletedFiles ); - } -} - - -//----------------------------------------------------------------------------- -// Refresh perforce information -//----------------------------------------------------------------------------- -void PerforceFileList::Refresh() -{ - /* - // Slow method.. does too many perforce operations - for ( int i = FirstItem(); i != InvalidItemID(); i = NextItem( i ) ) - { - const char *pFile = GetFile( i ); - - P4File_t fileInfo; - bool bIsFileInPerforce = p4->GetFileInfo( pFile, &fileInfo ); - bool bFileExists = g_pFullFileSystem->FileExists( pFile ); - RefreshPerforceState( i, bFileExists, bIsFileInPerforce ? &fileInfo : NULL ); - } - */ - - // NOTE: Reducing the # of perforce calls is important for performance - int nCount = m_Directories.GetNumStrings(); - for ( int i = 0; i < nCount; ++i ) - { - const char *pDirectory = m_Directories.String(i); - DirectoryInfo_t *pInfo = &m_Directories[i]; - - // Retrives files, uses faster method to avoid finding clientspec - CUtlVector &fileList = p4->GetFileListUsingClientSpec( pDirectory, pInfo->m_ClientSpec ); - int nFileCount = fileList.Count(); - bool *pFound = (bool*)_alloca( nFileCount * sizeof(bool) ); - memset( pFound, 0, nFileCount * sizeof(bool) ); - - int nItemCount = pInfo->m_ItemIDs.Count(); - for ( int j = 0; j < nItemCount; ++j ) - { - int nItemID = pInfo->m_ItemIDs[j]; - const char *pFileName = GetFile( nItemID ); - bool bFileExists = g_pFullFileSystem->FileExists( pFileName ); - P4File_t *pFileInfo = FindFileInPerforceList( pFileName, fileList, pFound ); - RefreshPerforceState( nItemID, bFileExists, pFileInfo ); - } - } -} - - -//----------------------------------------------------------------------------- -// Is a particular list item a directory? -//----------------------------------------------------------------------------- -bool PerforceFileList::IsDirectoryItem( int nItemID ) -{ - KeyValues *kv = GetItem( nItemID ); - return kv->GetInt( "directory", 0 ) != 0; -} - - -//----------------------------------------------------------------------------- -// Returns the file associated with a particular item ID -//----------------------------------------------------------------------------- -const char *PerforceFileList::GetFile( int nItemID ) -{ - KeyValues *kv = GetItem( nItemID ); - Assert( kv ); - return kv->GetString( "fullpath", "" ); -} - - -//----------------------------------------------------------------------------- -// Find the item ID associated with a particular file -//----------------------------------------------------------------------------- -int PerforceFileList::FindFile( const char *pFullPath ) -{ - for ( int i = FirstItem(); i != InvalidItemID(); i = NextItem( i ) ) - { - const char *pFile = GetFile( i ); - if ( !Q_stricmp( pFile, pFullPath ) ) - return i; - } - return InvalidItemID(); -} - - -//----------------------------------------------------------------------------- -// Is a file already in the list? -//----------------------------------------------------------------------------- -bool PerforceFileList::IsFileInList( const char *pFullPath ) -{ - return ( FindFile( pFullPath ) != InvalidItemID() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Double-click: expand folders -//----------------------------------------------------------------------------- -void PerforceFileList::OnMouseDoublePressed( MouseCode code ) -{ - if ( code == MOUSE_LEFT ) - { - // select the item - OnMousePressed(code); - - // post a special message - if ( GetSelectedItemsCount() > 0 ) - { - PostActionSignal( new KeyValues("ItemDoubleClicked" ) ); - } - return; - } - - BaseClass::OnMouseDoublePressed( code ); -} - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Contains a list of files, determines their perforce status +// +// $NoKeywords: $ +//===========================================================================// + +#include +#include +#include +#include +#include "tier1/KeyValues.h" +#include +#include "filesystem.h" +#include "p4lib/ip4.h" +#include "tier2/tier2.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + + +using namespace vgui; + + +static int ListFileNameSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + NOTE_UNUSED( pPanel ); + + bool dir1 = item1.kv->GetInt("directory") == 1; + bool dir2 = item2.kv->GetInt("directory") == 1; + + // if they're both not directories of files, return if dir1 is a directory (before files) + if ( dir1 != dir2 ) + { + return dir1 ? -1 : 1; + } + + const char *string1 = item1.kv->GetString("text"); + const char *string2 = item2.kv->GetString("text"); + + // YWB: Mimic windows behavior where filenames starting with numbers are sorted based on numeric part + int num1 = Q_atoi( string1 ); + int num2 = Q_atoi( string2 ); + + if ( num1 != 0 && + num2 != 0 ) + { + if ( num1 < num2 ) + return -1; + else if ( num1 > num2 ) + return 1; + } + + // Push numbers before everything else + if ( num1 != 0 ) + { + return -1; + } + + // Push numbers before everything else + if ( num2 != 0 ) + { + return 1; + } + + return Q_stricmp( string1, string2 ); +} + +static int ListBaseStringSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName ) +{ + bool dir1 = item1.kv->GetInt("directory") == 1; + bool dir2 = item2.kv->GetInt("directory") == 1; + + // if they're both not directories of files, return if dir1 is a directory (before files) + if (dir1 != dir2) + { + return -1; + } + + const char *string1 = item1.kv->GetString(fieldName); + const char *string2 = item2.kv->GetString(fieldName); + int cval = Q_stricmp(string1, string2); + if ( cval == 0 ) + { + // Use filename to break ties + return ListFileNameSortFunc( pPanel, item1, item2 ); + } + + return cval; +} + +static int ListBaseIntegerSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName ) +{ + bool dir1 = item1.kv->GetInt("directory") == 1; + bool dir2 = item2.kv->GetInt("directory") == 1; + + // if they're both not directories of files, return if dir1 is a directory (before files) + if (dir1 != dir2) + { + return -1; + } + + int i1 = item1.kv->GetInt(fieldName); + int i2 = item2.kv->GetInt(fieldName); + if ( i1 == i2 ) + { + // Use filename to break ties + return ListFileNameSortFunc( pPanel, item1, item2 ); + } + + return ( i1 < i2 ) ? -1 : 1; +} + +static int ListFileSizeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + return ListBaseIntegerSortFunc( pPanel, item1, item2, "filesizeint" ); +} + +static int ListFileAttributesSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + return ListBaseStringSortFunc( pPanel, item1, item2, "attributes" ); +} + +static int ListFileTypeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + return ListBaseStringSortFunc( pPanel, item1, item2, "type" ); +} + + +//----------------------------------------------------------------------------- +// Dictionary of start dir contexts +//----------------------------------------------------------------------------- +struct ColumnInfo_t +{ + char const *columnName; + char const *columnText; + int startingWidth; + int minWidth; + int maxWidth; + int flags; + SortFunc *pfnSort; + Label::Alignment alignment; +}; + +static ColumnInfo_t g_ColInfo[] = +{ + { "text", "#PerforceFileList_Col_Name", 175, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileNameSortFunc , Label::a_west }, + { "type", "#PerforceFileList_Col_Type", 150, 20, 10000, 0, &ListFileTypeSortFunc , Label::a_west }, + { "in_perforce", "#PerforceFileList_Col_InPerforce", 50, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileAttributesSortFunc , Label::a_west }, + { "synched", "#PerforceFileList_Col_Synched", 50, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileAttributesSortFunc , Label::a_west }, + { "checked_out", "#PerforceFileList_Col_Checked_Out", 50, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileAttributesSortFunc , Label::a_west }, + { "attributes", "#PerforceFileList_Col_Attributes", 50, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileAttributesSortFunc , Label::a_west }, +}; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +PerforceFileList::PerforceFileList( Panel *pParent, const char *pPanelName ) : + BaseClass( pParent, pPanelName ) +{ + SetMultiselectEnabled( false ); + m_bShowDeletedFiles = false; + + // list panel + for ( int i = 0; i < ARRAYSIZE( g_ColInfo ); ++i ) + { + const ColumnInfo_t& info = g_ColInfo[ i ]; + + AddColumnHeader( i, info.columnName, info.columnText, info.startingWidth, info.minWidth, info.maxWidth, info.flags ); + SetSortFunc( i, info.pfnSort ); + SetColumnTextAlignment( i, info.alignment ); + } + + SetSortColumn( 0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +PerforceFileList::~PerforceFileList() +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: Apply scheme settings +//----------------------------------------------------------------------------- +void PerforceFileList::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + ImageList *pImageList = new ImageList( false ); + pImageList->AddImage( scheme()->GetImage( "resource/icon_file", false ) ); + pImageList->AddImage( scheme()->GetImage( "resource/icon_folder", false ) ); + pImageList->AddImage( scheme()->GetImage( "resource/icon_folder_selected", false ) ); + + SetImageList( pImageList, true ); +} + + +//----------------------------------------------------------------------------- +// Toggle showing deleted files or not +//----------------------------------------------------------------------------- +void PerforceFileList::ShowDeletedFiles( bool bShowDeletedFiles ) +{ + if ( m_bShowDeletedFiles != bShowDeletedFiles ) + { + m_bShowDeletedFiles = bShowDeletedFiles; + + for ( int i = FirstItem(); i != InvalidItemID(); i = NextItem( i ) ) + { + KeyValues *pKeyValues = GetItem( i ); + if ( !pKeyValues->GetInt( "deleted", 0 ) ) + continue; + + SetItemVisible( i, m_bShowDeletedFiles ); + } + } +} + + +//----------------------------------------------------------------------------- +// Add a directory to the directory list, returns client spec +//----------------------------------------------------------------------------- +void PerforceFileList::AddItemToDirectoryList( const char *pFullPath, int nItemID, bool bIsDirectory ) +{ + char pDirectoryBuf[MAX_PATH]; + Q_ExtractFilePath( pFullPath, pDirectoryBuf, sizeof(pDirectoryBuf) ); + Q_StripTrailingSlash( pDirectoryBuf ); + pFullPath = pDirectoryBuf; + + DirectoryInfo_t *pInfo; + UtlSymId_t i = m_Directories.Find( pFullPath ); + if ( i != m_Directories.InvalidIndex() ) + { + pInfo = &m_Directories[i]; + } + else + { + char pClientSpec[MAX_PATH]; + if ( !p4->GetClientSpecForDirectory( pFullPath, pClientSpec, sizeof(pClientSpec) ) ) + { + pClientSpec[0] = 0; + } + + pInfo = &m_Directories[ pFullPath ]; + pInfo->m_ClientSpec = pClientSpec; + } + + pInfo->m_ItemIDs.AddToTail( nItemID ); +} + + +//----------------------------------------------------------------------------- +// Add a file to the file list. +//----------------------------------------------------------------------------- +int PerforceFileList::AddFileToFileList( const char *pFullPath, bool bExistsOnDisk ) +{ + bool bIsFileWriteable = bExistsOnDisk ? g_pFullFileSystem->IsFileWritable( pFullPath, NULL ) : true; + + // add the file to the list + KeyValues *kv = new KeyValues("item"); + + const char *pRelativePath = Q_UnqualifiedFileName( pFullPath ); + kv->SetString( "text", pRelativePath ); + kv->SetString( "fullpath", pFullPath ); + kv->SetInt( "image", 1 ); + + IImage *pImage = surface()->GetIconImageForFullPath( pFullPath ); + if ( pImage ) + { + kv->SetPtr( "iconImage", (void *)pImage ); + } + + kv->SetInt( "imageSelected", 1 ); + kv->SetInt( "directory", 0 ); + + // These are computed by Refresh + kv->SetInt( "in_perforce", 0 ); + kv->SetInt( "synched", 0 ); + kv->SetInt( "checked_out", 0 ); + kv->SetInt( "deleted", 0 ); + + wchar_t pFileType[ 80 ]; + g_pFullFileSystem->GetFileTypeForFullPath( pFullPath, pFileType, sizeof( pFileType ) ); + + kv->SetWString( "type", pFileType ); + kv->SetString( "attributes", bIsFileWriteable ? "" : "R" ); + + int nItemID = AddItem( kv, 0, false, false ); + kv->deleteThis(); + + AddItemToDirectoryList( pFullPath, nItemID, false ); + return nItemID; +} + + +//----------------------------------------------------------------------------- +// Add a directory to the file list. +//----------------------------------------------------------------------------- +int PerforceFileList::AddDirectoryToFileList( const char *pFullPath, bool bExistsOnDisk ) +{ + KeyValues *kv = new KeyValues("item"); + + const char *pRelativePath = Q_UnqualifiedFileName( pFullPath ); + kv->SetString( "text", pRelativePath ); + kv->SetString( "fullpath", pFullPath ); + kv->SetPtr( "iconImage", (void *)NULL ); + kv->SetInt( "image", 2 ); + kv->SetInt( "imageSelected", 3 ); + kv->SetInt( "directory", 1 ); + + // These are computed by Refresh + kv->SetInt( "in_perforce", 0 ); + kv->SetInt( "synched", 0 ); + kv->SetInt( "checked_out", 0 ); + kv->SetInt( "deleted", 0 ); + + kv->SetString( "type", "#PerforceFileList_FileType_Folder" ); + kv->SetString( "attributes", "D" ); + + int nItemID = AddItem(kv, 0, false, false); + kv->deleteThis(); + + AddItemToDirectoryList( pFullPath, nItemID, true ); + return nItemID; +} + + +//----------------------------------------------------------------------------- +// Add a file or directory to the file list. +//----------------------------------------------------------------------------- +int PerforceFileList::AddFile( const char *pFullPath, int nFileExists, int nIsDirectory ) +{ + if ( !pFullPath ) + return InvalidItemID(); + + if ( !Q_IsAbsolutePath( pFullPath ) ) + { + Warning( "Absolute paths required for PerforceFileList::AddFile!\n" + "\"%s\" is not an abolute path", pFullPath ); + return InvalidItemID(); + } + + char pFixedPath[MAX_PATH]; + Q_strncpy( pFixedPath, pFullPath, sizeof(pFixedPath) ); + Q_FixSlashes( pFixedPath ); + + // Check to see if the file is on disk + int nItemID = -1; + bool bFileExists, bIsDirectory; + if ( nFileExists < 0 ) + { + bFileExists = g_pFullFileSystem->FileExists( pFixedPath ) ; + } + else + { + bFileExists = ( nFileExists != 0 ); + } + + if ( nIsDirectory < 0 ) + { + if ( bFileExists ) + { + bIsDirectory = g_pFullFileSystem->IsDirectory( pFixedPath ); + } + else + { + int nLen = Q_strlen( pFixedPath ); + bIsDirectory = ( pFixedPath[nLen-1] == CORRECT_PATH_SEPARATOR ); + } + } + else + { + bIsDirectory = ( nIsDirectory != 0 ); + } + + if ( bIsDirectory ) + { + nItemID = AddDirectoryToFileList( pFixedPath, bFileExists ); + } + else + { + nItemID = AddFileToFileList( pFixedPath, bFileExists ); + } + + return nItemID; +} + + +//----------------------------------------------------------------------------- +// Remove all files from the list +//----------------------------------------------------------------------------- +void PerforceFileList::RemoveAllFiles() +{ + RemoveAll(); + m_Directories.Clear(); +} + + +//----------------------------------------------------------------------------- +// Finds a file in the p4 list +//----------------------------------------------------------------------------- +static P4File_t *FindFileInPerforceList( const char *pFileName, CUtlVector &fileList, bool *pFound ) +{ + int nCount = fileList.Count(); + for ( int i = 0; i < nCount; ++i ) + { + if ( pFound[i] ) + continue; + + const char *pPerforceFileName = p4->String( fileList[i].m_sLocalFile ); + if ( !Q_stricmp( pPerforceFileName, pFileName ) ) + { + pFound[i] = true; + return &fileList[i]; + } + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Refresh perforce information +//----------------------------------------------------------------------------- +void PerforceFileList::RefreshPerforceState( int nItemID, bool bFileExists, P4File_t *pFileInfo ) +{ + KeyValues *kv = GetItem( nItemID ); + + bool bIsSynched = false; + bool bIsFileInPerforce = (pFileInfo != NULL); + if ( bIsFileInPerforce ) + { + if ( pFileInfo->m_bDeleted != bFileExists ) + { + bIsSynched = ( pFileInfo->m_bDeleted || ( pFileInfo->m_iHeadRevision == pFileInfo->m_iHaveRevision ) ); + } + } + else + { + bIsSynched = !bFileExists; + } + + bool bIsDeleted = bIsFileInPerforce && !bFileExists && pFileInfo->m_bDeleted; + + kv->SetInt( "in_perforce", bIsFileInPerforce ); + kv->SetInt( "synched", bIsSynched ); + kv->SetInt( "checked_out", bIsFileInPerforce && ( pFileInfo->m_eOpenState != P4FILE_UNOPENED ) ); + kv->SetInt( "deleted", bIsDeleted ); + + if ( bIsDeleted ) + { + SetItemVisible( nItemID, m_bShowDeletedFiles ); + } +} + + +//----------------------------------------------------------------------------- +// Refresh perforce information +//----------------------------------------------------------------------------- +void PerforceFileList::Refresh() +{ + /* + // Slow method.. does too many perforce operations + for ( int i = FirstItem(); i != InvalidItemID(); i = NextItem( i ) ) + { + const char *pFile = GetFile( i ); + + P4File_t fileInfo; + bool bIsFileInPerforce = p4->GetFileInfo( pFile, &fileInfo ); + bool bFileExists = g_pFullFileSystem->FileExists( pFile ); + RefreshPerforceState( i, bFileExists, bIsFileInPerforce ? &fileInfo : NULL ); + } + */ + + // NOTE: Reducing the # of perforce calls is important for performance + int nCount = m_Directories.GetNumStrings(); + for ( int i = 0; i < nCount; ++i ) + { + const char *pDirectory = m_Directories.String(i); + DirectoryInfo_t *pInfo = &m_Directories[i]; + + // Retrives files, uses faster method to avoid finding clientspec + CUtlVector &fileList = p4->GetFileListUsingClientSpec( pDirectory, pInfo->m_ClientSpec ); + int nFileCount = fileList.Count(); + bool *pFound = (bool*)_alloca( nFileCount * sizeof(bool) ); + memset( pFound, 0, nFileCount * sizeof(bool) ); + + int nItemCount = pInfo->m_ItemIDs.Count(); + for ( int j = 0; j < nItemCount; ++j ) + { + int nItemID = pInfo->m_ItemIDs[j]; + const char *pFileName = GetFile( nItemID ); + bool bFileExists = g_pFullFileSystem->FileExists( pFileName ); + P4File_t *pFileInfo = FindFileInPerforceList( pFileName, fileList, pFound ); + RefreshPerforceState( nItemID, bFileExists, pFileInfo ); + } + } +} + + +//----------------------------------------------------------------------------- +// Is a particular list item a directory? +//----------------------------------------------------------------------------- +bool PerforceFileList::IsDirectoryItem( int nItemID ) +{ + KeyValues *kv = GetItem( nItemID ); + return kv->GetInt( "directory", 0 ) != 0; +} + + +//----------------------------------------------------------------------------- +// Returns the file associated with a particular item ID +//----------------------------------------------------------------------------- +const char *PerforceFileList::GetFile( int nItemID ) +{ + KeyValues *kv = GetItem( nItemID ); + Assert( kv ); + return kv->GetString( "fullpath", "" ); +} + + +//----------------------------------------------------------------------------- +// Find the item ID associated with a particular file +//----------------------------------------------------------------------------- +int PerforceFileList::FindFile( const char *pFullPath ) +{ + for ( int i = FirstItem(); i != InvalidItemID(); i = NextItem( i ) ) + { + const char *pFile = GetFile( i ); + if ( !Q_stricmp( pFile, pFullPath ) ) + return i; + } + return InvalidItemID(); +} + + +//----------------------------------------------------------------------------- +// Is a file already in the list? +//----------------------------------------------------------------------------- +bool PerforceFileList::IsFileInList( const char *pFullPath ) +{ + return ( FindFile( pFullPath ) != InvalidItemID() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Double-click: expand folders +//----------------------------------------------------------------------------- +void PerforceFileList::OnMouseDoublePressed( MouseCode code ) +{ + if ( code == MOUSE_LEFT ) + { + // select the item + OnMousePressed(code); + + // post a special message + if ( GetSelectedItemsCount() > 0 ) + { + PostActionSignal( new KeyValues("ItemDoubleClicked" ) ); + } + return; + } + + BaseClass::OnMouseDoublePressed( code ); +} + + diff --git a/mp/src/vgui2/vgui_controls/ProgressBar.cpp b/mp/src/vgui2/vgui_controls/ProgressBar.cpp index 8fe77d2f..90ce28bd 100644 --- a/mp/src/vgui2/vgui_controls/ProgressBar.cpp +++ b/mp/src/vgui2/vgui_controls/ProgressBar.cpp @@ -1,427 +1,427 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY( ProgressBar ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -ProgressBar::ProgressBar(Panel *parent, const char *panelName) : Panel(parent, panelName) -{ - _progress = 0.0f; - m_pszDialogVar = NULL; - SetSegmentInfo( 4, 8 ); - SetBarInset( 4 ); - SetMargin( 0 ); - m_iProgressDirection = PROGRESS_EAST; -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -ProgressBar::~ProgressBar() -{ - delete [] m_pszDialogVar; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void ProgressBar::SetSegmentInfo( int gap, int width ) -{ - _segmentGap = gap; - _segmentWide = width; -} - -//----------------------------------------------------------------------------- -// Purpose: returns the number of segment blocks drawn -//----------------------------------------------------------------------------- -int ProgressBar::GetDrawnSegmentCount() -{ - int wide, tall; - GetSize(wide, tall); - int segmentTotal = wide / (_segmentGap + _segmentWide); - return (int)(segmentTotal * _progress); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ProgressBar::PaintBackground() -{ - int wide, tall; - GetSize(wide, tall); - - surface()->DrawSetColor(GetBgColor()); - surface()->DrawFilledRect(0, 0, wide, tall); -} - -void ProgressBar::PaintSegment( int &x, int &y, int tall, int wide ) -{ - switch( m_iProgressDirection ) - { - case PROGRESS_EAST: - x += _segmentGap; - surface()->DrawFilledRect(x, y, x + _segmentWide, y + tall - (y * 2)); - x += _segmentWide; - break; - - case PROGRESS_WEST: - x -= _segmentGap + _segmentWide; - surface()->DrawFilledRect(x, y, x + _segmentWide, y + tall - (y * 2)); - break; - - case PROGRESS_NORTH: - y -= _segmentGap + _segmentWide; - surface()->DrawFilledRect(x, y, x + wide - (x * 2), y + _segmentWide ); - break; - - case PROGRESS_SOUTH: - y += _segmentGap; - surface()->DrawFilledRect(x, y, x + wide - (x * 2), y + _segmentWide ); - y += _segmentWide; - break; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ProgressBar::Paint() -{ - int wide, tall; - GetSize(wide, tall); - - // gaps - int segmentTotal = 0, segmentsDrawn = 0; - int x = 0, y = 0; - - switch( m_iProgressDirection ) - { - case PROGRESS_WEST: - wide -= 2 * m_iBarMargin; - x = wide - m_iBarMargin; - y = m_iBarInset; - segmentTotal = wide / (_segmentGap + _segmentWide); - segmentsDrawn = (int)(segmentTotal * _progress); - break; - - case PROGRESS_EAST: - wide -= 2 * m_iBarMargin; - x = m_iBarMargin; - y = m_iBarInset; - segmentTotal = wide / (_segmentGap + _segmentWide); - segmentsDrawn = (int)(segmentTotal * _progress); - break; - - case PROGRESS_NORTH: - tall -= 2 * m_iBarMargin; - x = m_iBarInset; - y = tall - m_iBarMargin; - segmentTotal = tall / (_segmentGap + _segmentWide); - segmentsDrawn = (int)(segmentTotal * _progress); - break; - - case PROGRESS_SOUTH: - tall -= 2 * m_iBarMargin; - x = m_iBarInset; - y = m_iBarMargin; - segmentTotal = tall / (_segmentGap + _segmentWide); - segmentsDrawn = (int)(segmentTotal * _progress); - break; - } - - surface()->DrawSetColor(GetFgColor()); - for (int i = 0; i < segmentsDrawn; i++) - { - PaintSegment( x, y, tall, wide ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ProgressBar::SetProgress(float progress) -{ - if (progress != _progress) - { - // clamp the progress value within the range - if (progress < 0.0f) - { - progress = 0.0f; - } - else if (progress > 1.0f) - { - progress = 1.0f; - } - - _progress = progress; - Repaint(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -float ProgressBar::GetProgress() -{ - return _progress; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ProgressBar::ApplySchemeSettings(IScheme *pScheme) -{ - Panel::ApplySchemeSettings(pScheme); - - SetFgColor(GetSchemeColor("ProgressBar.FgColor", pScheme)); - SetBgColor(GetSchemeColor("ProgressBar.BgColor", pScheme)); - SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); -} - -//----------------------------------------------------------------------------- -// Purpose: utility function for calculating a time remaining string -//----------------------------------------------------------------------------- -bool ProgressBar::ConstructTimeRemainingString(wchar_t *output, int outputBufferSizeInBytes, float startTime, float currentTime, float currentProgress, float lastProgressUpdateTime, bool addRemainingSuffix) -{ - Assert(lastProgressUpdateTime <= currentTime); - output[0] = 0; - - // calculate pre-extrapolation values - float timeElapsed = lastProgressUpdateTime - startTime; - float totalTime = timeElapsed / currentProgress; - - // calculate seconds - int secondsRemaining = (int)(totalTime - timeElapsed); - if (lastProgressUpdateTime < currentTime) - { - // old update, extrapolate - float progressRate = currentProgress / timeElapsed; - float extrapolatedProgress = progressRate * (currentTime - startTime); - float extrapolatedTotalTime = (currentTime - startTime) / extrapolatedProgress; - secondsRemaining = (int)(extrapolatedTotalTime - timeElapsed); - } - // if there's some time, make sure it's at least one second left - if ( secondsRemaining == 0 && ( ( totalTime - timeElapsed ) > 0 ) ) - { - secondsRemaining = 1; - } - - // calculate minutes - int minutesRemaining = 0; - while (secondsRemaining >= 60) - { - minutesRemaining++; - secondsRemaining -= 60; - } - - char minutesBuf[16]; - Q_snprintf(minutesBuf, sizeof( minutesBuf ), "%d", minutesRemaining); - char secondsBuf[16]; - Q_snprintf(secondsBuf, sizeof( secondsBuf ), "%d", secondsRemaining); - - if (minutesRemaining > 0) - { - wchar_t unicodeMinutes[16]; - g_pVGuiLocalize->ConvertANSIToUnicode(minutesBuf, unicodeMinutes, sizeof( unicodeMinutes )); - wchar_t unicodeSeconds[16]; - g_pVGuiLocalize->ConvertANSIToUnicode(secondsBuf, unicodeSeconds, sizeof( unicodeSeconds )); - - const char *unlocalizedString = "#vgui_TimeLeftMinutesSeconds"; - if (minutesRemaining == 1 && secondsRemaining == 1) - { - unlocalizedString = "#vgui_TimeLeftMinuteSecond"; - } - else if (minutesRemaining == 1) - { - unlocalizedString = "#vgui_TimeLeftMinuteSeconds"; - } - else if (secondsRemaining == 1) - { - unlocalizedString = "#vgui_TimeLeftMinutesSecond"; - } - - char unlocString[64]; - Q_strncpy(unlocString, unlocalizedString,sizeof( unlocString )); - if (addRemainingSuffix) - { - Q_strncat(unlocString, "Remaining", sizeof(unlocString ), COPY_ALL_CHARACTERS); - } - g_pVGuiLocalize->ConstructString(output, outputBufferSizeInBytes, g_pVGuiLocalize->Find(unlocString), 2, unicodeMinutes, unicodeSeconds); - - } - else if (secondsRemaining > 0) - { - wchar_t unicodeSeconds[16]; - g_pVGuiLocalize->ConvertANSIToUnicode(secondsBuf, unicodeSeconds, sizeof( unicodeSeconds )); - - const char *unlocalizedString = "#vgui_TimeLeftSeconds"; - if (secondsRemaining == 1) - { - unlocalizedString = "#vgui_TimeLeftSecond"; - } - char unlocString[64]; - Q_strncpy(unlocString, unlocalizedString,sizeof(unlocString)); - if (addRemainingSuffix) - { - Q_strncat(unlocString, "Remaining",sizeof(unlocString), COPY_ALL_CHARACTERS); - } - g_pVGuiLocalize->ConstructString(output, outputBufferSizeInBytes, g_pVGuiLocalize->Find(unlocString), 1, unicodeSeconds); - } - else - { - return false; - } - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void ProgressBar::SetBarInset( int pixels ) -{ - m_iBarInset = pixels; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -int ProgressBar::GetBarInset( void ) -{ - return m_iBarInset; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void ProgressBar::SetMargin( int pixels ) -{ - m_iBarMargin = pixels; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -int ProgressBar::GetMargin() -{ - return m_iBarMargin; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ProgressBar::ApplySettings(KeyValues *inResourceData) -{ - _progress = inResourceData->GetFloat("progress", 0.0f); - - const char *dialogVar = inResourceData->GetString("variable", ""); - if (dialogVar && *dialogVar) - { - m_pszDialogVar = new char[strlen(dialogVar) + 1]; - strcpy(m_pszDialogVar, dialogVar); - } - - BaseClass::ApplySettings(inResourceData); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ProgressBar::GetSettings(KeyValues *outResourceData) -{ - BaseClass::GetSettings(outResourceData); - outResourceData->SetFloat("progress", _progress ); - - if (m_pszDialogVar) - { - outResourceData->SetString("variable", m_pszDialogVar); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Returns a string description of the panel fields for use in the UI -//----------------------------------------------------------------------------- -const char *ProgressBar::GetDescription( void ) -{ - static char buf[1024]; - _snprintf(buf, sizeof(buf), "%s, string progress, string variable", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: updates progress bar bases on values -//----------------------------------------------------------------------------- -void ProgressBar::OnDialogVariablesChanged(KeyValues *dialogVariables) -{ - if (m_pszDialogVar) - { - int val = dialogVariables->GetInt(m_pszDialogVar, -1); - if (val >= 0.0f) - { - SetProgress(val / 100.0f); - } - } -} - - -DECLARE_BUILD_FACTORY( ContinuousProgressBar ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -ContinuousProgressBar::ContinuousProgressBar(Panel *parent, const char *panelName) : ProgressBar(parent, panelName) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ContinuousProgressBar::Paint() -{ - int x = 0, y = 0; - int wide, tall; - GetSize(wide, tall); - - surface()->DrawSetColor(GetFgColor()); - - switch( m_iProgressDirection ) - { - case PROGRESS_EAST: - surface()->DrawFilledRect( x, y, x + (int)( wide * _progress ), y + tall ); - break; - - case PROGRESS_WEST: - surface()->DrawFilledRect( x + (int)( wide * ( 1.0f - _progress ) ), y, x + wide, y + tall ); - break; - - case PROGRESS_NORTH: - surface()->DrawFilledRect( x, y + (int)( tall * ( 1.0f - _progress ) ), x + wide, y + tall ); - break; - - case PROGRESS_SOUTH: - surface()->DrawFilledRect( x, y, x + wide, y + (int)( tall * _progress ) ); - break; - } +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY( ProgressBar ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ProgressBar::ProgressBar(Panel *parent, const char *panelName) : Panel(parent, panelName) +{ + _progress = 0.0f; + m_pszDialogVar = NULL; + SetSegmentInfo( 4, 8 ); + SetBarInset( 4 ); + SetMargin( 0 ); + m_iProgressDirection = PROGRESS_EAST; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +ProgressBar::~ProgressBar() +{ + delete [] m_pszDialogVar; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void ProgressBar::SetSegmentInfo( int gap, int width ) +{ + _segmentGap = gap; + _segmentWide = width; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the number of segment blocks drawn +//----------------------------------------------------------------------------- +int ProgressBar::GetDrawnSegmentCount() +{ + int wide, tall; + GetSize(wide, tall); + int segmentTotal = wide / (_segmentGap + _segmentWide); + return (int)(segmentTotal * _progress); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ProgressBar::PaintBackground() +{ + int wide, tall; + GetSize(wide, tall); + + surface()->DrawSetColor(GetBgColor()); + surface()->DrawFilledRect(0, 0, wide, tall); +} + +void ProgressBar::PaintSegment( int &x, int &y, int tall, int wide ) +{ + switch( m_iProgressDirection ) + { + case PROGRESS_EAST: + x += _segmentGap; + surface()->DrawFilledRect(x, y, x + _segmentWide, y + tall - (y * 2)); + x += _segmentWide; + break; + + case PROGRESS_WEST: + x -= _segmentGap + _segmentWide; + surface()->DrawFilledRect(x, y, x + _segmentWide, y + tall - (y * 2)); + break; + + case PROGRESS_NORTH: + y -= _segmentGap + _segmentWide; + surface()->DrawFilledRect(x, y, x + wide - (x * 2), y + _segmentWide ); + break; + + case PROGRESS_SOUTH: + y += _segmentGap; + surface()->DrawFilledRect(x, y, x + wide - (x * 2), y + _segmentWide ); + y += _segmentWide; + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ProgressBar::Paint() +{ + int wide, tall; + GetSize(wide, tall); + + // gaps + int segmentTotal = 0, segmentsDrawn = 0; + int x = 0, y = 0; + + switch( m_iProgressDirection ) + { + case PROGRESS_WEST: + wide -= 2 * m_iBarMargin; + x = wide - m_iBarMargin; + y = m_iBarInset; + segmentTotal = wide / (_segmentGap + _segmentWide); + segmentsDrawn = (int)(segmentTotal * _progress); + break; + + case PROGRESS_EAST: + wide -= 2 * m_iBarMargin; + x = m_iBarMargin; + y = m_iBarInset; + segmentTotal = wide / (_segmentGap + _segmentWide); + segmentsDrawn = (int)(segmentTotal * _progress); + break; + + case PROGRESS_NORTH: + tall -= 2 * m_iBarMargin; + x = m_iBarInset; + y = tall - m_iBarMargin; + segmentTotal = tall / (_segmentGap + _segmentWide); + segmentsDrawn = (int)(segmentTotal * _progress); + break; + + case PROGRESS_SOUTH: + tall -= 2 * m_iBarMargin; + x = m_iBarInset; + y = m_iBarMargin; + segmentTotal = tall / (_segmentGap + _segmentWide); + segmentsDrawn = (int)(segmentTotal * _progress); + break; + } + + surface()->DrawSetColor(GetFgColor()); + for (int i = 0; i < segmentsDrawn; i++) + { + PaintSegment( x, y, tall, wide ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ProgressBar::SetProgress(float progress) +{ + if (progress != _progress) + { + // clamp the progress value within the range + if (progress < 0.0f) + { + progress = 0.0f; + } + else if (progress > 1.0f) + { + progress = 1.0f; + } + + _progress = progress; + Repaint(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +float ProgressBar::GetProgress() +{ + return _progress; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ProgressBar::ApplySchemeSettings(IScheme *pScheme) +{ + Panel::ApplySchemeSettings(pScheme); + + SetFgColor(GetSchemeColor("ProgressBar.FgColor", pScheme)); + SetBgColor(GetSchemeColor("ProgressBar.BgColor", pScheme)); + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); +} + +//----------------------------------------------------------------------------- +// Purpose: utility function for calculating a time remaining string +//----------------------------------------------------------------------------- +bool ProgressBar::ConstructTimeRemainingString(wchar_t *output, int outputBufferSizeInBytes, float startTime, float currentTime, float currentProgress, float lastProgressUpdateTime, bool addRemainingSuffix) +{ + Assert(lastProgressUpdateTime <= currentTime); + output[0] = 0; + + // calculate pre-extrapolation values + float timeElapsed = lastProgressUpdateTime - startTime; + float totalTime = timeElapsed / currentProgress; + + // calculate seconds + int secondsRemaining = (int)(totalTime - timeElapsed); + if (lastProgressUpdateTime < currentTime) + { + // old update, extrapolate + float progressRate = currentProgress / timeElapsed; + float extrapolatedProgress = progressRate * (currentTime - startTime); + float extrapolatedTotalTime = (currentTime - startTime) / extrapolatedProgress; + secondsRemaining = (int)(extrapolatedTotalTime - timeElapsed); + } + // if there's some time, make sure it's at least one second left + if ( secondsRemaining == 0 && ( ( totalTime - timeElapsed ) > 0 ) ) + { + secondsRemaining = 1; + } + + // calculate minutes + int minutesRemaining = 0; + while (secondsRemaining >= 60) + { + minutesRemaining++; + secondsRemaining -= 60; + } + + char minutesBuf[16]; + Q_snprintf(minutesBuf, sizeof( minutesBuf ), "%d", minutesRemaining); + char secondsBuf[16]; + Q_snprintf(secondsBuf, sizeof( secondsBuf ), "%d", secondsRemaining); + + if (minutesRemaining > 0) + { + wchar_t unicodeMinutes[16]; + g_pVGuiLocalize->ConvertANSIToUnicode(minutesBuf, unicodeMinutes, sizeof( unicodeMinutes )); + wchar_t unicodeSeconds[16]; + g_pVGuiLocalize->ConvertANSIToUnicode(secondsBuf, unicodeSeconds, sizeof( unicodeSeconds )); + + const char *unlocalizedString = "#vgui_TimeLeftMinutesSeconds"; + if (minutesRemaining == 1 && secondsRemaining == 1) + { + unlocalizedString = "#vgui_TimeLeftMinuteSecond"; + } + else if (minutesRemaining == 1) + { + unlocalizedString = "#vgui_TimeLeftMinuteSeconds"; + } + else if (secondsRemaining == 1) + { + unlocalizedString = "#vgui_TimeLeftMinutesSecond"; + } + + char unlocString[64]; + Q_strncpy(unlocString, unlocalizedString,sizeof( unlocString )); + if (addRemainingSuffix) + { + Q_strncat(unlocString, "Remaining", sizeof(unlocString ), COPY_ALL_CHARACTERS); + } + g_pVGuiLocalize->ConstructString(output, outputBufferSizeInBytes, g_pVGuiLocalize->Find(unlocString), 2, unicodeMinutes, unicodeSeconds); + + } + else if (secondsRemaining > 0) + { + wchar_t unicodeSeconds[16]; + g_pVGuiLocalize->ConvertANSIToUnicode(secondsBuf, unicodeSeconds, sizeof( unicodeSeconds )); + + const char *unlocalizedString = "#vgui_TimeLeftSeconds"; + if (secondsRemaining == 1) + { + unlocalizedString = "#vgui_TimeLeftSecond"; + } + char unlocString[64]; + Q_strncpy(unlocString, unlocalizedString,sizeof(unlocString)); + if (addRemainingSuffix) + { + Q_strncat(unlocString, "Remaining",sizeof(unlocString), COPY_ALL_CHARACTERS); + } + g_pVGuiLocalize->ConstructString(output, outputBufferSizeInBytes, g_pVGuiLocalize->Find(unlocString), 1, unicodeSeconds); + } + else + { + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void ProgressBar::SetBarInset( int pixels ) +{ + m_iBarInset = pixels; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +int ProgressBar::GetBarInset( void ) +{ + return m_iBarInset; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void ProgressBar::SetMargin( int pixels ) +{ + m_iBarMargin = pixels; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +int ProgressBar::GetMargin() +{ + return m_iBarMargin; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ProgressBar::ApplySettings(KeyValues *inResourceData) +{ + _progress = inResourceData->GetFloat("progress", 0.0f); + + const char *dialogVar = inResourceData->GetString("variable", ""); + if (dialogVar && *dialogVar) + { + m_pszDialogVar = new char[strlen(dialogVar) + 1]; + strcpy(m_pszDialogVar, dialogVar); + } + + BaseClass::ApplySettings(inResourceData); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ProgressBar::GetSettings(KeyValues *outResourceData) +{ + BaseClass::GetSettings(outResourceData); + outResourceData->SetFloat("progress", _progress ); + + if (m_pszDialogVar) + { + outResourceData->SetString("variable", m_pszDialogVar); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a string description of the panel fields for use in the UI +//----------------------------------------------------------------------------- +const char *ProgressBar::GetDescription( void ) +{ + static char buf[1024]; + _snprintf(buf, sizeof(buf), "%s, string progress, string variable", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: updates progress bar bases on values +//----------------------------------------------------------------------------- +void ProgressBar::OnDialogVariablesChanged(KeyValues *dialogVariables) +{ + if (m_pszDialogVar) + { + int val = dialogVariables->GetInt(m_pszDialogVar, -1); + if (val >= 0.0f) + { + SetProgress(val / 100.0f); + } + } +} + + +DECLARE_BUILD_FACTORY( ContinuousProgressBar ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ContinuousProgressBar::ContinuousProgressBar(Panel *parent, const char *panelName) : ProgressBar(parent, panelName) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ContinuousProgressBar::Paint() +{ + int x = 0, y = 0; + int wide, tall; + GetSize(wide, tall); + + surface()->DrawSetColor(GetFgColor()); + + switch( m_iProgressDirection ) + { + case PROGRESS_EAST: + surface()->DrawFilledRect( x, y, x + (int)( wide * _progress ), y + tall ); + break; + + case PROGRESS_WEST: + surface()->DrawFilledRect( x + (int)( wide * ( 1.0f - _progress ) ), y, x + wide, y + tall ); + break; + + case PROGRESS_NORTH: + surface()->DrawFilledRect( x, y + (int)( tall * ( 1.0f - _progress ) ), x + wide, y + tall ); + break; + + case PROGRESS_SOUTH: + surface()->DrawFilledRect( x, y, x + wide, y + (int)( tall * _progress ) ); + break; + } } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/ProgressBox.cpp b/mp/src/vgui2/vgui_controls/ProgressBox.cpp index 56624380..c67f1737 100644 --- a/mp/src/vgui2/vgui_controls/ProgressBox.cpp +++ b/mp/src/vgui2/vgui_controls/ProgressBox.cpp @@ -1,360 +1,360 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -ProgressBox::ProgressBox(const char *title, const char *text, const char *pszUnknownTimeString, Panel *parent) : Frame(parent, NULL, parent ? false : true) -{ - // save off the non-localized title, since we may need to dynamically localize it (on progress updates) - const wchar_t *ws = g_pVGuiLocalize->Find(title); - if (ws) - { - wcsncpy(m_wszTitleString, ws, sizeof(m_wszTitleString) / sizeof(wchar_t)); - } - else - { - g_pVGuiLocalize->ConvertANSIToUnicode(title, m_wszTitleString, sizeof(m_wszTitleString)); - } - - m_pMessageLabel = new Label(this, NULL, pszUnknownTimeString); - - ws = g_pVGuiLocalize->Find(text); - if (ws) - { - wcsncpy(m_wcsInfoString, ws, sizeof(m_wcsInfoString) / sizeof(wchar_t)); - } - else - { - m_wcsInfoString[0] = 0; - } - - ws = g_pVGuiLocalize->Find(pszUnknownTimeString); - if (ws) - { - wcsncpy(m_wszUnknownTimeString, ws, sizeof(m_wszUnknownTimeString) / sizeof(wchar_t)); - } - else - { - m_wszUnknownTimeString[0] = 0; - } - Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -ProgressBox::ProgressBox(const wchar_t *wszTitle, const wchar_t *wszText, const wchar_t *wszUnknownTimeString, Panel *parent) : Frame(parent, NULL, parent ? false : true) -{ - wcsncpy(m_wszTitleString, wszTitle, sizeof(m_wszTitleString) / sizeof(wchar_t)); - m_pMessageLabel = new Label(this, NULL, wszUnknownTimeString); - wcsncpy(m_wcsInfoString, wszText, sizeof(m_wcsInfoString) / sizeof(wchar_t)); - wcsncpy(m_wszUnknownTimeString, wszUnknownTimeString, sizeof(m_wszUnknownTimeString) / sizeof(wchar_t)); - Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor Helper -//----------------------------------------------------------------------------- -void ProgressBox::Init() -{ - m_pProgressBar = new ProgressBar(this, NULL); - m_pProgressBar->SetVisible(false); - - m_pCancelButton = new Button(this, NULL, "#VGui_Cancel"); - m_pCancelButton->SetSize(72, 24); - m_pCancelButton->SetCommand("Cancel"); - - SetMenuButtonResponsive(false); - SetMinimizeButtonVisible(false); - SetCancelButtonVisible(false); - SetSizeable(false); - SetSize(384, 128); - m_flCurrentProgress = 0.0f; - m_flFirstProgressUpdate = -0.1f; - m_flLastProgressUpdate = 0.0f; - - // mark ourselves as needed ticked once a second, to force us to repaint - ivgui()->AddTickSignal(GetVPanel(), 1000); - - UpdateTitle(); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -ProgressBox::~ProgressBox() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: resize the message label -//----------------------------------------------------------------------------- -void ProgressBox::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - int wide, tall; - m_pMessageLabel->GetContentSize(wide, tall); - SetSize(384, tall + 92); - m_pMessageLabel->SetSize(344, tall); -} - -//----------------------------------------------------------------------------- -// Purpose: Put the message box into a modal state -// Does not suspend execution - use addActionSignal to get return value -//----------------------------------------------------------------------------- -void ProgressBox::DoModal(Frame *pFrameOver) -{ - ShowWindow(pFrameOver); - input()->SetAppModalSurface(GetVPanel()); -} - -//----------------------------------------------------------------------------- -// Purpose: Activates the window -//----------------------------------------------------------------------------- -void ProgressBox::ShowWindow(Frame *pFrameOver) -{ - // move to the middle of the screen - // get the screen size - int wide, tall; - // get our dialog size - GetSize(wide, tall); - - if (pFrameOver) - { - int frameX, frameY; - int frameWide, frameTall; - pFrameOver->GetPos(frameX, frameY); - pFrameOver->GetSize(frameWide, frameTall); - - SetPos((frameWide - wide) / 2 + frameX, (frameTall - tall) / 2 + frameY); - } - else - { - int swide, stall; - surface()->GetScreenSize(swide, stall); - // put the dialog in the middle of the screen - SetPos((swide - wide) / 2, (stall - tall) / 2); - } - - BaseClass::Activate(); -} - -//----------------------------------------------------------------------------- -// Purpose: Put the text and OK buttons in correct place -//----------------------------------------------------------------------------- -void ProgressBox::PerformLayout() -{ - int x, y, wide, tall; - GetClientArea(x, y, wide, tall); - wide += x; - tall += y; - - int leftEdge = x + 16; - m_pMessageLabel->SetPos(leftEdge, y + 12); - m_pProgressBar->SetPos(leftEdge, y + 14 + m_pMessageLabel->GetTall() + 2); - m_pProgressBar->SetSize(wide - 44, 24); - - if (m_pCancelButton->IsVisible()) - { - // make room for cancel - int px, py, pw, pt; - int offs = m_pCancelButton->GetWide(); - m_pProgressBar->GetBounds(px, py, pw, pt); - m_pCancelButton->SetPos(px + pw - offs, py); - m_pProgressBar->SetSize(pw - offs - 10, pt); - } - - BaseClass::PerformLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: updates progress bar, range [0, 1] -//----------------------------------------------------------------------------- -void ProgressBox::SetProgress(float progress) -{ - Assert(progress >= 0.0f && progress <= 1.0f); - m_pProgressBar->SetProgress(progress); - m_pProgressBar->SetVisible(true); - - // only update progress timings if the progress has actually changed - if (progress != m_flCurrentProgress) - { - // store off timings for calculating time remaining - if (m_flFirstProgressUpdate < 0.0f) - { - m_flFirstProgressUpdate = (float)system()->GetFrameTime(); - } - m_flCurrentProgress = progress; - m_flLastProgressUpdate = (float)system()->GetFrameTime(); - - UpdateTitle(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets the info text -//----------------------------------------------------------------------------- -void ProgressBox::SetText(const char *text) -{ - m_pMessageLabel->SetText(text); -} - -//----------------------------------------------------------------------------- -// Purpose: Updates the dialog title text -//----------------------------------------------------------------------------- -void ProgressBox::UpdateTitle() -{ - // update progress text - wchar_t unicode[256]; - wchar_t completion[64]; - if ((int)(m_flCurrentProgress * 100.0f) > 0) - { - _snwprintf(completion, sizeof(completion) / sizeof(wchar_t), L"- %d%% complete", (int)(m_flCurrentProgress * 100.0f)); - } - else - { - completion[0] = 0; - } - g_pVGuiLocalize->ConstructString(unicode, sizeof(unicode), m_wszTitleString, 1, completion); - SetTitle(unicode, true); -} - -//----------------------------------------------------------------------------- -// Purpose: called every render -//----------------------------------------------------------------------------- -void ProgressBox::OnThink() -{ - // calculate the progress made - if (m_flFirstProgressUpdate >= 0.0f && m_wcsInfoString[0]) - { - wchar_t timeRemaining[128]; - if (ProgressBar::ConstructTimeRemainingString(timeRemaining, sizeof(timeRemaining), m_flFirstProgressUpdate, (float)system()->GetFrameTime(), m_flCurrentProgress, m_flLastProgressUpdate, true)) - { - wchar_t unicode[256]; - g_pVGuiLocalize->ConstructString(unicode, sizeof(unicode), m_wcsInfoString, 1, timeRemaining); - m_pMessageLabel->SetText(unicode); - } - else - { - m_pMessageLabel->SetText(m_wszUnknownTimeString); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Forces us to repaint once per second -//----------------------------------------------------------------------------- -void ProgressBox::OnTick() -{ - if (m_flFirstProgressUpdate >= 0.0f) - { - Repaint(); - } - - BaseClass::OnTick(); -} - -//----------------------------------------------------------------------------- -// Purpose: Handles ESC closing dialog -//----------------------------------------------------------------------------- -void ProgressBox::OnCommand(const char *command) -{ - if (!stricmp(command, "Cancel")) - { - OnCancel(); - } - else - { - BaseClass::OnCommand(command); - } -} - -//----------------------------------------------------------------------------- -// Purpose: close button pressed -//----------------------------------------------------------------------------- -void ProgressBox::OnCloseFrameButtonPressed() -{ - OnCancel(); -} - -//----------------------------------------------------------------------------- -// Purpose: Deletes self when closed -//----------------------------------------------------------------------------- -void ProgressBox::OnClose() -{ - BaseClass::OnClose(); - // modal surface is released on deletion - MarkForDeletion(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ProgressBox::OnShutdownRequest() -{ - // Shutdown the dialog - PostMessage(this, new KeyValues("Command", "command", "Cancel")); -} - -//----------------------------------------------------------------------------- -// Purpose: On update cancelled -//----------------------------------------------------------------------------- -void ProgressBox::OnCancel() -{ - // post a message that we've been cancelled - PostActionSignal(new KeyValues("ProgressBoxCancelled")); - - // close this dialog - Close(); -} - -//----------------------------------------------------------------------------- -// Purpose: Toggles visibility of the close box. -//----------------------------------------------------------------------------- -void ProgressBox::SetCancelButtonVisible(bool state) -{ - BaseClass::SetCloseButtonVisible(state); - m_pCancelButton->SetVisible(state); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ProgressBox::SetCancelButtonEnabled(bool state) -{ - m_pCancelButton->SetEnabled(state); - BaseClass::SetCloseButtonVisible(state); - InvalidateLayout(); - Repaint(); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ProgressBox::ProgressBox(const char *title, const char *text, const char *pszUnknownTimeString, Panel *parent) : Frame(parent, NULL, parent ? false : true) +{ + // save off the non-localized title, since we may need to dynamically localize it (on progress updates) + const wchar_t *ws = g_pVGuiLocalize->Find(title); + if (ws) + { + wcsncpy(m_wszTitleString, ws, sizeof(m_wszTitleString) / sizeof(wchar_t)); + } + else + { + g_pVGuiLocalize->ConvertANSIToUnicode(title, m_wszTitleString, sizeof(m_wszTitleString)); + } + + m_pMessageLabel = new Label(this, NULL, pszUnknownTimeString); + + ws = g_pVGuiLocalize->Find(text); + if (ws) + { + wcsncpy(m_wcsInfoString, ws, sizeof(m_wcsInfoString) / sizeof(wchar_t)); + } + else + { + m_wcsInfoString[0] = 0; + } + + ws = g_pVGuiLocalize->Find(pszUnknownTimeString); + if (ws) + { + wcsncpy(m_wszUnknownTimeString, ws, sizeof(m_wszUnknownTimeString) / sizeof(wchar_t)); + } + else + { + m_wszUnknownTimeString[0] = 0; + } + Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ProgressBox::ProgressBox(const wchar_t *wszTitle, const wchar_t *wszText, const wchar_t *wszUnknownTimeString, Panel *parent) : Frame(parent, NULL, parent ? false : true) +{ + wcsncpy(m_wszTitleString, wszTitle, sizeof(m_wszTitleString) / sizeof(wchar_t)); + m_pMessageLabel = new Label(this, NULL, wszUnknownTimeString); + wcsncpy(m_wcsInfoString, wszText, sizeof(m_wcsInfoString) / sizeof(wchar_t)); + wcsncpy(m_wszUnknownTimeString, wszUnknownTimeString, sizeof(m_wszUnknownTimeString) / sizeof(wchar_t)); + Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor Helper +//----------------------------------------------------------------------------- +void ProgressBox::Init() +{ + m_pProgressBar = new ProgressBar(this, NULL); + m_pProgressBar->SetVisible(false); + + m_pCancelButton = new Button(this, NULL, "#VGui_Cancel"); + m_pCancelButton->SetSize(72, 24); + m_pCancelButton->SetCommand("Cancel"); + + SetMenuButtonResponsive(false); + SetMinimizeButtonVisible(false); + SetCancelButtonVisible(false); + SetSizeable(false); + SetSize(384, 128); + m_flCurrentProgress = 0.0f; + m_flFirstProgressUpdate = -0.1f; + m_flLastProgressUpdate = 0.0f; + + // mark ourselves as needed ticked once a second, to force us to repaint + ivgui()->AddTickSignal(GetVPanel(), 1000); + + UpdateTitle(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +ProgressBox::~ProgressBox() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: resize the message label +//----------------------------------------------------------------------------- +void ProgressBox::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + int wide, tall; + m_pMessageLabel->GetContentSize(wide, tall); + SetSize(384, tall + 92); + m_pMessageLabel->SetSize(344, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: Put the message box into a modal state +// Does not suspend execution - use addActionSignal to get return value +//----------------------------------------------------------------------------- +void ProgressBox::DoModal(Frame *pFrameOver) +{ + ShowWindow(pFrameOver); + input()->SetAppModalSurface(GetVPanel()); +} + +//----------------------------------------------------------------------------- +// Purpose: Activates the window +//----------------------------------------------------------------------------- +void ProgressBox::ShowWindow(Frame *pFrameOver) +{ + // move to the middle of the screen + // get the screen size + int wide, tall; + // get our dialog size + GetSize(wide, tall); + + if (pFrameOver) + { + int frameX, frameY; + int frameWide, frameTall; + pFrameOver->GetPos(frameX, frameY); + pFrameOver->GetSize(frameWide, frameTall); + + SetPos((frameWide - wide) / 2 + frameX, (frameTall - tall) / 2 + frameY); + } + else + { + int swide, stall; + surface()->GetScreenSize(swide, stall); + // put the dialog in the middle of the screen + SetPos((swide - wide) / 2, (stall - tall) / 2); + } + + BaseClass::Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: Put the text and OK buttons in correct place +//----------------------------------------------------------------------------- +void ProgressBox::PerformLayout() +{ + int x, y, wide, tall; + GetClientArea(x, y, wide, tall); + wide += x; + tall += y; + + int leftEdge = x + 16; + m_pMessageLabel->SetPos(leftEdge, y + 12); + m_pProgressBar->SetPos(leftEdge, y + 14 + m_pMessageLabel->GetTall() + 2); + m_pProgressBar->SetSize(wide - 44, 24); + + if (m_pCancelButton->IsVisible()) + { + // make room for cancel + int px, py, pw, pt; + int offs = m_pCancelButton->GetWide(); + m_pProgressBar->GetBounds(px, py, pw, pt); + m_pCancelButton->SetPos(px + pw - offs, py); + m_pProgressBar->SetSize(pw - offs - 10, pt); + } + + BaseClass::PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: updates progress bar, range [0, 1] +//----------------------------------------------------------------------------- +void ProgressBox::SetProgress(float progress) +{ + Assert(progress >= 0.0f && progress <= 1.0f); + m_pProgressBar->SetProgress(progress); + m_pProgressBar->SetVisible(true); + + // only update progress timings if the progress has actually changed + if (progress != m_flCurrentProgress) + { + // store off timings for calculating time remaining + if (m_flFirstProgressUpdate < 0.0f) + { + m_flFirstProgressUpdate = (float)system()->GetFrameTime(); + } + m_flCurrentProgress = progress; + m_flLastProgressUpdate = (float)system()->GetFrameTime(); + + UpdateTitle(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets the info text +//----------------------------------------------------------------------------- +void ProgressBox::SetText(const char *text) +{ + m_pMessageLabel->SetText(text); +} + +//----------------------------------------------------------------------------- +// Purpose: Updates the dialog title text +//----------------------------------------------------------------------------- +void ProgressBox::UpdateTitle() +{ + // update progress text + wchar_t unicode[256]; + wchar_t completion[64]; + if ((int)(m_flCurrentProgress * 100.0f) > 0) + { + _snwprintf(completion, sizeof(completion) / sizeof(wchar_t), L"- %d%% complete", (int)(m_flCurrentProgress * 100.0f)); + } + else + { + completion[0] = 0; + } + g_pVGuiLocalize->ConstructString(unicode, sizeof(unicode), m_wszTitleString, 1, completion); + SetTitle(unicode, true); +} + +//----------------------------------------------------------------------------- +// Purpose: called every render +//----------------------------------------------------------------------------- +void ProgressBox::OnThink() +{ + // calculate the progress made + if (m_flFirstProgressUpdate >= 0.0f && m_wcsInfoString[0]) + { + wchar_t timeRemaining[128]; + if (ProgressBar::ConstructTimeRemainingString(timeRemaining, sizeof(timeRemaining), m_flFirstProgressUpdate, (float)system()->GetFrameTime(), m_flCurrentProgress, m_flLastProgressUpdate, true)) + { + wchar_t unicode[256]; + g_pVGuiLocalize->ConstructString(unicode, sizeof(unicode), m_wcsInfoString, 1, timeRemaining); + m_pMessageLabel->SetText(unicode); + } + else + { + m_pMessageLabel->SetText(m_wszUnknownTimeString); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Forces us to repaint once per second +//----------------------------------------------------------------------------- +void ProgressBox::OnTick() +{ + if (m_flFirstProgressUpdate >= 0.0f) + { + Repaint(); + } + + BaseClass::OnTick(); +} + +//----------------------------------------------------------------------------- +// Purpose: Handles ESC closing dialog +//----------------------------------------------------------------------------- +void ProgressBox::OnCommand(const char *command) +{ + if (!stricmp(command, "Cancel")) + { + OnCancel(); + } + else + { + BaseClass::OnCommand(command); + } +} + +//----------------------------------------------------------------------------- +// Purpose: close button pressed +//----------------------------------------------------------------------------- +void ProgressBox::OnCloseFrameButtonPressed() +{ + OnCancel(); +} + +//----------------------------------------------------------------------------- +// Purpose: Deletes self when closed +//----------------------------------------------------------------------------- +void ProgressBox::OnClose() +{ + BaseClass::OnClose(); + // modal surface is released on deletion + MarkForDeletion(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ProgressBox::OnShutdownRequest() +{ + // Shutdown the dialog + PostMessage(this, new KeyValues("Command", "command", "Cancel")); +} + +//----------------------------------------------------------------------------- +// Purpose: On update cancelled +//----------------------------------------------------------------------------- +void ProgressBox::OnCancel() +{ + // post a message that we've been cancelled + PostActionSignal(new KeyValues("ProgressBoxCancelled")); + + // close this dialog + Close(); +} + +//----------------------------------------------------------------------------- +// Purpose: Toggles visibility of the close box. +//----------------------------------------------------------------------------- +void ProgressBox::SetCancelButtonVisible(bool state) +{ + BaseClass::SetCloseButtonVisible(state); + m_pCancelButton->SetVisible(state); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ProgressBox::SetCancelButtonEnabled(bool state) +{ + m_pCancelButton->SetEnabled(state); + BaseClass::SetCloseButtonVisible(state); + InvalidateLayout(); + Repaint(); +} diff --git a/mp/src/vgui2/vgui_controls/PropertyDialog.cpp b/mp/src/vgui2/vgui_controls/PropertyDialog.cpp index 3f81dbd3..568b23e2 100644 --- a/mp/src/vgui2/vgui_controls/PropertyDialog.cpp +++ b/mp/src/vgui2/vgui_controls/PropertyDialog.cpp @@ -1,303 +1,303 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include - -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -PropertyDialog::PropertyDialog(Panel *parent, const char *panelName) : Frame(parent, panelName) -{ - // create the property sheet - _propertySheet = new PropertySheet(this, "Sheet"); - _propertySheet->AddActionSignalTarget(this); - _propertySheet->SetTabPosition(1); - - // add the buttons - _okButton = new Button(this, "OKButton", "#PropertyDialog_OK"); - _okButton->AddActionSignalTarget(this); - _okButton->SetTabPosition(2); - _okButton->SetCommand("OK"); - GetFocusNavGroup().SetDefaultButton(_okButton); - - _cancelButton = new Button(this, "CancelButton", "#PropertyDialog_Cancel"); - _cancelButton->AddActionSignalTarget(this); - _cancelButton->SetTabPosition(3); - _cancelButton->SetCommand("Cancel"); - - _applyButton = new Button(this, "ApplyButton", "#PropertyDialog_Apply"); - _applyButton->AddActionSignalTarget(this); - _applyButton->SetTabPosition(4); - _applyButton->SetVisible(false); // default to not visible - _applyButton->SetEnabled(false); // default to not enabled - _applyButton->SetCommand("Apply"); - - SetSizeable(false); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -PropertyDialog::~PropertyDialog() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Returns a pointer to the PropertySheet this dialog encapsulates -// Output : PropertySheet * -//----------------------------------------------------------------------------- -PropertySheet *PropertyDialog::GetPropertySheet() -{ - return _propertySheet; -} - -//----------------------------------------------------------------------------- -// Purpose: Gets a pointer to the currently active page. -// Output : Panel -//----------------------------------------------------------------------------- -Panel *PropertyDialog::GetActivePage() -{ - return _propertySheet->GetActivePage(); -} - -//----------------------------------------------------------------------------- -// Purpose: Wrapped function -//----------------------------------------------------------------------------- -void PropertyDialog::AddPage(Panel *page, const char *title) -{ - _propertySheet->AddPage(page, title); -} - -//----------------------------------------------------------------------------- -// Purpose: reloads the data in all the property page -//----------------------------------------------------------------------------- -void PropertyDialog::ResetAllData() -{ - _propertySheet->ResetAllData(); -} - -//----------------------------------------------------------------------------- -// Purpose: Applies any changes -//----------------------------------------------------------------------------- -void PropertyDialog::ApplyChanges() -{ - OnCommand("Apply"); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets up the sheet -//----------------------------------------------------------------------------- -void PropertyDialog::PerformLayout() -{ - BaseClass::PerformLayout(); - - int iBottom = m_iSheetInsetBottom; - if ( IsProportional() ) - { - iBottom = scheme()->GetProportionalScaledValueEx( GetScheme(), iBottom ); - } - - int x, y, wide, tall; - GetClientArea(x, y, wide, tall); - _propertySheet->SetBounds(x, y, wide, tall - iBottom); - - - // move the buttons to the bottom-right corner - int xpos = x + wide - 80; - int ypos = tall + y - 28; - - if (_applyButton->IsVisible()) - { - _applyButton->SetBounds(xpos, ypos, 72, 24); - xpos -= 80; - } - - if (_cancelButton->IsVisible()) - { - _cancelButton->SetBounds(xpos, ypos, 72, 24); - xpos -= 80; - } - - _okButton->SetBounds(xpos, ypos, 72, 24); - - _propertySheet->InvalidateLayout(); // tell the propertysheet to redraw! - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Handles command text from the buttons -//----------------------------------------------------------------------------- -void PropertyDialog::OnCommand(const char *command) -{ - if (!stricmp(command, "OK")) - { - if ( OnOK(false) ) - { - OnCommand("Close"); - } - _applyButton->SetEnabled(false); - } - else if (!stricmp(command, "Cancel")) - { - OnCancel(); - Close(); - } - else if (!stricmp(command, "Apply")) - { - OnOK(true); - _applyButton->SetEnabled(false); - InvalidateLayout(); - } - else - { - BaseClass::OnCommand(command); - } -} - -//----------------------------------------------------------------------------- -// Purpose: called when the Cancel button is pressed -//----------------------------------------------------------------------------- -void PropertyDialog::OnCancel() -{ - // designed to be overridden -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : code - -//----------------------------------------------------------------------------- -void PropertyDialog::OnKeyCodeTyped(KeyCode code) -{ - // this has been removed, since it conflicts with how we use the escape key in the game -// if (code == KEY_ESCAPE) -// { -// OnCommand("Cancel"); -// } -// else - { - BaseClass::OnKeyCodeTyped(code); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Command handler -//----------------------------------------------------------------------------- -bool PropertyDialog::OnOK(bool applyOnly) -{ - // the sheet should have the pages apply changes before we tell the world - _propertySheet->ApplyChanges(); - - // this should tell anybody who's watching us that we're done - PostActionSignal(new KeyValues("ApplyChanges")); - - // default to closing - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Overrides build mode so it edits the sub panel -//----------------------------------------------------------------------------- -void PropertyDialog::ActivateBuildMode() -{ - // no subpanel, no build mode - EditablePanel *panel = dynamic_cast(GetActivePage()); - if (!panel) - return; - - panel->ActivateBuildMode(); -} - -//----------------------------------------------------------------------------- -// Purpose: sets the text on the OK/Cancel buttons, overriding the default -//----------------------------------------------------------------------------- -void PropertyDialog::SetOKButtonText(const char *text) -{ - _okButton->SetText(text); -} - -//----------------------------------------------------------------------------- -// Purpose: sets the text on the OK/Cancel buttons, overriding the default -//----------------------------------------------------------------------------- -void PropertyDialog::SetCancelButtonText(const char *text) -{ - _cancelButton->SetText(text); -} - -//----------------------------------------------------------------------------- -// Purpose: sets the text on the apply buttons, overriding the default -//----------------------------------------------------------------------------- -void PropertyDialog::SetApplyButtonText(const char *text) -{ - _applyButton->SetText(text); -} - -//----------------------------------------------------------------------------- -// Purpose: changes the visibility of the buttons -//----------------------------------------------------------------------------- -void PropertyDialog::SetOKButtonVisible(bool state) -{ - _okButton->SetVisible(state); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: changes the visibility of the buttons -//----------------------------------------------------------------------------- -void PropertyDialog::SetCancelButtonVisible(bool state) -{ - _cancelButton->SetVisible(state); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: changes the visibility of the buttons -//----------------------------------------------------------------------------- -void PropertyDialog::SetApplyButtonVisible(bool state) -{ - _applyButton->SetVisible(state); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: when a sheet changes, enable the apply button -//----------------------------------------------------------------------------- -void PropertyDialog::OnApplyButtonEnable() -{ - if (_applyButton->IsEnabled()) - return; - - EnableApplyButton(true); -} - -//----------------------------------------------------------------------------- -// Purpose: enable/disable the apply button -//----------------------------------------------------------------------------- -void PropertyDialog::EnableApplyButton(bool bEnable) -{ - _applyButton->SetEnabled(bEnable); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PropertyDialog::RequestFocus(int direction) -{ - _propertySheet->RequestFocus(direction); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include + +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +PropertyDialog::PropertyDialog(Panel *parent, const char *panelName) : Frame(parent, panelName) +{ + // create the property sheet + _propertySheet = new PropertySheet(this, "Sheet"); + _propertySheet->AddActionSignalTarget(this); + _propertySheet->SetTabPosition(1); + + // add the buttons + _okButton = new Button(this, "OKButton", "#PropertyDialog_OK"); + _okButton->AddActionSignalTarget(this); + _okButton->SetTabPosition(2); + _okButton->SetCommand("OK"); + GetFocusNavGroup().SetDefaultButton(_okButton); + + _cancelButton = new Button(this, "CancelButton", "#PropertyDialog_Cancel"); + _cancelButton->AddActionSignalTarget(this); + _cancelButton->SetTabPosition(3); + _cancelButton->SetCommand("Cancel"); + + _applyButton = new Button(this, "ApplyButton", "#PropertyDialog_Apply"); + _applyButton->AddActionSignalTarget(this); + _applyButton->SetTabPosition(4); + _applyButton->SetVisible(false); // default to not visible + _applyButton->SetEnabled(false); // default to not enabled + _applyButton->SetCommand("Apply"); + + SetSizeable(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +PropertyDialog::~PropertyDialog() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a pointer to the PropertySheet this dialog encapsulates +// Output : PropertySheet * +//----------------------------------------------------------------------------- +PropertySheet *PropertyDialog::GetPropertySheet() +{ + return _propertySheet; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets a pointer to the currently active page. +// Output : Panel +//----------------------------------------------------------------------------- +Panel *PropertyDialog::GetActivePage() +{ + return _propertySheet->GetActivePage(); +} + +//----------------------------------------------------------------------------- +// Purpose: Wrapped function +//----------------------------------------------------------------------------- +void PropertyDialog::AddPage(Panel *page, const char *title) +{ + _propertySheet->AddPage(page, title); +} + +//----------------------------------------------------------------------------- +// Purpose: reloads the data in all the property page +//----------------------------------------------------------------------------- +void PropertyDialog::ResetAllData() +{ + _propertySheet->ResetAllData(); +} + +//----------------------------------------------------------------------------- +// Purpose: Applies any changes +//----------------------------------------------------------------------------- +void PropertyDialog::ApplyChanges() +{ + OnCommand("Apply"); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets up the sheet +//----------------------------------------------------------------------------- +void PropertyDialog::PerformLayout() +{ + BaseClass::PerformLayout(); + + int iBottom = m_iSheetInsetBottom; + if ( IsProportional() ) + { + iBottom = scheme()->GetProportionalScaledValueEx( GetScheme(), iBottom ); + } + + int x, y, wide, tall; + GetClientArea(x, y, wide, tall); + _propertySheet->SetBounds(x, y, wide, tall - iBottom); + + + // move the buttons to the bottom-right corner + int xpos = x + wide - 80; + int ypos = tall + y - 28; + + if (_applyButton->IsVisible()) + { + _applyButton->SetBounds(xpos, ypos, 72, 24); + xpos -= 80; + } + + if (_cancelButton->IsVisible()) + { + _cancelButton->SetBounds(xpos, ypos, 72, 24); + xpos -= 80; + } + + _okButton->SetBounds(xpos, ypos, 72, 24); + + _propertySheet->InvalidateLayout(); // tell the propertysheet to redraw! + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Handles command text from the buttons +//----------------------------------------------------------------------------- +void PropertyDialog::OnCommand(const char *command) +{ + if (!stricmp(command, "OK")) + { + if ( OnOK(false) ) + { + OnCommand("Close"); + } + _applyButton->SetEnabled(false); + } + else if (!stricmp(command, "Cancel")) + { + OnCancel(); + Close(); + } + else if (!stricmp(command, "Apply")) + { + OnOK(true); + _applyButton->SetEnabled(false); + InvalidateLayout(); + } + else + { + BaseClass::OnCommand(command); + } +} + +//----------------------------------------------------------------------------- +// Purpose: called when the Cancel button is pressed +//----------------------------------------------------------------------------- +void PropertyDialog::OnCancel() +{ + // designed to be overridden +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : code - +//----------------------------------------------------------------------------- +void PropertyDialog::OnKeyCodeTyped(KeyCode code) +{ + // this has been removed, since it conflicts with how we use the escape key in the game +// if (code == KEY_ESCAPE) +// { +// OnCommand("Cancel"); +// } +// else + { + BaseClass::OnKeyCodeTyped(code); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Command handler +//----------------------------------------------------------------------------- +bool PropertyDialog::OnOK(bool applyOnly) +{ + // the sheet should have the pages apply changes before we tell the world + _propertySheet->ApplyChanges(); + + // this should tell anybody who's watching us that we're done + PostActionSignal(new KeyValues("ApplyChanges")); + + // default to closing + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Overrides build mode so it edits the sub panel +//----------------------------------------------------------------------------- +void PropertyDialog::ActivateBuildMode() +{ + // no subpanel, no build mode + EditablePanel *panel = dynamic_cast(GetActivePage()); + if (!panel) + return; + + panel->ActivateBuildMode(); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the text on the OK/Cancel buttons, overriding the default +//----------------------------------------------------------------------------- +void PropertyDialog::SetOKButtonText(const char *text) +{ + _okButton->SetText(text); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the text on the OK/Cancel buttons, overriding the default +//----------------------------------------------------------------------------- +void PropertyDialog::SetCancelButtonText(const char *text) +{ + _cancelButton->SetText(text); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the text on the apply buttons, overriding the default +//----------------------------------------------------------------------------- +void PropertyDialog::SetApplyButtonText(const char *text) +{ + _applyButton->SetText(text); +} + +//----------------------------------------------------------------------------- +// Purpose: changes the visibility of the buttons +//----------------------------------------------------------------------------- +void PropertyDialog::SetOKButtonVisible(bool state) +{ + _okButton->SetVisible(state); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: changes the visibility of the buttons +//----------------------------------------------------------------------------- +void PropertyDialog::SetCancelButtonVisible(bool state) +{ + _cancelButton->SetVisible(state); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: changes the visibility of the buttons +//----------------------------------------------------------------------------- +void PropertyDialog::SetApplyButtonVisible(bool state) +{ + _applyButton->SetVisible(state); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: when a sheet changes, enable the apply button +//----------------------------------------------------------------------------- +void PropertyDialog::OnApplyButtonEnable() +{ + if (_applyButton->IsEnabled()) + return; + + EnableApplyButton(true); +} + +//----------------------------------------------------------------------------- +// Purpose: enable/disable the apply button +//----------------------------------------------------------------------------- +void PropertyDialog::EnableApplyButton(bool bEnable) +{ + _applyButton->SetEnabled(bEnable); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PropertyDialog::RequestFocus(int direction) +{ + _propertySheet->RequestFocus(direction); +} diff --git a/mp/src/vgui2/vgui_controls/PropertyPage.cpp b/mp/src/vgui2/vgui_controls/PropertyPage.cpp index 2a97e7c0..36aa102d 100644 --- a/mp/src/vgui2/vgui_controls/PropertyPage.cpp +++ b/mp/src/vgui2/vgui_controls/PropertyPage.cpp @@ -1,114 +1,114 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "vgui/IScheme.h" -#include "vgui/KeyCode.h" -#include "vgui/ISurface.h" -#include "KeyValues.h" - -#include "vgui_controls/PropertyPage.h" -#include "vgui_controls/Controls.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -PropertyPage::PropertyPage(Panel *parent, const char *panelName) : EditablePanel(parent, panelName) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -PropertyPage::~PropertyPage() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Called when page is loaded. Data should be reloaded from document into controls. -//----------------------------------------------------------------------------- -void PropertyPage::OnResetData() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Called when the OK / Apply button is pressed. Changed data should be written into document. -//----------------------------------------------------------------------------- -void PropertyPage::OnApplyChanges() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Designed to be overriden -//----------------------------------------------------------------------------- -void PropertyPage::OnPageShow() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Designed to be overriden -//----------------------------------------------------------------------------- -void PropertyPage::OnPageHide() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pageTab - -//----------------------------------------------------------------------------- -void PropertyPage::OnPageTabActivated(Panel *pageTab) -{ - _pageTab = pageTab; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PropertyPage::OnKeyCodeTyped(KeyCode code) -{ - switch (code) - { - // left and right only get propogated to parents if our tab has focus - case KEY_RIGHT: - { - if (_pageTab != 0 && _pageTab->HasFocus()) - BaseClass::OnKeyCodeTyped(code); - break; - } - case KEY_LEFT: - { - if (_pageTab != 0 && _pageTab->HasFocus()) - BaseClass::OnKeyCodeTyped(code); - break; - } - default: - BaseClass::OnKeyCodeTyped(code); - break; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PropertyPage::SetVisible(bool state) -{ - if (IsVisible() && !state) - { - // if we're going away and we have a current button, get rid of it - if (GetFocusNavGroup().GetCurrentDefaultButton()) - { - GetFocusNavGroup().SetCurrentDefaultButton(NULL); - } - } - - BaseClass::SetVisible(state); -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "vgui/IScheme.h" +#include "vgui/KeyCode.h" +#include "vgui/ISurface.h" +#include "KeyValues.h" + +#include "vgui_controls/PropertyPage.h" +#include "vgui_controls/Controls.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +PropertyPage::PropertyPage(Panel *parent, const char *panelName) : EditablePanel(parent, panelName) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +PropertyPage::~PropertyPage() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Called when page is loaded. Data should be reloaded from document into controls. +//----------------------------------------------------------------------------- +void PropertyPage::OnResetData() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the OK / Apply button is pressed. Changed data should be written into document. +//----------------------------------------------------------------------------- +void PropertyPage::OnApplyChanges() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Designed to be overriden +//----------------------------------------------------------------------------- +void PropertyPage::OnPageShow() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Designed to be overriden +//----------------------------------------------------------------------------- +void PropertyPage::OnPageHide() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pageTab - +//----------------------------------------------------------------------------- +void PropertyPage::OnPageTabActivated(Panel *pageTab) +{ + _pageTab = pageTab; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PropertyPage::OnKeyCodeTyped(KeyCode code) +{ + switch (code) + { + // left and right only get propogated to parents if our tab has focus + case KEY_RIGHT: + { + if (_pageTab != 0 && _pageTab->HasFocus()) + BaseClass::OnKeyCodeTyped(code); + break; + } + case KEY_LEFT: + { + if (_pageTab != 0 && _pageTab->HasFocus()) + BaseClass::OnKeyCodeTyped(code); + break; + } + default: + BaseClass::OnKeyCodeTyped(code); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PropertyPage::SetVisible(bool state) +{ + if (IsVisible() && !state) + { + // if we're going away and we have a current button, get rid of it + if (GetFocusNavGroup().GetCurrentDefaultButton()) + { + GetFocusNavGroup().SetCurrentDefaultButton(NULL); + } + } + + BaseClass::SetVisible(state); +} + diff --git a/mp/src/vgui2/vgui_controls/PropertySheet.cpp b/mp/src/vgui2/vgui_controls/PropertySheet.cpp index b6705b55..18a36ca2 100644 --- a/mp/src/vgui2/vgui_controls/PropertySheet.cpp +++ b/mp/src/vgui2/vgui_controls/PropertySheet.cpp @@ -1,1674 +1,1674 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "vgui_controls/AnimationController.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -namespace vgui -{ - -class ContextLabel : public Label -{ - DECLARE_CLASS_SIMPLE( ContextLabel, Label ); -public: - - ContextLabel( Button *parent, char const *panelName, char const *text ): - BaseClass( (Panel *)parent, panelName, text ), - m_pTabButton( parent ) - { - SetBlockDragChaining( true ); - } - - virtual void OnMousePressed( MouseCode code ) - { - if ( m_pTabButton ) - { - m_pTabButton->FireActionSignal(); - } - } - - virtual void OnMouseReleased( MouseCode code ) - { - BaseClass::OnMouseReleased( code ); - - if ( GetParent() ) - { - GetParent()->OnCommand( "ShowContextMenu" ); - } - } - - virtual void ApplySchemeSettings( IScheme *pScheme ) - { - BaseClass::ApplySchemeSettings( pScheme ); - - HFont marlett = pScheme->GetFont( "Marlett" ); - SetFont( marlett ); - SetTextInset( 0, 0 ); - SetContentAlignment( Label::a_northwest ); - - if ( GetParent() ) - { - SetFgColor( pScheme->GetColor( "Button.TextColor", GetParent()->GetFgColor() ) ); - SetBgColor( GetParent()->GetBgColor() ); - } - } -private: - - Button *m_pTabButton; -}; - -//----------------------------------------------------------------------------- -// Purpose: Helper for drag drop -// Input : msglist - -// Output : static PropertySheet -//----------------------------------------------------------------------------- -static PropertySheet *IsDroppingSheet( CUtlVector< KeyValues * >& msglist ) -{ - if ( msglist.Count() == 0 ) - return NULL; - - KeyValues *data = msglist[ 0 ]; - PropertySheet *sheet = reinterpret_cast< PropertySheet * >( data->GetPtr( "propertysheet" ) ); - if ( sheet ) - return sheet; - - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: A single tab -//----------------------------------------------------------------------------- -class PageTab : public Button -{ - DECLARE_CLASS_SIMPLE( PageTab, Button ); - -private: - bool _active; - Color _textColor; - Color _dimTextColor; - int m_bMaxTabWidth; - IBorder *m_pActiveBorder; - IBorder *m_pNormalBorder; - PropertySheet *m_pParent; - Panel *m_pPage; - ImagePanel *m_pImage; - char *m_pszImageName; - bool m_bShowContextLabel; - bool m_bAttemptingDrop; - ContextLabel *m_pContextLabel; - long m_hoverActivatePageTime; - long m_dropHoverTime; - -public: - PageTab(PropertySheet *parent, const char *panelName, const char *text, char const *imageName, int maxTabWidth, Panel *page, bool showContextButton, long hoverActivatePageTime = -1 ) : - Button( (Panel *)parent, panelName, text), - m_pParent( parent ), - m_pPage( page ), - m_pImage( 0 ), - m_pszImageName( 0 ), - m_bShowContextLabel( showContextButton ), - m_bAttemptingDrop( false ), - m_hoverActivatePageTime( hoverActivatePageTime ), - m_dropHoverTime( -1 ) - { - SetCommand(new KeyValues("TabPressed")); - _active = false; - m_bMaxTabWidth = maxTabWidth; - SetDropEnabled( true ); - SetDragEnabled( m_pParent->IsDraggableTab() ); - if ( imageName ) - { - m_pImage = new ImagePanel( this, text ); - int buflen = Q_strlen( imageName ) + 1; - m_pszImageName = new char[ buflen ]; - Q_strncpy( m_pszImageName, imageName, buflen ); - - } - SetMouseClickEnabled( MOUSE_RIGHT, true ); - m_pContextLabel = m_bShowContextLabel ? new ContextLabel( this, "Context", "9" ) : NULL; - - REGISTER_COLOR_AS_OVERRIDABLE( _textColor, "selectedcolor" ); - REGISTER_COLOR_AS_OVERRIDABLE( _dimTextColor, "unselectedcolor" ); - } - - ~PageTab() - { - delete[] m_pszImageName; - } - - virtual void Paint() - { - BaseClass::Paint(); - } - - virtual void OnCursorEntered() - { - m_dropHoverTime = system()->GetTimeMillis(); - } - - virtual void OnCursorExited() - { - m_dropHoverTime = -1; - } - - virtual void OnThink() - { - if ( m_bAttemptingDrop && m_hoverActivatePageTime >= 0 && m_dropHoverTime >= 0 ) - { - long hoverTime = system()->GetTimeMillis() - m_dropHoverTime; - if ( hoverTime > m_hoverActivatePageTime ) - { - FireActionSignal(); - SetSelected(true); - Repaint(); - } - } - m_bAttemptingDrop = false; - - BaseClass::OnThink(); - } - - virtual bool IsDroppable( CUtlVector< KeyValues * >&msglist ) - { - m_bAttemptingDrop = true; - - if ( !GetParent() ) - return false; - - PropertySheet *sheet = IsDroppingSheet( msglist ); - if ( sheet ) - return GetParent()->IsDroppable( msglist ); - - return BaseClass::IsDroppable( msglist ); - } - - virtual void OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ) - { - PropertySheet *sheet = IsDroppingSheet( msglist ); - if ( sheet ) - { - Panel *target = GetParent()->GetDropTarget( msglist ); - if ( target ) - { - // Fixme, mouse pos could be wrong... - target->OnDroppablePanelPaint( msglist, dragPanels ); - return; - } - } - - // Just highlight the tab if dropping onto active page via the tab - BaseClass::OnDroppablePanelPaint( msglist, dragPanels ); - } - - virtual void OnPanelDropped( CUtlVector< KeyValues * >& msglist ) - { - PropertySheet *sheet = IsDroppingSheet( msglist ); - if ( sheet ) - { - Panel *target = GetParent()->GetDropTarget( msglist ); - if ( target ) - { - // Fixme, mouse pos could be wrong... - target->OnPanelDropped( msglist ); - } - } - - // Defer to active page... - Panel *active = m_pParent->GetActivePage(); - if ( !active || !active->IsDroppable( msglist ) ) - return; - - active->OnPanelDropped( msglist ); - } - - virtual void OnDragFailed( CUtlVector< KeyValues * >& msglist ) - { - PropertySheet *sheet = IsDroppingSheet( msglist ); - if ( !sheet ) - return; - - // Create a new property sheet - if ( m_pParent->IsDraggableTab() ) - { - if ( msglist.Count() == 1 ) - { - KeyValues *data = msglist[ 0 ]; - int screenx = data->GetInt( "screenx" ); - int screeny = data->GetInt( "screeny" ); - - // m_pParent->ScreenToLocal( screenx, screeny ); - if ( !m_pParent->IsWithin( screenx, screeny ) ) - { - Panel *page = reinterpret_cast< Panel * >( data->GetPtr( "propertypage" ) ); - PropertySheet *sheet = reinterpret_cast< PropertySheet * >( data->GetPtr( "propertysheet" ) ); - char const *title = data->GetString( "tabname", "" ); - if ( !page || !sheet ) - return; - - // Can only create if sheet was part of a ToolWindow derived object - ToolWindow *tw = dynamic_cast< ToolWindow * >( sheet->GetParent() ); - if ( tw ) - { - IToolWindowFactory *factory = tw->GetToolWindowFactory(); - if ( factory ) - { - bool hasContextMenu = sheet->PageHasContextMenu( page ); - sheet->RemovePage( page ); - factory->InstanceToolWindow( tw->GetParent(), sheet->ShouldShowContextButtons(), page, title, hasContextMenu ); - - if ( sheet->GetNumPages() == 0 ) - { - tw->MarkForDeletion(); - } - } - } - } - } - } - } - - virtual void OnCreateDragData( KeyValues *msg ) - { - Assert( m_pParent->IsDraggableTab() ); - - msg->SetPtr( "propertypage", m_pPage ); - msg->SetPtr( "propertysheet", m_pParent ); - char sz[ 256 ]; - GetText( sz, sizeof( sz ) ); - msg->SetString( "tabname", sz ); - msg->SetString( "text", sz ); - } - - virtual void ApplySchemeSettings(IScheme *pScheme) - { - // set up the scheme settings - Button::ApplySchemeSettings(pScheme); - - _textColor = GetSchemeColor("PropertySheet.SelectedTextColor", GetFgColor(), pScheme); - _dimTextColor = GetSchemeColor("PropertySheet.TextColor", GetFgColor(), pScheme); - m_pActiveBorder = pScheme->GetBorder("TabActiveBorder"); - m_pNormalBorder = pScheme->GetBorder("TabBorder"); - - if ( m_pImage ) - { - ClearImages(); - m_pImage->SetImage(scheme()->GetImage(m_pszImageName, false)); - AddImage( m_pImage->GetImage(), 2 ); - int w, h; - m_pImage->GetSize( w, h ); - w += m_pContextLabel ? 10 : 0; - if ( m_pContextLabel ) - { - m_pImage->SetPos( 10, 0 ); - } - SetSize( w + 4, h + 2 ); - } - else - { - int wide, tall; - int contentWide, contentTall; - GetSize(wide, tall); - GetContentSize(contentWide, contentTall); - - wide = max(m_bMaxTabWidth, contentWide + 10); // 10 = 5 pixels margin on each side - wide += m_pContextLabel ? 10 : 0; - SetSize(wide, tall); - } - - if ( m_pContextLabel ) - { - SetTextInset( 12, 0 ); - } - } - - virtual void ApplySettings( KeyValues *inResourceData ) - { - const char *pBorder = inResourceData->GetString("activeborder_override", ""); - if (*pBorder) - { - m_pActiveBorder = scheme()->GetIScheme(GetScheme())->GetBorder( pBorder ); - } - pBorder = inResourceData->GetString("normalborder_override", ""); - if (*pBorder) - { - m_pNormalBorder = scheme()->GetIScheme(GetScheme())->GetBorder( pBorder ); - } - BaseClass::ApplySettings(inResourceData); - } - - virtual void OnCommand( char const *cmd ) - { - if ( !Q_stricmp( cmd, "ShowContextMenu" ) ) - { - KeyValues *kv = new KeyValues("OpenContextMenu"); - kv->SetPtr( "page", m_pPage ); - kv->SetPtr( "contextlabel", m_pContextLabel ); - PostActionSignal( kv ); - return; - } - BaseClass::OnCommand( cmd ); - } - - IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) - { - if (_active) - { - return m_pActiveBorder; - } - return m_pNormalBorder; - } - - virtual Color GetButtonFgColor() - { - if (_active) - { - return _textColor; - } - else - { - return _dimTextColor; - } - } - - virtual void SetActive(bool state) - { - _active = state; - SetZPos( state ? 100 : 0 ); - InvalidateLayout(); - Repaint(); - } - - virtual void SetTabWidth( int iWidth ) - { - m_bMaxTabWidth = iWidth; - InvalidateLayout(); - } - - virtual bool CanBeDefaultButton(void) - { - return false; - } - - //Fire action signal when mouse is pressed down instead of on release. - virtual void OnMousePressed(MouseCode code) - { - // check for context menu open - if (!IsEnabled()) - return; - - if (!IsMouseClickEnabled(code)) - return; - - if (IsUseCaptureMouseEnabled()) - { - { - RequestFocus(); - FireActionSignal(); - SetSelected(true); - Repaint(); - } - - // lock mouse input to going to this button - input()->SetMouseCapture(GetVPanel()); - } - } - - virtual void OnMouseReleased(MouseCode code) - { - // ensure mouse capture gets released - if (IsUseCaptureMouseEnabled()) - { - input()->SetMouseCapture(NULL); - } - - // make sure the button gets unselected - SetSelected(false); - Repaint(); - - if (code == MOUSE_RIGHT) - { - KeyValues *kv = new KeyValues("OpenContextMenu"); - kv->SetPtr( "page", m_pPage ); - kv->SetPtr( "contextlabel", m_pContextLabel ); - PostActionSignal( kv ); - } - } - - virtual void PerformLayout() - { - BaseClass::PerformLayout(); - - if ( m_pContextLabel ) - { - int w, h; - GetSize( w, h ); - m_pContextLabel->SetBounds( 0, 0, 10, h ); - } - } -}; - - -}; // namespace vgui - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -PropertySheet::PropertySheet( - Panel *parent, - const char *panelName, - bool draggableTabs /*= false*/ ) : BaseClass(parent, panelName) -{ - _activePage = NULL; - _activeTab = NULL; - _tabWidth = 64; - _activeTabIndex = 0; - _showTabs = true; - _combo = NULL; - _tabFocus = false; - m_flPageTransitionEffectTime = 0.0f; - m_bSmallTabs = false; - m_tabFont = 0; - m_bDraggableTabs = draggableTabs; - m_pTabKV = NULL; - m_iTabHeight = 0; - m_iTabHeightSmall = 0; - - if ( m_bDraggableTabs ) - { - SetDropEnabled( true ); - } - - m_bKBNavigationEnabled = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor, associates pages with a combo box -//----------------------------------------------------------------------------- -PropertySheet::PropertySheet(Panel *parent, const char *panelName, ComboBox *combo) : BaseClass(parent, panelName) -{ - _activePage = NULL; - _activeTab = NULL; - _tabWidth = 64; - _activeTabIndex = 0; - _combo=combo; - _combo->AddActionSignalTarget(this); - _showTabs = false; - _tabFocus = false; - m_flPageTransitionEffectTime = 0.0f; - m_bSmallTabs = false; - m_tabFont = 0; - m_bDraggableTabs = false; - m_pTabKV = NULL; - m_iTabHeight = 0; - m_iTabHeightSmall = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -PropertySheet::~PropertySheet() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: ToolWindow uses this to drag tools from container to container by dragging the tab -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool PropertySheet::IsDraggableTab() const -{ - return m_bDraggableTabs; -} - -void PropertySheet::SetDraggableTabs( bool state ) -{ - m_bDraggableTabs = state; -} - -//----------------------------------------------------------------------------- -// Purpose: Lower profile tabs -// Input : state - -//----------------------------------------------------------------------------- -void PropertySheet::SetSmallTabs( bool state ) -{ - m_bSmallTabs = state; - m_tabFont = scheme()->GetIScheme( GetScheme() )->GetFont( m_bSmallTabs ? "DefaultVerySmall" : "Default" ); - int c = m_PageTabs.Count(); - for ( int i = 0; i < c ; ++i ) - { - PageTab *tab = m_PageTabs[ i ]; - Assert( tab ); - tab->SetFont( m_tabFont ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool PropertySheet::IsSmallTabs() const -{ - return m_bSmallTabs; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : state - -//----------------------------------------------------------------------------- -void PropertySheet::ShowContextButtons( bool state ) -{ - m_bContextButton = state; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool PropertySheet::ShouldShowContextButtons() const -{ - return m_bContextButton; -} - -int PropertySheet::FindPage( Panel *page ) const -{ - int c = m_Pages.Count(); - for ( int i = 0; i < c; ++i ) - { - if ( m_Pages[ i ].page == page ) - return i; - } - - return m_Pages.InvalidIndex(); -} - -//----------------------------------------------------------------------------- -// Purpose: adds a page to the sheet -//----------------------------------------------------------------------------- -void PropertySheet::AddPage(Panel *page, const char *title, char const *imageName /*= NULL*/, bool bHasContextMenu /*= false*/ ) -{ - if (!page) - return; - - // don't add the page if we already have it - if ( FindPage( page ) != m_Pages.InvalidIndex() ) - return; - - long hoverActivatePageTime = 250; - PageTab *tab = new PageTab(this, "tab", title, imageName, _tabWidth, page, m_bContextButton && bHasContextMenu, hoverActivatePageTime ); - if ( m_bDraggableTabs ) - { - tab->SetDragEnabled( true ); - } - - tab->SetFont( m_tabFont ); - if(_showTabs) - { - tab->AddActionSignalTarget(this); - } - else if (_combo) - { - _combo->AddItem(title, NULL); - } - - if ( m_pTabKV ) - { - tab->ApplySettings( m_pTabKV ); - } - - m_PageTabs.AddToTail(tab); - - Page_t info; - info.page = page; - info.contextMenu = m_bContextButton && bHasContextMenu; - - m_Pages.AddToTail( info ); - - page->SetParent(this); - page->AddActionSignalTarget(this); - PostMessage(page, new KeyValues("ResetData")); - - page->SetVisible(false); - InvalidateLayout(); - - if (!_activePage) - { - // first page becomes the active page - ChangeActiveTab( 0 ); - if ( _activePage ) - { - _activePage->RequestFocus( 0 ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PropertySheet::SetActivePage(Panel *page) -{ - // walk the list looking for this page - int index = FindPage( page ); - if (!m_Pages.IsValidIndex(index)) - return; - - ChangeActiveTab(index); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PropertySheet::SetTabWidth(int pixels) -{ - if ( pixels < 0 ) - { - if( !_activeTab ) - return; - - int nTall; - _activeTab->GetContentSize( pixels, nTall ); - } - - if ( _tabWidth == pixels ) - return; - - _tabWidth = pixels; - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: reloads the data in all the property page -//----------------------------------------------------------------------------- -void PropertySheet::ResetAllData() -{ - // iterate all the dialogs resetting them - for (int i = 0; i < m_Pages.Count(); i++) - { - ipanel()->SendMessage(m_Pages[i].page->GetVPanel(), new KeyValues("ResetData"), GetVPanel()); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Applies any changes made by the dialog -//----------------------------------------------------------------------------- -void PropertySheet::ApplyChanges() -{ - // iterate all the dialogs resetting them - for (int i = 0; i < m_Pages.Count(); i++) - { - ipanel()->SendMessage(m_Pages[i].page->GetVPanel(), new KeyValues("ApplyChanges"), GetVPanel()); - } -} - -//----------------------------------------------------------------------------- -// Purpose: gets a pointer to the currently active page -//----------------------------------------------------------------------------- -Panel *PropertySheet::GetActivePage() -{ - return _activePage; -} - -//----------------------------------------------------------------------------- -// Purpose: gets a pointer to the currently active tab -//----------------------------------------------------------------------------- -Panel *PropertySheet::GetActiveTab() -{ - return _activeTab; -} - -//----------------------------------------------------------------------------- -// Purpose: returns the number of panels in the sheet -//----------------------------------------------------------------------------- -int PropertySheet::GetNumPages() -{ - return m_Pages.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: returns the name contained in the active tab -// Input : a text buffer to contain the output -//----------------------------------------------------------------------------- -void PropertySheet::GetActiveTabTitle (char *textOut, int bufferLen ) -{ - if(_activeTab) _activeTab->GetText(textOut, bufferLen); -} - -//----------------------------------------------------------------------------- -// Purpose: returns the name contained in the active tab -// Input : a text buffer to contain the output -//----------------------------------------------------------------------------- -bool PropertySheet::GetTabTitle( int i, char *textOut, int bufferLen ) -{ - if ( i < 0 || i >= m_PageTabs.Count() ) - { - return false; - } - - m_PageTabs[i]->GetText(textOut, bufferLen); - return true; -} - -bool PropertySheet::SetTabTitle( int i, char *pchTitle ) -{ - if ( i < 0 || i >= m_PageTabs.Count() ) - { - return false; - } - - m_PageTabs[ i ]->SetText( pchTitle ); - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the index of the currently active page -//----------------------------------------------------------------------------- -int PropertySheet::GetActivePageNum() -{ - for (int i = 0; i < m_Pages.Count(); i++) - { - if (m_Pages[i].page == _activePage) - { - return i; - } - } - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: Forwards focus requests to current active page -//----------------------------------------------------------------------------- -void PropertySheet::RequestFocus(int direction) -{ - if (direction == -1 || direction == 0) - { - if (_activePage) - { - _activePage->RequestFocus(direction); - _tabFocus = false; - } - } - else - { - if (_showTabs && _activeTab) - { - _activeTab->RequestFocus(direction); - _tabFocus = true; - } - else if (_activePage) - { - _activePage->RequestFocus(direction); - _tabFocus = false; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: moves focus back -//----------------------------------------------------------------------------- -bool PropertySheet::RequestFocusPrev(VPANEL panel) -{ - if (_tabFocus || !_showTabs || !_activeTab) - { - _tabFocus = false; - return BaseClass::RequestFocusPrev(panel); - } - else - { - if (GetVParent()) - { - PostMessage(GetVParent(), new KeyValues("FindDefaultButton")); - } - _activeTab->RequestFocus(-1); - _tabFocus = true; - return true; - } -} - -//----------------------------------------------------------------------------- -// Purpose: moves focus forward -//----------------------------------------------------------------------------- -bool PropertySheet::RequestFocusNext(VPANEL panel) -{ - if (!_tabFocus || !_activePage) - { - return BaseClass::RequestFocusNext(panel); - } - else - { - if (!_activeTab) - { - return BaseClass::RequestFocusNext(panel); - } - else - { - _activePage->RequestFocus(1); - _tabFocus = false; - return true; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Gets scheme settings -//----------------------------------------------------------------------------- -void PropertySheet::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - // a little backwards-compatibility with old scheme files - IBorder *pBorder = pScheme->GetBorder("PropertySheetBorder"); - if (pBorder == pScheme->GetBorder("Default")) - { - // get the old name - pBorder = pScheme->GetBorder("RaisedBorder"); - } - - SetBorder(pBorder); - m_flPageTransitionEffectTime = atof(pScheme->GetResourceString("PropertySheet.TransitionEffectTime")); - - m_tabFont = pScheme->GetFont( m_bSmallTabs ? "DefaultVerySmall" : "Default" ); - - if ( m_pTabKV ) - { - for (int i = 0; i < m_PageTabs.Count(); i++) - { - m_PageTabs[i]->ApplySettings( m_pTabKV ); - } - } - - - //============================================================================= - // HPE_BEGIN: - // [tj] Here, we used to use a single size variable and overwrite it when we scaled. - // This led to problems when we changes resolutions, so now we recalcuate the absolute - // size from the relative size each time (based on proportionality) - //============================================================================= - if ( IsProportional() ) - { - m_iTabHeight = scheme()->GetProportionalScaledValueEx( GetScheme(), m_iSpecifiedTabHeight ); - m_iTabHeightSmall = scheme()->GetProportionalScaledValueEx( GetScheme(), m_iSpecifiedTabHeightSmall ); - } - else - { - m_iTabHeight = m_iSpecifiedTabHeight; - m_iTabHeightSmall = m_iSpecifiedTabHeightSmall; - } - //============================================================================= - // HPE_END - //============================================================================= -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PropertySheet::ApplySettings(KeyValues *inResourceData) -{ - BaseClass::ApplySettings(inResourceData); - - KeyValues *pTabKV = inResourceData->FindKey( "tabskv" ); - if ( pTabKV ) - { - if ( m_pTabKV ) - { - m_pTabKV->deleteThis(); - } - m_pTabKV = new KeyValues("tabkv"); - pTabKV->CopySubkeys( m_pTabKV ); - } - - KeyValues *pTabWidthKV = inResourceData->FindKey( "tabwidth" ); - if ( pTabWidthKV ) - { - _tabWidth = scheme()->GetProportionalScaledValueEx(GetScheme(), pTabWidthKV->GetInt()); - for (int i = 0; i < m_PageTabs.Count(); i++) - { - m_PageTabs[i]->SetTabWidth( _tabWidth ); - } - } - - KeyValues *pTransitionKV = inResourceData->FindKey( "transition_time" ); - if ( pTransitionKV ) - { - m_flPageTransitionEffectTime = pTransitionKV->GetFloat(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Paint our border specially, with the tabs in mind -//----------------------------------------------------------------------------- -void PropertySheet::PaintBorder() -{ - IBorder *border = GetBorder(); - if (!border) - return; - - // draw the border, but with a break at the active tab - int px = 0, py = 0, pwide = 0, ptall = 0; - if (_activeTab) - { - _activeTab->GetBounds(px, py, pwide, ptall); - ptall -= 1; - } - - // draw the border underneath the buttons, with a break - int wide, tall; - GetSize(wide, tall); - border->Paint(0, py + ptall, wide, tall, IBorder::SIDE_TOP, px + 1, px + pwide - 1); -} - -//----------------------------------------------------------------------------- -// Purpose: Lays out the dialog -//----------------------------------------------------------------------------- -void PropertySheet::PerformLayout() -{ - BaseClass::PerformLayout(); - - int x, y, wide, tall; - GetBounds(x, y, wide, tall); - if (_activePage) - { - int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight; - - if(_showTabs) - { - _activePage->SetBounds(0, tabHeight, wide, tall - tabHeight); - } - else - { - _activePage->SetBounds(0, 0, wide, tall ); - } - _activePage->InvalidateLayout(); - } - - - int xtab; - int limit = m_PageTabs.Count(); - - xtab = m_iTabXIndent; - - // draw the visible tabs - if (_showTabs) - { - for (int i = 0; i < limit; i++) - { - int tabHeight = IsSmallTabs() ? (m_iTabHeightSmall-1) : (m_iTabHeight-1); - - int width, tall; - m_PageTabs[i]->GetSize(width, tall); - - if ( m_bTabFitText ) - { - m_PageTabs[i]->SizeToContents(); - width = m_PageTabs[i]->GetWide(); - - int iXInset, iYInset; - m_PageTabs[i]->GetTextInset( &iXInset, &iYInset ); - width += (iXInset * 2); - } - - if (m_PageTabs[i] == _activeTab) - { - // active tab is taller - _activeTab->SetBounds(xtab, 2, width, tabHeight); - } - else - { - m_PageTabs[i]->SetBounds(xtab, 4, width, tabHeight - 2); - } - m_PageTabs[i]->SetVisible(true); - xtab += (width + 1) + m_iTabXDelta; - } - } - else - { - for (int i = 0; i < limit; i++) - { - m_PageTabs[i]->SetVisible(false); - } - } - - // ensure draw order (page drawing over all the tabs except one) - if (_activePage) - { - _activePage->MoveToFront(); - _activePage->Repaint(); - } - if (_activeTab) - { - _activeTab->MoveToFront(); - _activeTab->Repaint(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Switches the active panel -//----------------------------------------------------------------------------- -void PropertySheet::OnTabPressed(Panel *panel) -{ - // look for the tab in the list - for (int i = 0; i < m_PageTabs.Count(); i++) - { - if (m_PageTabs[i] == panel) - { - // flip to the new tab - ChangeActiveTab(i); - return; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: returns the panel associated with index i -// Input : the index of the panel to return -//----------------------------------------------------------------------------- -Panel *PropertySheet::GetPage(int i) -{ - if(i<0 || i>=m_Pages.Count()) - { - return NULL; - } - - return m_Pages[i].page; -} - - -//----------------------------------------------------------------------------- -// Purpose: disables page by name -//----------------------------------------------------------------------------- -void PropertySheet::DisablePage(const char *title) -{ - SetPageEnabled(title, false); -} - -//----------------------------------------------------------------------------- -// Purpose: enables page by name -//----------------------------------------------------------------------------- -void PropertySheet::EnablePage(const char *title) -{ - SetPageEnabled(title, true); -} - -//----------------------------------------------------------------------------- -// Purpose: enabled or disables page by name -//----------------------------------------------------------------------------- -void PropertySheet::SetPageEnabled(const char *title, bool state) -{ - for (int i = 0; i < m_PageTabs.Count(); i++) - { - if (_showTabs) - { - char tmp[50]; - m_PageTabs[i]->GetText(tmp,50); - if (!strnicmp(title,tmp,strlen(tmp))) - { - m_PageTabs[i]->SetEnabled(state); - } - } - else - { - _combo->SetItemEnabled(title,state); - } - } -} - -void PropertySheet::RemoveAllPages() -{ - int c = m_Pages.Count(); - for ( int i = c - 1; i >= 0 ; --i ) - { - RemovePage( m_Pages[ i ].page ); - } -} - -void PropertySheet::DeleteAllPages() -{ - int c = m_Pages.Count(); - for ( int i = c - 1; i >= 0 ; --i ) - { - DeletePage( m_Pages[ i ].page ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: deletes the page associated with panel -// Input : *panel - the panel of the page to remove -//----------------------------------------------------------------------------- -void PropertySheet::RemovePage(Panel *panel) -{ - int location = FindPage( panel ); - if ( location == m_Pages.InvalidIndex() ) - return; - - // Since it's being deleted, don't animate!!! - m_hPreviouslyActivePage = NULL; - _activeTab = NULL; - - // ASSUMPTION = that the number of pages equals number of tabs - if( _showTabs ) - { - m_PageTabs[location]->RemoveActionSignalTarget( this ); - } - // now remove the tab - PageTab *tab = m_PageTabs[ location ]; - m_PageTabs.Remove( location ); - tab->MarkForDeletion(); - - // Remove from page list - m_Pages.Remove( location ); - - // Unparent - panel->SetParent( (Panel *)NULL ); - - if ( _activePage == panel ) - { - _activePage = NULL; - // if this page is currently active, backup to the page before this. - ChangeActiveTab( max( location - 1, 0 ) ); - } - - PerformLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: deletes the page associated with panel -// Input : *panel - the panel of the page to remove -//----------------------------------------------------------------------------- -void PropertySheet::DeletePage(Panel *panel) -{ - Assert( panel ); - RemovePage( panel ); - panel->MarkForDeletion(); -} - -//----------------------------------------------------------------------------- -// Purpose: flips to the new tab, sending out all the right notifications -// flipping to a tab activates the tab. -//----------------------------------------------------------------------------- -void PropertySheet::ChangeActiveTab( int index ) -{ - if ( !m_Pages.IsValidIndex( index ) ) - { - _activeTab = NULL; - if ( m_Pages.Count() > 0 ) - { - _activePage = NULL; - - if ( index < 0 ) - { - ChangeActiveTab( m_Pages.Count() - 1 ); - } - else - { - ChangeActiveTab( 0 ); - } - } - return; - } - - if ( m_Pages[index].page == _activePage ) - { - if ( _activeTab ) - { - _activeTab->RequestFocus(); - } - _tabFocus = true; - return; - } - - int c = m_Pages.Count(); - for ( int i = 0; i < c; ++i ) - { - m_Pages[ i ].page->SetVisible( false ); - } - - m_hPreviouslyActivePage = _activePage; - // notify old page - if (_activePage) - { - ivgui()->PostMessage(_activePage->GetVPanel(), new KeyValues("PageHide"), GetVPanel()); - KeyValues *msg = new KeyValues("PageTabActivated"); - msg->SetPtr("panel", (Panel *)NULL); - ivgui()->PostMessage(_activePage->GetVPanel(), msg, GetVPanel()); - } - if (_activeTab) - { - //_activeTabIndex=index; - _activeTab->SetActive(false); - - // does the old tab have the focus? - _tabFocus = _activeTab->HasFocus(); - } - else - { - _tabFocus = false; - } - - // flip page - _activePage = m_Pages[index].page; - _activeTab = m_PageTabs[index]; - _activeTabIndex = index; - - _activePage->SetVisible(true); - _activePage->MoveToFront(); - - _activeTab->SetVisible(true); - _activeTab->MoveToFront(); - _activeTab->SetActive(true); - - if (_tabFocus) - { - // if a tab already has focused,give the new tab the focus - _activeTab->RequestFocus(); - } - else - { - // otherwise, give the focus to the page - _activePage->RequestFocus(); - } - - if (!_showTabs) - { - _combo->ActivateItemByRow(index); - } - - _activePage->MakeReadyForUse(); - - // transition effect - if (m_flPageTransitionEffectTime) - { - if (m_hPreviouslyActivePage.Get()) - { - // fade out the previous page - GetAnimationController()->RunAnimationCommand(m_hPreviouslyActivePage, "Alpha", 0.0f, 0.0f, m_flPageTransitionEffectTime / 2, AnimationController::INTERPOLATOR_LINEAR); - } - - // fade in the new page - _activePage->SetAlpha(0); - GetAnimationController()->RunAnimationCommand(_activePage, "Alpha", 255.0f, m_flPageTransitionEffectTime / 2, m_flPageTransitionEffectTime / 2, AnimationController::INTERPOLATOR_LINEAR); - } - else - { - if (m_hPreviouslyActivePage.Get()) - { - // no transition, just hide the previous page - m_hPreviouslyActivePage->SetVisible(false); - } - _activePage->SetAlpha( 255 ); - } - - // notify - ivgui()->PostMessage(_activePage->GetVPanel(), new KeyValues("PageShow"), GetVPanel()); - - KeyValues *msg = new KeyValues("PageTabActivated"); - msg->SetPtr("panel", (Panel *)_activeTab); - ivgui()->PostMessage(_activePage->GetVPanel(), msg, GetVPanel()); - - // tell parent - PostActionSignal(new KeyValues("PageChanged")); - - // Repaint - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Gets the panel with the specified hotkey, from the current page -//----------------------------------------------------------------------------- -Panel *PropertySheet::HasHotkey(wchar_t key) -{ - if (!_activePage) - return NULL; - - for (int i = 0; i < _activePage->GetChildCount(); i++) - { - Panel *hot = _activePage->GetChild(i)->HasHotkey(key); - if (hot) - { - return hot; - } - } - - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: catches the opencontextmenu event -//----------------------------------------------------------------------------- -void PropertySheet::OnOpenContextMenu( KeyValues *params ) -{ - // tell parent - KeyValues *kv = params->MakeCopy(); - PostActionSignal( kv ); - Panel *page = reinterpret_cast< Panel * >( params->GetPtr( "page" ) ); - if ( page ) - { - PostMessage( page->GetVPanel(), params->MakeCopy() ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handle key presses, through tabs. -//----------------------------------------------------------------------------- -void PropertySheet::OnKeyCodePressed(KeyCode code) -{ - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - - if ( ctrl && shift && alt && code == KEY_B ) - { - // enable build mode - EditablePanel *ep = dynamic_cast< EditablePanel * >( GetActivePage() ); - if ( ep ) - { - ep->ActivateBuildMode(); - return; - } - } - - if ( IsKBNavigationEnabled() ) - { - ButtonCode_t nButtonCode = GetBaseButtonCode( code ); - - switch ( nButtonCode ) - { - // for now left and right arrows just open or close submenus if they are there. - case KEY_RIGHT: - case KEY_XBUTTON_RIGHT: - case KEY_XSTICK1_RIGHT: - case KEY_XSTICK2_RIGHT: - { - ChangeActiveTab(_activeTabIndex+1); - break; - } - case KEY_LEFT: - case KEY_XBUTTON_LEFT: - case KEY_XSTICK1_LEFT: - case KEY_XSTICK2_LEFT: - { - ChangeActiveTab(_activeTabIndex-1); - break; - } - default: - BaseClass::OnKeyCodePressed(code); - break; - } - } - else - { - BaseClass::OnKeyCodePressed(code); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Called by the associated combo box (if in that mode), changes the current panel -//----------------------------------------------------------------------------- -void PropertySheet::OnTextChanged(Panel *panel,const wchar_t *wszText) -{ - if ( panel == _combo ) - { - wchar_t tabText[30]; - for(int i = 0 ; i < m_PageTabs.Count() ; i++ ) - { - tabText[0] = 0; - m_PageTabs[i]->GetText(tabText,30); - if ( !wcsicmp(wszText,tabText) ) - { - ChangeActiveTab(i); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PropertySheet::OnCommand(const char *command) -{ - // propogate the close command to our parent - if (!stricmp(command, "Close") && GetVParent()) - { - CallParentFunction(new KeyValues("Command", "command", command)); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PropertySheet::OnApplyButtonEnable() -{ - // tell parent - PostActionSignal(new KeyValues("ApplyButtonEnable")); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PropertySheet::OnCurrentDefaultButtonSet( vgui::VPANEL defaultButton ) -{ - // forward the message up - if (GetVParent()) - { - KeyValues *msg = new KeyValues("CurrentDefaultButtonSet"); - msg->SetInt("button", ivgui()->PanelToHandle( defaultButton ) ); - PostMessage(GetVParent(), msg); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PropertySheet::OnDefaultButtonSet( VPANEL defaultButton ) -{ - // forward the message up - if (GetVParent()) - { - KeyValues *msg = new KeyValues("DefaultButtonSet"); - msg->SetInt("button", ivgui()->PanelToHandle( defaultButton ) ); - PostMessage(GetVParent(), msg); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void PropertySheet::OnFindDefaultButton() -{ - if (GetVParent()) - { - PostMessage(GetVParent(), new KeyValues("FindDefaultButton")); - } -} - -bool PropertySheet::PageHasContextMenu( Panel *page ) const -{ - int pageNum = FindPage( page ); - if ( pageNum == m_Pages.InvalidIndex() ) - return false; - - return m_Pages[ pageNum ].contextMenu; -} - -void PropertySheet::OnPanelDropped( CUtlVector< KeyValues * >& msglist ) -{ - if ( msglist.Count() != 1 ) - { - return; - } - - PropertySheet *sheet = IsDroppingSheet( msglist ); - if ( !sheet ) - { - // Defer to active page - if ( _activePage && _activePage->IsDropEnabled() ) - { - return _activePage->OnPanelDropped( msglist ); - } - return; - } - - KeyValues *data = msglist[ 0 ]; - - Panel *page = reinterpret_cast< Panel * >( data->GetPtr( "propertypage" ) ); - char const *title = data->GetString( "tabname", "" ); - if ( !page || !sheet ) - return; - - // Can only create if sheet was part of a ToolWindow derived object - ToolWindow *tw = dynamic_cast< ToolWindow * >( sheet->GetParent() ); - if ( tw ) - { - IToolWindowFactory *factory = tw->GetToolWindowFactory(); - if ( factory ) - { - bool showContext = sheet->PageHasContextMenu( page ); - sheet->RemovePage( page ); - if ( sheet->GetNumPages() == 0 ) - { - tw->MarkForDeletion(); - } - - AddPage( page, title, NULL, showContext ); - } - } -} - -bool PropertySheet::IsDroppable( CUtlVector< KeyValues * >& msglist ) -{ - if ( !m_bDraggableTabs ) - return false; - - if ( msglist.Count() != 1 ) - { - return false; - } - - int mx, my; - input()->GetCursorPos( mx, my ); - ScreenToLocal( mx, my ); - - int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight; - if ( my > tabHeight ) - return false; - - PropertySheet *sheet = IsDroppingSheet( msglist ); - if ( !sheet ) - { - return false; - } - - if ( sheet == this ) - return false; - - return true; -} - -// Mouse is now over a droppable panel -void PropertySheet::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ) -{ - // Convert this panel's bounds to screen space - int x, y, w, h; - - GetSize( w, h ); - - int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight; - h = tabHeight + 4; - - x = y = 0; - LocalToScreen( x, y ); - - surface()->DrawSetColor( GetDropFrameColor() ); - // Draw 2 pixel frame - surface()->DrawOutlinedRect( x, y, x + w, y + h ); - surface()->DrawOutlinedRect( x+1, y+1, x + w-1, y + h-1 ); - - if ( !IsDroppable( msglist ) ) - { - return; - } - - if ( !_showTabs ) - { - return; - } - - // Draw a fake new tab... - - x = 0; - y = 2; - w = 1; - h = tabHeight; - - int last = m_PageTabs.Count(); - if ( last != 0 ) - { - m_PageTabs[ last - 1 ]->GetBounds( x, y, w, h ); - } - - // Compute left edge of "fake" tab - - x += ( w + 1 ); - - // Compute size of new panel - KeyValues *data = msglist[ 0 ]; - char const *text = data->GetString( "tabname", "" ); - Assert( text ); - - PageTab *fakeTab = new PageTab( this, "FakeTab", text, NULL, _tabWidth, NULL, false ); - fakeTab->SetBounds( x, 4, w, tabHeight - 4 ); - fakeTab->SetFont( m_tabFont ); - SETUP_PANEL( fakeTab ); - fakeTab->Repaint(); - surface()->SolveTraverse( fakeTab->GetVPanel(), true ); - surface()->PaintTraverse( fakeTab->GetVPanel() ); - delete fakeTab; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : state - -//----------------------------------------------------------------------------- -void PropertySheet::SetKBNavigationEnabled( bool state ) -{ - m_bKBNavigationEnabled = state; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool PropertySheet::IsKBNavigationEnabled() const -{ - return m_bKBNavigationEnabled; -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vgui_controls/AnimationController.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +namespace vgui +{ + +class ContextLabel : public Label +{ + DECLARE_CLASS_SIMPLE( ContextLabel, Label ); +public: + + ContextLabel( Button *parent, char const *panelName, char const *text ): + BaseClass( (Panel *)parent, panelName, text ), + m_pTabButton( parent ) + { + SetBlockDragChaining( true ); + } + + virtual void OnMousePressed( MouseCode code ) + { + if ( m_pTabButton ) + { + m_pTabButton->FireActionSignal(); + } + } + + virtual void OnMouseReleased( MouseCode code ) + { + BaseClass::OnMouseReleased( code ); + + if ( GetParent() ) + { + GetParent()->OnCommand( "ShowContextMenu" ); + } + } + + virtual void ApplySchemeSettings( IScheme *pScheme ) + { + BaseClass::ApplySchemeSettings( pScheme ); + + HFont marlett = pScheme->GetFont( "Marlett" ); + SetFont( marlett ); + SetTextInset( 0, 0 ); + SetContentAlignment( Label::a_northwest ); + + if ( GetParent() ) + { + SetFgColor( pScheme->GetColor( "Button.TextColor", GetParent()->GetFgColor() ) ); + SetBgColor( GetParent()->GetBgColor() ); + } + } +private: + + Button *m_pTabButton; +}; + +//----------------------------------------------------------------------------- +// Purpose: Helper for drag drop +// Input : msglist - +// Output : static PropertySheet +//----------------------------------------------------------------------------- +static PropertySheet *IsDroppingSheet( CUtlVector< KeyValues * >& msglist ) +{ + if ( msglist.Count() == 0 ) + return NULL; + + KeyValues *data = msglist[ 0 ]; + PropertySheet *sheet = reinterpret_cast< PropertySheet * >( data->GetPtr( "propertysheet" ) ); + if ( sheet ) + return sheet; + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: A single tab +//----------------------------------------------------------------------------- +class PageTab : public Button +{ + DECLARE_CLASS_SIMPLE( PageTab, Button ); + +private: + bool _active; + Color _textColor; + Color _dimTextColor; + int m_bMaxTabWidth; + IBorder *m_pActiveBorder; + IBorder *m_pNormalBorder; + PropertySheet *m_pParent; + Panel *m_pPage; + ImagePanel *m_pImage; + char *m_pszImageName; + bool m_bShowContextLabel; + bool m_bAttemptingDrop; + ContextLabel *m_pContextLabel; + long m_hoverActivatePageTime; + long m_dropHoverTime; + +public: + PageTab(PropertySheet *parent, const char *panelName, const char *text, char const *imageName, int maxTabWidth, Panel *page, bool showContextButton, long hoverActivatePageTime = -1 ) : + Button( (Panel *)parent, panelName, text), + m_pParent( parent ), + m_pPage( page ), + m_pImage( 0 ), + m_pszImageName( 0 ), + m_bShowContextLabel( showContextButton ), + m_bAttemptingDrop( false ), + m_hoverActivatePageTime( hoverActivatePageTime ), + m_dropHoverTime( -1 ) + { + SetCommand(new KeyValues("TabPressed")); + _active = false; + m_bMaxTabWidth = maxTabWidth; + SetDropEnabled( true ); + SetDragEnabled( m_pParent->IsDraggableTab() ); + if ( imageName ) + { + m_pImage = new ImagePanel( this, text ); + int buflen = Q_strlen( imageName ) + 1; + m_pszImageName = new char[ buflen ]; + Q_strncpy( m_pszImageName, imageName, buflen ); + + } + SetMouseClickEnabled( MOUSE_RIGHT, true ); + m_pContextLabel = m_bShowContextLabel ? new ContextLabel( this, "Context", "9" ) : NULL; + + REGISTER_COLOR_AS_OVERRIDABLE( _textColor, "selectedcolor" ); + REGISTER_COLOR_AS_OVERRIDABLE( _dimTextColor, "unselectedcolor" ); + } + + ~PageTab() + { + delete[] m_pszImageName; + } + + virtual void Paint() + { + BaseClass::Paint(); + } + + virtual void OnCursorEntered() + { + m_dropHoverTime = system()->GetTimeMillis(); + } + + virtual void OnCursorExited() + { + m_dropHoverTime = -1; + } + + virtual void OnThink() + { + if ( m_bAttemptingDrop && m_hoverActivatePageTime >= 0 && m_dropHoverTime >= 0 ) + { + long hoverTime = system()->GetTimeMillis() - m_dropHoverTime; + if ( hoverTime > m_hoverActivatePageTime ) + { + FireActionSignal(); + SetSelected(true); + Repaint(); + } + } + m_bAttemptingDrop = false; + + BaseClass::OnThink(); + } + + virtual bool IsDroppable( CUtlVector< KeyValues * >&msglist ) + { + m_bAttemptingDrop = true; + + if ( !GetParent() ) + return false; + + PropertySheet *sheet = IsDroppingSheet( msglist ); + if ( sheet ) + return GetParent()->IsDroppable( msglist ); + + return BaseClass::IsDroppable( msglist ); + } + + virtual void OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ) + { + PropertySheet *sheet = IsDroppingSheet( msglist ); + if ( sheet ) + { + Panel *target = GetParent()->GetDropTarget( msglist ); + if ( target ) + { + // Fixme, mouse pos could be wrong... + target->OnDroppablePanelPaint( msglist, dragPanels ); + return; + } + } + + // Just highlight the tab if dropping onto active page via the tab + BaseClass::OnDroppablePanelPaint( msglist, dragPanels ); + } + + virtual void OnPanelDropped( CUtlVector< KeyValues * >& msglist ) + { + PropertySheet *sheet = IsDroppingSheet( msglist ); + if ( sheet ) + { + Panel *target = GetParent()->GetDropTarget( msglist ); + if ( target ) + { + // Fixme, mouse pos could be wrong... + target->OnPanelDropped( msglist ); + } + } + + // Defer to active page... + Panel *active = m_pParent->GetActivePage(); + if ( !active || !active->IsDroppable( msglist ) ) + return; + + active->OnPanelDropped( msglist ); + } + + virtual void OnDragFailed( CUtlVector< KeyValues * >& msglist ) + { + PropertySheet *sheet = IsDroppingSheet( msglist ); + if ( !sheet ) + return; + + // Create a new property sheet + if ( m_pParent->IsDraggableTab() ) + { + if ( msglist.Count() == 1 ) + { + KeyValues *data = msglist[ 0 ]; + int screenx = data->GetInt( "screenx" ); + int screeny = data->GetInt( "screeny" ); + + // m_pParent->ScreenToLocal( screenx, screeny ); + if ( !m_pParent->IsWithin( screenx, screeny ) ) + { + Panel *page = reinterpret_cast< Panel * >( data->GetPtr( "propertypage" ) ); + PropertySheet *sheet = reinterpret_cast< PropertySheet * >( data->GetPtr( "propertysheet" ) ); + char const *title = data->GetString( "tabname", "" ); + if ( !page || !sheet ) + return; + + // Can only create if sheet was part of a ToolWindow derived object + ToolWindow *tw = dynamic_cast< ToolWindow * >( sheet->GetParent() ); + if ( tw ) + { + IToolWindowFactory *factory = tw->GetToolWindowFactory(); + if ( factory ) + { + bool hasContextMenu = sheet->PageHasContextMenu( page ); + sheet->RemovePage( page ); + factory->InstanceToolWindow( tw->GetParent(), sheet->ShouldShowContextButtons(), page, title, hasContextMenu ); + + if ( sheet->GetNumPages() == 0 ) + { + tw->MarkForDeletion(); + } + } + } + } + } + } + } + + virtual void OnCreateDragData( KeyValues *msg ) + { + Assert( m_pParent->IsDraggableTab() ); + + msg->SetPtr( "propertypage", m_pPage ); + msg->SetPtr( "propertysheet", m_pParent ); + char sz[ 256 ]; + GetText( sz, sizeof( sz ) ); + msg->SetString( "tabname", sz ); + msg->SetString( "text", sz ); + } + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + // set up the scheme settings + Button::ApplySchemeSettings(pScheme); + + _textColor = GetSchemeColor("PropertySheet.SelectedTextColor", GetFgColor(), pScheme); + _dimTextColor = GetSchemeColor("PropertySheet.TextColor", GetFgColor(), pScheme); + m_pActiveBorder = pScheme->GetBorder("TabActiveBorder"); + m_pNormalBorder = pScheme->GetBorder("TabBorder"); + + if ( m_pImage ) + { + ClearImages(); + m_pImage->SetImage(scheme()->GetImage(m_pszImageName, false)); + AddImage( m_pImage->GetImage(), 2 ); + int w, h; + m_pImage->GetSize( w, h ); + w += m_pContextLabel ? 10 : 0; + if ( m_pContextLabel ) + { + m_pImage->SetPos( 10, 0 ); + } + SetSize( w + 4, h + 2 ); + } + else + { + int wide, tall; + int contentWide, contentTall; + GetSize(wide, tall); + GetContentSize(contentWide, contentTall); + + wide = max(m_bMaxTabWidth, contentWide + 10); // 10 = 5 pixels margin on each side + wide += m_pContextLabel ? 10 : 0; + SetSize(wide, tall); + } + + if ( m_pContextLabel ) + { + SetTextInset( 12, 0 ); + } + } + + virtual void ApplySettings( KeyValues *inResourceData ) + { + const char *pBorder = inResourceData->GetString("activeborder_override", ""); + if (*pBorder) + { + m_pActiveBorder = scheme()->GetIScheme(GetScheme())->GetBorder( pBorder ); + } + pBorder = inResourceData->GetString("normalborder_override", ""); + if (*pBorder) + { + m_pNormalBorder = scheme()->GetIScheme(GetScheme())->GetBorder( pBorder ); + } + BaseClass::ApplySettings(inResourceData); + } + + virtual void OnCommand( char const *cmd ) + { + if ( !Q_stricmp( cmd, "ShowContextMenu" ) ) + { + KeyValues *kv = new KeyValues("OpenContextMenu"); + kv->SetPtr( "page", m_pPage ); + kv->SetPtr( "contextlabel", m_pContextLabel ); + PostActionSignal( kv ); + return; + } + BaseClass::OnCommand( cmd ); + } + + IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) + { + if (_active) + { + return m_pActiveBorder; + } + return m_pNormalBorder; + } + + virtual Color GetButtonFgColor() + { + if (_active) + { + return _textColor; + } + else + { + return _dimTextColor; + } + } + + virtual void SetActive(bool state) + { + _active = state; + SetZPos( state ? 100 : 0 ); + InvalidateLayout(); + Repaint(); + } + + virtual void SetTabWidth( int iWidth ) + { + m_bMaxTabWidth = iWidth; + InvalidateLayout(); + } + + virtual bool CanBeDefaultButton(void) + { + return false; + } + + //Fire action signal when mouse is pressed down instead of on release. + virtual void OnMousePressed(MouseCode code) + { + // check for context menu open + if (!IsEnabled()) + return; + + if (!IsMouseClickEnabled(code)) + return; + + if (IsUseCaptureMouseEnabled()) + { + { + RequestFocus(); + FireActionSignal(); + SetSelected(true); + Repaint(); + } + + // lock mouse input to going to this button + input()->SetMouseCapture(GetVPanel()); + } + } + + virtual void OnMouseReleased(MouseCode code) + { + // ensure mouse capture gets released + if (IsUseCaptureMouseEnabled()) + { + input()->SetMouseCapture(NULL); + } + + // make sure the button gets unselected + SetSelected(false); + Repaint(); + + if (code == MOUSE_RIGHT) + { + KeyValues *kv = new KeyValues("OpenContextMenu"); + kv->SetPtr( "page", m_pPage ); + kv->SetPtr( "contextlabel", m_pContextLabel ); + PostActionSignal( kv ); + } + } + + virtual void PerformLayout() + { + BaseClass::PerformLayout(); + + if ( m_pContextLabel ) + { + int w, h; + GetSize( w, h ); + m_pContextLabel->SetBounds( 0, 0, 10, h ); + } + } +}; + + +}; // namespace vgui + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +PropertySheet::PropertySheet( + Panel *parent, + const char *panelName, + bool draggableTabs /*= false*/ ) : BaseClass(parent, panelName) +{ + _activePage = NULL; + _activeTab = NULL; + _tabWidth = 64; + _activeTabIndex = 0; + _showTabs = true; + _combo = NULL; + _tabFocus = false; + m_flPageTransitionEffectTime = 0.0f; + m_bSmallTabs = false; + m_tabFont = 0; + m_bDraggableTabs = draggableTabs; + m_pTabKV = NULL; + m_iTabHeight = 0; + m_iTabHeightSmall = 0; + + if ( m_bDraggableTabs ) + { + SetDropEnabled( true ); + } + + m_bKBNavigationEnabled = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor, associates pages with a combo box +//----------------------------------------------------------------------------- +PropertySheet::PropertySheet(Panel *parent, const char *panelName, ComboBox *combo) : BaseClass(parent, panelName) +{ + _activePage = NULL; + _activeTab = NULL; + _tabWidth = 64; + _activeTabIndex = 0; + _combo=combo; + _combo->AddActionSignalTarget(this); + _showTabs = false; + _tabFocus = false; + m_flPageTransitionEffectTime = 0.0f; + m_bSmallTabs = false; + m_tabFont = 0; + m_bDraggableTabs = false; + m_pTabKV = NULL; + m_iTabHeight = 0; + m_iTabHeightSmall = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +PropertySheet::~PropertySheet() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: ToolWindow uses this to drag tools from container to container by dragging the tab +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool PropertySheet::IsDraggableTab() const +{ + return m_bDraggableTabs; +} + +void PropertySheet::SetDraggableTabs( bool state ) +{ + m_bDraggableTabs = state; +} + +//----------------------------------------------------------------------------- +// Purpose: Lower profile tabs +// Input : state - +//----------------------------------------------------------------------------- +void PropertySheet::SetSmallTabs( bool state ) +{ + m_bSmallTabs = state; + m_tabFont = scheme()->GetIScheme( GetScheme() )->GetFont( m_bSmallTabs ? "DefaultVerySmall" : "Default" ); + int c = m_PageTabs.Count(); + for ( int i = 0; i < c ; ++i ) + { + PageTab *tab = m_PageTabs[ i ]; + Assert( tab ); + tab->SetFont( m_tabFont ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool PropertySheet::IsSmallTabs() const +{ + return m_bSmallTabs; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : state - +//----------------------------------------------------------------------------- +void PropertySheet::ShowContextButtons( bool state ) +{ + m_bContextButton = state; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool PropertySheet::ShouldShowContextButtons() const +{ + return m_bContextButton; +} + +int PropertySheet::FindPage( Panel *page ) const +{ + int c = m_Pages.Count(); + for ( int i = 0; i < c; ++i ) + { + if ( m_Pages[ i ].page == page ) + return i; + } + + return m_Pages.InvalidIndex(); +} + +//----------------------------------------------------------------------------- +// Purpose: adds a page to the sheet +//----------------------------------------------------------------------------- +void PropertySheet::AddPage(Panel *page, const char *title, char const *imageName /*= NULL*/, bool bHasContextMenu /*= false*/ ) +{ + if (!page) + return; + + // don't add the page if we already have it + if ( FindPage( page ) != m_Pages.InvalidIndex() ) + return; + + long hoverActivatePageTime = 250; + PageTab *tab = new PageTab(this, "tab", title, imageName, _tabWidth, page, m_bContextButton && bHasContextMenu, hoverActivatePageTime ); + if ( m_bDraggableTabs ) + { + tab->SetDragEnabled( true ); + } + + tab->SetFont( m_tabFont ); + if(_showTabs) + { + tab->AddActionSignalTarget(this); + } + else if (_combo) + { + _combo->AddItem(title, NULL); + } + + if ( m_pTabKV ) + { + tab->ApplySettings( m_pTabKV ); + } + + m_PageTabs.AddToTail(tab); + + Page_t info; + info.page = page; + info.contextMenu = m_bContextButton && bHasContextMenu; + + m_Pages.AddToTail( info ); + + page->SetParent(this); + page->AddActionSignalTarget(this); + PostMessage(page, new KeyValues("ResetData")); + + page->SetVisible(false); + InvalidateLayout(); + + if (!_activePage) + { + // first page becomes the active page + ChangeActiveTab( 0 ); + if ( _activePage ) + { + _activePage->RequestFocus( 0 ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PropertySheet::SetActivePage(Panel *page) +{ + // walk the list looking for this page + int index = FindPage( page ); + if (!m_Pages.IsValidIndex(index)) + return; + + ChangeActiveTab(index); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PropertySheet::SetTabWidth(int pixels) +{ + if ( pixels < 0 ) + { + if( !_activeTab ) + return; + + int nTall; + _activeTab->GetContentSize( pixels, nTall ); + } + + if ( _tabWidth == pixels ) + return; + + _tabWidth = pixels; + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: reloads the data in all the property page +//----------------------------------------------------------------------------- +void PropertySheet::ResetAllData() +{ + // iterate all the dialogs resetting them + for (int i = 0; i < m_Pages.Count(); i++) + { + ipanel()->SendMessage(m_Pages[i].page->GetVPanel(), new KeyValues("ResetData"), GetVPanel()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Applies any changes made by the dialog +//----------------------------------------------------------------------------- +void PropertySheet::ApplyChanges() +{ + // iterate all the dialogs resetting them + for (int i = 0; i < m_Pages.Count(); i++) + { + ipanel()->SendMessage(m_Pages[i].page->GetVPanel(), new KeyValues("ApplyChanges"), GetVPanel()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: gets a pointer to the currently active page +//----------------------------------------------------------------------------- +Panel *PropertySheet::GetActivePage() +{ + return _activePage; +} + +//----------------------------------------------------------------------------- +// Purpose: gets a pointer to the currently active tab +//----------------------------------------------------------------------------- +Panel *PropertySheet::GetActiveTab() +{ + return _activeTab; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the number of panels in the sheet +//----------------------------------------------------------------------------- +int PropertySheet::GetNumPages() +{ + return m_Pages.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the name contained in the active tab +// Input : a text buffer to contain the output +//----------------------------------------------------------------------------- +void PropertySheet::GetActiveTabTitle (char *textOut, int bufferLen ) +{ + if(_activeTab) _activeTab->GetText(textOut, bufferLen); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the name contained in the active tab +// Input : a text buffer to contain the output +//----------------------------------------------------------------------------- +bool PropertySheet::GetTabTitle( int i, char *textOut, int bufferLen ) +{ + if ( i < 0 || i >= m_PageTabs.Count() ) + { + return false; + } + + m_PageTabs[i]->GetText(textOut, bufferLen); + return true; +} + +bool PropertySheet::SetTabTitle( int i, char *pchTitle ) +{ + if ( i < 0 || i >= m_PageTabs.Count() ) + { + return false; + } + + m_PageTabs[ i ]->SetText( pchTitle ); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the index of the currently active page +//----------------------------------------------------------------------------- +int PropertySheet::GetActivePageNum() +{ + for (int i = 0; i < m_Pages.Count(); i++) + { + if (m_Pages[i].page == _activePage) + { + return i; + } + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Forwards focus requests to current active page +//----------------------------------------------------------------------------- +void PropertySheet::RequestFocus(int direction) +{ + if (direction == -1 || direction == 0) + { + if (_activePage) + { + _activePage->RequestFocus(direction); + _tabFocus = false; + } + } + else + { + if (_showTabs && _activeTab) + { + _activeTab->RequestFocus(direction); + _tabFocus = true; + } + else if (_activePage) + { + _activePage->RequestFocus(direction); + _tabFocus = false; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: moves focus back +//----------------------------------------------------------------------------- +bool PropertySheet::RequestFocusPrev(VPANEL panel) +{ + if (_tabFocus || !_showTabs || !_activeTab) + { + _tabFocus = false; + return BaseClass::RequestFocusPrev(panel); + } + else + { + if (GetVParent()) + { + PostMessage(GetVParent(), new KeyValues("FindDefaultButton")); + } + _activeTab->RequestFocus(-1); + _tabFocus = true; + return true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: moves focus forward +//----------------------------------------------------------------------------- +bool PropertySheet::RequestFocusNext(VPANEL panel) +{ + if (!_tabFocus || !_activePage) + { + return BaseClass::RequestFocusNext(panel); + } + else + { + if (!_activeTab) + { + return BaseClass::RequestFocusNext(panel); + } + else + { + _activePage->RequestFocus(1); + _tabFocus = false; + return true; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Gets scheme settings +//----------------------------------------------------------------------------- +void PropertySheet::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + // a little backwards-compatibility with old scheme files + IBorder *pBorder = pScheme->GetBorder("PropertySheetBorder"); + if (pBorder == pScheme->GetBorder("Default")) + { + // get the old name + pBorder = pScheme->GetBorder("RaisedBorder"); + } + + SetBorder(pBorder); + m_flPageTransitionEffectTime = atof(pScheme->GetResourceString("PropertySheet.TransitionEffectTime")); + + m_tabFont = pScheme->GetFont( m_bSmallTabs ? "DefaultVerySmall" : "Default" ); + + if ( m_pTabKV ) + { + for (int i = 0; i < m_PageTabs.Count(); i++) + { + m_PageTabs[i]->ApplySettings( m_pTabKV ); + } + } + + + //============================================================================= + // HPE_BEGIN: + // [tj] Here, we used to use a single size variable and overwrite it when we scaled. + // This led to problems when we changes resolutions, so now we recalcuate the absolute + // size from the relative size each time (based on proportionality) + //============================================================================= + if ( IsProportional() ) + { + m_iTabHeight = scheme()->GetProportionalScaledValueEx( GetScheme(), m_iSpecifiedTabHeight ); + m_iTabHeightSmall = scheme()->GetProportionalScaledValueEx( GetScheme(), m_iSpecifiedTabHeightSmall ); + } + else + { + m_iTabHeight = m_iSpecifiedTabHeight; + m_iTabHeightSmall = m_iSpecifiedTabHeightSmall; + } + //============================================================================= + // HPE_END + //============================================================================= +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PropertySheet::ApplySettings(KeyValues *inResourceData) +{ + BaseClass::ApplySettings(inResourceData); + + KeyValues *pTabKV = inResourceData->FindKey( "tabskv" ); + if ( pTabKV ) + { + if ( m_pTabKV ) + { + m_pTabKV->deleteThis(); + } + m_pTabKV = new KeyValues("tabkv"); + pTabKV->CopySubkeys( m_pTabKV ); + } + + KeyValues *pTabWidthKV = inResourceData->FindKey( "tabwidth" ); + if ( pTabWidthKV ) + { + _tabWidth = scheme()->GetProportionalScaledValueEx(GetScheme(), pTabWidthKV->GetInt()); + for (int i = 0; i < m_PageTabs.Count(); i++) + { + m_PageTabs[i]->SetTabWidth( _tabWidth ); + } + } + + KeyValues *pTransitionKV = inResourceData->FindKey( "transition_time" ); + if ( pTransitionKV ) + { + m_flPageTransitionEffectTime = pTransitionKV->GetFloat(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Paint our border specially, with the tabs in mind +//----------------------------------------------------------------------------- +void PropertySheet::PaintBorder() +{ + IBorder *border = GetBorder(); + if (!border) + return; + + // draw the border, but with a break at the active tab + int px = 0, py = 0, pwide = 0, ptall = 0; + if (_activeTab) + { + _activeTab->GetBounds(px, py, pwide, ptall); + ptall -= 1; + } + + // draw the border underneath the buttons, with a break + int wide, tall; + GetSize(wide, tall); + border->Paint(0, py + ptall, wide, tall, IBorder::SIDE_TOP, px + 1, px + pwide - 1); +} + +//----------------------------------------------------------------------------- +// Purpose: Lays out the dialog +//----------------------------------------------------------------------------- +void PropertySheet::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y, wide, tall; + GetBounds(x, y, wide, tall); + if (_activePage) + { + int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight; + + if(_showTabs) + { + _activePage->SetBounds(0, tabHeight, wide, tall - tabHeight); + } + else + { + _activePage->SetBounds(0, 0, wide, tall ); + } + _activePage->InvalidateLayout(); + } + + + int xtab; + int limit = m_PageTabs.Count(); + + xtab = m_iTabXIndent; + + // draw the visible tabs + if (_showTabs) + { + for (int i = 0; i < limit; i++) + { + int tabHeight = IsSmallTabs() ? (m_iTabHeightSmall-1) : (m_iTabHeight-1); + + int width, tall; + m_PageTabs[i]->GetSize(width, tall); + + if ( m_bTabFitText ) + { + m_PageTabs[i]->SizeToContents(); + width = m_PageTabs[i]->GetWide(); + + int iXInset, iYInset; + m_PageTabs[i]->GetTextInset( &iXInset, &iYInset ); + width += (iXInset * 2); + } + + if (m_PageTabs[i] == _activeTab) + { + // active tab is taller + _activeTab->SetBounds(xtab, 2, width, tabHeight); + } + else + { + m_PageTabs[i]->SetBounds(xtab, 4, width, tabHeight - 2); + } + m_PageTabs[i]->SetVisible(true); + xtab += (width + 1) + m_iTabXDelta; + } + } + else + { + for (int i = 0; i < limit; i++) + { + m_PageTabs[i]->SetVisible(false); + } + } + + // ensure draw order (page drawing over all the tabs except one) + if (_activePage) + { + _activePage->MoveToFront(); + _activePage->Repaint(); + } + if (_activeTab) + { + _activeTab->MoveToFront(); + _activeTab->Repaint(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Switches the active panel +//----------------------------------------------------------------------------- +void PropertySheet::OnTabPressed(Panel *panel) +{ + // look for the tab in the list + for (int i = 0; i < m_PageTabs.Count(); i++) + { + if (m_PageTabs[i] == panel) + { + // flip to the new tab + ChangeActiveTab(i); + return; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns the panel associated with index i +// Input : the index of the panel to return +//----------------------------------------------------------------------------- +Panel *PropertySheet::GetPage(int i) +{ + if(i<0 || i>=m_Pages.Count()) + { + return NULL; + } + + return m_Pages[i].page; +} + + +//----------------------------------------------------------------------------- +// Purpose: disables page by name +//----------------------------------------------------------------------------- +void PropertySheet::DisablePage(const char *title) +{ + SetPageEnabled(title, false); +} + +//----------------------------------------------------------------------------- +// Purpose: enables page by name +//----------------------------------------------------------------------------- +void PropertySheet::EnablePage(const char *title) +{ + SetPageEnabled(title, true); +} + +//----------------------------------------------------------------------------- +// Purpose: enabled or disables page by name +//----------------------------------------------------------------------------- +void PropertySheet::SetPageEnabled(const char *title, bool state) +{ + for (int i = 0; i < m_PageTabs.Count(); i++) + { + if (_showTabs) + { + char tmp[50]; + m_PageTabs[i]->GetText(tmp,50); + if (!strnicmp(title,tmp,strlen(tmp))) + { + m_PageTabs[i]->SetEnabled(state); + } + } + else + { + _combo->SetItemEnabled(title,state); + } + } +} + +void PropertySheet::RemoveAllPages() +{ + int c = m_Pages.Count(); + for ( int i = c - 1; i >= 0 ; --i ) + { + RemovePage( m_Pages[ i ].page ); + } +} + +void PropertySheet::DeleteAllPages() +{ + int c = m_Pages.Count(); + for ( int i = c - 1; i >= 0 ; --i ) + { + DeletePage( m_Pages[ i ].page ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: deletes the page associated with panel +// Input : *panel - the panel of the page to remove +//----------------------------------------------------------------------------- +void PropertySheet::RemovePage(Panel *panel) +{ + int location = FindPage( panel ); + if ( location == m_Pages.InvalidIndex() ) + return; + + // Since it's being deleted, don't animate!!! + m_hPreviouslyActivePage = NULL; + _activeTab = NULL; + + // ASSUMPTION = that the number of pages equals number of tabs + if( _showTabs ) + { + m_PageTabs[location]->RemoveActionSignalTarget( this ); + } + // now remove the tab + PageTab *tab = m_PageTabs[ location ]; + m_PageTabs.Remove( location ); + tab->MarkForDeletion(); + + // Remove from page list + m_Pages.Remove( location ); + + // Unparent + panel->SetParent( (Panel *)NULL ); + + if ( _activePage == panel ) + { + _activePage = NULL; + // if this page is currently active, backup to the page before this. + ChangeActiveTab( max( location - 1, 0 ) ); + } + + PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: deletes the page associated with panel +// Input : *panel - the panel of the page to remove +//----------------------------------------------------------------------------- +void PropertySheet::DeletePage(Panel *panel) +{ + Assert( panel ); + RemovePage( panel ); + panel->MarkForDeletion(); +} + +//----------------------------------------------------------------------------- +// Purpose: flips to the new tab, sending out all the right notifications +// flipping to a tab activates the tab. +//----------------------------------------------------------------------------- +void PropertySheet::ChangeActiveTab( int index ) +{ + if ( !m_Pages.IsValidIndex( index ) ) + { + _activeTab = NULL; + if ( m_Pages.Count() > 0 ) + { + _activePage = NULL; + + if ( index < 0 ) + { + ChangeActiveTab( m_Pages.Count() - 1 ); + } + else + { + ChangeActiveTab( 0 ); + } + } + return; + } + + if ( m_Pages[index].page == _activePage ) + { + if ( _activeTab ) + { + _activeTab->RequestFocus(); + } + _tabFocus = true; + return; + } + + int c = m_Pages.Count(); + for ( int i = 0; i < c; ++i ) + { + m_Pages[ i ].page->SetVisible( false ); + } + + m_hPreviouslyActivePage = _activePage; + // notify old page + if (_activePage) + { + ivgui()->PostMessage(_activePage->GetVPanel(), new KeyValues("PageHide"), GetVPanel()); + KeyValues *msg = new KeyValues("PageTabActivated"); + msg->SetPtr("panel", (Panel *)NULL); + ivgui()->PostMessage(_activePage->GetVPanel(), msg, GetVPanel()); + } + if (_activeTab) + { + //_activeTabIndex=index; + _activeTab->SetActive(false); + + // does the old tab have the focus? + _tabFocus = _activeTab->HasFocus(); + } + else + { + _tabFocus = false; + } + + // flip page + _activePage = m_Pages[index].page; + _activeTab = m_PageTabs[index]; + _activeTabIndex = index; + + _activePage->SetVisible(true); + _activePage->MoveToFront(); + + _activeTab->SetVisible(true); + _activeTab->MoveToFront(); + _activeTab->SetActive(true); + + if (_tabFocus) + { + // if a tab already has focused,give the new tab the focus + _activeTab->RequestFocus(); + } + else + { + // otherwise, give the focus to the page + _activePage->RequestFocus(); + } + + if (!_showTabs) + { + _combo->ActivateItemByRow(index); + } + + _activePage->MakeReadyForUse(); + + // transition effect + if (m_flPageTransitionEffectTime) + { + if (m_hPreviouslyActivePage.Get()) + { + // fade out the previous page + GetAnimationController()->RunAnimationCommand(m_hPreviouslyActivePage, "Alpha", 0.0f, 0.0f, m_flPageTransitionEffectTime / 2, AnimationController::INTERPOLATOR_LINEAR); + } + + // fade in the new page + _activePage->SetAlpha(0); + GetAnimationController()->RunAnimationCommand(_activePage, "Alpha", 255.0f, m_flPageTransitionEffectTime / 2, m_flPageTransitionEffectTime / 2, AnimationController::INTERPOLATOR_LINEAR); + } + else + { + if (m_hPreviouslyActivePage.Get()) + { + // no transition, just hide the previous page + m_hPreviouslyActivePage->SetVisible(false); + } + _activePage->SetAlpha( 255 ); + } + + // notify + ivgui()->PostMessage(_activePage->GetVPanel(), new KeyValues("PageShow"), GetVPanel()); + + KeyValues *msg = new KeyValues("PageTabActivated"); + msg->SetPtr("panel", (Panel *)_activeTab); + ivgui()->PostMessage(_activePage->GetVPanel(), msg, GetVPanel()); + + // tell parent + PostActionSignal(new KeyValues("PageChanged")); + + // Repaint + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the panel with the specified hotkey, from the current page +//----------------------------------------------------------------------------- +Panel *PropertySheet::HasHotkey(wchar_t key) +{ + if (!_activePage) + return NULL; + + for (int i = 0; i < _activePage->GetChildCount(); i++) + { + Panel *hot = _activePage->GetChild(i)->HasHotkey(key); + if (hot) + { + return hot; + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: catches the opencontextmenu event +//----------------------------------------------------------------------------- +void PropertySheet::OnOpenContextMenu( KeyValues *params ) +{ + // tell parent + KeyValues *kv = params->MakeCopy(); + PostActionSignal( kv ); + Panel *page = reinterpret_cast< Panel * >( params->GetPtr( "page" ) ); + if ( page ) + { + PostMessage( page->GetVPanel(), params->MakeCopy() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle key presses, through tabs. +//----------------------------------------------------------------------------- +void PropertySheet::OnKeyCodePressed(KeyCode code) +{ + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + + if ( ctrl && shift && alt && code == KEY_B ) + { + // enable build mode + EditablePanel *ep = dynamic_cast< EditablePanel * >( GetActivePage() ); + if ( ep ) + { + ep->ActivateBuildMode(); + return; + } + } + + if ( IsKBNavigationEnabled() ) + { + ButtonCode_t nButtonCode = GetBaseButtonCode( code ); + + switch ( nButtonCode ) + { + // for now left and right arrows just open or close submenus if they are there. + case KEY_RIGHT: + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + case KEY_XSTICK2_RIGHT: + { + ChangeActiveTab(_activeTabIndex+1); + break; + } + case KEY_LEFT: + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case KEY_XSTICK2_LEFT: + { + ChangeActiveTab(_activeTabIndex-1); + break; + } + default: + BaseClass::OnKeyCodePressed(code); + break; + } + } + else + { + BaseClass::OnKeyCodePressed(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called by the associated combo box (if in that mode), changes the current panel +//----------------------------------------------------------------------------- +void PropertySheet::OnTextChanged(Panel *panel,const wchar_t *wszText) +{ + if ( panel == _combo ) + { + wchar_t tabText[30]; + for(int i = 0 ; i < m_PageTabs.Count() ; i++ ) + { + tabText[0] = 0; + m_PageTabs[i]->GetText(tabText,30); + if ( !wcsicmp(wszText,tabText) ) + { + ChangeActiveTab(i); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PropertySheet::OnCommand(const char *command) +{ + // propogate the close command to our parent + if (!stricmp(command, "Close") && GetVParent()) + { + CallParentFunction(new KeyValues("Command", "command", command)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PropertySheet::OnApplyButtonEnable() +{ + // tell parent + PostActionSignal(new KeyValues("ApplyButtonEnable")); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PropertySheet::OnCurrentDefaultButtonSet( vgui::VPANEL defaultButton ) +{ + // forward the message up + if (GetVParent()) + { + KeyValues *msg = new KeyValues("CurrentDefaultButtonSet"); + msg->SetInt("button", ivgui()->PanelToHandle( defaultButton ) ); + PostMessage(GetVParent(), msg); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PropertySheet::OnDefaultButtonSet( VPANEL defaultButton ) +{ + // forward the message up + if (GetVParent()) + { + KeyValues *msg = new KeyValues("DefaultButtonSet"); + msg->SetInt("button", ivgui()->PanelToHandle( defaultButton ) ); + PostMessage(GetVParent(), msg); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PropertySheet::OnFindDefaultButton() +{ + if (GetVParent()) + { + PostMessage(GetVParent(), new KeyValues("FindDefaultButton")); + } +} + +bool PropertySheet::PageHasContextMenu( Panel *page ) const +{ + int pageNum = FindPage( page ); + if ( pageNum == m_Pages.InvalidIndex() ) + return false; + + return m_Pages[ pageNum ].contextMenu; +} + +void PropertySheet::OnPanelDropped( CUtlVector< KeyValues * >& msglist ) +{ + if ( msglist.Count() != 1 ) + { + return; + } + + PropertySheet *sheet = IsDroppingSheet( msglist ); + if ( !sheet ) + { + // Defer to active page + if ( _activePage && _activePage->IsDropEnabled() ) + { + return _activePage->OnPanelDropped( msglist ); + } + return; + } + + KeyValues *data = msglist[ 0 ]; + + Panel *page = reinterpret_cast< Panel * >( data->GetPtr( "propertypage" ) ); + char const *title = data->GetString( "tabname", "" ); + if ( !page || !sheet ) + return; + + // Can only create if sheet was part of a ToolWindow derived object + ToolWindow *tw = dynamic_cast< ToolWindow * >( sheet->GetParent() ); + if ( tw ) + { + IToolWindowFactory *factory = tw->GetToolWindowFactory(); + if ( factory ) + { + bool showContext = sheet->PageHasContextMenu( page ); + sheet->RemovePage( page ); + if ( sheet->GetNumPages() == 0 ) + { + tw->MarkForDeletion(); + } + + AddPage( page, title, NULL, showContext ); + } + } +} + +bool PropertySheet::IsDroppable( CUtlVector< KeyValues * >& msglist ) +{ + if ( !m_bDraggableTabs ) + return false; + + if ( msglist.Count() != 1 ) + { + return false; + } + + int mx, my; + input()->GetCursorPos( mx, my ); + ScreenToLocal( mx, my ); + + int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight; + if ( my > tabHeight ) + return false; + + PropertySheet *sheet = IsDroppingSheet( msglist ); + if ( !sheet ) + { + return false; + } + + if ( sheet == this ) + return false; + + return true; +} + +// Mouse is now over a droppable panel +void PropertySheet::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ) +{ + // Convert this panel's bounds to screen space + int x, y, w, h; + + GetSize( w, h ); + + int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight; + h = tabHeight + 4; + + x = y = 0; + LocalToScreen( x, y ); + + surface()->DrawSetColor( GetDropFrameColor() ); + // Draw 2 pixel frame + surface()->DrawOutlinedRect( x, y, x + w, y + h ); + surface()->DrawOutlinedRect( x+1, y+1, x + w-1, y + h-1 ); + + if ( !IsDroppable( msglist ) ) + { + return; + } + + if ( !_showTabs ) + { + return; + } + + // Draw a fake new tab... + + x = 0; + y = 2; + w = 1; + h = tabHeight; + + int last = m_PageTabs.Count(); + if ( last != 0 ) + { + m_PageTabs[ last - 1 ]->GetBounds( x, y, w, h ); + } + + // Compute left edge of "fake" tab + + x += ( w + 1 ); + + // Compute size of new panel + KeyValues *data = msglist[ 0 ]; + char const *text = data->GetString( "tabname", "" ); + Assert( text ); + + PageTab *fakeTab = new PageTab( this, "FakeTab", text, NULL, _tabWidth, NULL, false ); + fakeTab->SetBounds( x, 4, w, tabHeight - 4 ); + fakeTab->SetFont( m_tabFont ); + SETUP_PANEL( fakeTab ); + fakeTab->Repaint(); + surface()->SolveTraverse( fakeTab->GetVPanel(), true ); + surface()->PaintTraverse( fakeTab->GetVPanel() ); + delete fakeTab; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : state - +//----------------------------------------------------------------------------- +void PropertySheet::SetKBNavigationEnabled( bool state ) +{ + m_bKBNavigationEnabled = state; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool PropertySheet::IsKBNavigationEnabled() const +{ + return m_bKBNavigationEnabled; +} diff --git a/mp/src/vgui2/vgui_controls/QueryBox.cpp b/mp/src/vgui2/vgui_controls/QueryBox.cpp index 71da25e2..44226993 100644 --- a/mp/src/vgui2/vgui_controls/QueryBox.cpp +++ b/mp/src/vgui2/vgui_controls/QueryBox.cpp @@ -1,222 +1,222 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// This class is a message box that has two buttons, ok and cancel instead of -// just the ok button of a message box. We use a message box class for the ok button -// and implement another button here. -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -QueryBox::QueryBox(const char *title, const char *queryText, vgui::Panel *parent) : MessageBox(title, queryText,parent) -{ - SetDeleteSelfOnClose(true); - m_pCancelButton = new Button(this, "CancelButton", "#QueryBox_Cancel"); - m_pCancelButton->SetCommand("Cancel"); - m_pOkButton->SetCommand("OK"); - m_pCancelCommand = NULL; - m_pOkCommand = NULL; - - m_pOkButton->SetTabPosition(1); - m_pCancelButton->SetTabPosition(2); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -QueryBox::QueryBox(const wchar_t *wszTitle, const wchar_t *wszQueryText,vgui::Panel *parent) : MessageBox(wszTitle, wszQueryText,parent) -{ - SetDeleteSelfOnClose(true); - m_pCancelButton = new Button(this, "CancelButton", "#QueryBox_Cancel"); - m_pCancelButton->SetCommand("Cancel"); - m_pOkButton->SetCommand("OK"); - m_pCancelCommand = NULL; - m_pOkCommand = NULL; - - m_pOkButton->SetTabPosition(1); - m_pCancelButton->SetTabPosition(2); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -QueryBox::~QueryBox() -{ - delete m_pCancelButton; - - if ( m_pOkCommand ) - { - m_pOkCommand->deleteThis(); - } - if ( m_pCancelCommand ) - { - m_pCancelCommand->deleteThis(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Layout the window for drawing -//----------------------------------------------------------------------------- -void QueryBox::PerformLayout() -{ - BaseClass::PerformLayout(); - - int boxWidth, boxTall; - GetSize(boxWidth, boxTall); - - int x, y, wide, tall; - GetClientArea(x, y, wide, tall); - wide += x; - tall += y; - - int oldWide, oldTall; - m_pCancelButton->GetSize(oldWide, oldTall); - - int btnWide, btnTall; - m_pCancelButton->GetContentSize(btnWide, btnTall); - btnWide = max(oldWide, btnWide + 10); - btnTall = max(oldTall, btnTall + 10); - m_pCancelButton->SetSize(btnWide, btnTall); - -//nt boxWidth, boxTall; - GetSize(boxWidth, boxTall); -// wide = max(wide, btnWide * 2 + 100); -// SetSize(wide, tall); - - m_pOkButton->SetPos((wide/2)-(m_pOkButton->GetWide())-1 + x, tall - m_pOkButton->GetTall() - 15); - m_pCancelButton->SetPos((wide/2) + x+16, tall - m_pCancelButton->GetTall() - 15); - -} - -//----------------------------------------------------------------------------- -// Purpose: Handles command text from the buttons -// Deletes self when closed -//----------------------------------------------------------------------------- -void QueryBox::OnCommand(const char *command) -{ - if (!stricmp(command, "OK")) - { - OnCommand("Close"); - - if ( m_pOkCommand ) - { - PostActionSignal(m_pOkCommand->MakeCopy()); - } - } - else if (!stricmp(command, "Cancel")) - { - OnCommand("Close"); - - if (m_pCancelCommand) - { - PostActionSignal(m_pCancelCommand->MakeCopy()); - } - } - - BaseClass::OnCommand(command); - -} - -//----------------------------------------------------------------------------- -// Purpose: Set the keyvalues to send when ok button is hit -//----------------------------------------------------------------------------- -void QueryBox::SetOKCommand(KeyValues *keyValues) -{ - if ( m_pOkCommand ) - { - m_pOkCommand->deleteThis(); - } - - m_pOkCommand = keyValues; -} - -//----------------------------------------------------------------------------- -// Purpose: Set a value of the ok command -//----------------------------------------------------------------------------- -void QueryBox::SetOKCommandValue(const char *keyName, int value) -{ - if ( !m_pOkCommand ) - { - m_pOkCommand = new KeyValues("Command"); - } - - m_pOkCommand->SetInt(keyName, value); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the keyvalues to send when the cancel button is hit -//----------------------------------------------------------------------------- -void QueryBox::SetCancelCommand(KeyValues *keyValues) -{ - if ( m_pCancelCommand ) - { - m_pCancelCommand->deleteThis(); - } - - m_pCancelCommand = keyValues; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the cancel button text -//----------------------------------------------------------------------------- -void QueryBox::SetCancelButtonText(const char* buttonText) -{ - m_pCancelButton->SetText(buttonText); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the cancel button text -//----------------------------------------------------------------------------- -void QueryBox::SetCancelButtonText(const wchar_t* wszButtonText) -{ - m_pCancelButton->SetText(wszButtonText); - InvalidateLayout(); -} - -void QueryBox::OnKeyCodeTyped( KeyCode code ) -{ - if ( code == KEY_ESCAPE ) - { - OnCommand("Cancel"); - } - else - { - Frame::OnKeyCodeTyped(code); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void QueryBox::OnKeyCodePressed( KeyCode code ) -{ - if ( code == KEY_XBUTTON_B ) - { - OnCommand("Cancel"); - } - else - { - Frame::OnKeyCodePressed(code); - } -} - - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// This class is a message box that has two buttons, ok and cancel instead of +// just the ok button of a message box. We use a message box class for the ok button +// and implement another button here. +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +QueryBox::QueryBox(const char *title, const char *queryText, vgui::Panel *parent) : MessageBox(title, queryText,parent) +{ + SetDeleteSelfOnClose(true); + m_pCancelButton = new Button(this, "CancelButton", "#QueryBox_Cancel"); + m_pCancelButton->SetCommand("Cancel"); + m_pOkButton->SetCommand("OK"); + m_pCancelCommand = NULL; + m_pOkCommand = NULL; + + m_pOkButton->SetTabPosition(1); + m_pCancelButton->SetTabPosition(2); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +QueryBox::QueryBox(const wchar_t *wszTitle, const wchar_t *wszQueryText,vgui::Panel *parent) : MessageBox(wszTitle, wszQueryText,parent) +{ + SetDeleteSelfOnClose(true); + m_pCancelButton = new Button(this, "CancelButton", "#QueryBox_Cancel"); + m_pCancelButton->SetCommand("Cancel"); + m_pOkButton->SetCommand("OK"); + m_pCancelCommand = NULL; + m_pOkCommand = NULL; + + m_pOkButton->SetTabPosition(1); + m_pCancelButton->SetTabPosition(2); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +QueryBox::~QueryBox() +{ + delete m_pCancelButton; + + if ( m_pOkCommand ) + { + m_pOkCommand->deleteThis(); + } + if ( m_pCancelCommand ) + { + m_pCancelCommand->deleteThis(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Layout the window for drawing +//----------------------------------------------------------------------------- +void QueryBox::PerformLayout() +{ + BaseClass::PerformLayout(); + + int boxWidth, boxTall; + GetSize(boxWidth, boxTall); + + int x, y, wide, tall; + GetClientArea(x, y, wide, tall); + wide += x; + tall += y; + + int oldWide, oldTall; + m_pCancelButton->GetSize(oldWide, oldTall); + + int btnWide, btnTall; + m_pCancelButton->GetContentSize(btnWide, btnTall); + btnWide = max(oldWide, btnWide + 10); + btnTall = max(oldTall, btnTall + 10); + m_pCancelButton->SetSize(btnWide, btnTall); + +//nt boxWidth, boxTall; + GetSize(boxWidth, boxTall); +// wide = max(wide, btnWide * 2 + 100); +// SetSize(wide, tall); + + m_pOkButton->SetPos((wide/2)-(m_pOkButton->GetWide())-1 + x, tall - m_pOkButton->GetTall() - 15); + m_pCancelButton->SetPos((wide/2) + x+16, tall - m_pCancelButton->GetTall() - 15); + +} + +//----------------------------------------------------------------------------- +// Purpose: Handles command text from the buttons +// Deletes self when closed +//----------------------------------------------------------------------------- +void QueryBox::OnCommand(const char *command) +{ + if (!stricmp(command, "OK")) + { + OnCommand("Close"); + + if ( m_pOkCommand ) + { + PostActionSignal(m_pOkCommand->MakeCopy()); + } + } + else if (!stricmp(command, "Cancel")) + { + OnCommand("Close"); + + if (m_pCancelCommand) + { + PostActionSignal(m_pCancelCommand->MakeCopy()); + } + } + + BaseClass::OnCommand(command); + +} + +//----------------------------------------------------------------------------- +// Purpose: Set the keyvalues to send when ok button is hit +//----------------------------------------------------------------------------- +void QueryBox::SetOKCommand(KeyValues *keyValues) +{ + if ( m_pOkCommand ) + { + m_pOkCommand->deleteThis(); + } + + m_pOkCommand = keyValues; +} + +//----------------------------------------------------------------------------- +// Purpose: Set a value of the ok command +//----------------------------------------------------------------------------- +void QueryBox::SetOKCommandValue(const char *keyName, int value) +{ + if ( !m_pOkCommand ) + { + m_pOkCommand = new KeyValues("Command"); + } + + m_pOkCommand->SetInt(keyName, value); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the keyvalues to send when the cancel button is hit +//----------------------------------------------------------------------------- +void QueryBox::SetCancelCommand(KeyValues *keyValues) +{ + if ( m_pCancelCommand ) + { + m_pCancelCommand->deleteThis(); + } + + m_pCancelCommand = keyValues; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the cancel button text +//----------------------------------------------------------------------------- +void QueryBox::SetCancelButtonText(const char* buttonText) +{ + m_pCancelButton->SetText(buttonText); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the cancel button text +//----------------------------------------------------------------------------- +void QueryBox::SetCancelButtonText(const wchar_t* wszButtonText) +{ + m_pCancelButton->SetText(wszButtonText); + InvalidateLayout(); +} + +void QueryBox::OnKeyCodeTyped( KeyCode code ) +{ + if ( code == KEY_ESCAPE ) + { + OnCommand("Cancel"); + } + else + { + Frame::OnKeyCodeTyped(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void QueryBox::OnKeyCodePressed( KeyCode code ) +{ + if ( code == KEY_XBUTTON_B ) + { + OnCommand("Cancel"); + } + else + { + Frame::OnKeyCodePressed(code); + } +} + + + diff --git a/mp/src/vgui2/vgui_controls/RadioButton.cpp b/mp/src/vgui2/vgui_controls/RadioButton.cpp index f6f49034..5d0dbeef 100644 --- a/mp/src/vgui2/vgui_controls/RadioButton.cpp +++ b/mp/src/vgui2/vgui_controls/RadioButton.cpp @@ -1,419 +1,419 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -enum direction -{ - UP = -1, - DOWN = 1, -}; - -//----------------------------------------------------------------------------- -// Purpose: Check box image -//----------------------------------------------------------------------------- -void RadioImage::Paint() -{ - DrawSetTextFont(GetFont()); - - // draw background - if (_radioButton->IsEnabled()) - { - DrawSetTextColor(_bgColor); - } - else - { - DrawSetTextColor(_radioButton->GetBgColor()); - } - DrawPrintChar(0, 1, 'n'); - - // draw border circl - DrawSetTextColor(_borderColor1); - DrawPrintChar(0, 1, 'j'); - DrawSetTextColor(_borderColor2); - DrawPrintChar(0, 1, 'k'); - - // draw selected check - if (_radioButton->IsSelected()) - { - DrawSetTextColor(_checkColor); - DrawPrintChar(0, 1, 'h'); - } -} - -DECLARE_BUILD_FACTORY_DEFAULT_TEXT( RadioButton, RadioButton ); - -//----------------------------------------------------------------------------- -// Purpose: Create a radio button. -//----------------------------------------------------------------------------- -RadioButton::RadioButton(Panel *parent, const char *panelName, const char *text) : ToggleButton(parent, panelName, text) -{ - SetContentAlignment(a_west); - - // create the image - _radioBoxImage = new RadioImage(this); - - _oldTabPosition = 0; - _subTabPosition = 0; - - SetTextImageIndex(1); - SetImageAtIndex(0, _radioBoxImage, 0); - - SetButtonActivationType(ACTIVATE_ONPRESSED); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -RadioButton::~RadioButton() -{ - delete _radioBoxImage; -} - -//----------------------------------------------------------------------------- -// Purpose: Apply resource file scheme. -//----------------------------------------------------------------------------- -void RadioButton::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - _radioBoxImage->_bgColor = GetSchemeColor("CheckButton.BgColor", Color(150, 150, 150, 0), pScheme); - _radioBoxImage->_borderColor1 = GetSchemeColor("CheckButton.Border1", Color(20, 20, 20, 0), pScheme); - _radioBoxImage->_borderColor2 = GetSchemeColor("CheckButton.Border2", Color(90, 90, 90, 0), pScheme); - _radioBoxImage->_checkColor = GetSchemeColor("CheckButton.Check", Color(20, 20, 20, 0), pScheme); - - SetFgColor(GetSchemeColor("RadioButton.TextColor", pScheme)); - _selectedFgColor = GetSchemeColor("RadioButton.SelectedTextColor", GetSchemeColor("ControlText", pScheme), pScheme); - - SetDefaultColor( GetFgColor(), GetBgColor() ); - - SetArmedColor( GetSchemeColor("RadioButton.ArmedTextColor", pScheme), GetButtonArmedBgColor() ); - - SetContentAlignment(a_west); - - // reloading the scheme wipes out lists of images - _radioBoxImage->SetFont( pScheme->GetFont("Marlett", IsProportional()) ); - _radioBoxImage->ResizeImageToContent(); - SetImageAtIndex(0, _radioBoxImage, 0); - - // don't draw a background - SetPaintBackgroundEnabled(false); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the border style of the button, Radio buttons have no border -//----------------------------------------------------------------------------- -IBorder *RadioButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) -{ - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the tab position of the radio button with the set of radio buttons -// A group of RadioButtons must have the same TabPosition, with [1, n] subtabpositions -//----------------------------------------------------------------------------- -int RadioButton::GetSubTabPosition() -{ - return _subTabPosition; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the tab position of the radio button with the set of radio buttons -// A group of RadioButtons must have the same TabPosition, with [1, n] subtabpositions -//----------------------------------------------------------------------------- -void RadioButton::SetSubTabPosition(int position) -{ - _subTabPosition = position; -} - -//----------------------------------------------------------------------------- -// Purpose: Return the RadioButton's real tab position (its Panel one changes) -//----------------------------------------------------------------------------- -int RadioButton::GetRadioTabPosition() -{ - return _oldTabPosition; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the radio button checked. When a radio button is checked, a -// message is sent to all other radio buttons in the same group so -// they will become unchecked. -//----------------------------------------------------------------------------- -void RadioButton::SetSelected(bool state) -{ - InternalSetSelected( state, true ); -} - -void RadioButton::InternalSetSelected(bool state, bool bFireEvents) -{ - if (state == true) - { - if (!IsEnabled()) - return; - - // restore our tab position - SetTabPosition(_oldTabPosition); - - // Should we send notifications? - if ( bFireEvents ) - { - // send a message - KeyValues *msg = new KeyValues("RadioButtonChecked"); - msg->SetPtr("panel", this); - msg->SetInt("tabposition", _oldTabPosition); - - // send a message to all other panels on the same level as heirarchy, - // so that other radio buttons know to shut off - VPANEL radioParent = GetVParent(); - if (radioParent) - { - for (int i = 0; i < ipanel()->GetChildCount(radioParent); i++) - { - VPANEL child = ipanel()->GetChild(radioParent, i); - if (child != GetVPanel()) - { - ivgui()->PostMessage(child, msg->MakeCopy(), GetVPanel()); - } - } - } - - RequestFocus(); - PostActionSignal(msg); - } - } - else - { - // remove ourselves from the tab order - if (GetTabPosition()) - { - _oldTabPosition = GetTabPosition(); - } - SetTabPosition(0); - } - - InvalidateLayout(); - Repaint(); - - ToggleButton::SetSelected(state); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the selection state without firing any events -//----------------------------------------------------------------------------- -void RadioButton::SilentSetSelected(bool state) -{ - InternalSetSelected( state, false ); -} - -//----------------------------------------------------------------------------- -// Purpose: Set up the text color before doing normal layout -//----------------------------------------------------------------------------- -void RadioButton::PerformLayout() -{ - if (IsSelected()) - { - SetFgColor(_selectedFgColor); - } - else - { - SetFgColor(GetButtonFgColor()); - } - - BaseClass::PerformLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Apply resource settings including button state and text color. -//----------------------------------------------------------------------------- -void RadioButton::ApplySettings(KeyValues *inResourceData) -{ - ToggleButton::ApplySettings(inResourceData); - SetTextColorState(CS_NORMAL); - - _subTabPosition = inResourceData->GetInt("SubTabPosition"); - _oldTabPosition = inResourceData->GetInt("TabPosition"); - SetImageAtIndex(0, _radioBoxImage, 0); -} - -//----------------------------------------------------------------------------- -// Purpose: Get resource settings including button state, text color, and subTabPosition -//----------------------------------------------------------------------------- -void RadioButton::GetSettings(KeyValues *outResourceData) -{ - ToggleButton::GetSettings(outResourceData); - outResourceData->SetInt("SubTabPosition", _subTabPosition); - outResourceData->SetInt("TabPosition", GetRadioTabPosition()); -} - -//----------------------------------------------------------------------------- -// Purpose: Describe editing details -//----------------------------------------------------------------------------- -const char *RadioButton::GetDescription( void ) -{ - static char buf[1024]; - Q_snprintf(buf, sizeof(buf), "%s, int SubTabPosition", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: When a radio button is checked, all other radio buttons -// in the same group become unchecked. -//----------------------------------------------------------------------------- -void RadioButton::OnRadioButtonChecked(int tabPosition) -{ - // make sure we're in the same tab group - if (tabPosition != _oldTabPosition) - return; - - // wouldn't be sent to us from ourselves, so another radio button has taken over - SetSelected(false); -} - -//----------------------------------------------------------------------------- -// Purpose: Draws the selection rectangle -//----------------------------------------------------------------------------- -void RadioButton::Paint() -{ - BaseClass::Paint(); - - /* - if (HasFocus()) - { - int tx0, ty0, tx1, ty1; - _textImage->GetPos(tx0, ty0); - _textImage->GetSize(tx1, ty1); - DrawFocusBorder(tx0, ty0, tx0 + tx1, ty0 + ty1); - } - */ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void RadioButton::DoClick() -{ - SetSelected(true); -} - -//----------------------------------------------------------------------------- -// Purpose: Handle arrow key movement -//----------------------------------------------------------------------------- -void RadioButton::OnKeyCodeTyped(KeyCode code) -{ - switch (code) - { - case KEY_ENTER: - case KEY_SPACE: - { - if (!IsSelected()) - { - SetSelected(true); - } - else - { - Panel::OnKeyCodeTyped(code); - } - } - break; - - case KEY_DOWN: - case KEY_RIGHT: - { - RadioButton *bestRadio = FindBestRadioButton( DOWN ); - if (bestRadio) - { - bestRadio->SetSelected(true); - } - } - break; - case KEY_UP: - case KEY_LEFT: - { - RadioButton *bestRadio = FindBestRadioButton( UP ); - if (bestRadio) - { - bestRadio->SetSelected(true); - } - } - break; - - default: - BaseClass::OnKeyCodeTyped(code); - break; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Find the correct radio button to move to. -// Input : direction - the direction we are moving, up or down. -//----------------------------------------------------------------------------- -RadioButton *RadioButton::FindBestRadioButton(int direction) -{ - RadioButton *bestRadio = NULL; - int highestRadio = 0; - Panel *pr = GetParent(); - if (pr) - { - // find the radio button to go to next - for (int i = 0; i < pr->GetChildCount(); i++) - { - RadioButton *child = dynamic_cast(pr->GetChild(i)); - if (child && child->GetRadioTabPosition() == _oldTabPosition) - { - if (child->GetSubTabPosition() == _subTabPosition + direction) - { - bestRadio = child; - break; - } - if ( (child->GetSubTabPosition() == 0) && (direction == DOWN) ) - { - bestRadio = child; - continue; - } - else if ( (child->GetSubTabPosition() > highestRadio) && (direction == UP) ) - { - bestRadio = child; - highestRadio = bestRadio->GetSubTabPosition(); - continue; - } - if (!bestRadio) - { - bestRadio = child; - } - } - } - - if (bestRadio) - { - bestRadio->RequestFocus(); - } - - InvalidateLayout(); - Repaint(); - } - - return bestRadio; -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +enum direction +{ + UP = -1, + DOWN = 1, +}; + +//----------------------------------------------------------------------------- +// Purpose: Check box image +//----------------------------------------------------------------------------- +void RadioImage::Paint() +{ + DrawSetTextFont(GetFont()); + + // draw background + if (_radioButton->IsEnabled()) + { + DrawSetTextColor(_bgColor); + } + else + { + DrawSetTextColor(_radioButton->GetBgColor()); + } + DrawPrintChar(0, 1, 'n'); + + // draw border circl + DrawSetTextColor(_borderColor1); + DrawPrintChar(0, 1, 'j'); + DrawSetTextColor(_borderColor2); + DrawPrintChar(0, 1, 'k'); + + // draw selected check + if (_radioButton->IsSelected()) + { + DrawSetTextColor(_checkColor); + DrawPrintChar(0, 1, 'h'); + } +} + +DECLARE_BUILD_FACTORY_DEFAULT_TEXT( RadioButton, RadioButton ); + +//----------------------------------------------------------------------------- +// Purpose: Create a radio button. +//----------------------------------------------------------------------------- +RadioButton::RadioButton(Panel *parent, const char *panelName, const char *text) : ToggleButton(parent, panelName, text) +{ + SetContentAlignment(a_west); + + // create the image + _radioBoxImage = new RadioImage(this); + + _oldTabPosition = 0; + _subTabPosition = 0; + + SetTextImageIndex(1); + SetImageAtIndex(0, _radioBoxImage, 0); + + SetButtonActivationType(ACTIVATE_ONPRESSED); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +RadioButton::~RadioButton() +{ + delete _radioBoxImage; +} + +//----------------------------------------------------------------------------- +// Purpose: Apply resource file scheme. +//----------------------------------------------------------------------------- +void RadioButton::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + _radioBoxImage->_bgColor = GetSchemeColor("CheckButton.BgColor", Color(150, 150, 150, 0), pScheme); + _radioBoxImage->_borderColor1 = GetSchemeColor("CheckButton.Border1", Color(20, 20, 20, 0), pScheme); + _radioBoxImage->_borderColor2 = GetSchemeColor("CheckButton.Border2", Color(90, 90, 90, 0), pScheme); + _radioBoxImage->_checkColor = GetSchemeColor("CheckButton.Check", Color(20, 20, 20, 0), pScheme); + + SetFgColor(GetSchemeColor("RadioButton.TextColor", pScheme)); + _selectedFgColor = GetSchemeColor("RadioButton.SelectedTextColor", GetSchemeColor("ControlText", pScheme), pScheme); + + SetDefaultColor( GetFgColor(), GetBgColor() ); + + SetArmedColor( GetSchemeColor("RadioButton.ArmedTextColor", pScheme), GetButtonArmedBgColor() ); + + SetContentAlignment(a_west); + + // reloading the scheme wipes out lists of images + _radioBoxImage->SetFont( pScheme->GetFont("Marlett", IsProportional()) ); + _radioBoxImage->ResizeImageToContent(); + SetImageAtIndex(0, _radioBoxImage, 0); + + // don't draw a background + SetPaintBackgroundEnabled(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the border style of the button, Radio buttons have no border +//----------------------------------------------------------------------------- +IBorder *RadioButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) +{ + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the tab position of the radio button with the set of radio buttons +// A group of RadioButtons must have the same TabPosition, with [1, n] subtabpositions +//----------------------------------------------------------------------------- +int RadioButton::GetSubTabPosition() +{ + return _subTabPosition; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the tab position of the radio button with the set of radio buttons +// A group of RadioButtons must have the same TabPosition, with [1, n] subtabpositions +//----------------------------------------------------------------------------- +void RadioButton::SetSubTabPosition(int position) +{ + _subTabPosition = position; +} + +//----------------------------------------------------------------------------- +// Purpose: Return the RadioButton's real tab position (its Panel one changes) +//----------------------------------------------------------------------------- +int RadioButton::GetRadioTabPosition() +{ + return _oldTabPosition; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the radio button checked. When a radio button is checked, a +// message is sent to all other radio buttons in the same group so +// they will become unchecked. +//----------------------------------------------------------------------------- +void RadioButton::SetSelected(bool state) +{ + InternalSetSelected( state, true ); +} + +void RadioButton::InternalSetSelected(bool state, bool bFireEvents) +{ + if (state == true) + { + if (!IsEnabled()) + return; + + // restore our tab position + SetTabPosition(_oldTabPosition); + + // Should we send notifications? + if ( bFireEvents ) + { + // send a message + KeyValues *msg = new KeyValues("RadioButtonChecked"); + msg->SetPtr("panel", this); + msg->SetInt("tabposition", _oldTabPosition); + + // send a message to all other panels on the same level as heirarchy, + // so that other radio buttons know to shut off + VPANEL radioParent = GetVParent(); + if (radioParent) + { + for (int i = 0; i < ipanel()->GetChildCount(radioParent); i++) + { + VPANEL child = ipanel()->GetChild(radioParent, i); + if (child != GetVPanel()) + { + ivgui()->PostMessage(child, msg->MakeCopy(), GetVPanel()); + } + } + } + + RequestFocus(); + PostActionSignal(msg); + } + } + else + { + // remove ourselves from the tab order + if (GetTabPosition()) + { + _oldTabPosition = GetTabPosition(); + } + SetTabPosition(0); + } + + InvalidateLayout(); + Repaint(); + + ToggleButton::SetSelected(state); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the selection state without firing any events +//----------------------------------------------------------------------------- +void RadioButton::SilentSetSelected(bool state) +{ + InternalSetSelected( state, false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Set up the text color before doing normal layout +//----------------------------------------------------------------------------- +void RadioButton::PerformLayout() +{ + if (IsSelected()) + { + SetFgColor(_selectedFgColor); + } + else + { + SetFgColor(GetButtonFgColor()); + } + + BaseClass::PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Apply resource settings including button state and text color. +//----------------------------------------------------------------------------- +void RadioButton::ApplySettings(KeyValues *inResourceData) +{ + ToggleButton::ApplySettings(inResourceData); + SetTextColorState(CS_NORMAL); + + _subTabPosition = inResourceData->GetInt("SubTabPosition"); + _oldTabPosition = inResourceData->GetInt("TabPosition"); + SetImageAtIndex(0, _radioBoxImage, 0); +} + +//----------------------------------------------------------------------------- +// Purpose: Get resource settings including button state, text color, and subTabPosition +//----------------------------------------------------------------------------- +void RadioButton::GetSettings(KeyValues *outResourceData) +{ + ToggleButton::GetSettings(outResourceData); + outResourceData->SetInt("SubTabPosition", _subTabPosition); + outResourceData->SetInt("TabPosition", GetRadioTabPosition()); +} + +//----------------------------------------------------------------------------- +// Purpose: Describe editing details +//----------------------------------------------------------------------------- +const char *RadioButton::GetDescription( void ) +{ + static char buf[1024]; + Q_snprintf(buf, sizeof(buf), "%s, int SubTabPosition", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: When a radio button is checked, all other radio buttons +// in the same group become unchecked. +//----------------------------------------------------------------------------- +void RadioButton::OnRadioButtonChecked(int tabPosition) +{ + // make sure we're in the same tab group + if (tabPosition != _oldTabPosition) + return; + + // wouldn't be sent to us from ourselves, so another radio button has taken over + SetSelected(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Draws the selection rectangle +//----------------------------------------------------------------------------- +void RadioButton::Paint() +{ + BaseClass::Paint(); + + /* + if (HasFocus()) + { + int tx0, ty0, tx1, ty1; + _textImage->GetPos(tx0, ty0); + _textImage->GetSize(tx1, ty1); + DrawFocusBorder(tx0, ty0, tx0 + tx1, ty0 + ty1); + } + */ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void RadioButton::DoClick() +{ + SetSelected(true); +} + +//----------------------------------------------------------------------------- +// Purpose: Handle arrow key movement +//----------------------------------------------------------------------------- +void RadioButton::OnKeyCodeTyped(KeyCode code) +{ + switch (code) + { + case KEY_ENTER: + case KEY_SPACE: + { + if (!IsSelected()) + { + SetSelected(true); + } + else + { + Panel::OnKeyCodeTyped(code); + } + } + break; + + case KEY_DOWN: + case KEY_RIGHT: + { + RadioButton *bestRadio = FindBestRadioButton( DOWN ); + if (bestRadio) + { + bestRadio->SetSelected(true); + } + } + break; + case KEY_UP: + case KEY_LEFT: + { + RadioButton *bestRadio = FindBestRadioButton( UP ); + if (bestRadio) + { + bestRadio->SetSelected(true); + } + } + break; + + default: + BaseClass::OnKeyCodeTyped(code); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Find the correct radio button to move to. +// Input : direction - the direction we are moving, up or down. +//----------------------------------------------------------------------------- +RadioButton *RadioButton::FindBestRadioButton(int direction) +{ + RadioButton *bestRadio = NULL; + int highestRadio = 0; + Panel *pr = GetParent(); + if (pr) + { + // find the radio button to go to next + for (int i = 0; i < pr->GetChildCount(); i++) + { + RadioButton *child = dynamic_cast(pr->GetChild(i)); + if (child && child->GetRadioTabPosition() == _oldTabPosition) + { + if (child->GetSubTabPosition() == _subTabPosition + direction) + { + bestRadio = child; + break; + } + if ( (child->GetSubTabPosition() == 0) && (direction == DOWN) ) + { + bestRadio = child; + continue; + } + else if ( (child->GetSubTabPosition() > highestRadio) && (direction == UP) ) + { + bestRadio = child; + highestRadio = bestRadio->GetSubTabPosition(); + continue; + } + if (!bestRadio) + { + bestRadio = child; + } + } + } + + if (bestRadio) + { + bestRadio->RequestFocus(); + } + + InvalidateLayout(); + Repaint(); + } + + return bestRadio; +} diff --git a/mp/src/vgui2/vgui_controls/RichText.cpp b/mp/src/vgui2/vgui_controls/RichText.cpp index c4c76682..8d28c4ee 100644 --- a/mp/src/vgui2/vgui_controls/RichText.cpp +++ b/mp/src/vgui2/vgui_controls/RichText.cpp @@ -1,2744 +1,2744 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "vgui_controls/pch_vgui_controls.h" -#include "vgui/ILocalize.h" - -// memdbgon must be the last include file in a .cpp file -#include "tier0/memdbgon.h" - -enum -{ - MAX_BUFFER_SIZE = 999999, // maximum size of text buffer - DRAW_OFFSET_X = 3, - DRAW_OFFSET_Y = 1, -}; - -using namespace vgui; - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -namespace vgui -{ - -//#define DRAW_CLICK_PANELS - -//----------------------------------------------------------------------------- -// Purpose: Panel used for clickable URL's -//----------------------------------------------------------------------------- -class ClickPanel : public Panel -{ - DECLARE_CLASS_SIMPLE( ClickPanel, Panel ); - -public: - ClickPanel(Panel *parent) - { - _viewIndex = 0; - _textIndex = 0; - SetParent(parent); - AddActionSignalTarget(parent); - - SetCursor(dc_hand); - - SetPaintBackgroundEnabled(false); - SetPaintEnabled(false); -// SetPaintAppearanceEnabled(false); - -#if defined( DRAW_CLICK_PANELS ) - SetPaintEnabled(true); -#endif - } - - void SetTextIndex( int linkStartIndex, int viewStartIndex ) - { - _textIndex = linkStartIndex; - _viewIndex = viewStartIndex; - } - -#if defined( DRAW_CLICK_PANELS ) - virtual void Paint() - { - surface()->DrawSetColor( Color( 255, 0, 0, 255 ) ); - surface()->DrawOutlinedRect( 0, 0, GetWide(), GetTall() ); - } -#endif - - int GetTextIndex() - { - return _textIndex; - } - - int GetViewTextIndex() - { - return _viewIndex; - } - - void OnMousePressed(MouseCode code) - { - if (code == MOUSE_LEFT) - { - PostActionSignal(new KeyValues("ClickPanel", "index", _textIndex)); - } - else - { - GetParent()->OnMousePressed( code ); - } - } - -private: - int _textIndex; - int _viewIndex; -}; - - -//----------------------------------------------------------------------------- -// Purpose: Panel used only to draw the interior border region -//----------------------------------------------------------------------------- -class RichTextInterior : public Panel -{ - DECLARE_CLASS_SIMPLE( RichTextInterior, Panel ); - -public: - RichTextInterior( RichText *pParent, const char *pchName ) : BaseClass( pParent, pchName ) - { - SetKeyBoardInputEnabled( false ); - SetMouseInputEnabled( false ); - SetPaintBackgroundEnabled( false ); - SetPaintEnabled( false ); - m_pRichText = pParent; - } - -/* virtual IAppearance *GetAppearance() - { - if ( m_pRichText->IsScrollbarVisible() ) - return m_pAppearanceScrollbar; - - return BaseClass::GetAppearance(); - }*/ - - virtual void ApplySchemeSettings( IScheme *pScheme ) - { - BaseClass::ApplySchemeSettings( pScheme ); -// m_pAppearanceScrollbar = FindSchemeAppearance( pScheme, "scrollbar_visible" ); - } - -private: - RichText *m_pRichText; -// IAppearance *m_pAppearanceScrollbar; -}; - -}; // namespace vgui - -DECLARE_BUILD_FACTORY( RichText ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -RichText::RichText(Panel *parent, const char *panelName) : BaseClass(parent, panelName) -{ - m_bAllTextAlphaIsZero = false; - _font = INVALID_FONT; - m_hFontUnderline = INVALID_FONT; - - m_bRecalcLineBreaks = true; - m_pszInitialText = NULL; - _cursorPos = 0; - _mouseSelection = false; - _mouseDragSelection = false; - _vertScrollBar = new ScrollBar(this, "ScrollBar", true); - _vertScrollBar->AddActionSignalTarget(this); - _recalcSavedRenderState = true; - _maxCharCount = (64 * 1024); - AddActionSignalTarget(this); - m_pInterior = new RichTextInterior( this, NULL ); - - //a -1 for _select[0] means that the selection is empty - _select[0] = -1; - _select[1] = -1; - m_pEditMenu = NULL; - - SetCursor(dc_ibeam); - - //position the cursor so it is at the end of the text - GotoTextEnd(); - - // set default foreground color to black - _defaultTextColor = Color(0, 0, 0, 0); - - // initialize the line break array - InvalidateLineBreakStream(); - - if ( IsProportional() ) - { - int width, height; - int sw,sh; - surface()->GetProportionalBase( width, height ); - surface()->GetScreenSize(sw, sh); - - _drawOffsetX = static_cast( static_cast( DRAW_OFFSET_X )*( static_cast( sw )/ static_cast( width ))); - _drawOffsetY = static_cast( static_cast( DRAW_OFFSET_Y )*( static_cast( sw )/ static_cast( width ))); - } - else - { - _drawOffsetX = DRAW_OFFSET_X; - _drawOffsetY = DRAW_OFFSET_Y; - } - - // add a basic format string - TFormatStream stream; - stream.color = _defaultTextColor; - stream.fade.flFadeStartTime = 0.0f; - stream.fade.flFadeLength = -1.0f; - stream.pixelsIndent = 0; - stream.textStreamIndex = 0; - stream.textClickable = false; - m_FormatStream.AddToTail(stream); - - m_bResetFades = false; - m_bInteractive = true; - m_bUnusedScrollbarInvis = false; -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -RichText::~RichText() -{ - delete [] m_pszInitialText; - delete m_pEditMenu; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void RichText::SetDrawOffsets( int ofsx, int ofsy ) -{ - _drawOffsetX = ofsx; - _drawOffsetY = ofsy; -} - -//----------------------------------------------------------------------------- -// Purpose: sets it as drawing text only - used for embedded RichText control into other text drawing situations -//----------------------------------------------------------------------------- -void RichText::SetDrawTextOnly() -{ - SetDrawOffsets( 0, 0 ); - SetPaintBackgroundEnabled( false ); -// SetPaintAppearanceEnabled( false ); - SetPostChildPaintEnabled( false ); - m_pInterior->SetVisible( false ); - SetVerticalScrollbar( false ); -} - -//----------------------------------------------------------------------------- -// Purpose: configures colors -//----------------------------------------------------------------------------- -void RichText::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - _font = pScheme->GetFont("Default", IsProportional() ); - m_hFontUnderline = pScheme->GetFont("DefaultUnderline", IsProportional() ); - - SetFgColor(GetSchemeColor("RichText.TextColor", pScheme)); - SetBgColor(GetSchemeColor("RichText.BgColor", pScheme)); - - _selectionTextColor = GetSchemeColor("RichText.SelectedTextColor", GetFgColor(), pScheme); - _selectionColor = GetSchemeColor("RichText.SelectedBgColor", pScheme); - - if ( Q_strlen( pScheme->GetResourceString( "RichText.InsetX" ) ) ) - { - SetDrawOffsets( atoi( pScheme->GetResourceString( "RichText.InsetX" ) ), atoi( pScheme->GetResourceString( "RichText.InsetY" ) ) ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: if the default format color isn't set then set it -//----------------------------------------------------------------------------- -void RichText::SetFgColor( Color color ) -{ - // Replace default format color if - // the stream is empty and the color is the default ( or the previous FgColor ) - if ( m_FormatStream.Size() == 1 && - ( m_FormatStream[0].color == _defaultTextColor || m_FormatStream[0].color == GetFgColor() ) ) - { - m_FormatStream[0].color = color; - } - - BaseClass::SetFgColor( color ); -} - -//----------------------------------------------------------------------------- -// Purpose: Sends a message if the data has changed -// Turns off any selected text in the window if we are not using the edit menu -//----------------------------------------------------------------------------- -void RichText::OnKillFocus() -{ - // check if we clicked the right mouse button or if it is down - bool mouseRightClicked = input()->WasMousePressed(MOUSE_RIGHT); - bool mouseRightUp = input()->WasMouseReleased(MOUSE_RIGHT); - bool mouseRightDown = input()->IsMouseDown(MOUSE_RIGHT); - - if (mouseRightClicked || mouseRightDown || mouseRightUp ) - { - // get the start and ends of the selection area - int start, end; - if (GetSelectedRange(start, end)) // we have selected text - { - // see if we clicked in the selection area - int startX, startY; - CursorToPixelSpace(start, startX, startY); - int endX, endY; - CursorToPixelSpace(end, endX, endY); - int cursorX, cursorY; - input()->GetCursorPos(cursorX, cursorY); - ScreenToLocal(cursorX, cursorY); - - // check the area vertically - // we need to handle the horizontal edge cases eventually - int fontTall = GetLineHeight(); - endY = endY + fontTall; - if ((startY < cursorY) && (endY > cursorY)) - { - // if we clicked in the selection area, leave the text highlighted - return; - } - } - } - - // clear any selection - SelectNone(); - - // chain - BaseClass::OnKillFocus(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Wipe line breaks after the size of a panel has been changed -//----------------------------------------------------------------------------- -void RichText::OnSizeChanged( int wide, int tall ) -{ - BaseClass::OnSizeChanged( wide, tall ); - - // blow away the line breaks list - _invalidateVerticalScrollbarSlider = true; - InvalidateLineBreakStream(); - InvalidateLayout(); - - if ( _vertScrollBar->IsVisible() ) - { - _vertScrollBar->MakeReadyForUse(); - m_pInterior->SetBounds( 0, 0, wide - _vertScrollBar->GetWide(), tall ); - } - else - { - m_pInterior->SetBounds( 0, 0, wide, tall ); - } -} - - -const wchar_t *RichText::ResolveLocalizedTextAndVariables( char const *pchLookup, wchar_t *outbuf, size_t outbufsizeinbytes ) -{ - if ( pchLookup[ 0 ] == '#' ) - { - // try lookup in localization tables - StringIndex_t index = g_pVGuiLocalize->FindIndex( pchLookup + 1 ); - if ( index == INVALID_LOCALIZE_STRING_INDEX ) - { -/* // if it's not found, maybe it's a special expanded variable - look for an expansion - char rgchT[MAX_PATH]; - - // get the variables - KeyValues *variables = GetDialogVariables_R(); - if ( variables ) - { - // see if any are any special vars to put in - for ( KeyValues *pkv = variables->GetFirstSubKey(); pkv != NULL; pkv = pkv->GetNextKey() ) - { - if ( !Q_strncmp( pkv->GetName(), "$", 1 ) ) - { - // make a new lookup, with this key appended - Q_snprintf( rgchT, sizeof( rgchT ), "%s%s=%s", pchLookup, pkv->GetName(), pkv->GetString() ); - index = localize()->FindIndex( rgchT ); - break; - } - } - } - */ - } - - // see if we have a valid string - if ( index != INVALID_LOCALIZE_STRING_INDEX ) - { - wchar_t *format = g_pVGuiLocalize->GetValueByIndex( index ); - Assert( format ); - if ( format ) - { - /*// Try and substitute variables if any - KeyValues *variables = GetDialogVariables_R(); - if ( variables ) - { - localize()->ConstructString( outbuf, outbufsizeinbytes, index, variables ); - return outbuf; - }*/ - } - V_wcsncpy( outbuf, format, outbufsizeinbytes ); - return outbuf; - } - } - - Q_UTF8ToUnicode( pchLookup, outbuf, outbufsizeinbytes ); - return outbuf; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the text array -// Using this function will cause all lineBreaks to be discarded. -// This is because this fxn replaces the contents of the text buffer. -// For modifying large buffers use insert functions. -//----------------------------------------------------------------------------- -void RichText::SetText(const char *text) -{ - if (!text) - { - text = ""; - } - - wchar_t unicode[1024]; - - if (text[0] == '#') - { - ResolveLocalizedTextAndVariables( text, unicode, sizeof( unicode ) ); - SetText( unicode ); - return; - } - - // convert to unicode - Q_UTF8ToUnicode(text, unicode, sizeof(unicode)); - SetText(unicode); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void RichText::SetText(const wchar_t *text) -{ - // reset the formatting stream - m_FormatStream.RemoveAll(); - TFormatStream stream; - stream.color = GetFgColor(); - stream.fade.flFadeLength = -1.0f; - stream.fade.flFadeStartTime = 0.0f; - stream.pixelsIndent = 0; - stream.textStreamIndex = 0; - stream.textClickable = false; - m_FormatStream.AddToTail(stream); - - // set the new text stream - m_TextStream.RemoveAll(); - if ( text && *text ) - { - int textLen = wcslen(text) + 1; - m_TextStream.EnsureCapacity(textLen); - for(int i = 0; i < textLen; i++) - { - m_TextStream.AddToTail(text[i]); - } - } - GotoTextStart(); - SelectNone(); - - // blow away the line breaks list - InvalidateLineBreakStream(); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Given cursor's position in the text buffer, convert it to -// the local window's x and y pixel coordinates -// Input: cursorPos: cursor index -// Output: cx, cy, the corresponding coords in the local window -//----------------------------------------------------------------------------- -void RichText::CursorToPixelSpace(int cursorPos, int &cx, int &cy) -{ - int yStart = _drawOffsetY; - int x = _drawOffsetX, y = yStart; - _pixelsIndent = 0; - int lineBreakIndexIndex = 0; - - for (int i = GetStartDrawIndex(lineBreakIndexIndex); i < m_TextStream.Count(); i++) - { - wchar_t ch = m_TextStream[i]; - - // if we've found the position, break - if (cursorPos == i) - { - // if we've passed a line break go to that - if (m_LineBreaks[lineBreakIndexIndex] == i) - { - // add another line - AddAnotherLine(x, y); - lineBreakIndexIndex++; - } - break; - } - - // if we've passed a line break go to that - if (m_LineBreaks[lineBreakIndexIndex] == i) - { - // add another line - AddAnotherLine(x, y); - lineBreakIndexIndex++; - } - - // add to the current position - x += surface()->GetCharacterWidth(_font, ch); - } - - cx = x; - cy = y; -} - -//----------------------------------------------------------------------------- -// Purpose: Converts local pixel coordinates to an index in the text buffer -//----------------------------------------------------------------------------- -int RichText::PixelToCursorSpace(int cx, int cy) -{ - int fontTall = GetLineHeight(); - - // where to start reading - int yStart = _drawOffsetY; - int x = _drawOffsetX, y = yStart; - _pixelsIndent = 0; - int lineBreakIndexIndex = 0; - - int startIndex = GetStartDrawIndex(lineBreakIndexIndex); - if (_recalcSavedRenderState) - { - RecalculateDefaultState(startIndex); - } - - _pixelsIndent = m_CachedRenderState.pixelsIndent; - _currentTextClickable = m_CachedRenderState.textClickable; - TRenderState renderState = m_CachedRenderState; - - bool onRightLine = false; - int i; - for (i = startIndex; i < m_TextStream.Count(); i++) - { - wchar_t ch = m_TextStream[i]; - - renderState.x = x; - if ( UpdateRenderState( i, renderState ) ) - { - x = renderState.x; - } - - // if we are on the right line but off the end of if put the cursor at the end of the line - if (m_LineBreaks[lineBreakIndexIndex] == i) - { - // add another line - AddAnotherLine(x, y); - lineBreakIndexIndex++; - - if (onRightLine) - break; - } - - // check to see if we're on the right line - if (cy < yStart) - { - // cursor is above panel - onRightLine = true; - } - else if (cy >= y && (cy < (y + fontTall + _drawOffsetY))) - { - onRightLine = true; - } - - int wide = surface()->GetCharacterWidth(_font, ch); - - // if we've found the position, break - if (onRightLine) - { - if (cx > GetWide()) // off right side of window - { - } - else if (cx < (_drawOffsetX + renderState.pixelsIndent) || cy < yStart) // off left side of window - { - // Msg( "PixelToCursorSpace() off left size, returning %d '%c'\n", i, m_TextStream[i] ); - return i; // move cursor one to left - } - - if (cx >= x && cx < (x + wide)) - { - // check which side of the letter they're on - if (cx < (x + (wide * 0.5))) // left side - { - // Msg( "PixelToCursorSpace() on the left size, returning %d '%c'\n", i, m_TextStream[i] ); - return i; - } - else // right side - { - // Msg( "PixelToCursorSpace() on the right size, returning %d '%c'\n", i + 1, m_TextStream[i + 1] ); - return i + 1; - } - } - } - x += wide; - } - - // Msg( "PixelToCursorSpace() never hit, returning %d\n", i ); - return i; -} - -//----------------------------------------------------------------------------- -// Purpose: Draws a string of characters in the panel -// Input: iFirst - Index of the first character to draw -// iLast - Index of the last character to draw -// renderState - Render state to use -// font- font to use -// Output: returns the width of the character drawn -//----------------------------------------------------------------------------- -int RichText::DrawString(int iFirst, int iLast, TRenderState &renderState, HFont font) -{ -// VPROF( "RichText::DrawString" ); - - // Calculate the render size - int fontTall = surface()->GetFontTall(font); - // BUGBUG John: This won't exactly match the rendered size - int charWide = 0; - for ( int i = iFirst; i <= iLast; i++ ) - { - wchar_t ch = m_TextStream[i]; -#if USE_GETKERNEDCHARWIDTH - wchar_t chBefore = 0; - wchar_t chAfter = 0; - if ( i > 0 ) - chBefore = m_TextStream[i-1]; - if ( i < iLast ) - chAfter = m_TextStream[i+1]; - float flWide = 0.0f, flabcA = 0.0f; - surface()->GetKernedCharWidth(font, ch, chBefore, chAfter, flWide, flabcA); - if ( ch == L' ' ) - flWide = ceil( flWide ); - charWide += floor( flWide + 0.6 ); -#else - charWide += surface()->GetCharacterWidth(font, ch); -#endif - } - - // draw selection, if any - int selection0 = -1, selection1 = -1; - GetSelectedRange(selection0, selection1); - - if (iFirst >= selection0 && iFirst < selection1) - { - // draw background selection color - surface()->DrawSetColor(_selectionColor); - surface()->DrawFilledRect(renderState.x, renderState.y, renderState.x + charWide, renderState.y + 1 + fontTall); - - // reset text color - surface()->DrawSetTextColor(_selectionTextColor); - m_bAllTextAlphaIsZero = false; - } - else - { - surface()->DrawSetTextColor(renderState.textColor); - } - - if ( renderState.textColor.a() != 0 ) - { - m_bAllTextAlphaIsZero = false; - surface()->DrawSetTextPos(renderState.x, renderState.y); - surface()->DrawPrintText(&m_TextStream[iFirst], iLast - iFirst + 1); - } - - return charWide; -} - -//----------------------------------------------------------------------------- -// Purpose: Finish drawing url -//----------------------------------------------------------------------------- -void RichText::FinishingURL(int x, int y) -{ - // finishing URL - if ( _clickableTextPanels.IsValidIndex( _clickableTextIndex ) ) - { - ClickPanel *clickPanel = _clickableTextPanels[ _clickableTextIndex ]; - int px, py; - clickPanel->GetPos(px, py); - int fontTall = GetLineHeight(); - clickPanel->SetSize( MAX( x - px, 6 ), y - py + fontTall ); - clickPanel->SetVisible(true); - - // if we haven't actually advanced any, step back and ignore this one - // this is probably a data input problem though, need to find root cause - if ( x - px <= 0 ) - { - --_clickableTextIndex; - clickPanel->SetVisible(false); - } - } -} - -void RichText::CalculateFade( TRenderState &renderState ) -{ - if ( m_FormatStream.IsValidIndex( renderState.formatStreamIndex ) ) - { - if ( m_bResetFades == false ) - { - if ( m_FormatStream[renderState.formatStreamIndex].fade.flFadeLength != -1.0f ) - { - float frac = ( m_FormatStream[renderState.formatStreamIndex].fade.flFadeStartTime - system()->GetCurrentTime() ) / m_FormatStream[renderState.formatStreamIndex].fade.flFadeLength; - - int alpha = frac * m_FormatStream[renderState.formatStreamIndex].fade.iOriginalAlpha; - alpha = clamp( alpha, 0, m_FormatStream[renderState.formatStreamIndex].fade.iOriginalAlpha ); - - renderState.textColor.SetColor( renderState.textColor.r(), renderState.textColor.g(), renderState.textColor.b(), alpha ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Draws the text in the panel -//----------------------------------------------------------------------------- -void RichText::Paint() -{ - // Assume the worst - m_bAllTextAlphaIsZero = true; - - HFont hFontCurrent = _font; - - // hide all the clickable panels until we know where they are to reside - for (int j = 0; j < _clickableTextPanels.Count(); j++) - { - _clickableTextPanels[j]->SetVisible(false); - } - - if ( !HasText() ) - return; - - int wide, tall; - GetSize( wide, tall ); - - int lineBreakIndexIndex = 0; - int startIndex = GetStartDrawIndex(lineBreakIndexIndex); - _currentTextClickable = false; - - _clickableTextIndex = GetClickableTextIndexStart(startIndex); - - // recalculate and cache the render state at the render start - if (_recalcSavedRenderState) - { - RecalculateDefaultState(startIndex); - } - // copy off the cached render state - TRenderState renderState = m_CachedRenderState; - - _pixelsIndent = m_CachedRenderState.pixelsIndent; - _currentTextClickable = m_CachedRenderState.textClickable; - - renderState.textClickable = _currentTextClickable; - renderState.textColor = m_FormatStream[renderState.formatStreamIndex].color; - CalculateFade( renderState ); - - renderState.formatStreamIndex++; - - if ( _currentTextClickable ) - { - _clickableTextIndex = startIndex; - } - - // where to start drawing - renderState.x = _drawOffsetX + _pixelsIndent; - renderState.y = _drawOffsetY; - - // draw the text - int selection0 = -1, selection1 = -1; - GetSelectedRange(selection0, selection1); - - surface()->DrawSetTextFont( hFontCurrent ); - - for (int i = startIndex; i < m_TextStream.Count() && renderState.y < tall; ) - { - // 1. - // Update our current render state based on the formatting and color streams, - // this has to happen if it's our very first iteration, or if we are actually changing - // state. - int nXBeforeStateChange = renderState.x; - if ( UpdateRenderState(i, renderState) || i == startIndex ) - { - // check for url state change - if (renderState.textClickable != _currentTextClickable) - { - if (renderState.textClickable) - { - // entering new URL - _clickableTextIndex++; - hFontCurrent = m_hFontUnderline; - surface()->DrawSetTextFont( hFontCurrent ); - - // set up the panel - ClickPanel *clickPanel = _clickableTextPanels.IsValidIndex( _clickableTextIndex ) ? _clickableTextPanels[_clickableTextIndex] : NULL; - - if (clickPanel) - { - clickPanel->SetPos(renderState.x, renderState.y); - } - } - else - { - FinishingURL(nXBeforeStateChange, renderState.y); - hFontCurrent = _font; - surface()->DrawSetTextFont( hFontCurrent ); - } - _currentTextClickable = renderState.textClickable; - } - } - - // 2. - // if we've passed a line break go to that - if ( m_LineBreaks.IsValidIndex( lineBreakIndexIndex ) && m_LineBreaks[lineBreakIndexIndex] <= i ) - { - if (_currentTextClickable) - { - FinishingURL(renderState.x, renderState.y); - } - - // add another line - AddAnotherLine(renderState.x, renderState.y); - lineBreakIndexIndex++; - - // Skip white space unless the previous line ended from the hard carriage return - if ( i && ( m_TextStream[i-1] != '\n' ) && ( m_TextStream[i-1] != '\r') ) - { - while ( m_TextStream[i] == L' ' ) - { - if ( i+1 < m_TextStream.Count() ) - ++i; - else - break; - } - } - - if (renderState.textClickable) - { - // move to the next URL - _clickableTextIndex++; - ClickPanel *clickPanel = _clickableTextPanels.IsValidIndex( _clickableTextIndex ) ? _clickableTextPanels[_clickableTextIndex] : NULL; - if (clickPanel) - { - clickPanel->SetPos(renderState.x, renderState.y); - } - } - } - - // 3. - // Calculate the range of text to draw all at once - int iLim = m_TextStream.Count(); - - // Stop at the next format change - if ( m_FormatStream.IsValidIndex(renderState.formatStreamIndex) && - m_FormatStream[renderState.formatStreamIndex].textStreamIndex < iLim && - m_FormatStream[renderState.formatStreamIndex].textStreamIndex >= i && - m_FormatStream[renderState.formatStreamIndex].textStreamIndex ) - { - iLim = m_FormatStream[renderState.formatStreamIndex].textStreamIndex; - } - - // Stop at the next line break - if ( m_LineBreaks.IsValidIndex( lineBreakIndexIndex ) && m_LineBreaks[lineBreakIndexIndex] < iLim ) - iLim = m_LineBreaks[lineBreakIndexIndex]; - - // Handle non-drawing characters specially - for ( int iT = i; iT < iLim; iT++ ) - { - if ( iswcntrl(m_TextStream[iT]) ) - { - iLim = iT; - break; - } - } - - // 4. - // Draw the current text range - if ( iLim <= i ) - { - if ( m_TextStream[i] == '\t' ) - { - int dxTabWidth = 8 * surface()->GetCharacterWidth(hFontCurrent, ' '); - dxTabWidth = MAX( 1, dxTabWidth ); - - renderState.x = ( dxTabWidth * ( 1 + ( renderState.x / dxTabWidth ) ) ); - } - i++; - } - else - { - renderState.x += DrawString(i, iLim - 1, renderState, hFontCurrent ); - i = iLim; - } - } - - if (renderState.textClickable) - { - FinishingURL(renderState.x, renderState.y); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int RichText::GetClickableTextIndexStart(int startIndex) -{ - // cycle to the right url panel for what is visible after the startIndex. - for (int i = 0; i < _clickableTextPanels.Count(); i++) - { - if (_clickableTextPanels[i]->GetViewTextIndex() >= startIndex) - { - return i - 1; - } - } - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: Recalcultes the formatting state from the specified index -//----------------------------------------------------------------------------- -void RichText::RecalculateDefaultState(int startIndex) -{ - if (!HasText() ) - return; - - Assert(startIndex < m_TextStream.Count()); - - m_CachedRenderState.textColor = GetFgColor(); - _pixelsIndent = 0; - _currentTextClickable = false; - _clickableTextIndex = GetClickableTextIndexStart(startIndex); - - // find where in the formatting stream we need to be - GenerateRenderStateForTextStreamIndex(startIndex, m_CachedRenderState); - _recalcSavedRenderState = false; -} - -//----------------------------------------------------------------------------- -// Purpose: updates a render state based on the formatting and color streams -// Output: true if we changed the render state -//----------------------------------------------------------------------------- -bool RichText::UpdateRenderState(int textStreamPos, TRenderState &renderState) -{ - // check the color stream - if (m_FormatStream.IsValidIndex(renderState.formatStreamIndex) && - m_FormatStream[renderState.formatStreamIndex].textStreamIndex == textStreamPos) - { - // set the current formatting - renderState.textColor = m_FormatStream[renderState.formatStreamIndex].color; - renderState.textClickable = m_FormatStream[renderState.formatStreamIndex].textClickable; - - CalculateFade( renderState ); - - int indentChange = m_FormatStream[renderState.formatStreamIndex].pixelsIndent - renderState.pixelsIndent; - renderState.pixelsIndent = m_FormatStream[renderState.formatStreamIndex].pixelsIndent; - - if (indentChange) - { - renderState.x = renderState.pixelsIndent + _drawOffsetX; - } - - //!! for supporting old functionality, store off state in globals - _pixelsIndent = renderState.pixelsIndent; - - // move to the next position in the color stream - renderState.formatStreamIndex++; - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the index in the format stream for the specified text stream index -//----------------------------------------------------------------------------- -int RichText::FindFormatStreamIndexForTextStreamPos(int textStreamIndex) -{ - int formatStreamIndex = 0; - for (; m_FormatStream.IsValidIndex(formatStreamIndex); formatStreamIndex++) - { - if (m_FormatStream[formatStreamIndex].textStreamIndex > textStreamIndex) - break; - } - - // step back to the color change before the new line - formatStreamIndex--; - if (!m_FormatStream.IsValidIndex(formatStreamIndex)) - { - formatStreamIndex = 0; - } - return formatStreamIndex; -} - -//----------------------------------------------------------------------------- -// Purpose: Generates a base renderstate given a index into the text stream -//----------------------------------------------------------------------------- -void RichText::GenerateRenderStateForTextStreamIndex(int textStreamIndex, TRenderState &renderState) -{ - // find where in the format stream we need to be given the specified place in the text stream - renderState.formatStreamIndex = FindFormatStreamIndexForTextStreamPos(textStreamIndex); - - // copy the state data - renderState.textColor = m_FormatStream[renderState.formatStreamIndex].color; - renderState.pixelsIndent = m_FormatStream[renderState.formatStreamIndex].pixelsIndent; - renderState.textClickable = m_FormatStream[renderState.formatStreamIndex].textClickable; -} - -//----------------------------------------------------------------------------- -// Purpose: Called pre render -//----------------------------------------------------------------------------- -void RichText::OnThink() -{ - if (m_bRecalcLineBreaks) - { - _recalcSavedRenderState = true; - RecalculateLineBreaks(); - - // recalculate scrollbar position - if (_invalidateVerticalScrollbarSlider) - { - LayoutVerticalScrollBarSlider(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Called when data changes or panel size changes -//----------------------------------------------------------------------------- -void RichText::PerformLayout() -{ - BaseClass::PerformLayout(); - - // force a Repaint - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: inserts a color change into the formatting stream -//----------------------------------------------------------------------------- -void RichText::InsertColorChange(Color col) -{ - // see if color already exists in text stream - TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1]; - if (prevItem.color == col) - { - // inserting same color into stream, just ignore - } - else if (prevItem.textStreamIndex == m_TextStream.Count()) - { - // this item is in the same place; update values - prevItem.color = col; - } - else - { - // add to text stream, based off existing item - TFormatStream streamItem = prevItem; - streamItem.color = col; - streamItem.textStreamIndex = m_TextStream.Count(); - m_FormatStream.AddToTail(streamItem); - } -} - -//----------------------------------------------------------------------------- -// Purpose: inserts a fade into the formatting stream -//----------------------------------------------------------------------------- -void RichText::InsertFade( float flSustain, float flLength ) -{ - // see if color already exists in text stream - TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1]; - if (prevItem.textStreamIndex == m_TextStream.Count()) - { - // this item is in the same place; update values - prevItem.fade.flFadeStartTime = system()->GetCurrentTime() + flSustain; - prevItem.fade.flFadeSustain = flSustain; - prevItem.fade.flFadeLength = flLength; - prevItem.fade.iOriginalAlpha = prevItem.color.a(); - } - else - { - // add to text stream, based off existing item - TFormatStream streamItem = prevItem; - - prevItem.fade.flFadeStartTime = system()->GetCurrentTime() + flSustain; - prevItem.fade.flFadeLength = flLength; - prevItem.fade.flFadeSustain = flSustain; - prevItem.fade.iOriginalAlpha = prevItem.color.a(); - - streamItem.textStreamIndex = m_TextStream.Count(); - m_FormatStream.AddToTail(streamItem); - } -} - -void RichText::ResetAllFades( bool bHold, bool bOnlyExpired, float flNewSustain ) -{ - m_bResetFades = bHold; - - if ( m_bResetFades == false ) - { - for (int i = 1; i < m_FormatStream.Count(); i++) - { - if ( bOnlyExpired == true ) - { - if ( m_FormatStream[i].fade.flFadeStartTime >= system()->GetCurrentTime() ) - continue; - } - - if ( flNewSustain == -1.0f ) - { - flNewSustain = m_FormatStream[i].fade.flFadeSustain; - } - - m_FormatStream[i].fade.flFadeStartTime = system()->GetCurrentTime() + flNewSustain; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: inserts an indent change into the formatting stream -//----------------------------------------------------------------------------- -void RichText::InsertIndentChange(int pixelsIndent) -{ - if (pixelsIndent < 0) - { - pixelsIndent = 0; - } - else if (pixelsIndent > 255) - { - pixelsIndent = 255; - } - - // see if indent change already exists in text stream - TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1]; - if (prevItem.pixelsIndent == pixelsIndent) - { - // inserting same indent into stream, just ignore - } - else if (prevItem.textStreamIndex == m_TextStream.Count()) - { - // this item is in the same place; update - prevItem.pixelsIndent = pixelsIndent; - } - else - { - // add to text stream, based off existing item - TFormatStream streamItem = prevItem; - streamItem.pixelsIndent = pixelsIndent; - streamItem.textStreamIndex = m_TextStream.Count(); - m_FormatStream.AddToTail(streamItem); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Inserts character Start for clickable text, eg. URLS -//----------------------------------------------------------------------------- -void RichText::InsertClickableTextStart( const char *pchClickAction ) -{ - // see if indent change already exists in text stream - TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1]; - TFormatStream *pFormatStream = &prevItem; - if (prevItem.textStreamIndex == m_TextStream.Count()) - { - // this item is in the same place; update - prevItem.textClickable = true; - pFormatStream->m_sClickableTextAction = pchClickAction; - } - else - { - // add to text stream, based off existing item - TFormatStream formatStreamCopy = prevItem; - int iFormatStream = m_FormatStream.AddToTail( formatStreamCopy ); - - // set the new params - pFormatStream = &m_FormatStream[iFormatStream]; - pFormatStream->textStreamIndex = m_TextStream.Count(); - pFormatStream->textClickable = true; - pFormatStream->m_sClickableTextAction = pchClickAction; - } - - // invalidate the layout to recalculate where the click panels should go - InvalidateLineBreakStream(); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Inserts character end for clickable text, eg. URLS -//----------------------------------------------------------------------------- -void RichText::InsertClickableTextEnd() -{ - // see if indent change already exists in text stream - TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1]; - if (!prevItem.textClickable) - { - // inserting same indent into stream, just ignore - } - else if (prevItem.textStreamIndex == m_TextStream.Count()) - { - // this item is in the same place; update - prevItem.textClickable = false; - } - else - { - // add to text stream, based off existing item - TFormatStream streamItem = prevItem; - streamItem.textClickable = false; - streamItem.textStreamIndex = m_TextStream.Count(); - m_FormatStream.AddToTail(streamItem); - } -} - -//----------------------------------------------------------------------------- -// Purpose: moves x,y to the Start of the next line of text -//----------------------------------------------------------------------------- -void RichText::AddAnotherLine(int &cx, int &cy) -{ - cx = _drawOffsetX + _pixelsIndent; - cy += (GetLineHeight() + _drawOffsetY); -} - -//----------------------------------------------------------------------------- -// Purpose: Recalculates line breaks -//----------------------------------------------------------------------------- -void RichText::RecalculateLineBreaks() -{ - if ( !m_bRecalcLineBreaks ) - return; - - int wide = GetWide(); - if (!wide) - return; - - wide -= _drawOffsetX; - - m_bRecalcLineBreaks = false; - _recalcSavedRenderState = true; - if (!HasText()) - return; - - int selection0 = -1, selection1 = -1; - - // subtract the scrollbar width - if (_vertScrollBar->IsVisible()) - { - wide -= _vertScrollBar->GetWide(); - } - - int x = _drawOffsetX, y = _drawOffsetY; - - HFont fontWordStart = INVALID_FONT; - int wordStartIndex = 0; - int lineStartIndex = 0; - bool hasWord = false; - bool justStartedNewLine = true; - bool wordStartedOnNewLine = true; - - int startChar = 0; - if (_recalculateBreaksIndex <= 0) - { - m_LineBreaks.RemoveAll(); - } - else - { - // remove the rest of the linebreaks list since its out of date. - for (int i = _recalculateBreaksIndex + 1; i < m_LineBreaks.Count(); ++i) - { - m_LineBreaks.Remove(i); - --i; // removing shrinks the list! - } - startChar = m_LineBreaks[_recalculateBreaksIndex]; - lineStartIndex = m_LineBreaks[_recalculateBreaksIndex]; - wordStartIndex = lineStartIndex; - } - - // handle the case where this char is a new line, in that case - // we have already taken its break index into account above so skip it. - if (m_TextStream[startChar] == '\r' || m_TextStream[startChar] == '\n') - { - startChar++; - lineStartIndex = startChar; - } - - // cycle to the right url panel for what is visible after the startIndex. - int clickableTextNum = GetClickableTextIndexStart(startChar); - clickableTextNum++; - - // initialize the renderstate with the start - TRenderState renderState; - GenerateRenderStateForTextStreamIndex(startChar, renderState); - _currentTextClickable = false; - - HFont font = _font; - - bool bForceBreak = false; - float flLineWidthSoFar = 0; - - // loop through all the characters - for (int i = startChar; i < m_TextStream.Count(); ++i) - { - wchar_t ch = m_TextStream[i]; - renderState.x = x; - if (UpdateRenderState(i, renderState)) - { - x = renderState.x; - int preI = i; - - // check for clickable text - if (renderState.textClickable != _currentTextClickable) - { - if (renderState.textClickable) - { - // make a new clickable text panel - if (clickableTextNum >= _clickableTextPanels.Count()) - { - _clickableTextPanels.AddToTail(new ClickPanel(this)); - } - - ClickPanel *clickPanel = _clickableTextPanels[clickableTextNum++]; - clickPanel->SetTextIndex(preI, preI); - } - - // url state change - _currentTextClickable = renderState.textClickable; - } - } - - bool bIsWSpace = iswspace( ch ) ? true : false; - - bool bPreviousWordStartedOnNewLine = wordStartedOnNewLine; - int iPreviousWordStartIndex = wordStartIndex; - if ( !bIsWSpace && ch != L'\t' && ch != L'\n' && ch != L'\r' ) - { - if (!hasWord) - { - // Start a new word - wordStartIndex = i; - hasWord = true; - wordStartedOnNewLine = justStartedNewLine; - fontWordStart = font; - } - // else append to the current word - } - else - { - // whitespace/punctuation character - // end the word - hasWord = false; - } - - float w = 0; - wchar_t wchBefore = 0; - wchar_t wchAfter = 0; - - if ( i > 0 && i > lineStartIndex && i != selection0 && i-1 != selection1 ) - wchBefore = m_TextStream[i-1]; - if ( i < m_TextStream.Count() - 1 && i+1 != selection0 && i != selection1 ) - wchAfter = m_TextStream[i+1]; - - float flabcA; - surface()->GetKernedCharWidth( font, ch, wchBefore, wchAfter, w, flabcA ); - flLineWidthSoFar += w; - - // See if we've exceeded the width we have available, with - if ( floor(flLineWidthSoFar + 0.6) + x > wide ) - { - bForceBreak = true; - } - - if (!iswcntrl(ch)) - { - justStartedNewLine = false; - } - - if ( bForceBreak || ch == '\r' || ch == '\n' ) - { - bForceBreak = false; - // add another line - AddAnotherLine(x, y); - - if ( ch == '\r' || ch == '\n' ) - { - // skip the newline so it's not at the beginning of the new line - lineStartIndex = i + 1; - m_LineBreaks.AddToTail(i + 1); - } - else if ( bPreviousWordStartedOnNewLine || iPreviousWordStartIndex <= lineStartIndex ) - { - lineStartIndex = i; - m_LineBreaks.AddToTail( i ); - - if (renderState.textClickable) - { - // need to split the url into two panels - int oldIndex = _clickableTextPanels[clickableTextNum - 1]->GetTextIndex(); - - // make a new clickable text panel - if (clickableTextNum >= _clickableTextPanels.Count()) - { - _clickableTextPanels.AddToTail(new ClickPanel(this)); - } - - ClickPanel *clickPanel = _clickableTextPanels[clickableTextNum++]; - clickPanel->SetTextIndex(oldIndex, i); - } - } - else - { - m_LineBreaks.AddToTail( iPreviousWordStartIndex ); - lineStartIndex = iPreviousWordStartIndex; - i = iPreviousWordStartIndex; - - TRenderState renderStateAtLastWord; - GenerateRenderStateForTextStreamIndex( i, renderStateAtLastWord ); - - // If the word is clickable, and that started prior to the beginning of the word, then we must split the click panel - if ( renderStateAtLastWord.textClickable && m_FormatStream[ renderStateAtLastWord.formatStreamIndex ].textStreamIndex < i ) - { - // need to split the url into two panels - int oldIndex = _clickableTextPanels[clickableTextNum - 1]->GetTextIndex(); - - // make a new clickable text panel - if (clickableTextNum >= _clickableTextPanels.Count()) - { - _clickableTextPanels.AddToTail(new ClickPanel(this)); - } - - ClickPanel *clickPanel = _clickableTextPanels[clickableTextNum++]; - clickPanel->SetTextIndex(oldIndex, i); - } - } - - flLineWidthSoFar = 0; - justStartedNewLine = true; - hasWord = false; - wordStartedOnNewLine = false; - _currentTextClickable = false; - continue; - } - } - - // end the list - m_LineBreaks.AddToTail(MAX_BUFFER_SIZE); - - // set up the scrollbar - _invalidateVerticalScrollbarSlider = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Recalculate where the vertical scroll bar slider should be -// based on the current cursor line we are on. -//----------------------------------------------------------------------------- -void RichText::LayoutVerticalScrollBarSlider() -{ - _invalidateVerticalScrollbarSlider = false; - - // set up the scrollbar - //if (!_vertScrollBar->IsVisible()) - // return; - - - // see where the scrollbar currently is - int previousValue = _vertScrollBar->GetValue(); - bool bCurrentlyAtEnd = false; - int rmin, rmax; - _vertScrollBar->GetRange(rmin, rmax); - if (rmax && (previousValue + rmin + _vertScrollBar->GetRangeWindow() == rmax)) - { - bCurrentlyAtEnd = true; - } - - // work out position to put scrollbar, factoring in insets - int wide, tall; - GetSize( wide, tall ); - - _vertScrollBar->SetPos( wide - _vertScrollBar->GetWide(), 0 ); - // scrollbar is inside the borders. - _vertScrollBar->SetSize( _vertScrollBar->GetWide(), tall ); - - // calculate how many lines we can fully display - int displayLines = tall / (GetLineHeight() + _drawOffsetY); - int numLines = m_LineBreaks.Count(); - - if (numLines <= displayLines) - { - // disable the scrollbar - _vertScrollBar->SetEnabled(false); - _vertScrollBar->SetRange(0, numLines); - _vertScrollBar->SetRangeWindow(numLines); - _vertScrollBar->SetValue(0); - - if ( m_bUnusedScrollbarInvis ) - { - SetVerticalScrollbar( false ); - } - } - else - { - if ( m_bUnusedScrollbarInvis ) - { - SetVerticalScrollbar( true ); - } - - // set the scrollbars range - _vertScrollBar->SetRange(0, numLines); - _vertScrollBar->SetRangeWindow(displayLines); - _vertScrollBar->SetEnabled(true); - - // this should make it scroll one line at a time - _vertScrollBar->SetButtonPressedScrollValue(1); - if (bCurrentlyAtEnd) - { - _vertScrollBar->SetValue(numLines - displayLines); - } - _vertScrollBar->InvalidateLayout(); - _vertScrollBar->Repaint(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Sets whether a vertical scrollbar is visible -//----------------------------------------------------------------------------- -void RichText::SetVerticalScrollbar(bool state) -{ - if (_vertScrollBar->IsVisible() != state) - { - _vertScrollBar->SetVisible(state); - InvalidateLineBreakStream(); - InvalidateLayout(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Create cut/copy/paste dropdown menu -//----------------------------------------------------------------------------- -void RichText::CreateEditMenu() -{ - // create a drop down cut/copy/paste menu appropriate for this object's states - if (m_pEditMenu) - delete m_pEditMenu; - m_pEditMenu = new Menu(this, "EditMenu"); - - - // add cut/copy/paste drop down options if its editable, just copy if it is not - m_pEditMenu->AddMenuItem("C&opy", new KeyValues("DoCopySelected"), this); - - m_pEditMenu->SetVisible(false); - m_pEditMenu->SetParent(this); - m_pEditMenu->AddActionSignalTarget(this); -} - -//----------------------------------------------------------------------------- -// Purpose: We want single line windows to scroll horizontally and select text -// in response to clicking and holding outside window -//----------------------------------------------------------------------------- -void RichText::OnMouseFocusTicked() -{ - // if a button is down move the scrollbar slider the appropriate direction - if (_mouseDragSelection) // text is being selected via mouse clicking and dragging - { - OnCursorMoved(0,0); // we want the text to scroll as if we were dragging - } -} - -//----------------------------------------------------------------------------- -// Purpose: If a cursor enters the window, we are not elegible for -// MouseFocusTicked events -//----------------------------------------------------------------------------- -void RichText::OnCursorEntered() -{ - _mouseDragSelection = false; // outside of window dont recieve drag scrolling ticks -} - -//----------------------------------------------------------------------------- -// Purpose: When the cursor is outside the window, if we are holding the mouse -// button down, then we want the window to scroll the text one char at a time using Ticks -//----------------------------------------------------------------------------- -void RichText::OnCursorExited() -{ - // outside of window recieve drag scrolling ticks - if (_mouseSelection) - { - _mouseDragSelection = true; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handle selection of text by mouse -//----------------------------------------------------------------------------- -void RichText::OnCursorMoved(int x, int y) -{ - if (_mouseSelection) - { - // update the cursor position - int x, y; - input()->GetCursorPos(x, y); - ScreenToLocal(x, y); - _cursorPos = PixelToCursorSpace(x, y); - - if (_cursorPos != _select[1]) - { - _select[1] = _cursorPos; - Repaint(); - } - // Msg( "selecting range [%d..%d]\n", _select[0], _select[1] ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handle mouse button down events. -//----------------------------------------------------------------------------- -void RichText::OnMousePressed(MouseCode code) -{ - if (code == MOUSE_LEFT) - { - // clear current selection - SelectNone(); - - // move the cursor to where the mouse was pressed - int x, y; - input()->GetCursorPos(x, y); - ScreenToLocal(x, y); - - _cursorPos = PixelToCursorSpace(x, y); - - if ( m_bInteractive ) - { - // enter selection mode - input()->SetMouseCapture(GetVPanel()); - _mouseSelection = true; - - if (_select[0] < 0) - { - // if no initial selection position, Start selection position at cursor - _select[0] = _cursorPos; - } - _select[1] = _cursorPos; - } - - RequestFocus(); - Repaint(); - } - else if (code == MOUSE_RIGHT) // check for context menu open - { - if ( m_bInteractive ) - { - CreateEditMenu(); - Assert(m_pEditMenu); - - OpenEditMenu(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handle mouse button up events -//----------------------------------------------------------------------------- -void RichText::OnMouseReleased(MouseCode code) -{ - _mouseSelection = false; - input()->SetMouseCapture(NULL); - - // make sure something has been selected - int cx0, cx1; - if (GetSelectedRange(cx0, cx1)) - { - if (cx1 - cx0 == 0) - { - // nullify selection - _select[0] = -1; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handle mouse double clicks -//----------------------------------------------------------------------------- -void RichText::OnMouseDoublePressed(MouseCode code) -{ - if ( !m_bInteractive ) - return; - - // left double clicking on a word selects the word - if (code == MOUSE_LEFT) - { - // move the cursor just as if you single clicked. - OnMousePressed(code); - // then find the start and end of the word we are in to highlight it. - int selectSpot[2]; - GotoWordLeft(); - selectSpot[0] = _cursorPos; - GotoWordRight(); - selectSpot[1] = _cursorPos; - - if ( _cursorPos > 0 && (_cursorPos-1) < m_TextStream.Count() ) - { - if (iswspace(m_TextStream[_cursorPos-1])) - { - selectSpot[1]--; - _cursorPos--; - } - } - - _select[0] = selectSpot[0]; - _select[1] = selectSpot[1]; - _mouseSelection = true; - } - -} - -//----------------------------------------------------------------------------- -// Purpose: Turn off text selection code when mouse button is not down -//----------------------------------------------------------------------------- -void RichText::OnMouseCaptureLost() -{ - _mouseSelection = false; -} - -//----------------------------------------------------------------------------- -// Purpose: Masks which keys get chained up -// Maps keyboard input to text window functions. -//----------------------------------------------------------------------------- -void RichText::OnKeyCodeTyped(KeyCode code) -{ - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - bool winkey = (input()->IsKeyDown(KEY_LWIN) || input()->IsKeyDown(KEY_RWIN)); - bool fallThrough = false; - - if ( ctrl || ( winkey && IsOSX() ) ) - { - switch(code) - { - case KEY_INSERT: - case KEY_C: - case KEY_X: - { - CopySelected(); - break; - } - case KEY_PAGEUP: - case KEY_HOME: - { - GotoTextStart(); - break; - } - case KEY_PAGEDOWN: - case KEY_END: - { - GotoTextEnd(); - break; - } - default: - { - fallThrough = true; - break; - } - } - } - else if (alt) - { - // do nothing with ALT-x keys - fallThrough = true; - } - else - { - switch(code) - { - case KEY_TAB: - case KEY_LSHIFT: - case KEY_RSHIFT: - case KEY_ESCAPE: - case KEY_ENTER: - { - fallThrough = true; - break; - } - case KEY_DELETE: - { - if (shift) - { - // shift-delete is cut - CopySelected(); - } - break; - } - case KEY_HOME: - { - GotoTextStart(); - break; - } - case KEY_END: - { - GotoTextEnd(); - break; - } - case KEY_PAGEUP: - { - // if there is a scroll bar scroll down one rangewindow - if (_vertScrollBar->IsVisible()) - { - int window = _vertScrollBar->GetRangeWindow(); - int newval = _vertScrollBar->GetValue(); - _vertScrollBar->SetValue(newval - window - 1); - } - break; - - } - case KEY_PAGEDOWN: - { - // if there is a scroll bar scroll down one rangewindow - if (_vertScrollBar->IsVisible()) - { - int window = _vertScrollBar->GetRangeWindow(); - int newval = _vertScrollBar->GetValue(); - _vertScrollBar->SetValue(newval + window + 1); - } - break; - } - default: - { - // return if any other char is pressed. - // as it will be a unicode char. - // and we don't want select[1] changed unless a char was pressed that this fxn handles - return; - } - } - } - - // select[1] is the location in the line where the blinking cursor started - _select[1] = _cursorPos; - - // chain back on some keys - if (fallThrough) - { - BaseClass::OnKeyCodeTyped(code); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Scrolls the list according to the mouse wheel movement -//----------------------------------------------------------------------------- -void RichText::OnMouseWheeled(int delta) -{ - MoveScrollBar(delta); -} - -//----------------------------------------------------------------------------- -// Purpose: Scrolls the list -// Input : delta - amount to move scrollbar up -//----------------------------------------------------------------------------- -void RichText::MoveScrollBar(int delta) -{ - MoveScrollBarDirect( delta * 3 ); -} - -//----------------------------------------------------------------------------- -// Purpose: Scrolls the list -// Input : delta - amount to move scrollbar up -//----------------------------------------------------------------------------- -void RichText::MoveScrollBarDirect(int delta) -{ - if (_vertScrollBar->IsVisible()) - { - int val = _vertScrollBar->GetValue(); - val -= delta; - _vertScrollBar->SetValue(val); - _recalcSavedRenderState = true; - } -} - -//----------------------------------------------------------------------------- -// Purpose: set the maximum number of chars in the text buffer -//----------------------------------------------------------------------------- -void RichText::SetMaximumCharCount(int maxChars) -{ - _maxCharCount = maxChars; -} - -//----------------------------------------------------------------------------- -// Purpose: Find out what line the cursor is on -//----------------------------------------------------------------------------- -int RichText::GetCursorLine() -{ - // always returns the last place - int pos = m_LineBreaks[m_LineBreaks.Count() - 1]; - Assert(pos == MAX_BUFFER_SIZE); - return pos; -} - -//----------------------------------------------------------------------------- -// Purpose: Move the cursor over to the Start of the next word to the right -//----------------------------------------------------------------------------- -void RichText::GotoWordRight() -{ - // search right until we hit a whitespace character or a newline - while (++_cursorPos < m_TextStream.Count()) - { - if (iswspace(m_TextStream[_cursorPos])) - break; - } - - // search right until we hit an nonspace character - while (++_cursorPos < m_TextStream.Count()) - { - if (!iswspace(m_TextStream[_cursorPos])) - break; - } - - if (_cursorPos > m_TextStream.Count()) - { - _cursorPos = m_TextStream.Count(); - } - - // now we are at the start of the next word - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Move the cursor over to the Start of the next word to the left -//----------------------------------------------------------------------------- -void RichText::GotoWordLeft() -{ - if (_cursorPos < 1) - return; - - // search left until we hit an nonspace character - while (--_cursorPos >= 0) - { - if (!iswspace(m_TextStream[_cursorPos])) - break; - } - - // search left until we hit a whitespace character - while (--_cursorPos >= 0) - { - if (iswspace(m_TextStream[_cursorPos])) - { - break; - } - } - - // we end one character off - _cursorPos++; - - // now we are at the start of the previous word - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Move cursor to the Start of the text buffer -//----------------------------------------------------------------------------- -void RichText::GotoTextStart() -{ - _cursorPos = 0; // set cursor to start - _invalidateVerticalScrollbarSlider = true; - // force scrollbar to the top - _vertScrollBar->SetValue(0); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Move cursor to the end of the text buffer -//----------------------------------------------------------------------------- -void RichText::GotoTextEnd() -{ - _cursorPos = m_TextStream.Count(); // set cursor to end of buffer - _invalidateVerticalScrollbarSlider = true; - - // force the scrollbar to the bottom - int min, max; - _vertScrollBar->GetRange(min, max); - _vertScrollBar->SetValue(max); - - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Culls the text stream down to a managable size -//----------------------------------------------------------------------------- -void RichText::TruncateTextStream() -{ - if (_maxCharCount < 1) - return; - - // choose a point to cull at - int cullPos = _maxCharCount / 2; - - // kill half the buffer - m_TextStream.RemoveMultiple(0, cullPos); - - // work out where in the format stream we can start - int formatIndex = FindFormatStreamIndexForTextStreamPos(cullPos); - if (formatIndex > 0) - { - // take a copy, make it first - m_FormatStream[0] = m_FormatStream[formatIndex]; - m_FormatStream[0].textStreamIndex = 0; - // kill the others - m_FormatStream.RemoveMultiple(1, formatIndex); - } - - // renormalize the remainder of the format stream - for (int i = 1; i < m_FormatStream.Count(); i++) - { - Assert(m_FormatStream[i].textStreamIndex > cullPos); - m_FormatStream[i].textStreamIndex -= cullPos; - } - - // mark everything to be recalculated - InvalidateLineBreakStream(); - InvalidateLayout(); - _invalidateVerticalScrollbarSlider = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Insert a character into the text buffer -//----------------------------------------------------------------------------- -void RichText::InsertChar(wchar_t wch) -{ - // throw away redundant linefeed characters - if ( wch == '\r' ) - return; - - if (_maxCharCount > 0 && m_TextStream.Count() > _maxCharCount) - { - TruncateTextStream(); - } - - // insert the new char at the end of the buffer - m_TextStream.AddToTail(wch); - - // mark the linebreak steam as needing recalculating from that point - _recalculateBreaksIndex = m_LineBreaks.Count() - 2; - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Insert a string into the text buffer, this is just a series -// of char inserts because we have to check each char is ok to insert -//----------------------------------------------------------------------------- -void RichText::InsertString(const char *text) -{ - if (text[0] == '#') - { - wchar_t unicode[ 1024 ]; - ResolveLocalizedTextAndVariables( text, unicode, sizeof( unicode ) ); - InsertString( unicode ); - return; - } - - // upgrade the ansi text to unicode to display it - int len = strlen(text); - wchar_t *unicode = (wchar_t *)_alloca((len + 1) * sizeof(wchar_t)); - Q_UTF8ToUnicode(text, unicode, ((len + 1) * sizeof(wchar_t))); - InsertString(unicode); -} - -//----------------------------------------------------------------------------- -// Purpose: Insertsa a unicode string into the buffer -//----------------------------------------------------------------------------- -void RichText::InsertString(const wchar_t *wszText) -{ - // insert the whole string - for (const wchar_t *ch = wszText; *ch != 0; ++ch) - { - InsertChar(*ch); - } - InvalidateLayout(); - m_bRecalcLineBreaks = true; - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Declare a selection empty -//----------------------------------------------------------------------------- -void RichText::SelectNone() -{ - // tag the selection as empty - _select[0] = -1; - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Load in the selection range so cx0 is the Start and cx1 is the end -// from smallest to highest (right to left) -//----------------------------------------------------------------------------- -bool RichText::GetSelectedRange(int &cx0, int &cx1) -{ - // if there is nothing selected return false - if (_select[0] == -1) - return false; - - // sort the two position so cx0 is the smallest - cx0 = _select[0]; - cx1 = _select[1]; - if (cx1 < cx0) - { - int temp = cx0; - cx0 = cx1; - cx1 = temp; - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Opens the cut/copy/paste dropdown menu -//----------------------------------------------------------------------------- -void RichText::OpenEditMenu() -{ - // get cursor position, this is local to this text edit window - // so we need to adjust it relative to the parent - int cursorX, cursorY; - input()->GetCursorPos(cursorX, cursorY); - - /* !! disabled since it recursively gets panel pointers, potentially across dll boundaries, - and doesn't need to be necessary (it's just for handling windowed mode) - - // find the frame that has no parent (the one on the desktop) - Panel *panel = this; - while ( panel->GetParent() != NULL) - { - panel = panel->GetParent(); - } - panel->ScreenToLocal(cursorX, cursorY); - int x, y; - // get base panel's postition - panel->GetPos(x, y); - - // adjust our cursor position accordingly - cursorX += x; - cursorY += y; - */ - - int x0, x1; - if (GetSelectedRange(x0, x1)) // there is something selected - { - m_pEditMenu->SetItemEnabled("&Cut", true); - m_pEditMenu->SetItemEnabled("C&opy", true); - } - else // there is nothing selected, disable cut/copy options - { - m_pEditMenu->SetItemEnabled("&Cut", false); - m_pEditMenu->SetItemEnabled("C&opy", false); - } - m_pEditMenu->SetVisible(true); - m_pEditMenu->RequestFocus(); - - // relayout the menu immediately so that we know it's size - m_pEditMenu->InvalidateLayout(true); - int menuWide, menuTall; - m_pEditMenu->GetSize(menuWide, menuTall); - - // work out where the cursor is and therefore the best place to put the menu - int wide, tall; - surface()->GetScreenSize(wide, tall); - - if (wide - menuWide > cursorX) - { - // menu hanging right - if (tall - menuTall > cursorY) - { - // menu hanging down - m_pEditMenu->SetPos(cursorX, cursorY); - } - else - { - // menu hanging up - m_pEditMenu->SetPos(cursorX, cursorY - menuTall); - } - } - else - { - // menu hanging left - if (tall - menuTall > cursorY) - { - // menu hanging down - m_pEditMenu->SetPos(cursorX - menuWide, cursorY); - } - else - { - // menu hanging up - m_pEditMenu->SetPos(cursorX - menuWide, cursorY - menuTall); - } - } - - m_pEditMenu->RequestFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: Cuts the selected chars from the buffer and -// copies them into the clipboard -//----------------------------------------------------------------------------- -void RichText::CutSelected() -{ - CopySelected(); - // have to request focus if we used the menu - RequestFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: Copies the selected chars into the clipboard -//----------------------------------------------------------------------------- -void RichText::CopySelected() -{ - int x0, x1; - if (GetSelectedRange(x0, x1)) - { - CUtlVector buf; - for (int i = x0; i <= x1; i++) - { - if ( m_TextStream.IsValidIndex(i) == false ) - continue; - - if (m_TextStream[i] == '\n') - { - buf.AddToTail( '\r' ); - } - // remove any rich edit commands - buf.AddToTail(m_TextStream[i]); - } - buf.AddToTail('\0'); - system()->SetClipboardText(buf.Base(), buf.Count() - 1); - } - - // have to request focus if we used the menu - RequestFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the index in the text buffer of the -// character the drawing should Start at -//----------------------------------------------------------------------------- -int RichText::GetStartDrawIndex(int &lineBreakIndexIndex) -{ - int startIndex = 0; - int startLine = _vertScrollBar->GetValue(); - - if ( startLine >= m_LineBreaks.Count() ) // incase the line breaks got reset and the scroll bar hasn't - { - startLine = m_LineBreaks.Count() - 1; - } - - lineBreakIndexIndex = startLine; - if (startLine && startLine < m_LineBreaks.Count()) - { - startIndex = m_LineBreaks[startLine - 1]; - } - - return startIndex; -} - -//----------------------------------------------------------------------------- -// Purpose: Get a string from text buffer -// Input: offset - index to Start reading from -// bufLen - length of string -//----------------------------------------------------------------------------- -void RichText::GetText(int offset, wchar_t *buf, int bufLenInBytes) -{ - if (!buf) - return; - - Assert( bufLenInBytes >= sizeof(buf[0]) ); - int bufLen = bufLenInBytes / sizeof(wchar_t); - int i; - for (i = offset; i < (offset + bufLen - 1); i++) - { - if (i >= m_TextStream.Count()) - break; - - buf[i-offset] = m_TextStream[i]; - } - buf[(i-offset)] = 0; - buf[bufLen-1] = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: gets text from the buffer -//----------------------------------------------------------------------------- -void RichText::GetText(int offset, char *pch, int bufLenInBytes) -{ - wchar_t rgwchT[4096]; - GetText(offset, rgwchT, sizeof(rgwchT)); - Q_UnicodeToUTF8(rgwchT, pch, bufLenInBytes); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the font of the buffer text -//----------------------------------------------------------------------------- -void RichText::SetFont(HFont font) -{ - _font = font; - InvalidateLayout(); - m_bRecalcLineBreaks = true; - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Called when the scrollbar slider is moved -//----------------------------------------------------------------------------- -void RichText::OnSliderMoved() -{ - _recalcSavedRenderState = true; - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool RichText::RequestInfo(KeyValues *outputData) -{ - if (!stricmp(outputData->GetName(), "GetText")) - { - wchar_t wbuf[512]; - GetText(0, wbuf, sizeof(wbuf)); - outputData->SetWString("text", wbuf); - return true; - } - - return BaseClass::RequestInfo(outputData); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void RichText::OnSetText(const wchar_t *text) -{ - SetText(text); -} - -//----------------------------------------------------------------------------- -// Purpose: Called when a URL, etc has been clicked on -//----------------------------------------------------------------------------- -void RichText::OnClickPanel(int index) -{ - wchar_t wBuf[512]; - int outIndex = 0; - - // parse out the clickable text, and send it to our listeners - _currentTextClickable = true; - TRenderState renderState; - GenerateRenderStateForTextStreamIndex(index, renderState); - for (int i = index; i < (sizeof(wBuf) - 1) && i < m_TextStream.Count(); i++) - { - // stop getting characters when text is no longer clickable - UpdateRenderState(i, renderState); - if (!renderState.textClickable) - break; - - // copy out the character - wBuf[outIndex++] = m_TextStream[i]; - } - - wBuf[outIndex] = 0; - - int iFormatSteam = FindFormatStreamIndexForTextStreamPos( index ); - if ( m_FormatStream[iFormatSteam].m_sClickableTextAction ) - { - Q_UTF8ToUnicode( m_FormatStream[iFormatSteam].m_sClickableTextAction.String(), wBuf, sizeof( wBuf ) ); - } - - PostActionSignal(new KeyValues("TextClicked", "text", wBuf)); - OnTextClicked(wBuf); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void RichText::ApplySettings(KeyValues *inResourceData) -{ - BaseClass::ApplySettings(inResourceData); - SetMaximumCharCount(inResourceData->GetInt("maxchars", -1)); - SetVerticalScrollbar(inResourceData->GetInt("scrollbar", 1)); - - // get the starting text, if any - const char *text = inResourceData->GetString("text", ""); - if (*text) - { - delete [] m_pszInitialText; - int len = Q_strlen(text) + 1; - m_pszInitialText = new char[ len ]; - Q_strncpy( m_pszInitialText, text, len ); - SetText(text); - } - else - { - const char *textfilename = inResourceData->GetString("textfile", NULL); - if ( textfilename ) - { - FileHandle_t f = g_pFullFileSystem->Open( textfilename, "rt" ); - if (!f) - { - Warning( "RichText: textfile parameter '%s' not found.\n", textfilename ); - return; - } - - int len = g_pFullFileSystem->Size( f ); - delete [] m_pszInitialText; - m_pszInitialText = new char[ len + 1 ]; - g_pFullFileSystem->Read( m_pszInitialText, len, f ); - m_pszInitialText[len - 1] = 0; - SetText( m_pszInitialText ); - - g_pFullFileSystem->Close( f ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void RichText::GetSettings(KeyValues *outResourceData) -{ - BaseClass::GetSettings(outResourceData); - outResourceData->SetInt("maxchars", _maxCharCount); - outResourceData->SetInt("scrollbar", _vertScrollBar->IsVisible() ); - if (m_pszInitialText) - { - outResourceData->SetString("text", m_pszInitialText); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -const char *RichText::GetDescription() -{ - static char buf[1024]; - Q_snprintf(buf, sizeof(buf), "%s, string text, bool scrollbar", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the number of lines in the window -//----------------------------------------------------------------------------- -int RichText::GetNumLines() -{ - return m_LineBreaks.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the height of the text entry window so all text will fit inside -//----------------------------------------------------------------------------- -void RichText::SetToFullHeight() -{ - PerformLayout(); - int wide, tall; - GetSize(wide, tall); - - tall = GetNumLines() * (GetLineHeight() + _drawOffsetY) + _drawOffsetY + 2; - SetSize (wide, tall); - PerformLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Select all the text. -//----------------------------------------------------------------------------- -void RichText::SelectAllText() -{ - _cursorPos = 0; - _select[0] = 0; - _select[1] = m_TextStream.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: Select all the text. -//----------------------------------------------------------------------------- -void RichText::SelectNoText() -{ - _select[0] = 0; - _select[1] = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void RichText::OnSetFocus() -{ - BaseClass::OnSetFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: Invalidates the current linebreak stream -//----------------------------------------------------------------------------- -void RichText::InvalidateLineBreakStream() -{ - // clear the buffer - m_LineBreaks.RemoveAll(); - m_LineBreaks.AddToTail(MAX_BUFFER_SIZE); - _recalculateBreaksIndex = 0; - m_bRecalcLineBreaks = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Inserts a text string while making URLs clickable/different color -// Input : *text - string that may contain URLs to make clickable/color coded -// URLTextColor - color for URL text -// normalTextColor - color for normal text -//----------------------------------------------------------------------------- -void RichText::InsertPossibleURLString(const char* text, Color URLTextColor, Color normalTextColor) -{ - InsertColorChange(normalTextColor); - - // parse out the string for URL's - int len = Q_strlen(text), pos = 0; - bool clickable = false; - char *pchURLText = (char *)stackalloc( len + 1 ); - char *pchURL = (char *)stackalloc( len + 1 ); - - while (pos < len) - { - pos = ParseTextStringForUrls( text, pos, pchURLText, len, pchURL, len, clickable ); - - if ( clickable ) - { - InsertClickableTextStart( pchURL ); - InsertColorChange( URLTextColor ); - } - - InsertString( pchURLText ); - - if ( clickable ) - { - InsertColorChange(normalTextColor); - InsertClickableTextEnd(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: looks for URLs in the string and returns information about the URL -//----------------------------------------------------------------------------- -int RichText::ParseTextStringForUrls( const char *text, int startPos, char *pchURLText, int cchURLText, char *pchURL, int cchURL, bool &clickable ) -{ - // scan for text that looks like a URL - int i = startPos; - while (text[i] != 0) - { - bool bURLFound = false; - - if ( !Q_strnicmp(text + i, "" ); - Q_strncpy( pchURL, text + i, min( pchURLEnd - text - i + 1, cchURL ) ); - i += ( pchURLEnd - text - i + 1 ); - - // get the url text - pchURLEnd = Q_strstr( text, "" ); - Q_strncpy( pchURLText, text + i, min( pchURLEnd - text - i + 1, cchURLText ) ); - i += ( pchURLEnd - text - i ); - i += Q_strlen( "" ); - - // we're done - return i; - } - else if (!Q_strnicmp(text + i, "www.", 4)) - { - // scan ahead for another '.' - bool bPeriodFound = false; - for (const char *ch = text + i + 5; ch != 0; ch++) - { - if (*ch == '.') - { - bPeriodFound = true; - break; - } - } - - // URL found - if (bPeriodFound) - { - bURLFound = true; - } - } - else if (!Q_strnicmp(text + i, "http://", 7)) - { - bURLFound = true; - } - else if (!Q_strnicmp(text + i, "ftp://", 6)) - { - bURLFound = true; - } - else if (!Q_strnicmp(text + i, "steam://", 8)) - { - bURLFound = true; - } - else if (!Q_strnicmp(text + i, "steambeta://", 12)) - { - bURLFound = true; - } - else if (!Q_strnicmp(text + i, "mailto:", 7)) - { - bURLFound = true; - } - else if (!Q_strnicmp(text + i, "\\\\", 2)) - { - bURLFound = true; - } - - if (bURLFound) - { - if (i == startPos) - { - // we're at the Start of a URL, so parse that out - clickable = true; - int outIndex = 0; - while (text[i] != 0 && !iswspace(text[i])) - { - pchURLText[outIndex++] = text[i++]; - } - pchURLText[outIndex] = 0; - Q_strncpy( pchURL, pchURLText, cchURL ); - return i; - } - else - { - // no url - break; - } - } - - // increment and loop - i++; - } - - // nothing found; - // parse out the text before the end - clickable = false; - int outIndex = 0; - int fromIndex = startPos; - while ( fromIndex < i && outIndex < cchURLText ) - { - pchURLText[outIndex++] = text[fromIndex++]; - } - pchURLText[outIndex] = 0; - Q_strncpy( pchURL, pchURLText, cchURL ); - - return i; -} - -//----------------------------------------------------------------------------- -// Purpose: Executes the text-clicked command, which opens a web browser by -// default. -//----------------------------------------------------------------------------- -void RichText::OnTextClicked(const wchar_t *wszText) -{ - // Strip leading/trailing quotes, which may be present on href tags or may not. - const wchar_t *pwchURL = wszText; - if ( pwchURL[0] == L'"' || pwchURL[0] == L'\'' ) - pwchURL = wszText + 1; - - char ansi[2048]; - Q_UnicodeToUTF8( pwchURL, ansi, sizeof(ansi) ); - - size_t strLen = Q_strlen(ansi); - if ( strLen && ( ansi[strLen-1] == '"' || ansi[strLen] == '\'' ) ) - { - ansi[strLen-1] = 0; - } - - if ( m_hPanelToHandleClickingURLs.Get() ) - { - PostMessage( m_hPanelToHandleClickingURLs.Get(), new KeyValues( "URLClicked", "url", ansi ) ); - } - else - { - system()->ShellExecute( "open", ansi ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void RichText::SetURLClickedHandler( Panel *pPanelToHandleClickMsg ) -{ - m_hPanelToHandleClickingURLs = pPanelToHandleClickMsg; -} - - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -bool RichText::IsScrollbarVisible() -{ - return _vertScrollBar->IsVisible(); -} - -void RichText::SetUnderlineFont( HFont font ) -{ - m_hFontUnderline = font; -} - -bool RichText::IsAllTextAlphaZero() const -{ - return m_bAllTextAlphaIsZero; -} - -bool RichText::HasText() const -{ - int c = m_TextStream.Count(); - if ( c == 0 ) - { - return false; - } - return true; -} - - - -//----------------------------------------------------------------------------- -// Purpose: Returns the height of the base font -//----------------------------------------------------------------------------- -int RichText::GetLineHeight() -{ - return surface()->GetFontTall( _font ); -} - - -#ifdef DBGFLAG_VALIDATE -//----------------------------------------------------------------------------- -// Purpose: Run a global validation pass on all of our data structures and memory -// allocations. -// Input: validator - Our global validator object -// pchName - Our name (typically a member var in our container) -//----------------------------------------------------------------------------- -void RichText::Validate( CValidator &validator, char *pchName ) -{ - validator.Push( "vgui::RichText", this, pchName ); - - ValidateObj( m_TextStream ); - ValidateObj( m_FormatStream ); - ValidateObj( m_LineBreaks ); - ValidateObj( _clickableTextPanels ); - validator.ClaimMemory( m_pszInitialText ); - - BaseClass::Validate( validator, "vgui::RichText" ); - - validator.Pop(); -} -#endif // DBGFLAG_VALIDATE - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "vgui_controls/pch_vgui_controls.h" +#include "vgui/ILocalize.h" + +// memdbgon must be the last include file in a .cpp file +#include "tier0/memdbgon.h" + +enum +{ + MAX_BUFFER_SIZE = 999999, // maximum size of text buffer + DRAW_OFFSET_X = 3, + DRAW_OFFSET_Y = 1, +}; + +using namespace vgui; + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +namespace vgui +{ + +//#define DRAW_CLICK_PANELS + +//----------------------------------------------------------------------------- +// Purpose: Panel used for clickable URL's +//----------------------------------------------------------------------------- +class ClickPanel : public Panel +{ + DECLARE_CLASS_SIMPLE( ClickPanel, Panel ); + +public: + ClickPanel(Panel *parent) + { + _viewIndex = 0; + _textIndex = 0; + SetParent(parent); + AddActionSignalTarget(parent); + + SetCursor(dc_hand); + + SetPaintBackgroundEnabled(false); + SetPaintEnabled(false); +// SetPaintAppearanceEnabled(false); + +#if defined( DRAW_CLICK_PANELS ) + SetPaintEnabled(true); +#endif + } + + void SetTextIndex( int linkStartIndex, int viewStartIndex ) + { + _textIndex = linkStartIndex; + _viewIndex = viewStartIndex; + } + +#if defined( DRAW_CLICK_PANELS ) + virtual void Paint() + { + surface()->DrawSetColor( Color( 255, 0, 0, 255 ) ); + surface()->DrawOutlinedRect( 0, 0, GetWide(), GetTall() ); + } +#endif + + int GetTextIndex() + { + return _textIndex; + } + + int GetViewTextIndex() + { + return _viewIndex; + } + + void OnMousePressed(MouseCode code) + { + if (code == MOUSE_LEFT) + { + PostActionSignal(new KeyValues("ClickPanel", "index", _textIndex)); + } + else + { + GetParent()->OnMousePressed( code ); + } + } + +private: + int _textIndex; + int _viewIndex; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Panel used only to draw the interior border region +//----------------------------------------------------------------------------- +class RichTextInterior : public Panel +{ + DECLARE_CLASS_SIMPLE( RichTextInterior, Panel ); + +public: + RichTextInterior( RichText *pParent, const char *pchName ) : BaseClass( pParent, pchName ) + { + SetKeyBoardInputEnabled( false ); + SetMouseInputEnabled( false ); + SetPaintBackgroundEnabled( false ); + SetPaintEnabled( false ); + m_pRichText = pParent; + } + +/* virtual IAppearance *GetAppearance() + { + if ( m_pRichText->IsScrollbarVisible() ) + return m_pAppearanceScrollbar; + + return BaseClass::GetAppearance(); + }*/ + + virtual void ApplySchemeSettings( IScheme *pScheme ) + { + BaseClass::ApplySchemeSettings( pScheme ); +// m_pAppearanceScrollbar = FindSchemeAppearance( pScheme, "scrollbar_visible" ); + } + +private: + RichText *m_pRichText; +// IAppearance *m_pAppearanceScrollbar; +}; + +}; // namespace vgui + +DECLARE_BUILD_FACTORY( RichText ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +RichText::RichText(Panel *parent, const char *panelName) : BaseClass(parent, panelName) +{ + m_bAllTextAlphaIsZero = false; + _font = INVALID_FONT; + m_hFontUnderline = INVALID_FONT; + + m_bRecalcLineBreaks = true; + m_pszInitialText = NULL; + _cursorPos = 0; + _mouseSelection = false; + _mouseDragSelection = false; + _vertScrollBar = new ScrollBar(this, "ScrollBar", true); + _vertScrollBar->AddActionSignalTarget(this); + _recalcSavedRenderState = true; + _maxCharCount = (64 * 1024); + AddActionSignalTarget(this); + m_pInterior = new RichTextInterior( this, NULL ); + + //a -1 for _select[0] means that the selection is empty + _select[0] = -1; + _select[1] = -1; + m_pEditMenu = NULL; + + SetCursor(dc_ibeam); + + //position the cursor so it is at the end of the text + GotoTextEnd(); + + // set default foreground color to black + _defaultTextColor = Color(0, 0, 0, 0); + + // initialize the line break array + InvalidateLineBreakStream(); + + if ( IsProportional() ) + { + int width, height; + int sw,sh; + surface()->GetProportionalBase( width, height ); + surface()->GetScreenSize(sw, sh); + + _drawOffsetX = static_cast( static_cast( DRAW_OFFSET_X )*( static_cast( sw )/ static_cast( width ))); + _drawOffsetY = static_cast( static_cast( DRAW_OFFSET_Y )*( static_cast( sw )/ static_cast( width ))); + } + else + { + _drawOffsetX = DRAW_OFFSET_X; + _drawOffsetY = DRAW_OFFSET_Y; + } + + // add a basic format string + TFormatStream stream; + stream.color = _defaultTextColor; + stream.fade.flFadeStartTime = 0.0f; + stream.fade.flFadeLength = -1.0f; + stream.pixelsIndent = 0; + stream.textStreamIndex = 0; + stream.textClickable = false; + m_FormatStream.AddToTail(stream); + + m_bResetFades = false; + m_bInteractive = true; + m_bUnusedScrollbarInvis = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +RichText::~RichText() +{ + delete [] m_pszInitialText; + delete m_pEditMenu; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void RichText::SetDrawOffsets( int ofsx, int ofsy ) +{ + _drawOffsetX = ofsx; + _drawOffsetY = ofsy; +} + +//----------------------------------------------------------------------------- +// Purpose: sets it as drawing text only - used for embedded RichText control into other text drawing situations +//----------------------------------------------------------------------------- +void RichText::SetDrawTextOnly() +{ + SetDrawOffsets( 0, 0 ); + SetPaintBackgroundEnabled( false ); +// SetPaintAppearanceEnabled( false ); + SetPostChildPaintEnabled( false ); + m_pInterior->SetVisible( false ); + SetVerticalScrollbar( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: configures colors +//----------------------------------------------------------------------------- +void RichText::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + _font = pScheme->GetFont("Default", IsProportional() ); + m_hFontUnderline = pScheme->GetFont("DefaultUnderline", IsProportional() ); + + SetFgColor(GetSchemeColor("RichText.TextColor", pScheme)); + SetBgColor(GetSchemeColor("RichText.BgColor", pScheme)); + + _selectionTextColor = GetSchemeColor("RichText.SelectedTextColor", GetFgColor(), pScheme); + _selectionColor = GetSchemeColor("RichText.SelectedBgColor", pScheme); + + if ( Q_strlen( pScheme->GetResourceString( "RichText.InsetX" ) ) ) + { + SetDrawOffsets( atoi( pScheme->GetResourceString( "RichText.InsetX" ) ), atoi( pScheme->GetResourceString( "RichText.InsetY" ) ) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: if the default format color isn't set then set it +//----------------------------------------------------------------------------- +void RichText::SetFgColor( Color color ) +{ + // Replace default format color if + // the stream is empty and the color is the default ( or the previous FgColor ) + if ( m_FormatStream.Size() == 1 && + ( m_FormatStream[0].color == _defaultTextColor || m_FormatStream[0].color == GetFgColor() ) ) + { + m_FormatStream[0].color = color; + } + + BaseClass::SetFgColor( color ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sends a message if the data has changed +// Turns off any selected text in the window if we are not using the edit menu +//----------------------------------------------------------------------------- +void RichText::OnKillFocus() +{ + // check if we clicked the right mouse button or if it is down + bool mouseRightClicked = input()->WasMousePressed(MOUSE_RIGHT); + bool mouseRightUp = input()->WasMouseReleased(MOUSE_RIGHT); + bool mouseRightDown = input()->IsMouseDown(MOUSE_RIGHT); + + if (mouseRightClicked || mouseRightDown || mouseRightUp ) + { + // get the start and ends of the selection area + int start, end; + if (GetSelectedRange(start, end)) // we have selected text + { + // see if we clicked in the selection area + int startX, startY; + CursorToPixelSpace(start, startX, startY); + int endX, endY; + CursorToPixelSpace(end, endX, endY); + int cursorX, cursorY; + input()->GetCursorPos(cursorX, cursorY); + ScreenToLocal(cursorX, cursorY); + + // check the area vertically + // we need to handle the horizontal edge cases eventually + int fontTall = GetLineHeight(); + endY = endY + fontTall; + if ((startY < cursorY) && (endY > cursorY)) + { + // if we clicked in the selection area, leave the text highlighted + return; + } + } + } + + // clear any selection + SelectNone(); + + // chain + BaseClass::OnKillFocus(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Wipe line breaks after the size of a panel has been changed +//----------------------------------------------------------------------------- +void RichText::OnSizeChanged( int wide, int tall ) +{ + BaseClass::OnSizeChanged( wide, tall ); + + // blow away the line breaks list + _invalidateVerticalScrollbarSlider = true; + InvalidateLineBreakStream(); + InvalidateLayout(); + + if ( _vertScrollBar->IsVisible() ) + { + _vertScrollBar->MakeReadyForUse(); + m_pInterior->SetBounds( 0, 0, wide - _vertScrollBar->GetWide(), tall ); + } + else + { + m_pInterior->SetBounds( 0, 0, wide, tall ); + } +} + + +const wchar_t *RichText::ResolveLocalizedTextAndVariables( char const *pchLookup, wchar_t *outbuf, size_t outbufsizeinbytes ) +{ + if ( pchLookup[ 0 ] == '#' ) + { + // try lookup in localization tables + StringIndex_t index = g_pVGuiLocalize->FindIndex( pchLookup + 1 ); + if ( index == INVALID_LOCALIZE_STRING_INDEX ) + { +/* // if it's not found, maybe it's a special expanded variable - look for an expansion + char rgchT[MAX_PATH]; + + // get the variables + KeyValues *variables = GetDialogVariables_R(); + if ( variables ) + { + // see if any are any special vars to put in + for ( KeyValues *pkv = variables->GetFirstSubKey(); pkv != NULL; pkv = pkv->GetNextKey() ) + { + if ( !Q_strncmp( pkv->GetName(), "$", 1 ) ) + { + // make a new lookup, with this key appended + Q_snprintf( rgchT, sizeof( rgchT ), "%s%s=%s", pchLookup, pkv->GetName(), pkv->GetString() ); + index = localize()->FindIndex( rgchT ); + break; + } + } + } + */ + } + + // see if we have a valid string + if ( index != INVALID_LOCALIZE_STRING_INDEX ) + { + wchar_t *format = g_pVGuiLocalize->GetValueByIndex( index ); + Assert( format ); + if ( format ) + { + /*// Try and substitute variables if any + KeyValues *variables = GetDialogVariables_R(); + if ( variables ) + { + localize()->ConstructString( outbuf, outbufsizeinbytes, index, variables ); + return outbuf; + }*/ + } + V_wcsncpy( outbuf, format, outbufsizeinbytes ); + return outbuf; + } + } + + Q_UTF8ToUnicode( pchLookup, outbuf, outbufsizeinbytes ); + return outbuf; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the text array +// Using this function will cause all lineBreaks to be discarded. +// This is because this fxn replaces the contents of the text buffer. +// For modifying large buffers use insert functions. +//----------------------------------------------------------------------------- +void RichText::SetText(const char *text) +{ + if (!text) + { + text = ""; + } + + wchar_t unicode[1024]; + + if (text[0] == '#') + { + ResolveLocalizedTextAndVariables( text, unicode, sizeof( unicode ) ); + SetText( unicode ); + return; + } + + // convert to unicode + Q_UTF8ToUnicode(text, unicode, sizeof(unicode)); + SetText(unicode); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void RichText::SetText(const wchar_t *text) +{ + // reset the formatting stream + m_FormatStream.RemoveAll(); + TFormatStream stream; + stream.color = GetFgColor(); + stream.fade.flFadeLength = -1.0f; + stream.fade.flFadeStartTime = 0.0f; + stream.pixelsIndent = 0; + stream.textStreamIndex = 0; + stream.textClickable = false; + m_FormatStream.AddToTail(stream); + + // set the new text stream + m_TextStream.RemoveAll(); + if ( text && *text ) + { + int textLen = wcslen(text) + 1; + m_TextStream.EnsureCapacity(textLen); + for(int i = 0; i < textLen; i++) + { + m_TextStream.AddToTail(text[i]); + } + } + GotoTextStart(); + SelectNone(); + + // blow away the line breaks list + InvalidateLineBreakStream(); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Given cursor's position in the text buffer, convert it to +// the local window's x and y pixel coordinates +// Input: cursorPos: cursor index +// Output: cx, cy, the corresponding coords in the local window +//----------------------------------------------------------------------------- +void RichText::CursorToPixelSpace(int cursorPos, int &cx, int &cy) +{ + int yStart = _drawOffsetY; + int x = _drawOffsetX, y = yStart; + _pixelsIndent = 0; + int lineBreakIndexIndex = 0; + + for (int i = GetStartDrawIndex(lineBreakIndexIndex); i < m_TextStream.Count(); i++) + { + wchar_t ch = m_TextStream[i]; + + // if we've found the position, break + if (cursorPos == i) + { + // if we've passed a line break go to that + if (m_LineBreaks[lineBreakIndexIndex] == i) + { + // add another line + AddAnotherLine(x, y); + lineBreakIndexIndex++; + } + break; + } + + // if we've passed a line break go to that + if (m_LineBreaks[lineBreakIndexIndex] == i) + { + // add another line + AddAnotherLine(x, y); + lineBreakIndexIndex++; + } + + // add to the current position + x += surface()->GetCharacterWidth(_font, ch); + } + + cx = x; + cy = y; +} + +//----------------------------------------------------------------------------- +// Purpose: Converts local pixel coordinates to an index in the text buffer +//----------------------------------------------------------------------------- +int RichText::PixelToCursorSpace(int cx, int cy) +{ + int fontTall = GetLineHeight(); + + // where to start reading + int yStart = _drawOffsetY; + int x = _drawOffsetX, y = yStart; + _pixelsIndent = 0; + int lineBreakIndexIndex = 0; + + int startIndex = GetStartDrawIndex(lineBreakIndexIndex); + if (_recalcSavedRenderState) + { + RecalculateDefaultState(startIndex); + } + + _pixelsIndent = m_CachedRenderState.pixelsIndent; + _currentTextClickable = m_CachedRenderState.textClickable; + TRenderState renderState = m_CachedRenderState; + + bool onRightLine = false; + int i; + for (i = startIndex; i < m_TextStream.Count(); i++) + { + wchar_t ch = m_TextStream[i]; + + renderState.x = x; + if ( UpdateRenderState( i, renderState ) ) + { + x = renderState.x; + } + + // if we are on the right line but off the end of if put the cursor at the end of the line + if (m_LineBreaks[lineBreakIndexIndex] == i) + { + // add another line + AddAnotherLine(x, y); + lineBreakIndexIndex++; + + if (onRightLine) + break; + } + + // check to see if we're on the right line + if (cy < yStart) + { + // cursor is above panel + onRightLine = true; + } + else if (cy >= y && (cy < (y + fontTall + _drawOffsetY))) + { + onRightLine = true; + } + + int wide = surface()->GetCharacterWidth(_font, ch); + + // if we've found the position, break + if (onRightLine) + { + if (cx > GetWide()) // off right side of window + { + } + else if (cx < (_drawOffsetX + renderState.pixelsIndent) || cy < yStart) // off left side of window + { + // Msg( "PixelToCursorSpace() off left size, returning %d '%c'\n", i, m_TextStream[i] ); + return i; // move cursor one to left + } + + if (cx >= x && cx < (x + wide)) + { + // check which side of the letter they're on + if (cx < (x + (wide * 0.5))) // left side + { + // Msg( "PixelToCursorSpace() on the left size, returning %d '%c'\n", i, m_TextStream[i] ); + return i; + } + else // right side + { + // Msg( "PixelToCursorSpace() on the right size, returning %d '%c'\n", i + 1, m_TextStream[i + 1] ); + return i + 1; + } + } + } + x += wide; + } + + // Msg( "PixelToCursorSpace() never hit, returning %d\n", i ); + return i; +} + +//----------------------------------------------------------------------------- +// Purpose: Draws a string of characters in the panel +// Input: iFirst - Index of the first character to draw +// iLast - Index of the last character to draw +// renderState - Render state to use +// font- font to use +// Output: returns the width of the character drawn +//----------------------------------------------------------------------------- +int RichText::DrawString(int iFirst, int iLast, TRenderState &renderState, HFont font) +{ +// VPROF( "RichText::DrawString" ); + + // Calculate the render size + int fontTall = surface()->GetFontTall(font); + // BUGBUG John: This won't exactly match the rendered size + int charWide = 0; + for ( int i = iFirst; i <= iLast; i++ ) + { + wchar_t ch = m_TextStream[i]; +#if USE_GETKERNEDCHARWIDTH + wchar_t chBefore = 0; + wchar_t chAfter = 0; + if ( i > 0 ) + chBefore = m_TextStream[i-1]; + if ( i < iLast ) + chAfter = m_TextStream[i+1]; + float flWide = 0.0f, flabcA = 0.0f; + surface()->GetKernedCharWidth(font, ch, chBefore, chAfter, flWide, flabcA); + if ( ch == L' ' ) + flWide = ceil( flWide ); + charWide += floor( flWide + 0.6 ); +#else + charWide += surface()->GetCharacterWidth(font, ch); +#endif + } + + // draw selection, if any + int selection0 = -1, selection1 = -1; + GetSelectedRange(selection0, selection1); + + if (iFirst >= selection0 && iFirst < selection1) + { + // draw background selection color + surface()->DrawSetColor(_selectionColor); + surface()->DrawFilledRect(renderState.x, renderState.y, renderState.x + charWide, renderState.y + 1 + fontTall); + + // reset text color + surface()->DrawSetTextColor(_selectionTextColor); + m_bAllTextAlphaIsZero = false; + } + else + { + surface()->DrawSetTextColor(renderState.textColor); + } + + if ( renderState.textColor.a() != 0 ) + { + m_bAllTextAlphaIsZero = false; + surface()->DrawSetTextPos(renderState.x, renderState.y); + surface()->DrawPrintText(&m_TextStream[iFirst], iLast - iFirst + 1); + } + + return charWide; +} + +//----------------------------------------------------------------------------- +// Purpose: Finish drawing url +//----------------------------------------------------------------------------- +void RichText::FinishingURL(int x, int y) +{ + // finishing URL + if ( _clickableTextPanels.IsValidIndex( _clickableTextIndex ) ) + { + ClickPanel *clickPanel = _clickableTextPanels[ _clickableTextIndex ]; + int px, py; + clickPanel->GetPos(px, py); + int fontTall = GetLineHeight(); + clickPanel->SetSize( MAX( x - px, 6 ), y - py + fontTall ); + clickPanel->SetVisible(true); + + // if we haven't actually advanced any, step back and ignore this one + // this is probably a data input problem though, need to find root cause + if ( x - px <= 0 ) + { + --_clickableTextIndex; + clickPanel->SetVisible(false); + } + } +} + +void RichText::CalculateFade( TRenderState &renderState ) +{ + if ( m_FormatStream.IsValidIndex( renderState.formatStreamIndex ) ) + { + if ( m_bResetFades == false ) + { + if ( m_FormatStream[renderState.formatStreamIndex].fade.flFadeLength != -1.0f ) + { + float frac = ( m_FormatStream[renderState.formatStreamIndex].fade.flFadeStartTime - system()->GetCurrentTime() ) / m_FormatStream[renderState.formatStreamIndex].fade.flFadeLength; + + int alpha = frac * m_FormatStream[renderState.formatStreamIndex].fade.iOriginalAlpha; + alpha = clamp( alpha, 0, m_FormatStream[renderState.formatStreamIndex].fade.iOriginalAlpha ); + + renderState.textColor.SetColor( renderState.textColor.r(), renderState.textColor.g(), renderState.textColor.b(), alpha ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draws the text in the panel +//----------------------------------------------------------------------------- +void RichText::Paint() +{ + // Assume the worst + m_bAllTextAlphaIsZero = true; + + HFont hFontCurrent = _font; + + // hide all the clickable panels until we know where they are to reside + for (int j = 0; j < _clickableTextPanels.Count(); j++) + { + _clickableTextPanels[j]->SetVisible(false); + } + + if ( !HasText() ) + return; + + int wide, tall; + GetSize( wide, tall ); + + int lineBreakIndexIndex = 0; + int startIndex = GetStartDrawIndex(lineBreakIndexIndex); + _currentTextClickable = false; + + _clickableTextIndex = GetClickableTextIndexStart(startIndex); + + // recalculate and cache the render state at the render start + if (_recalcSavedRenderState) + { + RecalculateDefaultState(startIndex); + } + // copy off the cached render state + TRenderState renderState = m_CachedRenderState; + + _pixelsIndent = m_CachedRenderState.pixelsIndent; + _currentTextClickable = m_CachedRenderState.textClickable; + + renderState.textClickable = _currentTextClickable; + renderState.textColor = m_FormatStream[renderState.formatStreamIndex].color; + CalculateFade( renderState ); + + renderState.formatStreamIndex++; + + if ( _currentTextClickable ) + { + _clickableTextIndex = startIndex; + } + + // where to start drawing + renderState.x = _drawOffsetX + _pixelsIndent; + renderState.y = _drawOffsetY; + + // draw the text + int selection0 = -1, selection1 = -1; + GetSelectedRange(selection0, selection1); + + surface()->DrawSetTextFont( hFontCurrent ); + + for (int i = startIndex; i < m_TextStream.Count() && renderState.y < tall; ) + { + // 1. + // Update our current render state based on the formatting and color streams, + // this has to happen if it's our very first iteration, or if we are actually changing + // state. + int nXBeforeStateChange = renderState.x; + if ( UpdateRenderState(i, renderState) || i == startIndex ) + { + // check for url state change + if (renderState.textClickable != _currentTextClickable) + { + if (renderState.textClickable) + { + // entering new URL + _clickableTextIndex++; + hFontCurrent = m_hFontUnderline; + surface()->DrawSetTextFont( hFontCurrent ); + + // set up the panel + ClickPanel *clickPanel = _clickableTextPanels.IsValidIndex( _clickableTextIndex ) ? _clickableTextPanels[_clickableTextIndex] : NULL; + + if (clickPanel) + { + clickPanel->SetPos(renderState.x, renderState.y); + } + } + else + { + FinishingURL(nXBeforeStateChange, renderState.y); + hFontCurrent = _font; + surface()->DrawSetTextFont( hFontCurrent ); + } + _currentTextClickable = renderState.textClickable; + } + } + + // 2. + // if we've passed a line break go to that + if ( m_LineBreaks.IsValidIndex( lineBreakIndexIndex ) && m_LineBreaks[lineBreakIndexIndex] <= i ) + { + if (_currentTextClickable) + { + FinishingURL(renderState.x, renderState.y); + } + + // add another line + AddAnotherLine(renderState.x, renderState.y); + lineBreakIndexIndex++; + + // Skip white space unless the previous line ended from the hard carriage return + if ( i && ( m_TextStream[i-1] != '\n' ) && ( m_TextStream[i-1] != '\r') ) + { + while ( m_TextStream[i] == L' ' ) + { + if ( i+1 < m_TextStream.Count() ) + ++i; + else + break; + } + } + + if (renderState.textClickable) + { + // move to the next URL + _clickableTextIndex++; + ClickPanel *clickPanel = _clickableTextPanels.IsValidIndex( _clickableTextIndex ) ? _clickableTextPanels[_clickableTextIndex] : NULL; + if (clickPanel) + { + clickPanel->SetPos(renderState.x, renderState.y); + } + } + } + + // 3. + // Calculate the range of text to draw all at once + int iLim = m_TextStream.Count(); + + // Stop at the next format change + if ( m_FormatStream.IsValidIndex(renderState.formatStreamIndex) && + m_FormatStream[renderState.formatStreamIndex].textStreamIndex < iLim && + m_FormatStream[renderState.formatStreamIndex].textStreamIndex >= i && + m_FormatStream[renderState.formatStreamIndex].textStreamIndex ) + { + iLim = m_FormatStream[renderState.formatStreamIndex].textStreamIndex; + } + + // Stop at the next line break + if ( m_LineBreaks.IsValidIndex( lineBreakIndexIndex ) && m_LineBreaks[lineBreakIndexIndex] < iLim ) + iLim = m_LineBreaks[lineBreakIndexIndex]; + + // Handle non-drawing characters specially + for ( int iT = i; iT < iLim; iT++ ) + { + if ( iswcntrl(m_TextStream[iT]) ) + { + iLim = iT; + break; + } + } + + // 4. + // Draw the current text range + if ( iLim <= i ) + { + if ( m_TextStream[i] == '\t' ) + { + int dxTabWidth = 8 * surface()->GetCharacterWidth(hFontCurrent, ' '); + dxTabWidth = MAX( 1, dxTabWidth ); + + renderState.x = ( dxTabWidth * ( 1 + ( renderState.x / dxTabWidth ) ) ); + } + i++; + } + else + { + renderState.x += DrawString(i, iLim - 1, renderState, hFontCurrent ); + i = iLim; + } + } + + if (renderState.textClickable) + { + FinishingURL(renderState.x, renderState.y); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int RichText::GetClickableTextIndexStart(int startIndex) +{ + // cycle to the right url panel for what is visible after the startIndex. + for (int i = 0; i < _clickableTextPanels.Count(); i++) + { + if (_clickableTextPanels[i]->GetViewTextIndex() >= startIndex) + { + return i - 1; + } + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Recalcultes the formatting state from the specified index +//----------------------------------------------------------------------------- +void RichText::RecalculateDefaultState(int startIndex) +{ + if (!HasText() ) + return; + + Assert(startIndex < m_TextStream.Count()); + + m_CachedRenderState.textColor = GetFgColor(); + _pixelsIndent = 0; + _currentTextClickable = false; + _clickableTextIndex = GetClickableTextIndexStart(startIndex); + + // find where in the formatting stream we need to be + GenerateRenderStateForTextStreamIndex(startIndex, m_CachedRenderState); + _recalcSavedRenderState = false; +} + +//----------------------------------------------------------------------------- +// Purpose: updates a render state based on the formatting and color streams +// Output: true if we changed the render state +//----------------------------------------------------------------------------- +bool RichText::UpdateRenderState(int textStreamPos, TRenderState &renderState) +{ + // check the color stream + if (m_FormatStream.IsValidIndex(renderState.formatStreamIndex) && + m_FormatStream[renderState.formatStreamIndex].textStreamIndex == textStreamPos) + { + // set the current formatting + renderState.textColor = m_FormatStream[renderState.formatStreamIndex].color; + renderState.textClickable = m_FormatStream[renderState.formatStreamIndex].textClickable; + + CalculateFade( renderState ); + + int indentChange = m_FormatStream[renderState.formatStreamIndex].pixelsIndent - renderState.pixelsIndent; + renderState.pixelsIndent = m_FormatStream[renderState.formatStreamIndex].pixelsIndent; + + if (indentChange) + { + renderState.x = renderState.pixelsIndent + _drawOffsetX; + } + + //!! for supporting old functionality, store off state in globals + _pixelsIndent = renderState.pixelsIndent; + + // move to the next position in the color stream + renderState.formatStreamIndex++; + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the index in the format stream for the specified text stream index +//----------------------------------------------------------------------------- +int RichText::FindFormatStreamIndexForTextStreamPos(int textStreamIndex) +{ + int formatStreamIndex = 0; + for (; m_FormatStream.IsValidIndex(formatStreamIndex); formatStreamIndex++) + { + if (m_FormatStream[formatStreamIndex].textStreamIndex > textStreamIndex) + break; + } + + // step back to the color change before the new line + formatStreamIndex--; + if (!m_FormatStream.IsValidIndex(formatStreamIndex)) + { + formatStreamIndex = 0; + } + return formatStreamIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: Generates a base renderstate given a index into the text stream +//----------------------------------------------------------------------------- +void RichText::GenerateRenderStateForTextStreamIndex(int textStreamIndex, TRenderState &renderState) +{ + // find where in the format stream we need to be given the specified place in the text stream + renderState.formatStreamIndex = FindFormatStreamIndexForTextStreamPos(textStreamIndex); + + // copy the state data + renderState.textColor = m_FormatStream[renderState.formatStreamIndex].color; + renderState.pixelsIndent = m_FormatStream[renderState.formatStreamIndex].pixelsIndent; + renderState.textClickable = m_FormatStream[renderState.formatStreamIndex].textClickable; +} + +//----------------------------------------------------------------------------- +// Purpose: Called pre render +//----------------------------------------------------------------------------- +void RichText::OnThink() +{ + if (m_bRecalcLineBreaks) + { + _recalcSavedRenderState = true; + RecalculateLineBreaks(); + + // recalculate scrollbar position + if (_invalidateVerticalScrollbarSlider) + { + LayoutVerticalScrollBarSlider(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called when data changes or panel size changes +//----------------------------------------------------------------------------- +void RichText::PerformLayout() +{ + BaseClass::PerformLayout(); + + // force a Repaint + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: inserts a color change into the formatting stream +//----------------------------------------------------------------------------- +void RichText::InsertColorChange(Color col) +{ + // see if color already exists in text stream + TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1]; + if (prevItem.color == col) + { + // inserting same color into stream, just ignore + } + else if (prevItem.textStreamIndex == m_TextStream.Count()) + { + // this item is in the same place; update values + prevItem.color = col; + } + else + { + // add to text stream, based off existing item + TFormatStream streamItem = prevItem; + streamItem.color = col; + streamItem.textStreamIndex = m_TextStream.Count(); + m_FormatStream.AddToTail(streamItem); + } +} + +//----------------------------------------------------------------------------- +// Purpose: inserts a fade into the formatting stream +//----------------------------------------------------------------------------- +void RichText::InsertFade( float flSustain, float flLength ) +{ + // see if color already exists in text stream + TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1]; + if (prevItem.textStreamIndex == m_TextStream.Count()) + { + // this item is in the same place; update values + prevItem.fade.flFadeStartTime = system()->GetCurrentTime() + flSustain; + prevItem.fade.flFadeSustain = flSustain; + prevItem.fade.flFadeLength = flLength; + prevItem.fade.iOriginalAlpha = prevItem.color.a(); + } + else + { + // add to text stream, based off existing item + TFormatStream streamItem = prevItem; + + prevItem.fade.flFadeStartTime = system()->GetCurrentTime() + flSustain; + prevItem.fade.flFadeLength = flLength; + prevItem.fade.flFadeSustain = flSustain; + prevItem.fade.iOriginalAlpha = prevItem.color.a(); + + streamItem.textStreamIndex = m_TextStream.Count(); + m_FormatStream.AddToTail(streamItem); + } +} + +void RichText::ResetAllFades( bool bHold, bool bOnlyExpired, float flNewSustain ) +{ + m_bResetFades = bHold; + + if ( m_bResetFades == false ) + { + for (int i = 1; i < m_FormatStream.Count(); i++) + { + if ( bOnlyExpired == true ) + { + if ( m_FormatStream[i].fade.flFadeStartTime >= system()->GetCurrentTime() ) + continue; + } + + if ( flNewSustain == -1.0f ) + { + flNewSustain = m_FormatStream[i].fade.flFadeSustain; + } + + m_FormatStream[i].fade.flFadeStartTime = system()->GetCurrentTime() + flNewSustain; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: inserts an indent change into the formatting stream +//----------------------------------------------------------------------------- +void RichText::InsertIndentChange(int pixelsIndent) +{ + if (pixelsIndent < 0) + { + pixelsIndent = 0; + } + else if (pixelsIndent > 255) + { + pixelsIndent = 255; + } + + // see if indent change already exists in text stream + TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1]; + if (prevItem.pixelsIndent == pixelsIndent) + { + // inserting same indent into stream, just ignore + } + else if (prevItem.textStreamIndex == m_TextStream.Count()) + { + // this item is in the same place; update + prevItem.pixelsIndent = pixelsIndent; + } + else + { + // add to text stream, based off existing item + TFormatStream streamItem = prevItem; + streamItem.pixelsIndent = pixelsIndent; + streamItem.textStreamIndex = m_TextStream.Count(); + m_FormatStream.AddToTail(streamItem); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Inserts character Start for clickable text, eg. URLS +//----------------------------------------------------------------------------- +void RichText::InsertClickableTextStart( const char *pchClickAction ) +{ + // see if indent change already exists in text stream + TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1]; + TFormatStream *pFormatStream = &prevItem; + if (prevItem.textStreamIndex == m_TextStream.Count()) + { + // this item is in the same place; update + prevItem.textClickable = true; + pFormatStream->m_sClickableTextAction = pchClickAction; + } + else + { + // add to text stream, based off existing item + TFormatStream formatStreamCopy = prevItem; + int iFormatStream = m_FormatStream.AddToTail( formatStreamCopy ); + + // set the new params + pFormatStream = &m_FormatStream[iFormatStream]; + pFormatStream->textStreamIndex = m_TextStream.Count(); + pFormatStream->textClickable = true; + pFormatStream->m_sClickableTextAction = pchClickAction; + } + + // invalidate the layout to recalculate where the click panels should go + InvalidateLineBreakStream(); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Inserts character end for clickable text, eg. URLS +//----------------------------------------------------------------------------- +void RichText::InsertClickableTextEnd() +{ + // see if indent change already exists in text stream + TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1]; + if (!prevItem.textClickable) + { + // inserting same indent into stream, just ignore + } + else if (prevItem.textStreamIndex == m_TextStream.Count()) + { + // this item is in the same place; update + prevItem.textClickable = false; + } + else + { + // add to text stream, based off existing item + TFormatStream streamItem = prevItem; + streamItem.textClickable = false; + streamItem.textStreamIndex = m_TextStream.Count(); + m_FormatStream.AddToTail(streamItem); + } +} + +//----------------------------------------------------------------------------- +// Purpose: moves x,y to the Start of the next line of text +//----------------------------------------------------------------------------- +void RichText::AddAnotherLine(int &cx, int &cy) +{ + cx = _drawOffsetX + _pixelsIndent; + cy += (GetLineHeight() + _drawOffsetY); +} + +//----------------------------------------------------------------------------- +// Purpose: Recalculates line breaks +//----------------------------------------------------------------------------- +void RichText::RecalculateLineBreaks() +{ + if ( !m_bRecalcLineBreaks ) + return; + + int wide = GetWide(); + if (!wide) + return; + + wide -= _drawOffsetX; + + m_bRecalcLineBreaks = false; + _recalcSavedRenderState = true; + if (!HasText()) + return; + + int selection0 = -1, selection1 = -1; + + // subtract the scrollbar width + if (_vertScrollBar->IsVisible()) + { + wide -= _vertScrollBar->GetWide(); + } + + int x = _drawOffsetX, y = _drawOffsetY; + + HFont fontWordStart = INVALID_FONT; + int wordStartIndex = 0; + int lineStartIndex = 0; + bool hasWord = false; + bool justStartedNewLine = true; + bool wordStartedOnNewLine = true; + + int startChar = 0; + if (_recalculateBreaksIndex <= 0) + { + m_LineBreaks.RemoveAll(); + } + else + { + // remove the rest of the linebreaks list since its out of date. + for (int i = _recalculateBreaksIndex + 1; i < m_LineBreaks.Count(); ++i) + { + m_LineBreaks.Remove(i); + --i; // removing shrinks the list! + } + startChar = m_LineBreaks[_recalculateBreaksIndex]; + lineStartIndex = m_LineBreaks[_recalculateBreaksIndex]; + wordStartIndex = lineStartIndex; + } + + // handle the case where this char is a new line, in that case + // we have already taken its break index into account above so skip it. + if (m_TextStream[startChar] == '\r' || m_TextStream[startChar] == '\n') + { + startChar++; + lineStartIndex = startChar; + } + + // cycle to the right url panel for what is visible after the startIndex. + int clickableTextNum = GetClickableTextIndexStart(startChar); + clickableTextNum++; + + // initialize the renderstate with the start + TRenderState renderState; + GenerateRenderStateForTextStreamIndex(startChar, renderState); + _currentTextClickable = false; + + HFont font = _font; + + bool bForceBreak = false; + float flLineWidthSoFar = 0; + + // loop through all the characters + for (int i = startChar; i < m_TextStream.Count(); ++i) + { + wchar_t ch = m_TextStream[i]; + renderState.x = x; + if (UpdateRenderState(i, renderState)) + { + x = renderState.x; + int preI = i; + + // check for clickable text + if (renderState.textClickable != _currentTextClickable) + { + if (renderState.textClickable) + { + // make a new clickable text panel + if (clickableTextNum >= _clickableTextPanels.Count()) + { + _clickableTextPanels.AddToTail(new ClickPanel(this)); + } + + ClickPanel *clickPanel = _clickableTextPanels[clickableTextNum++]; + clickPanel->SetTextIndex(preI, preI); + } + + // url state change + _currentTextClickable = renderState.textClickable; + } + } + + bool bIsWSpace = iswspace( ch ) ? true : false; + + bool bPreviousWordStartedOnNewLine = wordStartedOnNewLine; + int iPreviousWordStartIndex = wordStartIndex; + if ( !bIsWSpace && ch != L'\t' && ch != L'\n' && ch != L'\r' ) + { + if (!hasWord) + { + // Start a new word + wordStartIndex = i; + hasWord = true; + wordStartedOnNewLine = justStartedNewLine; + fontWordStart = font; + } + // else append to the current word + } + else + { + // whitespace/punctuation character + // end the word + hasWord = false; + } + + float w = 0; + wchar_t wchBefore = 0; + wchar_t wchAfter = 0; + + if ( i > 0 && i > lineStartIndex && i != selection0 && i-1 != selection1 ) + wchBefore = m_TextStream[i-1]; + if ( i < m_TextStream.Count() - 1 && i+1 != selection0 && i != selection1 ) + wchAfter = m_TextStream[i+1]; + + float flabcA; + surface()->GetKernedCharWidth( font, ch, wchBefore, wchAfter, w, flabcA ); + flLineWidthSoFar += w; + + // See if we've exceeded the width we have available, with + if ( floor(flLineWidthSoFar + 0.6) + x > wide ) + { + bForceBreak = true; + } + + if (!iswcntrl(ch)) + { + justStartedNewLine = false; + } + + if ( bForceBreak || ch == '\r' || ch == '\n' ) + { + bForceBreak = false; + // add another line + AddAnotherLine(x, y); + + if ( ch == '\r' || ch == '\n' ) + { + // skip the newline so it's not at the beginning of the new line + lineStartIndex = i + 1; + m_LineBreaks.AddToTail(i + 1); + } + else if ( bPreviousWordStartedOnNewLine || iPreviousWordStartIndex <= lineStartIndex ) + { + lineStartIndex = i; + m_LineBreaks.AddToTail( i ); + + if (renderState.textClickable) + { + // need to split the url into two panels + int oldIndex = _clickableTextPanels[clickableTextNum - 1]->GetTextIndex(); + + // make a new clickable text panel + if (clickableTextNum >= _clickableTextPanels.Count()) + { + _clickableTextPanels.AddToTail(new ClickPanel(this)); + } + + ClickPanel *clickPanel = _clickableTextPanels[clickableTextNum++]; + clickPanel->SetTextIndex(oldIndex, i); + } + } + else + { + m_LineBreaks.AddToTail( iPreviousWordStartIndex ); + lineStartIndex = iPreviousWordStartIndex; + i = iPreviousWordStartIndex; + + TRenderState renderStateAtLastWord; + GenerateRenderStateForTextStreamIndex( i, renderStateAtLastWord ); + + // If the word is clickable, and that started prior to the beginning of the word, then we must split the click panel + if ( renderStateAtLastWord.textClickable && m_FormatStream[ renderStateAtLastWord.formatStreamIndex ].textStreamIndex < i ) + { + // need to split the url into two panels + int oldIndex = _clickableTextPanels[clickableTextNum - 1]->GetTextIndex(); + + // make a new clickable text panel + if (clickableTextNum >= _clickableTextPanels.Count()) + { + _clickableTextPanels.AddToTail(new ClickPanel(this)); + } + + ClickPanel *clickPanel = _clickableTextPanels[clickableTextNum++]; + clickPanel->SetTextIndex(oldIndex, i); + } + } + + flLineWidthSoFar = 0; + justStartedNewLine = true; + hasWord = false; + wordStartedOnNewLine = false; + _currentTextClickable = false; + continue; + } + } + + // end the list + m_LineBreaks.AddToTail(MAX_BUFFER_SIZE); + + // set up the scrollbar + _invalidateVerticalScrollbarSlider = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Recalculate where the vertical scroll bar slider should be +// based on the current cursor line we are on. +//----------------------------------------------------------------------------- +void RichText::LayoutVerticalScrollBarSlider() +{ + _invalidateVerticalScrollbarSlider = false; + + // set up the scrollbar + //if (!_vertScrollBar->IsVisible()) + // return; + + + // see where the scrollbar currently is + int previousValue = _vertScrollBar->GetValue(); + bool bCurrentlyAtEnd = false; + int rmin, rmax; + _vertScrollBar->GetRange(rmin, rmax); + if (rmax && (previousValue + rmin + _vertScrollBar->GetRangeWindow() == rmax)) + { + bCurrentlyAtEnd = true; + } + + // work out position to put scrollbar, factoring in insets + int wide, tall; + GetSize( wide, tall ); + + _vertScrollBar->SetPos( wide - _vertScrollBar->GetWide(), 0 ); + // scrollbar is inside the borders. + _vertScrollBar->SetSize( _vertScrollBar->GetWide(), tall ); + + // calculate how many lines we can fully display + int displayLines = tall / (GetLineHeight() + _drawOffsetY); + int numLines = m_LineBreaks.Count(); + + if (numLines <= displayLines) + { + // disable the scrollbar + _vertScrollBar->SetEnabled(false); + _vertScrollBar->SetRange(0, numLines); + _vertScrollBar->SetRangeWindow(numLines); + _vertScrollBar->SetValue(0); + + if ( m_bUnusedScrollbarInvis ) + { + SetVerticalScrollbar( false ); + } + } + else + { + if ( m_bUnusedScrollbarInvis ) + { + SetVerticalScrollbar( true ); + } + + // set the scrollbars range + _vertScrollBar->SetRange(0, numLines); + _vertScrollBar->SetRangeWindow(displayLines); + _vertScrollBar->SetEnabled(true); + + // this should make it scroll one line at a time + _vertScrollBar->SetButtonPressedScrollValue(1); + if (bCurrentlyAtEnd) + { + _vertScrollBar->SetValue(numLines - displayLines); + } + _vertScrollBar->InvalidateLayout(); + _vertScrollBar->Repaint(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets whether a vertical scrollbar is visible +//----------------------------------------------------------------------------- +void RichText::SetVerticalScrollbar(bool state) +{ + if (_vertScrollBar->IsVisible() != state) + { + _vertScrollBar->SetVisible(state); + InvalidateLineBreakStream(); + InvalidateLayout(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Create cut/copy/paste dropdown menu +//----------------------------------------------------------------------------- +void RichText::CreateEditMenu() +{ + // create a drop down cut/copy/paste menu appropriate for this object's states + if (m_pEditMenu) + delete m_pEditMenu; + m_pEditMenu = new Menu(this, "EditMenu"); + + + // add cut/copy/paste drop down options if its editable, just copy if it is not + m_pEditMenu->AddMenuItem("C&opy", new KeyValues("DoCopySelected"), this); + + m_pEditMenu->SetVisible(false); + m_pEditMenu->SetParent(this); + m_pEditMenu->AddActionSignalTarget(this); +} + +//----------------------------------------------------------------------------- +// Purpose: We want single line windows to scroll horizontally and select text +// in response to clicking and holding outside window +//----------------------------------------------------------------------------- +void RichText::OnMouseFocusTicked() +{ + // if a button is down move the scrollbar slider the appropriate direction + if (_mouseDragSelection) // text is being selected via mouse clicking and dragging + { + OnCursorMoved(0,0); // we want the text to scroll as if we were dragging + } +} + +//----------------------------------------------------------------------------- +// Purpose: If a cursor enters the window, we are not elegible for +// MouseFocusTicked events +//----------------------------------------------------------------------------- +void RichText::OnCursorEntered() +{ + _mouseDragSelection = false; // outside of window dont recieve drag scrolling ticks +} + +//----------------------------------------------------------------------------- +// Purpose: When the cursor is outside the window, if we are holding the mouse +// button down, then we want the window to scroll the text one char at a time using Ticks +//----------------------------------------------------------------------------- +void RichText::OnCursorExited() +{ + // outside of window recieve drag scrolling ticks + if (_mouseSelection) + { + _mouseDragSelection = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle selection of text by mouse +//----------------------------------------------------------------------------- +void RichText::OnCursorMoved(int x, int y) +{ + if (_mouseSelection) + { + // update the cursor position + int x, y; + input()->GetCursorPos(x, y); + ScreenToLocal(x, y); + _cursorPos = PixelToCursorSpace(x, y); + + if (_cursorPos != _select[1]) + { + _select[1] = _cursorPos; + Repaint(); + } + // Msg( "selecting range [%d..%d]\n", _select[0], _select[1] ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle mouse button down events. +//----------------------------------------------------------------------------- +void RichText::OnMousePressed(MouseCode code) +{ + if (code == MOUSE_LEFT) + { + // clear current selection + SelectNone(); + + // move the cursor to where the mouse was pressed + int x, y; + input()->GetCursorPos(x, y); + ScreenToLocal(x, y); + + _cursorPos = PixelToCursorSpace(x, y); + + if ( m_bInteractive ) + { + // enter selection mode + input()->SetMouseCapture(GetVPanel()); + _mouseSelection = true; + + if (_select[0] < 0) + { + // if no initial selection position, Start selection position at cursor + _select[0] = _cursorPos; + } + _select[1] = _cursorPos; + } + + RequestFocus(); + Repaint(); + } + else if (code == MOUSE_RIGHT) // check for context menu open + { + if ( m_bInteractive ) + { + CreateEditMenu(); + Assert(m_pEditMenu); + + OpenEditMenu(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle mouse button up events +//----------------------------------------------------------------------------- +void RichText::OnMouseReleased(MouseCode code) +{ + _mouseSelection = false; + input()->SetMouseCapture(NULL); + + // make sure something has been selected + int cx0, cx1; + if (GetSelectedRange(cx0, cx1)) + { + if (cx1 - cx0 == 0) + { + // nullify selection + _select[0] = -1; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle mouse double clicks +//----------------------------------------------------------------------------- +void RichText::OnMouseDoublePressed(MouseCode code) +{ + if ( !m_bInteractive ) + return; + + // left double clicking on a word selects the word + if (code == MOUSE_LEFT) + { + // move the cursor just as if you single clicked. + OnMousePressed(code); + // then find the start and end of the word we are in to highlight it. + int selectSpot[2]; + GotoWordLeft(); + selectSpot[0] = _cursorPos; + GotoWordRight(); + selectSpot[1] = _cursorPos; + + if ( _cursorPos > 0 && (_cursorPos-1) < m_TextStream.Count() ) + { + if (iswspace(m_TextStream[_cursorPos-1])) + { + selectSpot[1]--; + _cursorPos--; + } + } + + _select[0] = selectSpot[0]; + _select[1] = selectSpot[1]; + _mouseSelection = true; + } + +} + +//----------------------------------------------------------------------------- +// Purpose: Turn off text selection code when mouse button is not down +//----------------------------------------------------------------------------- +void RichText::OnMouseCaptureLost() +{ + _mouseSelection = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Masks which keys get chained up +// Maps keyboard input to text window functions. +//----------------------------------------------------------------------------- +void RichText::OnKeyCodeTyped(KeyCode code) +{ + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + bool winkey = (input()->IsKeyDown(KEY_LWIN) || input()->IsKeyDown(KEY_RWIN)); + bool fallThrough = false; + + if ( ctrl || ( winkey && IsOSX() ) ) + { + switch(code) + { + case KEY_INSERT: + case KEY_C: + case KEY_X: + { + CopySelected(); + break; + } + case KEY_PAGEUP: + case KEY_HOME: + { + GotoTextStart(); + break; + } + case KEY_PAGEDOWN: + case KEY_END: + { + GotoTextEnd(); + break; + } + default: + { + fallThrough = true; + break; + } + } + } + else if (alt) + { + // do nothing with ALT-x keys + fallThrough = true; + } + else + { + switch(code) + { + case KEY_TAB: + case KEY_LSHIFT: + case KEY_RSHIFT: + case KEY_ESCAPE: + case KEY_ENTER: + { + fallThrough = true; + break; + } + case KEY_DELETE: + { + if (shift) + { + // shift-delete is cut + CopySelected(); + } + break; + } + case KEY_HOME: + { + GotoTextStart(); + break; + } + case KEY_END: + { + GotoTextEnd(); + break; + } + case KEY_PAGEUP: + { + // if there is a scroll bar scroll down one rangewindow + if (_vertScrollBar->IsVisible()) + { + int window = _vertScrollBar->GetRangeWindow(); + int newval = _vertScrollBar->GetValue(); + _vertScrollBar->SetValue(newval - window - 1); + } + break; + + } + case KEY_PAGEDOWN: + { + // if there is a scroll bar scroll down one rangewindow + if (_vertScrollBar->IsVisible()) + { + int window = _vertScrollBar->GetRangeWindow(); + int newval = _vertScrollBar->GetValue(); + _vertScrollBar->SetValue(newval + window + 1); + } + break; + } + default: + { + // return if any other char is pressed. + // as it will be a unicode char. + // and we don't want select[1] changed unless a char was pressed that this fxn handles + return; + } + } + } + + // select[1] is the location in the line where the blinking cursor started + _select[1] = _cursorPos; + + // chain back on some keys + if (fallThrough) + { + BaseClass::OnKeyCodeTyped(code); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Scrolls the list according to the mouse wheel movement +//----------------------------------------------------------------------------- +void RichText::OnMouseWheeled(int delta) +{ + MoveScrollBar(delta); +} + +//----------------------------------------------------------------------------- +// Purpose: Scrolls the list +// Input : delta - amount to move scrollbar up +//----------------------------------------------------------------------------- +void RichText::MoveScrollBar(int delta) +{ + MoveScrollBarDirect( delta * 3 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Scrolls the list +// Input : delta - amount to move scrollbar up +//----------------------------------------------------------------------------- +void RichText::MoveScrollBarDirect(int delta) +{ + if (_vertScrollBar->IsVisible()) + { + int val = _vertScrollBar->GetValue(); + val -= delta; + _vertScrollBar->SetValue(val); + _recalcSavedRenderState = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: set the maximum number of chars in the text buffer +//----------------------------------------------------------------------------- +void RichText::SetMaximumCharCount(int maxChars) +{ + _maxCharCount = maxChars; +} + +//----------------------------------------------------------------------------- +// Purpose: Find out what line the cursor is on +//----------------------------------------------------------------------------- +int RichText::GetCursorLine() +{ + // always returns the last place + int pos = m_LineBreaks[m_LineBreaks.Count() - 1]; + Assert(pos == MAX_BUFFER_SIZE); + return pos; +} + +//----------------------------------------------------------------------------- +// Purpose: Move the cursor over to the Start of the next word to the right +//----------------------------------------------------------------------------- +void RichText::GotoWordRight() +{ + // search right until we hit a whitespace character or a newline + while (++_cursorPos < m_TextStream.Count()) + { + if (iswspace(m_TextStream[_cursorPos])) + break; + } + + // search right until we hit an nonspace character + while (++_cursorPos < m_TextStream.Count()) + { + if (!iswspace(m_TextStream[_cursorPos])) + break; + } + + if (_cursorPos > m_TextStream.Count()) + { + _cursorPos = m_TextStream.Count(); + } + + // now we are at the start of the next word + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Move the cursor over to the Start of the next word to the left +//----------------------------------------------------------------------------- +void RichText::GotoWordLeft() +{ + if (_cursorPos < 1) + return; + + // search left until we hit an nonspace character + while (--_cursorPos >= 0) + { + if (!iswspace(m_TextStream[_cursorPos])) + break; + } + + // search left until we hit a whitespace character + while (--_cursorPos >= 0) + { + if (iswspace(m_TextStream[_cursorPos])) + { + break; + } + } + + // we end one character off + _cursorPos++; + + // now we are at the start of the previous word + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Move cursor to the Start of the text buffer +//----------------------------------------------------------------------------- +void RichText::GotoTextStart() +{ + _cursorPos = 0; // set cursor to start + _invalidateVerticalScrollbarSlider = true; + // force scrollbar to the top + _vertScrollBar->SetValue(0); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Move cursor to the end of the text buffer +//----------------------------------------------------------------------------- +void RichText::GotoTextEnd() +{ + _cursorPos = m_TextStream.Count(); // set cursor to end of buffer + _invalidateVerticalScrollbarSlider = true; + + // force the scrollbar to the bottom + int min, max; + _vertScrollBar->GetRange(min, max); + _vertScrollBar->SetValue(max); + + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Culls the text stream down to a managable size +//----------------------------------------------------------------------------- +void RichText::TruncateTextStream() +{ + if (_maxCharCount < 1) + return; + + // choose a point to cull at + int cullPos = _maxCharCount / 2; + + // kill half the buffer + m_TextStream.RemoveMultiple(0, cullPos); + + // work out where in the format stream we can start + int formatIndex = FindFormatStreamIndexForTextStreamPos(cullPos); + if (formatIndex > 0) + { + // take a copy, make it first + m_FormatStream[0] = m_FormatStream[formatIndex]; + m_FormatStream[0].textStreamIndex = 0; + // kill the others + m_FormatStream.RemoveMultiple(1, formatIndex); + } + + // renormalize the remainder of the format stream + for (int i = 1; i < m_FormatStream.Count(); i++) + { + Assert(m_FormatStream[i].textStreamIndex > cullPos); + m_FormatStream[i].textStreamIndex -= cullPos; + } + + // mark everything to be recalculated + InvalidateLineBreakStream(); + InvalidateLayout(); + _invalidateVerticalScrollbarSlider = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Insert a character into the text buffer +//----------------------------------------------------------------------------- +void RichText::InsertChar(wchar_t wch) +{ + // throw away redundant linefeed characters + if ( wch == '\r' ) + return; + + if (_maxCharCount > 0 && m_TextStream.Count() > _maxCharCount) + { + TruncateTextStream(); + } + + // insert the new char at the end of the buffer + m_TextStream.AddToTail(wch); + + // mark the linebreak steam as needing recalculating from that point + _recalculateBreaksIndex = m_LineBreaks.Count() - 2; + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Insert a string into the text buffer, this is just a series +// of char inserts because we have to check each char is ok to insert +//----------------------------------------------------------------------------- +void RichText::InsertString(const char *text) +{ + if (text[0] == '#') + { + wchar_t unicode[ 1024 ]; + ResolveLocalizedTextAndVariables( text, unicode, sizeof( unicode ) ); + InsertString( unicode ); + return; + } + + // upgrade the ansi text to unicode to display it + int len = strlen(text); + wchar_t *unicode = (wchar_t *)_alloca((len + 1) * sizeof(wchar_t)); + Q_UTF8ToUnicode(text, unicode, ((len + 1) * sizeof(wchar_t))); + InsertString(unicode); +} + +//----------------------------------------------------------------------------- +// Purpose: Insertsa a unicode string into the buffer +//----------------------------------------------------------------------------- +void RichText::InsertString(const wchar_t *wszText) +{ + // insert the whole string + for (const wchar_t *ch = wszText; *ch != 0; ++ch) + { + InsertChar(*ch); + } + InvalidateLayout(); + m_bRecalcLineBreaks = true; + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Declare a selection empty +//----------------------------------------------------------------------------- +void RichText::SelectNone() +{ + // tag the selection as empty + _select[0] = -1; + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Load in the selection range so cx0 is the Start and cx1 is the end +// from smallest to highest (right to left) +//----------------------------------------------------------------------------- +bool RichText::GetSelectedRange(int &cx0, int &cx1) +{ + // if there is nothing selected return false + if (_select[0] == -1) + return false; + + // sort the two position so cx0 is the smallest + cx0 = _select[0]; + cx1 = _select[1]; + if (cx1 < cx0) + { + int temp = cx0; + cx0 = cx1; + cx1 = temp; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Opens the cut/copy/paste dropdown menu +//----------------------------------------------------------------------------- +void RichText::OpenEditMenu() +{ + // get cursor position, this is local to this text edit window + // so we need to adjust it relative to the parent + int cursorX, cursorY; + input()->GetCursorPos(cursorX, cursorY); + + /* !! disabled since it recursively gets panel pointers, potentially across dll boundaries, + and doesn't need to be necessary (it's just for handling windowed mode) + + // find the frame that has no parent (the one on the desktop) + Panel *panel = this; + while ( panel->GetParent() != NULL) + { + panel = panel->GetParent(); + } + panel->ScreenToLocal(cursorX, cursorY); + int x, y; + // get base panel's postition + panel->GetPos(x, y); + + // adjust our cursor position accordingly + cursorX += x; + cursorY += y; + */ + + int x0, x1; + if (GetSelectedRange(x0, x1)) // there is something selected + { + m_pEditMenu->SetItemEnabled("&Cut", true); + m_pEditMenu->SetItemEnabled("C&opy", true); + } + else // there is nothing selected, disable cut/copy options + { + m_pEditMenu->SetItemEnabled("&Cut", false); + m_pEditMenu->SetItemEnabled("C&opy", false); + } + m_pEditMenu->SetVisible(true); + m_pEditMenu->RequestFocus(); + + // relayout the menu immediately so that we know it's size + m_pEditMenu->InvalidateLayout(true); + int menuWide, menuTall; + m_pEditMenu->GetSize(menuWide, menuTall); + + // work out where the cursor is and therefore the best place to put the menu + int wide, tall; + surface()->GetScreenSize(wide, tall); + + if (wide - menuWide > cursorX) + { + // menu hanging right + if (tall - menuTall > cursorY) + { + // menu hanging down + m_pEditMenu->SetPos(cursorX, cursorY); + } + else + { + // menu hanging up + m_pEditMenu->SetPos(cursorX, cursorY - menuTall); + } + } + else + { + // menu hanging left + if (tall - menuTall > cursorY) + { + // menu hanging down + m_pEditMenu->SetPos(cursorX - menuWide, cursorY); + } + else + { + // menu hanging up + m_pEditMenu->SetPos(cursorX - menuWide, cursorY - menuTall); + } + } + + m_pEditMenu->RequestFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Cuts the selected chars from the buffer and +// copies them into the clipboard +//----------------------------------------------------------------------------- +void RichText::CutSelected() +{ + CopySelected(); + // have to request focus if we used the menu + RequestFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Copies the selected chars into the clipboard +//----------------------------------------------------------------------------- +void RichText::CopySelected() +{ + int x0, x1; + if (GetSelectedRange(x0, x1)) + { + CUtlVector buf; + for (int i = x0; i <= x1; i++) + { + if ( m_TextStream.IsValidIndex(i) == false ) + continue; + + if (m_TextStream[i] == '\n') + { + buf.AddToTail( '\r' ); + } + // remove any rich edit commands + buf.AddToTail(m_TextStream[i]); + } + buf.AddToTail('\0'); + system()->SetClipboardText(buf.Base(), buf.Count() - 1); + } + + // have to request focus if we used the menu + RequestFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the index in the text buffer of the +// character the drawing should Start at +//----------------------------------------------------------------------------- +int RichText::GetStartDrawIndex(int &lineBreakIndexIndex) +{ + int startIndex = 0; + int startLine = _vertScrollBar->GetValue(); + + if ( startLine >= m_LineBreaks.Count() ) // incase the line breaks got reset and the scroll bar hasn't + { + startLine = m_LineBreaks.Count() - 1; + } + + lineBreakIndexIndex = startLine; + if (startLine && startLine < m_LineBreaks.Count()) + { + startIndex = m_LineBreaks[startLine - 1]; + } + + return startIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: Get a string from text buffer +// Input: offset - index to Start reading from +// bufLen - length of string +//----------------------------------------------------------------------------- +void RichText::GetText(int offset, wchar_t *buf, int bufLenInBytes) +{ + if (!buf) + return; + + Assert( bufLenInBytes >= sizeof(buf[0]) ); + int bufLen = bufLenInBytes / sizeof(wchar_t); + int i; + for (i = offset; i < (offset + bufLen - 1); i++) + { + if (i >= m_TextStream.Count()) + break; + + buf[i-offset] = m_TextStream[i]; + } + buf[(i-offset)] = 0; + buf[bufLen-1] = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: gets text from the buffer +//----------------------------------------------------------------------------- +void RichText::GetText(int offset, char *pch, int bufLenInBytes) +{ + wchar_t rgwchT[4096]; + GetText(offset, rgwchT, sizeof(rgwchT)); + Q_UnicodeToUTF8(rgwchT, pch, bufLenInBytes); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the font of the buffer text +//----------------------------------------------------------------------------- +void RichText::SetFont(HFont font) +{ + _font = font; + InvalidateLayout(); + m_bRecalcLineBreaks = true; + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the scrollbar slider is moved +//----------------------------------------------------------------------------- +void RichText::OnSliderMoved() +{ + _recalcSavedRenderState = true; + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool RichText::RequestInfo(KeyValues *outputData) +{ + if (!stricmp(outputData->GetName(), "GetText")) + { + wchar_t wbuf[512]; + GetText(0, wbuf, sizeof(wbuf)); + outputData->SetWString("text", wbuf); + return true; + } + + return BaseClass::RequestInfo(outputData); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void RichText::OnSetText(const wchar_t *text) +{ + SetText(text); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when a URL, etc has been clicked on +//----------------------------------------------------------------------------- +void RichText::OnClickPanel(int index) +{ + wchar_t wBuf[512]; + int outIndex = 0; + + // parse out the clickable text, and send it to our listeners + _currentTextClickable = true; + TRenderState renderState; + GenerateRenderStateForTextStreamIndex(index, renderState); + for (int i = index; i < (sizeof(wBuf) - 1) && i < m_TextStream.Count(); i++) + { + // stop getting characters when text is no longer clickable + UpdateRenderState(i, renderState); + if (!renderState.textClickable) + break; + + // copy out the character + wBuf[outIndex++] = m_TextStream[i]; + } + + wBuf[outIndex] = 0; + + int iFormatSteam = FindFormatStreamIndexForTextStreamPos( index ); + if ( m_FormatStream[iFormatSteam].m_sClickableTextAction ) + { + Q_UTF8ToUnicode( m_FormatStream[iFormatSteam].m_sClickableTextAction.String(), wBuf, sizeof( wBuf ) ); + } + + PostActionSignal(new KeyValues("TextClicked", "text", wBuf)); + OnTextClicked(wBuf); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void RichText::ApplySettings(KeyValues *inResourceData) +{ + BaseClass::ApplySettings(inResourceData); + SetMaximumCharCount(inResourceData->GetInt("maxchars", -1)); + SetVerticalScrollbar(inResourceData->GetInt("scrollbar", 1)); + + // get the starting text, if any + const char *text = inResourceData->GetString("text", ""); + if (*text) + { + delete [] m_pszInitialText; + int len = Q_strlen(text) + 1; + m_pszInitialText = new char[ len ]; + Q_strncpy( m_pszInitialText, text, len ); + SetText(text); + } + else + { + const char *textfilename = inResourceData->GetString("textfile", NULL); + if ( textfilename ) + { + FileHandle_t f = g_pFullFileSystem->Open( textfilename, "rt" ); + if (!f) + { + Warning( "RichText: textfile parameter '%s' not found.\n", textfilename ); + return; + } + + int len = g_pFullFileSystem->Size( f ); + delete [] m_pszInitialText; + m_pszInitialText = new char[ len + 1 ]; + g_pFullFileSystem->Read( m_pszInitialText, len, f ); + m_pszInitialText[len - 1] = 0; + SetText( m_pszInitialText ); + + g_pFullFileSystem->Close( f ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void RichText::GetSettings(KeyValues *outResourceData) +{ + BaseClass::GetSettings(outResourceData); + outResourceData->SetInt("maxchars", _maxCharCount); + outResourceData->SetInt("scrollbar", _vertScrollBar->IsVisible() ); + if (m_pszInitialText) + { + outResourceData->SetString("text", m_pszInitialText); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *RichText::GetDescription() +{ + static char buf[1024]; + Q_snprintf(buf, sizeof(buf), "%s, string text, bool scrollbar", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the number of lines in the window +//----------------------------------------------------------------------------- +int RichText::GetNumLines() +{ + return m_LineBreaks.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the height of the text entry window so all text will fit inside +//----------------------------------------------------------------------------- +void RichText::SetToFullHeight() +{ + PerformLayout(); + int wide, tall; + GetSize(wide, tall); + + tall = GetNumLines() * (GetLineHeight() + _drawOffsetY) + _drawOffsetY + 2; + SetSize (wide, tall); + PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Select all the text. +//----------------------------------------------------------------------------- +void RichText::SelectAllText() +{ + _cursorPos = 0; + _select[0] = 0; + _select[1] = m_TextStream.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: Select all the text. +//----------------------------------------------------------------------------- +void RichText::SelectNoText() +{ + _select[0] = 0; + _select[1] = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void RichText::OnSetFocus() +{ + BaseClass::OnSetFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Invalidates the current linebreak stream +//----------------------------------------------------------------------------- +void RichText::InvalidateLineBreakStream() +{ + // clear the buffer + m_LineBreaks.RemoveAll(); + m_LineBreaks.AddToTail(MAX_BUFFER_SIZE); + _recalculateBreaksIndex = 0; + m_bRecalcLineBreaks = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Inserts a text string while making URLs clickable/different color +// Input : *text - string that may contain URLs to make clickable/color coded +// URLTextColor - color for URL text +// normalTextColor - color for normal text +//----------------------------------------------------------------------------- +void RichText::InsertPossibleURLString(const char* text, Color URLTextColor, Color normalTextColor) +{ + InsertColorChange(normalTextColor); + + // parse out the string for URL's + int len = Q_strlen(text), pos = 0; + bool clickable = false; + char *pchURLText = (char *)stackalloc( len + 1 ); + char *pchURL = (char *)stackalloc( len + 1 ); + + while (pos < len) + { + pos = ParseTextStringForUrls( text, pos, pchURLText, len, pchURL, len, clickable ); + + if ( clickable ) + { + InsertClickableTextStart( pchURL ); + InsertColorChange( URLTextColor ); + } + + InsertString( pchURLText ); + + if ( clickable ) + { + InsertColorChange(normalTextColor); + InsertClickableTextEnd(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: looks for URLs in the string and returns information about the URL +//----------------------------------------------------------------------------- +int RichText::ParseTextStringForUrls( const char *text, int startPos, char *pchURLText, int cchURLText, char *pchURL, int cchURL, bool &clickable ) +{ + // scan for text that looks like a URL + int i = startPos; + while (text[i] != 0) + { + bool bURLFound = false; + + if ( !Q_strnicmp(text + i, "" ); + Q_strncpy( pchURL, text + i, min( pchURLEnd - text - i + 1, cchURL ) ); + i += ( pchURLEnd - text - i + 1 ); + + // get the url text + pchURLEnd = Q_strstr( text, "" ); + Q_strncpy( pchURLText, text + i, min( pchURLEnd - text - i + 1, cchURLText ) ); + i += ( pchURLEnd - text - i ); + i += Q_strlen( "" ); + + // we're done + return i; + } + else if (!Q_strnicmp(text + i, "www.", 4)) + { + // scan ahead for another '.' + bool bPeriodFound = false; + for (const char *ch = text + i + 5; ch != 0; ch++) + { + if (*ch == '.') + { + bPeriodFound = true; + break; + } + } + + // URL found + if (bPeriodFound) + { + bURLFound = true; + } + } + else if (!Q_strnicmp(text + i, "http://", 7)) + { + bURLFound = true; + } + else if (!Q_strnicmp(text + i, "ftp://", 6)) + { + bURLFound = true; + } + else if (!Q_strnicmp(text + i, "steam://", 8)) + { + bURLFound = true; + } + else if (!Q_strnicmp(text + i, "steambeta://", 12)) + { + bURLFound = true; + } + else if (!Q_strnicmp(text + i, "mailto:", 7)) + { + bURLFound = true; + } + else if (!Q_strnicmp(text + i, "\\\\", 2)) + { + bURLFound = true; + } + + if (bURLFound) + { + if (i == startPos) + { + // we're at the Start of a URL, so parse that out + clickable = true; + int outIndex = 0; + while (text[i] != 0 && !iswspace(text[i])) + { + pchURLText[outIndex++] = text[i++]; + } + pchURLText[outIndex] = 0; + Q_strncpy( pchURL, pchURLText, cchURL ); + return i; + } + else + { + // no url + break; + } + } + + // increment and loop + i++; + } + + // nothing found; + // parse out the text before the end + clickable = false; + int outIndex = 0; + int fromIndex = startPos; + while ( fromIndex < i && outIndex < cchURLText ) + { + pchURLText[outIndex++] = text[fromIndex++]; + } + pchURLText[outIndex] = 0; + Q_strncpy( pchURL, pchURLText, cchURL ); + + return i; +} + +//----------------------------------------------------------------------------- +// Purpose: Executes the text-clicked command, which opens a web browser by +// default. +//----------------------------------------------------------------------------- +void RichText::OnTextClicked(const wchar_t *wszText) +{ + // Strip leading/trailing quotes, which may be present on href tags or may not. + const wchar_t *pwchURL = wszText; + if ( pwchURL[0] == L'"' || pwchURL[0] == L'\'' ) + pwchURL = wszText + 1; + + char ansi[2048]; + Q_UnicodeToUTF8( pwchURL, ansi, sizeof(ansi) ); + + size_t strLen = Q_strlen(ansi); + if ( strLen && ( ansi[strLen-1] == '"' || ansi[strLen] == '\'' ) ) + { + ansi[strLen-1] = 0; + } + + if ( m_hPanelToHandleClickingURLs.Get() ) + { + PostMessage( m_hPanelToHandleClickingURLs.Get(), new KeyValues( "URLClicked", "url", ansi ) ); + } + else + { + system()->ShellExecute( "open", ansi ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void RichText::SetURLClickedHandler( Panel *pPanelToHandleClickMsg ) +{ + m_hPanelToHandleClickingURLs = pPanelToHandleClickMsg; +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +bool RichText::IsScrollbarVisible() +{ + return _vertScrollBar->IsVisible(); +} + +void RichText::SetUnderlineFont( HFont font ) +{ + m_hFontUnderline = font; +} + +bool RichText::IsAllTextAlphaZero() const +{ + return m_bAllTextAlphaIsZero; +} + +bool RichText::HasText() const +{ + int c = m_TextStream.Count(); + if ( c == 0 ) + { + return false; + } + return true; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Returns the height of the base font +//----------------------------------------------------------------------------- +int RichText::GetLineHeight() +{ + return surface()->GetFontTall( _font ); +} + + +#ifdef DBGFLAG_VALIDATE +//----------------------------------------------------------------------------- +// Purpose: Run a global validation pass on all of our data structures and memory +// allocations. +// Input: validator - Our global validator object +// pchName - Our name (typically a member var in our container) +//----------------------------------------------------------------------------- +void RichText::Validate( CValidator &validator, char *pchName ) +{ + validator.Push( "vgui::RichText", this, pchName ); + + ValidateObj( m_TextStream ); + ValidateObj( m_FormatStream ); + ValidateObj( m_LineBreaks ); + ValidateObj( _clickableTextPanels ); + validator.ClaimMemory( m_pszInitialText ); + + BaseClass::Validate( validator, "vgui::RichText" ); + + validator.Pop(); +} +#endif // DBGFLAG_VALIDATE + diff --git a/mp/src/vgui2/vgui_controls/RotatingProgressBar.cpp b/mp/src/vgui2/vgui_controls/RotatingProgressBar.cpp index b3356934..6ab9ccb5 100644 --- a/mp/src/vgui2/vgui_controls/RotatingProgressBar.cpp +++ b/mp/src/vgui2/vgui_controls/RotatingProgressBar.cpp @@ -1,200 +1,200 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "mathlib/mathlib.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY( RotatingProgressBar ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -RotatingProgressBar::RotatingProgressBar(Panel *parent, const char *panelName) : ProgressBar(parent, panelName) -{ - m_flStartRadians = 0; - m_flEndRadians = 0; - m_flLastAngle = 0; - - m_nTextureId = -1; - m_pszImageName = NULL; - - m_flTickDelay = 30; - - ivgui()->AddTickSignal(GetVPanel(), m_flTickDelay ); - - SetPaintBorderEnabled(false); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -RotatingProgressBar::~RotatingProgressBar() -{ - if ( vgui::surface() && m_nTextureId != -1 ) - { - vgui::surface()->DestroyTextureID( m_nTextureId ); - m_nTextureId = -1; - } - - delete [] m_pszImageName; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void RotatingProgressBar::ApplySettings(KeyValues *inResourceData) -{ - const char *imageName = inResourceData->GetString("image", ""); - if (*imageName) - { - SetImage( imageName ); - } - - // Find min and max rotations in radians - m_flStartRadians = DEG2RAD(inResourceData->GetFloat( "start_degrees", 0 ) ); - m_flEndRadians = DEG2RAD( inResourceData->GetFloat( "end_degrees", 0 ) ); - - // Start at 0 progress - m_flLastAngle = m_flStartRadians; - - // approach speed is specified in degrees per second. - // convert to radians per 1/30th of a second - float flDegressPerSecond = DEG2RAD( inResourceData->GetFloat( "approach_speed", 360.0 ) ); // default is super fast - m_flApproachSpeed = flDegressPerSecond * ( m_flTickDelay / 1000.0f ); // divide by number of frames in a second - - m_flRotOriginX = inResourceData->GetFloat( "rot_origin_x_percent", 0.5f ); - m_flRotOriginY = inResourceData->GetFloat( "rot_origin_y_percent", 0.5f ); - - m_flRotatingX = inResourceData->GetFloat( "rotating_x", 0 ); - m_flRotatingY = inResourceData->GetFloat( "rotating_y", 0 ); - m_flRotatingWide = inResourceData->GetFloat( "rotating_wide", 0 ); - m_flRotatingTall = inResourceData->GetFloat( "rotating_tall", 0 ); - - BaseClass::ApplySettings( inResourceData ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void RotatingProgressBar::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - if ( m_pszImageName && strlen( m_pszImageName ) > 0 ) - { - if ( m_nTextureId == -1 ) - { - m_nTextureId = surface()->CreateNewTextureID(); - } - - surface()->DrawSetTextureFile( m_nTextureId, m_pszImageName, true, false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets an image by file name -//----------------------------------------------------------------------------- -void RotatingProgressBar::SetImage(const char *imageName) -{ - if ( m_pszImageName ) - { - delete [] m_pszImageName; - m_pszImageName = NULL; - } - - const char *pszDir = "vgui/"; - int len = Q_strlen(imageName) + 1; - len += strlen(pszDir); - m_pszImageName = new char[ len ]; - Q_snprintf( m_pszImageName, len, "%s%s", pszDir, imageName ); - InvalidateLayout(false, true); // force applyschemesettings to run -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void RotatingProgressBar::PaintBackground() -{ - // No background -} - -//----------------------------------------------------------------------------- -// Purpose: Update event when we aren't drawing so we don't get huge sweeps -// when we start drawing it -//----------------------------------------------------------------------------- -void RotatingProgressBar::OnTick( void ) -{ - float flDesiredAngle = RemapVal( GetProgress(), 0.0, 1.0, m_flStartRadians, m_flEndRadians ); - m_flLastAngle = Approach( flDesiredAngle, m_flLastAngle, m_flApproachSpeed ); - - BaseClass::OnTick(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void RotatingProgressBar::Paint() -{ - // we have an image that we rotate based on the progress, - // where '0' is not rotated,'90' is rotated 90 degrees to the right. - // Image is rotated around its center. - - // desired rotation is GetProgress() ( 0.0 -> 1.0 ) mapped into - // ( m_flStartDegrees -> m_flEndDegrees ) - - vgui::surface()->DrawSetTexture( m_nTextureId ); - vgui::surface()->DrawSetColor( Color(255,255,255,255) ); - - int wide, tall; - GetSize( wide, tall ); - - float mid_x = m_flRotatingX + m_flRotOriginX * m_flRotatingWide; - float mid_y = m_flRotatingY + m_flRotOriginY * m_flRotatingTall; - - Vertex_t vert[4]; - - vert[0].Init( Vector2D( m_flRotatingX, m_flRotatingY ), Vector2D(0,0) ); - vert[1].Init( Vector2D( m_flRotatingX+m_flRotatingWide, m_flRotatingY ), Vector2D(1,0) ); - vert[2].Init( Vector2D( m_flRotatingX+m_flRotatingWide, m_flRotatingY+m_flRotatingTall ), Vector2D(1,1) ); - vert[3].Init( Vector2D( m_flRotatingX, m_flRotatingY+m_flRotatingTall ), Vector2D(0,1) ); - - float flCosA = cos(m_flLastAngle); - float flSinA = sin(m_flLastAngle); - - // rotate each point around (mid_x, mid_y) by flAngle radians - for ( int i=0;i<4;i++ ) - { - Vector2D result; - - // subtract the (x,y) we're rotating around, we'll add it on at the end. - vert[i].m_Position.x -= mid_x; - vert[i].m_Position.y -= mid_y; - - result.x = ( vert[i].m_Position.x * flCosA - vert[i].m_Position.y * flSinA ) + mid_x; - result.y = ( vert[i].m_Position.x * flSinA + vert[i].m_Position.y * flCosA ) + mid_y; - - vert[i].m_Position = result; - } - - vgui::surface()->DrawTexturedPolygon( 4, vert ); +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "mathlib/mathlib.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY( RotatingProgressBar ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +RotatingProgressBar::RotatingProgressBar(Panel *parent, const char *panelName) : ProgressBar(parent, panelName) +{ + m_flStartRadians = 0; + m_flEndRadians = 0; + m_flLastAngle = 0; + + m_nTextureId = -1; + m_pszImageName = NULL; + + m_flTickDelay = 30; + + ivgui()->AddTickSignal(GetVPanel(), m_flTickDelay ); + + SetPaintBorderEnabled(false); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +RotatingProgressBar::~RotatingProgressBar() +{ + if ( vgui::surface() && m_nTextureId != -1 ) + { + vgui::surface()->DestroyTextureID( m_nTextureId ); + m_nTextureId = -1; + } + + delete [] m_pszImageName; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void RotatingProgressBar::ApplySettings(KeyValues *inResourceData) +{ + const char *imageName = inResourceData->GetString("image", ""); + if (*imageName) + { + SetImage( imageName ); + } + + // Find min and max rotations in radians + m_flStartRadians = DEG2RAD(inResourceData->GetFloat( "start_degrees", 0 ) ); + m_flEndRadians = DEG2RAD( inResourceData->GetFloat( "end_degrees", 0 ) ); + + // Start at 0 progress + m_flLastAngle = m_flStartRadians; + + // approach speed is specified in degrees per second. + // convert to radians per 1/30th of a second + float flDegressPerSecond = DEG2RAD( inResourceData->GetFloat( "approach_speed", 360.0 ) ); // default is super fast + m_flApproachSpeed = flDegressPerSecond * ( m_flTickDelay / 1000.0f ); // divide by number of frames in a second + + m_flRotOriginX = inResourceData->GetFloat( "rot_origin_x_percent", 0.5f ); + m_flRotOriginY = inResourceData->GetFloat( "rot_origin_y_percent", 0.5f ); + + m_flRotatingX = inResourceData->GetFloat( "rotating_x", 0 ); + m_flRotatingY = inResourceData->GetFloat( "rotating_y", 0 ); + m_flRotatingWide = inResourceData->GetFloat( "rotating_wide", 0 ); + m_flRotatingTall = inResourceData->GetFloat( "rotating_tall", 0 ); + + BaseClass::ApplySettings( inResourceData ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void RotatingProgressBar::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + if ( m_pszImageName && strlen( m_pszImageName ) > 0 ) + { + if ( m_nTextureId == -1 ) + { + m_nTextureId = surface()->CreateNewTextureID(); + } + + surface()->DrawSetTextureFile( m_nTextureId, m_pszImageName, true, false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets an image by file name +//----------------------------------------------------------------------------- +void RotatingProgressBar::SetImage(const char *imageName) +{ + if ( m_pszImageName ) + { + delete [] m_pszImageName; + m_pszImageName = NULL; + } + + const char *pszDir = "vgui/"; + int len = Q_strlen(imageName) + 1; + len += strlen(pszDir); + m_pszImageName = new char[ len ]; + Q_snprintf( m_pszImageName, len, "%s%s", pszDir, imageName ); + InvalidateLayout(false, true); // force applyschemesettings to run +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void RotatingProgressBar::PaintBackground() +{ + // No background +} + +//----------------------------------------------------------------------------- +// Purpose: Update event when we aren't drawing so we don't get huge sweeps +// when we start drawing it +//----------------------------------------------------------------------------- +void RotatingProgressBar::OnTick( void ) +{ + float flDesiredAngle = RemapVal( GetProgress(), 0.0, 1.0, m_flStartRadians, m_flEndRadians ); + m_flLastAngle = Approach( flDesiredAngle, m_flLastAngle, m_flApproachSpeed ); + + BaseClass::OnTick(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void RotatingProgressBar::Paint() +{ + // we have an image that we rotate based on the progress, + // where '0' is not rotated,'90' is rotated 90 degrees to the right. + // Image is rotated around its center. + + // desired rotation is GetProgress() ( 0.0 -> 1.0 ) mapped into + // ( m_flStartDegrees -> m_flEndDegrees ) + + vgui::surface()->DrawSetTexture( m_nTextureId ); + vgui::surface()->DrawSetColor( Color(255,255,255,255) ); + + int wide, tall; + GetSize( wide, tall ); + + float mid_x = m_flRotatingX + m_flRotOriginX * m_flRotatingWide; + float mid_y = m_flRotatingY + m_flRotOriginY * m_flRotatingTall; + + Vertex_t vert[4]; + + vert[0].Init( Vector2D( m_flRotatingX, m_flRotatingY ), Vector2D(0,0) ); + vert[1].Init( Vector2D( m_flRotatingX+m_flRotatingWide, m_flRotatingY ), Vector2D(1,0) ); + vert[2].Init( Vector2D( m_flRotatingX+m_flRotatingWide, m_flRotatingY+m_flRotatingTall ), Vector2D(1,1) ); + vert[3].Init( Vector2D( m_flRotatingX, m_flRotatingY+m_flRotatingTall ), Vector2D(0,1) ); + + float flCosA = cos(m_flLastAngle); + float flSinA = sin(m_flLastAngle); + + // rotate each point around (mid_x, mid_y) by flAngle radians + for ( int i=0;i<4;i++ ) + { + Vector2D result; + + // subtract the (x,y) we're rotating around, we'll add it on at the end. + vert[i].m_Position.x -= mid_x; + vert[i].m_Position.y -= mid_y; + + result.x = ( vert[i].m_Position.x * flCosA - vert[i].m_Position.y * flSinA ) + mid_x; + result.y = ( vert[i].m_Position.x * flSinA + vert[i].m_Position.y * flCosA ) + mid_y; + + vert[i].m_Position = result; + } + + vgui::surface()->DrawTexturedPolygon( 4, vert ); } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/ScalableImagePanel.cpp b/mp/src/vgui2/vgui_controls/ScalableImagePanel.cpp index 8a2ad17d..24b707ba 100644 --- a/mp/src/vgui2/vgui_controls/ScalableImagePanel.cpp +++ b/mp/src/vgui2/vgui_controls/ScalableImagePanel.cpp @@ -1,266 +1,266 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#include -#include -#include -#include -#include - -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY( ScalableImagePanel ); - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -ScalableImagePanel::ScalableImagePanel(Panel *parent, const char *name) : Panel(parent, name) -{ - m_iSrcCornerHeight = 0; - m_iSrcCornerWidth = 0; - - m_iCornerHeight = 0; - m_iCornerWidth = 0; - - m_pszImageName = NULL; - m_pszDrawColorName = NULL; - - m_DrawColor = Color(255,255,255,255); - - m_flCornerWidthPercent = 0; - m_flCornerHeightPercent = 0; - - m_iTextureID = surface()->CreateNewTextureID(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -ScalableImagePanel::~ScalableImagePanel() -{ - delete [] m_pszImageName; - delete [] m_pszDrawColorName; - - if ( vgui::surface() && m_iTextureID != -1 ) - { - vgui::surface()->DestroyTextureID( m_iTextureID ); - m_iTextureID = -1; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScalableImagePanel::SetImage(const char *imageName) -{ - if ( *imageName ) - { - char szImage[MAX_PATH]; - - const char *pszDir = "vgui/"; - int len = Q_strlen(imageName) + 1; - len += strlen(pszDir); - Q_snprintf( szImage, len, "%s%s", pszDir, imageName ); - - if ( m_pszImageName && V_stricmp( szImage, m_pszImageName ) == 0 ) - return; - - delete [] m_pszImageName; - m_pszImageName = new char[ len ]; - Q_strncpy(m_pszImageName, szImage, len ); - } - else - { - delete [] m_pszImageName; - m_pszImageName = NULL; - } - - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScalableImagePanel::PaintBackground() -{ - int wide, tall; - GetSize(wide, tall); - - surface()->DrawSetColor( m_DrawColor.r(), m_DrawColor.g(), m_DrawColor.b(), GetAlpha() ); - surface()->DrawSetTexture( m_iTextureID ); - - int x = 0; - int y = 0; - - float uvx = 0; - float uvy = 0; - float uvw = 0, uvh = 0; - - float drawW, drawH; - - int row, col; - for ( row=0;row<3;row++ ) - { - x = 0; - uvx = 0; - - if ( row == 0 || row == 2 ) - { - //uvh - row 0 or 2, is src_corner_height - uvh = m_flCornerHeightPercent; - drawH = m_iCornerHeight; - } - else - { - //uvh - row 1, is tall - ( 2 * src_corner_height ) ( min 0 ) - uvh = max( 1.0 - 2 * m_flCornerHeightPercent, 0.0f ); - drawH = max( 0, ( tall - 2 * m_iCornerHeight ) ); - } - - for ( col=0;col<3;col++ ) - { - if ( col == 0 || col == 2 ) - { - //uvw - col 0 or 2, is src_corner_width - uvw = m_flCornerWidthPercent; - drawW = m_iCornerWidth; - } - else - { - //uvw - col 1, is wide - ( 2 * src_corner_width ) ( min 0 ) - uvw = max( 1.0 - 2 * m_flCornerWidthPercent, 0.0f ); - drawW = max( 0, ( wide - 2 * m_iCornerWidth ) ); - } - - Vector2D uv11( uvx, uvy ); - Vector2D uv21( uvx+uvw, uvy ); - Vector2D uv22( uvx+uvw, uvy+uvh ); - Vector2D uv12( uvx, uvy+uvh ); - - vgui::Vertex_t verts[4]; - verts[0].Init( Vector2D( x, y ), uv11 ); - verts[1].Init( Vector2D( x+drawW, y ), uv21 ); - verts[2].Init( Vector2D( x+drawW, y+drawH ), uv22 ); - verts[3].Init( Vector2D( x, y+drawH ), uv12 ); - - vgui::surface()->DrawTexturedPolygon( 4, verts ); - - x += drawW; - uvx += uvw; - } - - y += drawH; - uvy += uvh; - } - - vgui::surface()->DrawSetTexture(0); -} - -//----------------------------------------------------------------------------- -// Purpose: Gets control settings for editing -//----------------------------------------------------------------------------- -void ScalableImagePanel::GetSettings(KeyValues *outResourceData) -{ - BaseClass::GetSettings(outResourceData); - - if (m_pszDrawColorName) - { - outResourceData->SetString("drawcolor", m_pszDrawColorName); - } - - outResourceData->SetInt("src_corner_height", m_iSrcCornerHeight); - outResourceData->SetInt("src_corner_width", m_iSrcCornerWidth); - - outResourceData->SetInt("draw_corner_height", m_iCornerHeight); - outResourceData->SetInt("draw_corner_width", m_iCornerWidth); - - if (m_pszImageName) - { - outResourceData->SetString("image", m_pszImageName); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Applies designer settings from res file -//----------------------------------------------------------------------------- -void ScalableImagePanel::ApplySettings(KeyValues *inResourceData) -{ - BaseClass::ApplySettings(inResourceData); - - delete [] m_pszDrawColorName; - m_pszDrawColorName = NULL; - - const char *pszDrawColor = inResourceData->GetString("drawcolor", ""); - if (*pszDrawColor) - { - int r = 0, g = 0, b = 0, a = 255; - int len = Q_strlen(pszDrawColor) + 1; - m_pszDrawColorName = new char[ len ]; - Q_strncpy( m_pszDrawColorName, pszDrawColor, len ); - - if (sscanf(pszDrawColor, "%d %d %d %d", &r, &g, &b, &a) >= 3) - { - // it's a direct color - m_DrawColor = Color(r, g, b, a); - } - else - { - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - m_DrawColor = pScheme->GetColor(pszDrawColor, Color(0, 0, 0, 0)); - } - } - - m_iSrcCornerHeight = inResourceData->GetInt( "src_corner_height" ); - m_iSrcCornerWidth = inResourceData->GetInt( "src_corner_width" ); - - m_iCornerHeight = inResourceData->GetInt( "draw_corner_height" ); - m_iCornerWidth = inResourceData->GetInt( "draw_corner_width" ); - - if ( IsProportional() ) - { - // scale the x and y up to our screen co-ords - m_iCornerHeight = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iCornerHeight); - m_iCornerWidth = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iCornerWidth); - } - - const char *imageName = inResourceData->GetString("image", ""); - SetImage( imageName ); - - InvalidateLayout(); -} - -void ScalableImagePanel::PerformLayout( void ) -{ - if ( m_pszImageName ) - { - surface()->DrawSetTextureFile( m_iTextureID, m_pszImageName, true, false); - } - - // get image dimensions, compare to m_iSrcCornerHeight, m_iSrcCornerWidth - int wide,tall; - surface()->DrawGetTextureSize( m_iTextureID, wide, tall ); - - m_flCornerWidthPercent = ( wide > 0 ) ? ( (float)m_iSrcCornerWidth / (float)wide ) : 0; - m_flCornerHeightPercent = ( tall > 0 ) ? ( (float)m_iSrcCornerHeight / (float)tall ) : 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Describes editing details -//----------------------------------------------------------------------------- -const char *ScalableImagePanel::GetDescription() -{ - static char buf[1024]; - _snprintf(buf, sizeof(buf), "%s string image, int src_corner_height, int src_corner_width, int draw_corner_height, int draw_corner_width", BaseClass::GetDescription()); - return buf; +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include +#include +#include +#include +#include + +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY( ScalableImagePanel ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ScalableImagePanel::ScalableImagePanel(Panel *parent, const char *name) : Panel(parent, name) +{ + m_iSrcCornerHeight = 0; + m_iSrcCornerWidth = 0; + + m_iCornerHeight = 0; + m_iCornerWidth = 0; + + m_pszImageName = NULL; + m_pszDrawColorName = NULL; + + m_DrawColor = Color(255,255,255,255); + + m_flCornerWidthPercent = 0; + m_flCornerHeightPercent = 0; + + m_iTextureID = surface()->CreateNewTextureID(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ScalableImagePanel::~ScalableImagePanel() +{ + delete [] m_pszImageName; + delete [] m_pszDrawColorName; + + if ( vgui::surface() && m_iTextureID != -1 ) + { + vgui::surface()->DestroyTextureID( m_iTextureID ); + m_iTextureID = -1; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScalableImagePanel::SetImage(const char *imageName) +{ + if ( *imageName ) + { + char szImage[MAX_PATH]; + + const char *pszDir = "vgui/"; + int len = Q_strlen(imageName) + 1; + len += strlen(pszDir); + Q_snprintf( szImage, len, "%s%s", pszDir, imageName ); + + if ( m_pszImageName && V_stricmp( szImage, m_pszImageName ) == 0 ) + return; + + delete [] m_pszImageName; + m_pszImageName = new char[ len ]; + Q_strncpy(m_pszImageName, szImage, len ); + } + else + { + delete [] m_pszImageName; + m_pszImageName = NULL; + } + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScalableImagePanel::PaintBackground() +{ + int wide, tall; + GetSize(wide, tall); + + surface()->DrawSetColor( m_DrawColor.r(), m_DrawColor.g(), m_DrawColor.b(), GetAlpha() ); + surface()->DrawSetTexture( m_iTextureID ); + + int x = 0; + int y = 0; + + float uvx = 0; + float uvy = 0; + float uvw = 0, uvh = 0; + + float drawW, drawH; + + int row, col; + for ( row=0;row<3;row++ ) + { + x = 0; + uvx = 0; + + if ( row == 0 || row == 2 ) + { + //uvh - row 0 or 2, is src_corner_height + uvh = m_flCornerHeightPercent; + drawH = m_iCornerHeight; + } + else + { + //uvh - row 1, is tall - ( 2 * src_corner_height ) ( min 0 ) + uvh = max( 1.0 - 2 * m_flCornerHeightPercent, 0.0f ); + drawH = max( 0, ( tall - 2 * m_iCornerHeight ) ); + } + + for ( col=0;col<3;col++ ) + { + if ( col == 0 || col == 2 ) + { + //uvw - col 0 or 2, is src_corner_width + uvw = m_flCornerWidthPercent; + drawW = m_iCornerWidth; + } + else + { + //uvw - col 1, is wide - ( 2 * src_corner_width ) ( min 0 ) + uvw = max( 1.0 - 2 * m_flCornerWidthPercent, 0.0f ); + drawW = max( 0, ( wide - 2 * m_iCornerWidth ) ); + } + + Vector2D uv11( uvx, uvy ); + Vector2D uv21( uvx+uvw, uvy ); + Vector2D uv22( uvx+uvw, uvy+uvh ); + Vector2D uv12( uvx, uvy+uvh ); + + vgui::Vertex_t verts[4]; + verts[0].Init( Vector2D( x, y ), uv11 ); + verts[1].Init( Vector2D( x+drawW, y ), uv21 ); + verts[2].Init( Vector2D( x+drawW, y+drawH ), uv22 ); + verts[3].Init( Vector2D( x, y+drawH ), uv12 ); + + vgui::surface()->DrawTexturedPolygon( 4, verts ); + + x += drawW; + uvx += uvw; + } + + y += drawH; + uvy += uvh; + } + + vgui::surface()->DrawSetTexture(0); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets control settings for editing +//----------------------------------------------------------------------------- +void ScalableImagePanel::GetSettings(KeyValues *outResourceData) +{ + BaseClass::GetSettings(outResourceData); + + if (m_pszDrawColorName) + { + outResourceData->SetString("drawcolor", m_pszDrawColorName); + } + + outResourceData->SetInt("src_corner_height", m_iSrcCornerHeight); + outResourceData->SetInt("src_corner_width", m_iSrcCornerWidth); + + outResourceData->SetInt("draw_corner_height", m_iCornerHeight); + outResourceData->SetInt("draw_corner_width", m_iCornerWidth); + + if (m_pszImageName) + { + outResourceData->SetString("image", m_pszImageName); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Applies designer settings from res file +//----------------------------------------------------------------------------- +void ScalableImagePanel::ApplySettings(KeyValues *inResourceData) +{ + BaseClass::ApplySettings(inResourceData); + + delete [] m_pszDrawColorName; + m_pszDrawColorName = NULL; + + const char *pszDrawColor = inResourceData->GetString("drawcolor", ""); + if (*pszDrawColor) + { + int r = 0, g = 0, b = 0, a = 255; + int len = Q_strlen(pszDrawColor) + 1; + m_pszDrawColorName = new char[ len ]; + Q_strncpy( m_pszDrawColorName, pszDrawColor, len ); + + if (sscanf(pszDrawColor, "%d %d %d %d", &r, &g, &b, &a) >= 3) + { + // it's a direct color + m_DrawColor = Color(r, g, b, a); + } + else + { + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + m_DrawColor = pScheme->GetColor(pszDrawColor, Color(0, 0, 0, 0)); + } + } + + m_iSrcCornerHeight = inResourceData->GetInt( "src_corner_height" ); + m_iSrcCornerWidth = inResourceData->GetInt( "src_corner_width" ); + + m_iCornerHeight = inResourceData->GetInt( "draw_corner_height" ); + m_iCornerWidth = inResourceData->GetInt( "draw_corner_width" ); + + if ( IsProportional() ) + { + // scale the x and y up to our screen co-ords + m_iCornerHeight = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iCornerHeight); + m_iCornerWidth = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iCornerWidth); + } + + const char *imageName = inResourceData->GetString("image", ""); + SetImage( imageName ); + + InvalidateLayout(); +} + +void ScalableImagePanel::PerformLayout( void ) +{ + if ( m_pszImageName ) + { + surface()->DrawSetTextureFile( m_iTextureID, m_pszImageName, true, false); + } + + // get image dimensions, compare to m_iSrcCornerHeight, m_iSrcCornerWidth + int wide,tall; + surface()->DrawGetTextureSize( m_iTextureID, wide, tall ); + + m_flCornerWidthPercent = ( wide > 0 ) ? ( (float)m_iSrcCornerWidth / (float)wide ) : 0; + m_flCornerHeightPercent = ( tall > 0 ) ? ( (float)m_iSrcCornerHeight / (float)tall ) : 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Describes editing details +//----------------------------------------------------------------------------- +const char *ScalableImagePanel::GetDescription() +{ + static char buf[1024]; + _snprintf(buf, sizeof(buf), "%s string image, int src_corner_height, int src_corner_width, int draw_corner_height, int draw_corner_width", BaseClass::GetDescription()); + return buf; } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/ScrollBar.cpp b/mp/src/vgui2/vgui_controls/ScrollBar.cpp index 5330b41a..d4e8a9b4 100644 --- a/mp/src/vgui2/vgui_controls/ScrollBar.cpp +++ b/mp/src/vgui2/vgui_controls/ScrollBar.cpp @@ -1,802 +1,802 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -namespace -{ - -enum -{ // scroll bar will scroll a little, then continuous scroll like in windows - SCROLL_BAR_DELAY = 400, // default delay for all scroll bars - SCROLL_BAR_SPEED = 50, // this is how fast the bar scrolls when you hold down the arrow button - SCROLLBAR_DEFAULT_WIDTH = 17, -}; - -//----------------------------------------------------------------------------- -// Purpose: Scroll bar button-the arrow button that moves the slider up and down. -//----------------------------------------------------------------------------- -class ScrollBarButton : public Button -{ -public: - ScrollBarButton(Panel *parent, const char *panelName, const char *text) : Button(parent, panelName, text) - { - SetButtonActivationType(ACTIVATE_ONPRESSED); - - SetContentAlignment(Label::a_center); - } - - void OnMouseFocusTicked() - { - // pass straight up to parent - CallParentFunction(new KeyValues("MouseFocusTicked")); - } - - virtual void ApplySchemeSettings(IScheme *pScheme) - { - Button::ApplySchemeSettings(pScheme); - - SetFont(pScheme->GetFont("Marlett", IsProportional() )); - SetDefaultBorder(pScheme->GetBorder("ScrollBarButtonBorder")); - SetDepressedBorder(pScheme->GetBorder("ScrollBarButtonDepressedBorder")); - - SetDefaultColor(GetSchemeColor("ScrollBarButton.FgColor", pScheme), GetSchemeColor("ScrollBarButton.BgColor", pScheme)); - SetArmedColor(GetSchemeColor("ScrollBarButton.ArmedFgColor", pScheme), GetSchemeColor("ScrollBarButton.ArmedBgColor", pScheme)); - SetDepressedColor(GetSchemeColor("ScrollBarButton.DepressedFgColor", pScheme), GetSchemeColor("ScrollBarButton.DepressedBgColor", pScheme)); - } - - // Don't request focus. - // This will keep cursor focus in main window in text entry windows. - virtual void OnMousePressed(MouseCode code) - { - if (!IsEnabled()) - return; - - if (!IsMouseClickEnabled(code)) - return; - - if (IsUseCaptureMouseEnabled()) - { - { - SetSelected(true); - Repaint(); - } - - // lock mouse input to going to this button - input()->SetMouseCapture(GetVPanel()); - } - } - virtual void OnMouseReleased(MouseCode code) - { - if (!IsEnabled()) - return; - - if (!IsMouseClickEnabled(code)) - return; - - if (IsUseCaptureMouseEnabled()) - { - { - SetSelected(false); - Repaint(); - } - - // lock mouse input to going to this button - input()->SetMouseCapture(NULL); - } - - if( input()->GetMouseOver() == GetVPanel() ) - { - SetArmed( true ); - } - } - -}; - -} - -vgui::Panel *ScrollBar_Vertical_Factory() -{ - return new ScrollBar(NULL, NULL, true ); -} - -vgui::Panel *ScrollBar_Horizontal_Factory() -{ - return new ScrollBar(NULL, NULL, false ); -} - -DECLARE_BUILD_FACTORY_CUSTOM_ALIAS( ScrollBar, ScrollBar_Vertical, ScrollBar_Vertical_Factory ); -DECLARE_BUILD_FACTORY_CUSTOM_ALIAS( ScrollBar, ScrollBar_Horizontal, ScrollBar_Horizontal_Factory ); -// Default is a horizontal one -DECLARE_BUILD_FACTORY_CUSTOM( ScrollBar, ScrollBar_Horizontal_Factory ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -ScrollBar::ScrollBar(Panel *parent, const char *panelName, bool vertical) : Panel(parent, panelName) -{ - _slider=null; - _button[0]=null; - _button[1]=null; - _scrollDelay = SCROLL_BAR_DELAY; - _respond = true; - m_pUpArrow = NULL; - m_pLine = NULL; - m_pDownArrow = NULL; - m_pBox = NULL; - m_bNoButtons = false; - m_pOverriddenButtons[0] = NULL; - m_pOverriddenButtons[1] = NULL; - - if (vertical) - { - // FIXME: proportional changes needed??? - SetSlider(new ScrollBarSlider(NULL, "Slider", true)); - SetButton(new ScrollBarButton(NULL, "UpButton", "t"), 0); - SetButton(new ScrollBarButton(NULL, "DownButton", "u"), 1); - _button[0]->SetTextInset(0, 1); - _button[1]->SetTextInset(0, -1); - - SetSize(SCROLLBAR_DEFAULT_WIDTH, 64); - } - else - { - SetSlider(new ScrollBarSlider(NULL, NULL, false)); - SetButton(new ScrollBarButton(NULL, NULL, "w"), 0); - SetButton(new ScrollBarButton(NULL, NULL, "4"), 1); - _button[0]->SetTextInset(0, 0); - _button[1]->SetTextInset(0, 0); - - SetSize(64, SCROLLBAR_DEFAULT_WIDTH); - } - - Panel::SetPaintBorderEnabled(true); - Panel::SetPaintBackgroundEnabled(false); - Panel::SetPaintEnabled(true); - SetButtonPressedScrollValue(20); - SetBlockDragChaining( true ); - - Validate(); -} - -//----------------------------------------------------------------------------- -// Purpose: sets up the width of the scrollbar according to the scheme -//----------------------------------------------------------------------------- -void ScrollBar::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - const char *resourceString = pScheme->GetResourceString("ScrollBar.Wide"); - - if (resourceString) - { - int value = atoi(resourceString); - if (IsProportional()) - { - value = scheme()->GetProportionalScaledValueEx(GetScheme(), value); - } - - if (_slider && _slider->IsVertical()) - { - // we're vertical, so reset the width - SetSize( value, GetTall() ); - } - else - { - // we're horizontal, so the width means the height - SetSize( GetWide(), value ); - } - } - - UpdateButtonsForImages(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the slider's Paint border enabled. -//----------------------------------------------------------------------------- -void ScrollBar::SetPaintBorderEnabled(bool state) -{ - if ( _slider ) - { - _slider->SetPaintBorderEnabled( state ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBar::SetPaintBackgroundEnabled(bool state) -{ - if ( _slider ) - { - _slider->SetPaintBackgroundEnabled( state ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBar::SetPaintEnabled(bool state) -{ - if ( _slider ) - { - _slider->SetPaintEnabled( state ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Layout the scroll bar and buttons on screen -//----------------------------------------------------------------------------- -void ScrollBar::PerformLayout() -{ - if (_slider) - { - int wide, tall; - GetPaintSize(wide,tall); - if(_slider->IsVertical()) - { - if ( m_bNoButtons ) - { - _slider->SetBounds(0, 0, wide, tall + 1); - } - else - { - _slider->SetBounds(0, wide, wide, tall-(wide*2)+1); - _button[0]->SetBounds(0,0, wide, wide ); - _button[1]->SetBounds(0,tall-wide ,wide, wide ); - } - } - else - { - if ( m_bNoButtons ) - { - _slider->SetBounds(tall, 0, wide, tall + 1); - } - else - { - _slider->SetBounds(tall, -1, wide-(tall*2)+1, tall + 1 ); - _button[0]->SetBounds(0, 0, tall, tall); - _button[1]->SetBounds(wide-tall, 0, tall, tall); - } - } - - // Place the images over the appropriate controls - int x,y; - if ( m_pUpArrow ) - { - _button[0]->GetBounds( x,y,wide,tall ); - m_pUpArrow->SetBounds( x,y,wide,tall ); - } - if ( m_pDownArrow ) - { - _button[1]->GetBounds( x,y,wide,tall ); - m_pDownArrow->SetBounds( x,y,wide,tall ); - } - if ( m_pLine ) - { - _slider->GetBounds( x,y,wide,tall ); - m_pLine->SetBounds( x,y,wide,tall ); - } - if ( m_pBox ) - { - m_pBox->SetBounds( 0, wide, wide, wide ); - } - - _slider->MoveToFront(); - // after resizing our child, we should remind it to perform a layout - _slider->InvalidateLayout(); - - UpdateSliderImages(); - } - - if ( m_bAutoHideButtons ) - { - SetScrollbarButtonsVisible( _slider->IsSliderVisible() ); - } - - // get tooltips to draw - Panel::PerformLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the value of the scroll bar slider. -//----------------------------------------------------------------------------- -void ScrollBar::SetValue(int value) -{ - _slider->SetValue(value); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the value of the scroll bar slider. -//----------------------------------------------------------------------------- -int ScrollBar::GetValue() -{ - return _slider->GetValue(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the range of the scroll bar slider. -// This the range of numbers the slider can scroll through. -//----------------------------------------------------------------------------- -void ScrollBar::SetRange(int min,int max) -{ - _slider->SetRange(min,max); -} - -//----------------------------------------------------------------------------- -// Purpose: Gets the range of the scroll bar slider. -// This the range of numbers the slider can scroll through. -//----------------------------------------------------------------------------- -void ScrollBar::GetRange(int &min, int &max) -{ - _slider->GetRange(min, max); -} - -//----------------------------------------------------------------------------- -// Purpose: Send a message when the slider is moved. -// Input : value - -//----------------------------------------------------------------------------- -void ScrollBar::SendSliderMoveMessage(int value) -{ - PostActionSignal(new KeyValues("ScrollBarSliderMoved", "position", value)); -} - -//----------------------------------------------------------------------------- -// Purpose: Called when the Slider is dragged by the user -// Input : value - -//----------------------------------------------------------------------------- -void ScrollBar::OnSliderMoved(int value) -{ - SendSliderMoveMessage(value); - UpdateSliderImages(); -} - -//----------------------------------------------------------------------------- -// Purpose: Check if the scrollbar is vertical (true) or horizontal (false) -//----------------------------------------------------------------------------- -bool ScrollBar::IsVertical() -{ - return _slider->IsVertical(); -} - -//----------------------------------------------------------------------------- -// Purpose: Check if the the scrollbar slider has full range. -// Normally if you have a scroll bar and the range goes from a to b and -// the slider is sized to c, the range will go from a to b-c. -// This makes it so the slider goes from a to b fully. -//----------------------------------------------------------------------------- -bool ScrollBar::HasFullRange() -{ - return _slider->HasFullRange(); -} - -//----------------------------------------------------------------------------- -// Purpose: Setup the indexed scroll bar button with the input params. -//----------------------------------------------------------------------------- -//LEAK: new and old slider will leak -void ScrollBar::SetButton(Button *button, int index) -{ - if(_button[index]!=null) - { - _button[index]->SetParent((Panel *)NULL); - } - _button[index]=button; - _button[index]->SetParent(this); - _button[index]->AddActionSignalTarget(this); - _button[index]->SetCommand(new KeyValues("ScrollButtonPressed", "index", index)); - - Validate(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Return the indexed scroll bar button -//----------------------------------------------------------------------------- -Button* ScrollBar::GetButton(int index) -{ - return _button[index]; -} - - -//----------------------------------------------------------------------------- -// Purpose: Set up the slider. -//----------------------------------------------------------------------------- -//LEAK: new and old slider will leak -void ScrollBar::SetSlider(ScrollBarSlider *slider) -{ - if(_slider!=null) - { - _slider->SetParent((Panel *)NULL); - } - _slider=slider; - _slider->AddActionSignalTarget(this); - _slider->SetParent(this); - - Validate(); -} - -//----------------------------------------------------------------------------- -// Purpose: Return a pointer to the slider. -//----------------------------------------------------------------------------- -ScrollBarSlider *ScrollBar::GetSlider() -{ - return _slider; -} - -Button *ScrollBar::GetDepressedButton( int iIndex ) -{ - if ( iIndex == 0 ) - return ( m_pOverriddenButtons[0] ? m_pOverriddenButtons[0] : _button[0] ); - return ( m_pOverriddenButtons[1] ? m_pOverriddenButtons[1] : _button[1] ); -} - -//----------------------------------------------------------------------------- -// Purpose: Scrolls in response to clicking and holding on up or down arrow -// The idea is to have the slider move one step then delay a bit and then -// the bar starts moving at normal speed. This gives a stepping feeling -// to just clicking an arrow once. -//----------------------------------------------------------------------------- -void ScrollBar::OnMouseFocusTicked() -{ - int direction = 0; - - // top button is down - if ( GetDepressedButton(0)->IsDepressed() ) - { - direction = -1; - } - // bottom top button is down - else if (GetDepressedButton(1)->IsDepressed()) - { - direction = 1; - } - - // a button is down - if ( direction != 0 ) - { - RespondToScrollArrow(direction); - if (_scrollDelay < system()->GetTimeMillis()) - { - _scrollDelay = system()->GetTimeMillis() + SCROLL_BAR_SPEED; - _respond = true; - } - else - { - _respond = false; - } - } - // a button is not down. - else - { - // if neither button is down keep delay at max - _scrollDelay = system()->GetTimeMillis() + SCROLL_BAR_DELAY; - _respond = true; - } -} - -//----------------------------------------------------------------------------- -// Purpose: move scroll bar in response to the first button -// Input: button and direction to move scroll bar when that button is pressed -// direction can only by +/- 1 -// Output: whether button is down or not -//----------------------------------------------------------------------------- -void ScrollBar::RespondToScrollArrow(int const direction) -{ - if (_respond) - { - int newValue = _slider->GetValue() + (direction * _buttonPressedScrollValue); - _slider->SetValue(newValue); - SendSliderMoveMessage(newValue); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Trigger layout changes when the window size is changed. -//----------------------------------------------------------------------------- -void ScrollBar::OnSizeChanged(int wide, int tall) -{ - InvalidateLayout(); - _slider->InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set how far the scroll bar slider moves when a scroll bar button is -// pressed. -//----------------------------------------------------------------------------- -void ScrollBar::SetButtonPressedScrollValue(int value) -{ - _buttonPressedScrollValue=value; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the range of the rangewindow. This is how many -// lines are displayed at one time -// in the window the scroll bar is attached to. -// This also controls the size of the slider, its size is proportional -// to the number of lines displayed / total number of lines. -//----------------------------------------------------------------------------- -void ScrollBar::SetRangeWindow(int rangeWindow) -{ - _slider->SetRangeWindow(rangeWindow); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the range of the rangewindow. This is how many -// lines are displayed at one time -// in the window the scroll bar is attached to. -// This also controls the size of the slider, its size is proportional -// to the number of lines displayed / total number of lines. -//----------------------------------------------------------------------------- -int ScrollBar::GetRangeWindow() -{ - return _slider->GetRangeWindow(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBar::Validate() -{ - if ( _slider != null ) - { - int buttonOffset = 0; - - for( int i=0; i<2; i++ ) - { - if( _button[i] != null ) - { - if( _button[i]->IsVisible() ) - { - if( _slider->IsVertical() ) - { - buttonOffset += _button[i]->GetTall(); - } - else - { - buttonOffset += _button[i]->GetWide(); - } - } - } - } - - _slider->SetButtonOffset(buttonOffset); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBar::SetScrollbarButtonsVisible(bool visible) -{ - for( int i=0; i<2; i++ ) - { - if( _button[i] != null ) - { - _button[i]->SetShouldPaint( visible ); - _button[i]->SetEnabled( visible ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBar::UseImages( const char *pszUpArrow, const char *pszDownArrow, const char *pszLine, const char *pszBox ) -{ - if ( pszUpArrow ) - { - if ( !m_pUpArrow ) - { - m_pUpArrow = new vgui::ImagePanel( this, "UpArrow" ); - if ( m_pUpArrow ) - { - m_pUpArrow->SetImage( pszUpArrow ); - m_pUpArrow->SetShouldScaleImage( true ); - m_pUpArrow->SetFgColor( Color( 255, 255, 255, 255 ) ); - m_pUpArrow->SetAlpha( 255 ); - m_pUpArrow->SetZPos( -1 ); - } - } - - m_pUpArrow->SetImage( pszUpArrow ); - m_pUpArrow->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 ); - } - else if ( m_pUpArrow ) - { - m_pUpArrow->DeletePanel(); - m_pUpArrow = NULL; - } - - if ( pszDownArrow ) - { - if ( !m_pDownArrow ) - { - m_pDownArrow = new vgui::ImagePanel( this, "DownArrow" ); - if ( m_pDownArrow ) - { - m_pDownArrow->SetShouldScaleImage( true ); - m_pDownArrow->SetFgColor( Color( 255, 255, 255, 255 ) ); - m_pDownArrow->SetAlpha( 255 ); - m_pDownArrow->SetZPos( -1 ); - } - } - - m_pDownArrow->SetImage( pszDownArrow ); - m_pDownArrow->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 ); - } - else if ( m_pDownArrow ) - { - m_pDownArrow->DeletePanel(); - m_pDownArrow = NULL; - } - - if ( pszLine ) - { - if ( !m_pLine ) - { - m_pLine = new ImagePanel( this, "Line" ); - if ( m_pLine ) - { - m_pLine->SetShouldScaleImage( true ); - m_pLine->SetZPos( -1 ); - } - } - - m_pLine->SetImage( pszLine ); - m_pLine->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 ); - } - else if ( m_pLine ) - { - m_pLine->DeletePanel(); - m_pLine = NULL; - } - - if ( pszBox ) - { - if ( !m_pBox ) - { - m_pBox = new ImagePanel( this, "Box" ); - if ( m_pBox ) - { - m_pBox->SetShouldScaleImage( true ); - m_pBox->SetZPos( -1 ); - } - } - - m_pBox->SetImage( pszBox ); - m_pBox->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 ); - } - else if ( m_pBox ) - { - m_pBox->DeletePanel(); - m_pBox = NULL; - } - - UpdateButtonsForImages(); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBar::UpdateButtonsForImages( void ) -{ - // Turn off parts of our drawing based on which images we're replacing it with - if ( m_pUpArrow || m_pDownArrow ) - { - SetScrollbarButtonsVisible( false ); - _button[0]->SetPaintBorderEnabled( false ); - _button[1]->SetPaintBorderEnabled( false ); - m_bAutoHideButtons = false; - } - if ( m_pLine || m_pBox ) - { - SetPaintBackgroundEnabled( false ); - SetPaintBorderEnabled( false ); - - if ( _slider ) - { - _slider->SetPaintEnabled( false ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBar::UpdateSliderImages( void ) -{ - if ( m_pUpArrow && m_pDownArrow ) - { - // set the alpha on the up arrow - int nMin, nMax; - GetRange( nMin, nMax ); - int nScrollPos = GetValue(); - int nRangeWindow = GetRangeWindow(); - int nBottom = nMax - nRangeWindow; - if ( nBottom < 0 ) - { - nBottom = 0; - } - - // set the alpha on the up arrow - int nAlpha = ( nScrollPos - nMin <= 0 ) ? 90 : 255; - m_pUpArrow->SetAlpha( nAlpha ); - - // set the alpha on the down arrow - nAlpha = ( nScrollPos >= nBottom ) ? 90 : 255; - m_pDownArrow->SetAlpha( nAlpha ); - } - - if ( m_pLine && m_pBox ) - { - ScrollBarSlider *pSlider = GetSlider(); - if ( pSlider && pSlider->GetRangeWindow() > 0 ) - { - int x, y, w, t, min, max; - m_pLine->GetBounds( x, y, w, t ); - - // If our slider needs layout, force it to do it now - if ( pSlider->IsLayoutInvalid() ) - { - pSlider->InvalidateLayout( true ); - } - pSlider->GetNobPos( min, max ); - - if ( IsVertical() ) - { - m_pBox->SetBounds( x, y + min, w, ( max - min ) ); - } - else - { - m_pBox->SetBounds( x + min, 0, (max-min), t ); - } - } - } -} -void ScrollBar::ApplySettings( KeyValues *pInResourceData ) -{ - BaseClass::ApplySettings( pInResourceData ); - - m_bNoButtons = pInResourceData->GetBool( "nobuttons", false ); - - KeyValues *pSliderKV = pInResourceData->FindKey( "Slider" ); - if ( pSliderKV && _slider ) - { - _slider->ApplySettings( pSliderKV ); - } - - KeyValues *pDownButtonKV = pInResourceData->FindKey( "DownButton" ); - if ( pDownButtonKV && _button[0] ) - { - _button[0]->ApplySettings( pDownButtonKV ); - } - - KeyValues *pUpButtonKV = pInResourceData->FindKey( "UpButton" ); - if ( pUpButtonKV && _button[0] ) - { - _button[1]->ApplySettings( pUpButtonKV ); - } -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +namespace +{ + +enum +{ // scroll bar will scroll a little, then continuous scroll like in windows + SCROLL_BAR_DELAY = 400, // default delay for all scroll bars + SCROLL_BAR_SPEED = 50, // this is how fast the bar scrolls when you hold down the arrow button + SCROLLBAR_DEFAULT_WIDTH = 17, +}; + +//----------------------------------------------------------------------------- +// Purpose: Scroll bar button-the arrow button that moves the slider up and down. +//----------------------------------------------------------------------------- +class ScrollBarButton : public Button +{ +public: + ScrollBarButton(Panel *parent, const char *panelName, const char *text) : Button(parent, panelName, text) + { + SetButtonActivationType(ACTIVATE_ONPRESSED); + + SetContentAlignment(Label::a_center); + } + + void OnMouseFocusTicked() + { + // pass straight up to parent + CallParentFunction(new KeyValues("MouseFocusTicked")); + } + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + Button::ApplySchemeSettings(pScheme); + + SetFont(pScheme->GetFont("Marlett", IsProportional() )); + SetDefaultBorder(pScheme->GetBorder("ScrollBarButtonBorder")); + SetDepressedBorder(pScheme->GetBorder("ScrollBarButtonDepressedBorder")); + + SetDefaultColor(GetSchemeColor("ScrollBarButton.FgColor", pScheme), GetSchemeColor("ScrollBarButton.BgColor", pScheme)); + SetArmedColor(GetSchemeColor("ScrollBarButton.ArmedFgColor", pScheme), GetSchemeColor("ScrollBarButton.ArmedBgColor", pScheme)); + SetDepressedColor(GetSchemeColor("ScrollBarButton.DepressedFgColor", pScheme), GetSchemeColor("ScrollBarButton.DepressedBgColor", pScheme)); + } + + // Don't request focus. + // This will keep cursor focus in main window in text entry windows. + virtual void OnMousePressed(MouseCode code) + { + if (!IsEnabled()) + return; + + if (!IsMouseClickEnabled(code)) + return; + + if (IsUseCaptureMouseEnabled()) + { + { + SetSelected(true); + Repaint(); + } + + // lock mouse input to going to this button + input()->SetMouseCapture(GetVPanel()); + } + } + virtual void OnMouseReleased(MouseCode code) + { + if (!IsEnabled()) + return; + + if (!IsMouseClickEnabled(code)) + return; + + if (IsUseCaptureMouseEnabled()) + { + { + SetSelected(false); + Repaint(); + } + + // lock mouse input to going to this button + input()->SetMouseCapture(NULL); + } + + if( input()->GetMouseOver() == GetVPanel() ) + { + SetArmed( true ); + } + } + +}; + +} + +vgui::Panel *ScrollBar_Vertical_Factory() +{ + return new ScrollBar(NULL, NULL, true ); +} + +vgui::Panel *ScrollBar_Horizontal_Factory() +{ + return new ScrollBar(NULL, NULL, false ); +} + +DECLARE_BUILD_FACTORY_CUSTOM_ALIAS( ScrollBar, ScrollBar_Vertical, ScrollBar_Vertical_Factory ); +DECLARE_BUILD_FACTORY_CUSTOM_ALIAS( ScrollBar, ScrollBar_Horizontal, ScrollBar_Horizontal_Factory ); +// Default is a horizontal one +DECLARE_BUILD_FACTORY_CUSTOM( ScrollBar, ScrollBar_Horizontal_Factory ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ScrollBar::ScrollBar(Panel *parent, const char *panelName, bool vertical) : Panel(parent, panelName) +{ + _slider=null; + _button[0]=null; + _button[1]=null; + _scrollDelay = SCROLL_BAR_DELAY; + _respond = true; + m_pUpArrow = NULL; + m_pLine = NULL; + m_pDownArrow = NULL; + m_pBox = NULL; + m_bNoButtons = false; + m_pOverriddenButtons[0] = NULL; + m_pOverriddenButtons[1] = NULL; + + if (vertical) + { + // FIXME: proportional changes needed??? + SetSlider(new ScrollBarSlider(NULL, "Slider", true)); + SetButton(new ScrollBarButton(NULL, "UpButton", "t"), 0); + SetButton(new ScrollBarButton(NULL, "DownButton", "u"), 1); + _button[0]->SetTextInset(0, 1); + _button[1]->SetTextInset(0, -1); + + SetSize(SCROLLBAR_DEFAULT_WIDTH, 64); + } + else + { + SetSlider(new ScrollBarSlider(NULL, NULL, false)); + SetButton(new ScrollBarButton(NULL, NULL, "w"), 0); + SetButton(new ScrollBarButton(NULL, NULL, "4"), 1); + _button[0]->SetTextInset(0, 0); + _button[1]->SetTextInset(0, 0); + + SetSize(64, SCROLLBAR_DEFAULT_WIDTH); + } + + Panel::SetPaintBorderEnabled(true); + Panel::SetPaintBackgroundEnabled(false); + Panel::SetPaintEnabled(true); + SetButtonPressedScrollValue(20); + SetBlockDragChaining( true ); + + Validate(); +} + +//----------------------------------------------------------------------------- +// Purpose: sets up the width of the scrollbar according to the scheme +//----------------------------------------------------------------------------- +void ScrollBar::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + const char *resourceString = pScheme->GetResourceString("ScrollBar.Wide"); + + if (resourceString) + { + int value = atoi(resourceString); + if (IsProportional()) + { + value = scheme()->GetProportionalScaledValueEx(GetScheme(), value); + } + + if (_slider && _slider->IsVertical()) + { + // we're vertical, so reset the width + SetSize( value, GetTall() ); + } + else + { + // we're horizontal, so the width means the height + SetSize( GetWide(), value ); + } + } + + UpdateButtonsForImages(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the slider's Paint border enabled. +//----------------------------------------------------------------------------- +void ScrollBar::SetPaintBorderEnabled(bool state) +{ + if ( _slider ) + { + _slider->SetPaintBorderEnabled( state ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBar::SetPaintBackgroundEnabled(bool state) +{ + if ( _slider ) + { + _slider->SetPaintBackgroundEnabled( state ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBar::SetPaintEnabled(bool state) +{ + if ( _slider ) + { + _slider->SetPaintEnabled( state ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Layout the scroll bar and buttons on screen +//----------------------------------------------------------------------------- +void ScrollBar::PerformLayout() +{ + if (_slider) + { + int wide, tall; + GetPaintSize(wide,tall); + if(_slider->IsVertical()) + { + if ( m_bNoButtons ) + { + _slider->SetBounds(0, 0, wide, tall + 1); + } + else + { + _slider->SetBounds(0, wide, wide, tall-(wide*2)+1); + _button[0]->SetBounds(0,0, wide, wide ); + _button[1]->SetBounds(0,tall-wide ,wide, wide ); + } + } + else + { + if ( m_bNoButtons ) + { + _slider->SetBounds(tall, 0, wide, tall + 1); + } + else + { + _slider->SetBounds(tall, -1, wide-(tall*2)+1, tall + 1 ); + _button[0]->SetBounds(0, 0, tall, tall); + _button[1]->SetBounds(wide-tall, 0, tall, tall); + } + } + + // Place the images over the appropriate controls + int x,y; + if ( m_pUpArrow ) + { + _button[0]->GetBounds( x,y,wide,tall ); + m_pUpArrow->SetBounds( x,y,wide,tall ); + } + if ( m_pDownArrow ) + { + _button[1]->GetBounds( x,y,wide,tall ); + m_pDownArrow->SetBounds( x,y,wide,tall ); + } + if ( m_pLine ) + { + _slider->GetBounds( x,y,wide,tall ); + m_pLine->SetBounds( x,y,wide,tall ); + } + if ( m_pBox ) + { + m_pBox->SetBounds( 0, wide, wide, wide ); + } + + _slider->MoveToFront(); + // after resizing our child, we should remind it to perform a layout + _slider->InvalidateLayout(); + + UpdateSliderImages(); + } + + if ( m_bAutoHideButtons ) + { + SetScrollbarButtonsVisible( _slider->IsSliderVisible() ); + } + + // get tooltips to draw + Panel::PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the value of the scroll bar slider. +//----------------------------------------------------------------------------- +void ScrollBar::SetValue(int value) +{ + _slider->SetValue(value); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the value of the scroll bar slider. +//----------------------------------------------------------------------------- +int ScrollBar::GetValue() +{ + return _slider->GetValue(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the range of the scroll bar slider. +// This the range of numbers the slider can scroll through. +//----------------------------------------------------------------------------- +void ScrollBar::SetRange(int min,int max) +{ + _slider->SetRange(min,max); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the range of the scroll bar slider. +// This the range of numbers the slider can scroll through. +//----------------------------------------------------------------------------- +void ScrollBar::GetRange(int &min, int &max) +{ + _slider->GetRange(min, max); +} + +//----------------------------------------------------------------------------- +// Purpose: Send a message when the slider is moved. +// Input : value - +//----------------------------------------------------------------------------- +void ScrollBar::SendSliderMoveMessage(int value) +{ + PostActionSignal(new KeyValues("ScrollBarSliderMoved", "position", value)); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the Slider is dragged by the user +// Input : value - +//----------------------------------------------------------------------------- +void ScrollBar::OnSliderMoved(int value) +{ + SendSliderMoveMessage(value); + UpdateSliderImages(); +} + +//----------------------------------------------------------------------------- +// Purpose: Check if the scrollbar is vertical (true) or horizontal (false) +//----------------------------------------------------------------------------- +bool ScrollBar::IsVertical() +{ + return _slider->IsVertical(); +} + +//----------------------------------------------------------------------------- +// Purpose: Check if the the scrollbar slider has full range. +// Normally if you have a scroll bar and the range goes from a to b and +// the slider is sized to c, the range will go from a to b-c. +// This makes it so the slider goes from a to b fully. +//----------------------------------------------------------------------------- +bool ScrollBar::HasFullRange() +{ + return _slider->HasFullRange(); +} + +//----------------------------------------------------------------------------- +// Purpose: Setup the indexed scroll bar button with the input params. +//----------------------------------------------------------------------------- +//LEAK: new and old slider will leak +void ScrollBar::SetButton(Button *button, int index) +{ + if(_button[index]!=null) + { + _button[index]->SetParent((Panel *)NULL); + } + _button[index]=button; + _button[index]->SetParent(this); + _button[index]->AddActionSignalTarget(this); + _button[index]->SetCommand(new KeyValues("ScrollButtonPressed", "index", index)); + + Validate(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Return the indexed scroll bar button +//----------------------------------------------------------------------------- +Button* ScrollBar::GetButton(int index) +{ + return _button[index]; +} + + +//----------------------------------------------------------------------------- +// Purpose: Set up the slider. +//----------------------------------------------------------------------------- +//LEAK: new and old slider will leak +void ScrollBar::SetSlider(ScrollBarSlider *slider) +{ + if(_slider!=null) + { + _slider->SetParent((Panel *)NULL); + } + _slider=slider; + _slider->AddActionSignalTarget(this); + _slider->SetParent(this); + + Validate(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return a pointer to the slider. +//----------------------------------------------------------------------------- +ScrollBarSlider *ScrollBar::GetSlider() +{ + return _slider; +} + +Button *ScrollBar::GetDepressedButton( int iIndex ) +{ + if ( iIndex == 0 ) + return ( m_pOverriddenButtons[0] ? m_pOverriddenButtons[0] : _button[0] ); + return ( m_pOverriddenButtons[1] ? m_pOverriddenButtons[1] : _button[1] ); +} + +//----------------------------------------------------------------------------- +// Purpose: Scrolls in response to clicking and holding on up or down arrow +// The idea is to have the slider move one step then delay a bit and then +// the bar starts moving at normal speed. This gives a stepping feeling +// to just clicking an arrow once. +//----------------------------------------------------------------------------- +void ScrollBar::OnMouseFocusTicked() +{ + int direction = 0; + + // top button is down + if ( GetDepressedButton(0)->IsDepressed() ) + { + direction = -1; + } + // bottom top button is down + else if (GetDepressedButton(1)->IsDepressed()) + { + direction = 1; + } + + // a button is down + if ( direction != 0 ) + { + RespondToScrollArrow(direction); + if (_scrollDelay < system()->GetTimeMillis()) + { + _scrollDelay = system()->GetTimeMillis() + SCROLL_BAR_SPEED; + _respond = true; + } + else + { + _respond = false; + } + } + // a button is not down. + else + { + // if neither button is down keep delay at max + _scrollDelay = system()->GetTimeMillis() + SCROLL_BAR_DELAY; + _respond = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: move scroll bar in response to the first button +// Input: button and direction to move scroll bar when that button is pressed +// direction can only by +/- 1 +// Output: whether button is down or not +//----------------------------------------------------------------------------- +void ScrollBar::RespondToScrollArrow(int const direction) +{ + if (_respond) + { + int newValue = _slider->GetValue() + (direction * _buttonPressedScrollValue); + _slider->SetValue(newValue); + SendSliderMoveMessage(newValue); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Trigger layout changes when the window size is changed. +//----------------------------------------------------------------------------- +void ScrollBar::OnSizeChanged(int wide, int tall) +{ + InvalidateLayout(); + _slider->InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set how far the scroll bar slider moves when a scroll bar button is +// pressed. +//----------------------------------------------------------------------------- +void ScrollBar::SetButtonPressedScrollValue(int value) +{ + _buttonPressedScrollValue=value; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the range of the rangewindow. This is how many +// lines are displayed at one time +// in the window the scroll bar is attached to. +// This also controls the size of the slider, its size is proportional +// to the number of lines displayed / total number of lines. +//----------------------------------------------------------------------------- +void ScrollBar::SetRangeWindow(int rangeWindow) +{ + _slider->SetRangeWindow(rangeWindow); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the range of the rangewindow. This is how many +// lines are displayed at one time +// in the window the scroll bar is attached to. +// This also controls the size of the slider, its size is proportional +// to the number of lines displayed / total number of lines. +//----------------------------------------------------------------------------- +int ScrollBar::GetRangeWindow() +{ + return _slider->GetRangeWindow(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBar::Validate() +{ + if ( _slider != null ) + { + int buttonOffset = 0; + + for( int i=0; i<2; i++ ) + { + if( _button[i] != null ) + { + if( _button[i]->IsVisible() ) + { + if( _slider->IsVertical() ) + { + buttonOffset += _button[i]->GetTall(); + } + else + { + buttonOffset += _button[i]->GetWide(); + } + } + } + } + + _slider->SetButtonOffset(buttonOffset); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBar::SetScrollbarButtonsVisible(bool visible) +{ + for( int i=0; i<2; i++ ) + { + if( _button[i] != null ) + { + _button[i]->SetShouldPaint( visible ); + _button[i]->SetEnabled( visible ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBar::UseImages( const char *pszUpArrow, const char *pszDownArrow, const char *pszLine, const char *pszBox ) +{ + if ( pszUpArrow ) + { + if ( !m_pUpArrow ) + { + m_pUpArrow = new vgui::ImagePanel( this, "UpArrow" ); + if ( m_pUpArrow ) + { + m_pUpArrow->SetImage( pszUpArrow ); + m_pUpArrow->SetShouldScaleImage( true ); + m_pUpArrow->SetFgColor( Color( 255, 255, 255, 255 ) ); + m_pUpArrow->SetAlpha( 255 ); + m_pUpArrow->SetZPos( -1 ); + } + } + + m_pUpArrow->SetImage( pszUpArrow ); + m_pUpArrow->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 ); + } + else if ( m_pUpArrow ) + { + m_pUpArrow->DeletePanel(); + m_pUpArrow = NULL; + } + + if ( pszDownArrow ) + { + if ( !m_pDownArrow ) + { + m_pDownArrow = new vgui::ImagePanel( this, "DownArrow" ); + if ( m_pDownArrow ) + { + m_pDownArrow->SetShouldScaleImage( true ); + m_pDownArrow->SetFgColor( Color( 255, 255, 255, 255 ) ); + m_pDownArrow->SetAlpha( 255 ); + m_pDownArrow->SetZPos( -1 ); + } + } + + m_pDownArrow->SetImage( pszDownArrow ); + m_pDownArrow->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 ); + } + else if ( m_pDownArrow ) + { + m_pDownArrow->DeletePanel(); + m_pDownArrow = NULL; + } + + if ( pszLine ) + { + if ( !m_pLine ) + { + m_pLine = new ImagePanel( this, "Line" ); + if ( m_pLine ) + { + m_pLine->SetShouldScaleImage( true ); + m_pLine->SetZPos( -1 ); + } + } + + m_pLine->SetImage( pszLine ); + m_pLine->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 ); + } + else if ( m_pLine ) + { + m_pLine->DeletePanel(); + m_pLine = NULL; + } + + if ( pszBox ) + { + if ( !m_pBox ) + { + m_pBox = new ImagePanel( this, "Box" ); + if ( m_pBox ) + { + m_pBox->SetShouldScaleImage( true ); + m_pBox->SetZPos( -1 ); + } + } + + m_pBox->SetImage( pszBox ); + m_pBox->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 ); + } + else if ( m_pBox ) + { + m_pBox->DeletePanel(); + m_pBox = NULL; + } + + UpdateButtonsForImages(); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBar::UpdateButtonsForImages( void ) +{ + // Turn off parts of our drawing based on which images we're replacing it with + if ( m_pUpArrow || m_pDownArrow ) + { + SetScrollbarButtonsVisible( false ); + _button[0]->SetPaintBorderEnabled( false ); + _button[1]->SetPaintBorderEnabled( false ); + m_bAutoHideButtons = false; + } + if ( m_pLine || m_pBox ) + { + SetPaintBackgroundEnabled( false ); + SetPaintBorderEnabled( false ); + + if ( _slider ) + { + _slider->SetPaintEnabled( false ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBar::UpdateSliderImages( void ) +{ + if ( m_pUpArrow && m_pDownArrow ) + { + // set the alpha on the up arrow + int nMin, nMax; + GetRange( nMin, nMax ); + int nScrollPos = GetValue(); + int nRangeWindow = GetRangeWindow(); + int nBottom = nMax - nRangeWindow; + if ( nBottom < 0 ) + { + nBottom = 0; + } + + // set the alpha on the up arrow + int nAlpha = ( nScrollPos - nMin <= 0 ) ? 90 : 255; + m_pUpArrow->SetAlpha( nAlpha ); + + // set the alpha on the down arrow + nAlpha = ( nScrollPos >= nBottom ) ? 90 : 255; + m_pDownArrow->SetAlpha( nAlpha ); + } + + if ( m_pLine && m_pBox ) + { + ScrollBarSlider *pSlider = GetSlider(); + if ( pSlider && pSlider->GetRangeWindow() > 0 ) + { + int x, y, w, t, min, max; + m_pLine->GetBounds( x, y, w, t ); + + // If our slider needs layout, force it to do it now + if ( pSlider->IsLayoutInvalid() ) + { + pSlider->InvalidateLayout( true ); + } + pSlider->GetNobPos( min, max ); + + if ( IsVertical() ) + { + m_pBox->SetBounds( x, y + min, w, ( max - min ) ); + } + else + { + m_pBox->SetBounds( x + min, 0, (max-min), t ); + } + } + } +} +void ScrollBar::ApplySettings( KeyValues *pInResourceData ) +{ + BaseClass::ApplySettings( pInResourceData ); + + m_bNoButtons = pInResourceData->GetBool( "nobuttons", false ); + + KeyValues *pSliderKV = pInResourceData->FindKey( "Slider" ); + if ( pSliderKV && _slider ) + { + _slider->ApplySettings( pSliderKV ); + } + + KeyValues *pDownButtonKV = pInResourceData->FindKey( "DownButton" ); + if ( pDownButtonKV && _button[0] ) + { + _button[0]->ApplySettings( pDownButtonKV ); + } + + KeyValues *pUpButtonKV = pInResourceData->FindKey( "UpButton" ); + if ( pUpButtonKV && _button[0] ) + { + _button[1]->ApplySettings( pUpButtonKV ); + } +} diff --git a/mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp b/mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp index da54f3f0..32df2fae 100644 --- a/mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp +++ b/mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp @@ -1,606 +1,606 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#define PROTECTED_THINGS_DISABLE - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -//----------------------------------------------------------------------------- -// The ScrollBarSlider is the scroll bar nob that moves up and down in through a range. -//----------------------------------------------------------------------------- -ScrollBarSlider::ScrollBarSlider(Panel *parent, const char *panelName, bool vertical) : Panel(parent, panelName) -{ - _vertical=vertical; - _dragging=false; - _value=0; - _range[0]=0; - _range[1]=0; - _rangeWindow=0; - _buttonOffset=0; - _ScrollBarSliderBorder=NULL; - RecomputeNobPosFromValue(); - SetBlockDragChaining( true ); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the size of the ScrollBarSlider nob -//----------------------------------------------------------------------------- -void ScrollBarSlider::SetSize(int wide,int tall) -{ - BaseClass::SetSize(wide,tall); - RecomputeNobPosFromValue(); -} - -//----------------------------------------------------------------------------- -// Purpose: Whether the scroll bar is vertical (true) or not (false) -//----------------------------------------------------------------------------- -bool ScrollBarSlider::IsVertical() -{ - return _vertical; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the ScrollBarSlider value of the nob. -//----------------------------------------------------------------------------- -void ScrollBarSlider::SetValue(int value) -{ - int oldValue = _value; - - if (value > _range[1] - _rangeWindow) - { - // note our scrolling range must take into acount _rangeWindow - value = _range[1] - _rangeWindow; - } - - if (value < _range[0]) - { - value = _range[0]; - } - - _value = value; - RecomputeNobPosFromValue(); - - if (_value != oldValue) - { - SendScrollBarSliderMovedMessage(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Get the ScrollBarSlider value of the nob. -//----------------------------------------------------------------------------- -int ScrollBarSlider::GetValue() -{ - return _value; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBarSlider::PerformLayout() -{ - RecomputeNobPosFromValue(); - BaseClass::PerformLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Given the value of the ScrollBarSlider, adjust the ends of the nob. -//----------------------------------------------------------------------------- -void ScrollBarSlider::RecomputeNobPosFromValue() -{ - int wide, tall; - GetPaintSize(wide, tall); - - float fwide = (float)( wide - 1 ); - float ftall = (float)( tall - 1 ); - float frange = (float)(_range[1] -_range[0]); - float fvalue = (float)(_value - _range[0]); - float frangewindow = (float)(_rangeWindow); - float fper = ( frange != frangewindow ) ? fvalue / ( frange-frangewindow ) : 0; - -// Msg( "fwide: %f ftall: %f frange: %f fvalue: %f frangewindow: %f fper: %f\n", -// fwide, ftall, frange, fvalue, frangewindow, fper ); - - if ( frangewindow > 0 ) - { - if ( frange <= 0.0 ) - { - frange = 1.0; - } - - float width, length; - if (_vertical) - { - width = fwide; - length = ftall; - } - else - { - width = ftall; - length = fwide; - } - - // our size is proportional to frangewindow/frange - // the scroll bar nob's length reflects the amount of stuff on the screen - // vs the total amount of stuff we could scroll through in window - // so if a window showed half its contents and the other half is hidden the - // scroll bar's length is half the window. - // if everything is on the screen no nob is displayed - // frange is how many 'lines' of stuff we can display - // frangewindow is how many 'lines' are in the display window - - // proportion of whole window that is on screen - float proportion = frangewindow / frange; - float fnobsize = length * proportion; - if ( fnobsize < width ) fnobsize = (float)width; - - float freepixels = length - fnobsize; - - float firstpixel = freepixels * fper; - - _nobPos[0] = (int)( firstpixel ); - _nobPos[1] = (int)( firstpixel + fnobsize ); - - if ( _nobPos[1] > length ) - { - _nobPos[0] = (int)( length - fnobsize ); - _nobPos[1] = (int)length; - } - - } - - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the ScrollBarSlider value using the location of the nob ends. -//----------------------------------------------------------------------------- -void ScrollBarSlider::RecomputeValueFromNobPos() -{ - int wide, tall; - GetPaintSize(wide, tall); - - float fwide = (float)( wide - 1 ); - float ftall = (float)( tall - 1 ); - float frange = (float)( _range[1] - _range[0] ); - float fvalue = (float)( _value - _range[0] ); - float fnob = (float)_nobPos[0]; - float frangewindow = (float)(_rangeWindow); - - if ( frangewindow > 0 ) - { - if ( frange <= 0.0 ) - { - frange = 1.0; - } - - // set local width and length - float width, length; - if ( _vertical ) - { - width = fwide; - length = ftall; - } - else - { - width = ftall; - length = fwide; - } - - // calculate the size of the nob - float proportion = frangewindow / frange; - float fnobsize = length * proportion; - - if ( fnobsize < width ) - { - fnobsize = width; - } - - // Our scroll bar actually doesnt scroll through all frange lines in the truerange, we - // actually only scroll through frange-frangewindow number of lines so we must take that - // into account when we calculate the value - // convert to our local size system - - // Make sure we don't divide by zero - if ( length - fnobsize == 0 ) - { - fvalue = 0.0f; - } - else - { - fvalue = (frange - frangewindow) * ( fnob / ( length - fnobsize ) ); - } - } - - // check to see if we should just snap to the bottom - if (fabs(fvalue + _rangeWindow - _range[1]) < (0.01f * frange)) - { - // snap to the end - _value = _range[1] - _rangeWindow; - } - else - { - // Take care of rounding issues. - _value = (int)( fvalue + _range[0] + 0.5); - } - - // Clamp final result - _value = ( _value < (_range[1] - _rangeWindow) ) ? _value : (_range[1] - _rangeWindow); - - if (_value < _range[0]) - { - _value = _range[0]; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Check if the ScrollBarSlider can move through one or more pixels per -// unit of its range. -//----------------------------------------------------------------------------- -bool ScrollBarSlider::HasFullRange() -{ - int wide, tall; - GetPaintSize(wide, tall); - - float frangewindow = (float)(_rangeWindow); - - float checkAgainst = 0; - if(_vertical) - { - checkAgainst = (float)tall; - } - else - { - checkAgainst = (float)wide; - } - - if ( frangewindow > 0 ) - { - if( frangewindow <= ( checkAgainst + _buttonOffset ) ) - { - return true; - } - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Inform other watchers that the ScrollBarSlider was moved -//----------------------------------------------------------------------------- -void ScrollBarSlider::SendScrollBarSliderMovedMessage() -{ - // send a changed message - PostActionSignal(new KeyValues("ScrollBarSliderMoved", "position", _value)); -} - -//----------------------------------------------------------------------------- -// Purpose: Return true if this slider is actually drawing itself -//----------------------------------------------------------------------------- -bool ScrollBarSlider::IsSliderVisible( void ) -{ - int itemRange = _range[1] - _range[0]; - - // Don't draw nob, no items in list - if ( itemRange <= 0 ) - return false ; - - // Not enough range - if ( itemRange <= _rangeWindow ) - return false; - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBarSlider::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetFgColor(GetSchemeColor("ScrollBarSlider.FgColor", pScheme)); - SetBgColor(GetSchemeColor("ScrollBarSlider.BgColor", pScheme)); - - IBorder *newBorder = pScheme->GetBorder("ScrollBarSliderBorder"); - - if ( newBorder ) - { - _ScrollBarSliderBorder = newBorder; - } - else - { - _ScrollBarSliderBorder = pScheme->GetBorder("ButtonBorder"); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBarSlider::ApplySettings( KeyValues *pInResourceData ) -{ - BaseClass::ApplySettings( pInResourceData ); - - const char *pButtonBorderName = pInResourceData->GetString( "ButtonBorder", NULL ); - if ( pButtonBorderName ) - { - _ScrollBarSliderBorder = vgui::scheme()->GetIScheme( GetScheme() )->GetBorder( pButtonBorderName ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBarSlider::Paint() -{ - int wide,tall; - GetPaintSize(wide,tall); - - if ( !IsSliderVisible() ) - return; - - Color col = GetFgColor(); - surface()->DrawSetColor(col); - - if (_vertical) - { - if ( GetPaintBackgroundType() == 2 ) - { - DrawBox( 1, _nobPos[0], wide - 2, _nobPos[1] - _nobPos[0], col, 1.0f ); - } - else - { - // Nob - surface()->DrawFilledRect(1, _nobPos[0], wide - 2, _nobPos[1]); - } - - // border - if (_ScrollBarSliderBorder) - { - _ScrollBarSliderBorder->Paint(0, _nobPos[0], wide, _nobPos[1]); - } - } - else - { - // horizontal nob - surface()->DrawFilledRect(_nobPos[0], 1, _nobPos[1], tall - 2 ); - - // border - if (_ScrollBarSliderBorder) - { - _ScrollBarSliderBorder->Paint(_nobPos[0] - 1, 1, _nobPos[1], tall ); - } - } - -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBarSlider::PaintBackground() -{ -// BaseClass::PaintBackground(); - - int wide,tall; - GetPaintSize(wide,tall); - surface()->DrawSetColor(GetBgColor()); - surface()->DrawFilledRect(0, 0, wide-1, tall-1); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the range of the ScrollBarSlider -//----------------------------------------------------------------------------- -void ScrollBarSlider::SetRange(int min,int max) -{ - if(maxmax) - { - min=max; - } - - _range[0]=min; - _range[1]=max; - - // update the value (forces it within the range) - SetValue( _value ); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the range values of the ScrollBarSlider -//----------------------------------------------------------------------------- -void ScrollBarSlider::GetRange(int& min,int& max) -{ - min=_range[0]; - max=_range[1]; -} - -//----------------------------------------------------------------------------- -// Purpose: Respond to cursor movements, we only care about clicking and dragging -//----------------------------------------------------------------------------- -void ScrollBarSlider::OnCursorMoved(int x,int y) -{ - if (!_dragging) - { - return; - } - -// input()->GetCursorPos(x, y); -// ScreenToLocal(x, y); - - int wide, tall; - GetPaintSize(wide, tall); - - if (_vertical) - { - _nobPos[0] = _nobDragStartPos[0] + (y - _dragStartPos[1]); - _nobPos[1] = _nobDragStartPos[1] + (y - _dragStartPos[1]); - - if (_nobPos[1] > tall) - { - _nobPos[0] = tall - (_nobPos[1] - _nobPos[0]); - _nobPos[1] = tall; - SetValue( _range[1] - _rangeWindow ); - } - } - else - { - _nobPos[0] = _nobDragStartPos[0] + (x - _dragStartPos[0]); - _nobPos[1] = _nobDragStartPos[1] + (x - _dragStartPos[0]); - - if (_nobPos[1] > wide) - { - _nobPos[0] = wide - (_nobPos[1] - _nobPos[0]); - _nobPos[1] = wide; - } - - } - if (_nobPos[0] < 0) - { - _nobPos[1] = _nobPos[1] - _nobPos[0]; - _nobPos[0] = 0; - SetValue(0); - } - - InvalidateLayout(); // not invalidatelayout - because it won't draw while we're scrolling the slider - RecomputeValueFromNobPos(); -// Repaint(); - SendScrollBarSliderMovedMessage(); -} - -//----------------------------------------------------------------------------- -// Purpose: Respond to mouse clicks on the ScrollBarSlider -//----------------------------------------------------------------------------- -void ScrollBarSlider::OnMousePressed(MouseCode code) -{ - int x,y; - input()->GetCursorPos(x,y); - ScreenToLocal(x,y); - - if (_vertical) - { - if ((y >= _nobPos[0]) && (y < _nobPos[1])) - { - _dragging = true; - input()->SetMouseCapture(GetVPanel()); - _nobDragStartPos[0] = _nobPos[0]; - _nobDragStartPos[1] = _nobPos[1]; - _dragStartPos[0] = x; - _dragStartPos[1] = y; - } - else if (y < _nobPos[0]) - { - // jump the bar up by the range window - int val = GetValue(); - val -= _rangeWindow; - SetValue(val); - } - else if (y >= _nobPos[1]) - { - // jump the bar down by the range window - int val = GetValue(); - val += _rangeWindow; - SetValue(val); - } - } - else - { - if((x >= _nobPos[0]) && (x < _nobPos[1])) - { - _dragging = true; - input()->SetMouseCapture(GetVPanel()); - _nobDragStartPos[0] = _nobPos[0]; - _nobDragStartPos[1] = _nobPos[1]; - _dragStartPos[0] = x; - _dragStartPos[1] = y; - } - else if (x < _nobPos[0]) - { - // jump the bar up by the range window - int val = GetValue(); - val -= _rangeWindow; - SetValue(val); - } - else if (x >= _nobPos[1]) - { - // jump the bar down by the range window - int val = GetValue(); - val += _rangeWindow; - SetValue(val); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Treat double clicks as single clicks -//----------------------------------------------------------------------------- -void ScrollBarSlider::OnMouseDoublePressed(MouseCode code) -{ - OnMousePressed(code); -} - -//----------------------------------------------------------------------------- -// Purpose: Stop looking for mouse events when mouse is up. -//----------------------------------------------------------------------------- -void ScrollBarSlider::OnMouseReleased(MouseCode code) -{ - _dragging = false; - input()->SetMouseCapture(null); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the position of the ends of the ScrollBarSlider. -//----------------------------------------------------------------------------- -void ScrollBarSlider::GetNobPos(int& min, int& max) -{ - min=_nobPos[0]; - max=_nobPos[1]; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the number of lines visible in the window the ScrollBarSlider is attached to -//----------------------------------------------------------------------------- -void ScrollBarSlider::SetRangeWindow(int rangeWindow) -{ - _rangeWindow = rangeWindow; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the number of lines visible in the window the ScrollBarSlider is attached to -//----------------------------------------------------------------------------- -int ScrollBarSlider::GetRangeWindow() -{ - return _rangeWindow; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ScrollBarSlider::SetButtonOffset(int buttonOffset) -{ - _buttonOffset = buttonOffset; +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#define PROTECTED_THINGS_DISABLE + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +//----------------------------------------------------------------------------- +// The ScrollBarSlider is the scroll bar nob that moves up and down in through a range. +//----------------------------------------------------------------------------- +ScrollBarSlider::ScrollBarSlider(Panel *parent, const char *panelName, bool vertical) : Panel(parent, panelName) +{ + _vertical=vertical; + _dragging=false; + _value=0; + _range[0]=0; + _range[1]=0; + _rangeWindow=0; + _buttonOffset=0; + _ScrollBarSliderBorder=NULL; + RecomputeNobPosFromValue(); + SetBlockDragChaining( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the size of the ScrollBarSlider nob +//----------------------------------------------------------------------------- +void ScrollBarSlider::SetSize(int wide,int tall) +{ + BaseClass::SetSize(wide,tall); + RecomputeNobPosFromValue(); +} + +//----------------------------------------------------------------------------- +// Purpose: Whether the scroll bar is vertical (true) or not (false) +//----------------------------------------------------------------------------- +bool ScrollBarSlider::IsVertical() +{ + return _vertical; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the ScrollBarSlider value of the nob. +//----------------------------------------------------------------------------- +void ScrollBarSlider::SetValue(int value) +{ + int oldValue = _value; + + if (value > _range[1] - _rangeWindow) + { + // note our scrolling range must take into acount _rangeWindow + value = _range[1] - _rangeWindow; + } + + if (value < _range[0]) + { + value = _range[0]; + } + + _value = value; + RecomputeNobPosFromValue(); + + if (_value != oldValue) + { + SendScrollBarSliderMovedMessage(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get the ScrollBarSlider value of the nob. +//----------------------------------------------------------------------------- +int ScrollBarSlider::GetValue() +{ + return _value; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBarSlider::PerformLayout() +{ + RecomputeNobPosFromValue(); + BaseClass::PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Given the value of the ScrollBarSlider, adjust the ends of the nob. +//----------------------------------------------------------------------------- +void ScrollBarSlider::RecomputeNobPosFromValue() +{ + int wide, tall; + GetPaintSize(wide, tall); + + float fwide = (float)( wide - 1 ); + float ftall = (float)( tall - 1 ); + float frange = (float)(_range[1] -_range[0]); + float fvalue = (float)(_value - _range[0]); + float frangewindow = (float)(_rangeWindow); + float fper = ( frange != frangewindow ) ? fvalue / ( frange-frangewindow ) : 0; + +// Msg( "fwide: %f ftall: %f frange: %f fvalue: %f frangewindow: %f fper: %f\n", +// fwide, ftall, frange, fvalue, frangewindow, fper ); + + if ( frangewindow > 0 ) + { + if ( frange <= 0.0 ) + { + frange = 1.0; + } + + float width, length; + if (_vertical) + { + width = fwide; + length = ftall; + } + else + { + width = ftall; + length = fwide; + } + + // our size is proportional to frangewindow/frange + // the scroll bar nob's length reflects the amount of stuff on the screen + // vs the total amount of stuff we could scroll through in window + // so if a window showed half its contents and the other half is hidden the + // scroll bar's length is half the window. + // if everything is on the screen no nob is displayed + // frange is how many 'lines' of stuff we can display + // frangewindow is how many 'lines' are in the display window + + // proportion of whole window that is on screen + float proportion = frangewindow / frange; + float fnobsize = length * proportion; + if ( fnobsize < width ) fnobsize = (float)width; + + float freepixels = length - fnobsize; + + float firstpixel = freepixels * fper; + + _nobPos[0] = (int)( firstpixel ); + _nobPos[1] = (int)( firstpixel + fnobsize ); + + if ( _nobPos[1] > length ) + { + _nobPos[0] = (int)( length - fnobsize ); + _nobPos[1] = (int)length; + } + + } + + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the ScrollBarSlider value using the location of the nob ends. +//----------------------------------------------------------------------------- +void ScrollBarSlider::RecomputeValueFromNobPos() +{ + int wide, tall; + GetPaintSize(wide, tall); + + float fwide = (float)( wide - 1 ); + float ftall = (float)( tall - 1 ); + float frange = (float)( _range[1] - _range[0] ); + float fvalue = (float)( _value - _range[0] ); + float fnob = (float)_nobPos[0]; + float frangewindow = (float)(_rangeWindow); + + if ( frangewindow > 0 ) + { + if ( frange <= 0.0 ) + { + frange = 1.0; + } + + // set local width and length + float width, length; + if ( _vertical ) + { + width = fwide; + length = ftall; + } + else + { + width = ftall; + length = fwide; + } + + // calculate the size of the nob + float proportion = frangewindow / frange; + float fnobsize = length * proportion; + + if ( fnobsize < width ) + { + fnobsize = width; + } + + // Our scroll bar actually doesnt scroll through all frange lines in the truerange, we + // actually only scroll through frange-frangewindow number of lines so we must take that + // into account when we calculate the value + // convert to our local size system + + // Make sure we don't divide by zero + if ( length - fnobsize == 0 ) + { + fvalue = 0.0f; + } + else + { + fvalue = (frange - frangewindow) * ( fnob / ( length - fnobsize ) ); + } + } + + // check to see if we should just snap to the bottom + if (fabs(fvalue + _rangeWindow - _range[1]) < (0.01f * frange)) + { + // snap to the end + _value = _range[1] - _rangeWindow; + } + else + { + // Take care of rounding issues. + _value = (int)( fvalue + _range[0] + 0.5); + } + + // Clamp final result + _value = ( _value < (_range[1] - _rangeWindow) ) ? _value : (_range[1] - _rangeWindow); + + if (_value < _range[0]) + { + _value = _range[0]; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Check if the ScrollBarSlider can move through one or more pixels per +// unit of its range. +//----------------------------------------------------------------------------- +bool ScrollBarSlider::HasFullRange() +{ + int wide, tall; + GetPaintSize(wide, tall); + + float frangewindow = (float)(_rangeWindow); + + float checkAgainst = 0; + if(_vertical) + { + checkAgainst = (float)tall; + } + else + { + checkAgainst = (float)wide; + } + + if ( frangewindow > 0 ) + { + if( frangewindow <= ( checkAgainst + _buttonOffset ) ) + { + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Inform other watchers that the ScrollBarSlider was moved +//----------------------------------------------------------------------------- +void ScrollBarSlider::SendScrollBarSliderMovedMessage() +{ + // send a changed message + PostActionSignal(new KeyValues("ScrollBarSliderMoved", "position", _value)); +} + +//----------------------------------------------------------------------------- +// Purpose: Return true if this slider is actually drawing itself +//----------------------------------------------------------------------------- +bool ScrollBarSlider::IsSliderVisible( void ) +{ + int itemRange = _range[1] - _range[0]; + + // Don't draw nob, no items in list + if ( itemRange <= 0 ) + return false ; + + // Not enough range + if ( itemRange <= _rangeWindow ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBarSlider::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetFgColor(GetSchemeColor("ScrollBarSlider.FgColor", pScheme)); + SetBgColor(GetSchemeColor("ScrollBarSlider.BgColor", pScheme)); + + IBorder *newBorder = pScheme->GetBorder("ScrollBarSliderBorder"); + + if ( newBorder ) + { + _ScrollBarSliderBorder = newBorder; + } + else + { + _ScrollBarSliderBorder = pScheme->GetBorder("ButtonBorder"); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBarSlider::ApplySettings( KeyValues *pInResourceData ) +{ + BaseClass::ApplySettings( pInResourceData ); + + const char *pButtonBorderName = pInResourceData->GetString( "ButtonBorder", NULL ); + if ( pButtonBorderName ) + { + _ScrollBarSliderBorder = vgui::scheme()->GetIScheme( GetScheme() )->GetBorder( pButtonBorderName ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBarSlider::Paint() +{ + int wide,tall; + GetPaintSize(wide,tall); + + if ( !IsSliderVisible() ) + return; + + Color col = GetFgColor(); + surface()->DrawSetColor(col); + + if (_vertical) + { + if ( GetPaintBackgroundType() == 2 ) + { + DrawBox( 1, _nobPos[0], wide - 2, _nobPos[1] - _nobPos[0], col, 1.0f ); + } + else + { + // Nob + surface()->DrawFilledRect(1, _nobPos[0], wide - 2, _nobPos[1]); + } + + // border + if (_ScrollBarSliderBorder) + { + _ScrollBarSliderBorder->Paint(0, _nobPos[0], wide, _nobPos[1]); + } + } + else + { + // horizontal nob + surface()->DrawFilledRect(_nobPos[0], 1, _nobPos[1], tall - 2 ); + + // border + if (_ScrollBarSliderBorder) + { + _ScrollBarSliderBorder->Paint(_nobPos[0] - 1, 1, _nobPos[1], tall ); + } + } + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBarSlider::PaintBackground() +{ +// BaseClass::PaintBackground(); + + int wide,tall; + GetPaintSize(wide,tall); + surface()->DrawSetColor(GetBgColor()); + surface()->DrawFilledRect(0, 0, wide-1, tall-1); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the range of the ScrollBarSlider +//----------------------------------------------------------------------------- +void ScrollBarSlider::SetRange(int min,int max) +{ + if(maxmax) + { + min=max; + } + + _range[0]=min; + _range[1]=max; + + // update the value (forces it within the range) + SetValue( _value ); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the range values of the ScrollBarSlider +//----------------------------------------------------------------------------- +void ScrollBarSlider::GetRange(int& min,int& max) +{ + min=_range[0]; + max=_range[1]; +} + +//----------------------------------------------------------------------------- +// Purpose: Respond to cursor movements, we only care about clicking and dragging +//----------------------------------------------------------------------------- +void ScrollBarSlider::OnCursorMoved(int x,int y) +{ + if (!_dragging) + { + return; + } + +// input()->GetCursorPos(x, y); +// ScreenToLocal(x, y); + + int wide, tall; + GetPaintSize(wide, tall); + + if (_vertical) + { + _nobPos[0] = _nobDragStartPos[0] + (y - _dragStartPos[1]); + _nobPos[1] = _nobDragStartPos[1] + (y - _dragStartPos[1]); + + if (_nobPos[1] > tall) + { + _nobPos[0] = tall - (_nobPos[1] - _nobPos[0]); + _nobPos[1] = tall; + SetValue( _range[1] - _rangeWindow ); + } + } + else + { + _nobPos[0] = _nobDragStartPos[0] + (x - _dragStartPos[0]); + _nobPos[1] = _nobDragStartPos[1] + (x - _dragStartPos[0]); + + if (_nobPos[1] > wide) + { + _nobPos[0] = wide - (_nobPos[1] - _nobPos[0]); + _nobPos[1] = wide; + } + + } + if (_nobPos[0] < 0) + { + _nobPos[1] = _nobPos[1] - _nobPos[0]; + _nobPos[0] = 0; + SetValue(0); + } + + InvalidateLayout(); // not invalidatelayout - because it won't draw while we're scrolling the slider + RecomputeValueFromNobPos(); +// Repaint(); + SendScrollBarSliderMovedMessage(); +} + +//----------------------------------------------------------------------------- +// Purpose: Respond to mouse clicks on the ScrollBarSlider +//----------------------------------------------------------------------------- +void ScrollBarSlider::OnMousePressed(MouseCode code) +{ + int x,y; + input()->GetCursorPos(x,y); + ScreenToLocal(x,y); + + if (_vertical) + { + if ((y >= _nobPos[0]) && (y < _nobPos[1])) + { + _dragging = true; + input()->SetMouseCapture(GetVPanel()); + _nobDragStartPos[0] = _nobPos[0]; + _nobDragStartPos[1] = _nobPos[1]; + _dragStartPos[0] = x; + _dragStartPos[1] = y; + } + else if (y < _nobPos[0]) + { + // jump the bar up by the range window + int val = GetValue(); + val -= _rangeWindow; + SetValue(val); + } + else if (y >= _nobPos[1]) + { + // jump the bar down by the range window + int val = GetValue(); + val += _rangeWindow; + SetValue(val); + } + } + else + { + if((x >= _nobPos[0]) && (x < _nobPos[1])) + { + _dragging = true; + input()->SetMouseCapture(GetVPanel()); + _nobDragStartPos[0] = _nobPos[0]; + _nobDragStartPos[1] = _nobPos[1]; + _dragStartPos[0] = x; + _dragStartPos[1] = y; + } + else if (x < _nobPos[0]) + { + // jump the bar up by the range window + int val = GetValue(); + val -= _rangeWindow; + SetValue(val); + } + else if (x >= _nobPos[1]) + { + // jump the bar down by the range window + int val = GetValue(); + val += _rangeWindow; + SetValue(val); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Treat double clicks as single clicks +//----------------------------------------------------------------------------- +void ScrollBarSlider::OnMouseDoublePressed(MouseCode code) +{ + OnMousePressed(code); +} + +//----------------------------------------------------------------------------- +// Purpose: Stop looking for mouse events when mouse is up. +//----------------------------------------------------------------------------- +void ScrollBarSlider::OnMouseReleased(MouseCode code) +{ + _dragging = false; + input()->SetMouseCapture(null); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the position of the ends of the ScrollBarSlider. +//----------------------------------------------------------------------------- +void ScrollBarSlider::GetNobPos(int& min, int& max) +{ + min=_nobPos[0]; + max=_nobPos[1]; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the number of lines visible in the window the ScrollBarSlider is attached to +//----------------------------------------------------------------------------- +void ScrollBarSlider::SetRangeWindow(int rangeWindow) +{ + _rangeWindow = rangeWindow; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the number of lines visible in the window the ScrollBarSlider is attached to +//----------------------------------------------------------------------------- +int ScrollBarSlider::GetRangeWindow() +{ + return _rangeWindow; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScrollBarSlider::SetButtonOffset(int buttonOffset) +{ + _buttonOffset = buttonOffset; } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/ScrollableEditablePanel.cpp b/mp/src/vgui2/vgui_controls/ScrollableEditablePanel.cpp index 5d753b04..d734dba5 100644 --- a/mp/src/vgui2/vgui_controls/ScrollableEditablePanel.cpp +++ b/mp/src/vgui2/vgui_controls/ScrollableEditablePanel.cpp @@ -1,86 +1,86 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//============================================================================= - -#include "vgui_controls/ScrollableEditablePanel.h" -#include "vgui_controls/ScrollBar.h" -#include "vgui_controls/ScrollBarSlider.h" -#include "vgui_controls/Button.h" -#include "KeyValues.h" - -// NOTE: This has to be the last file included! -#include "tier0/memdbgon.h" - - -using namespace vgui; - -ScrollableEditablePanel::ScrollableEditablePanel( vgui::Panel *pParent, vgui::EditablePanel *pChild, const char *pName ) : - BaseClass( pParent, pName ) -{ - m_pChild = pChild; - m_pChild->SetParent( this ); - - m_pScrollBar = new vgui::ScrollBar( this, "VerticalScrollBar", true ); - m_pScrollBar->SetWide( 16 ); - m_pScrollBar->SetAutoResize( PIN_TOPRIGHT, AUTORESIZE_DOWN, 0, 0, -16, 0 ); - m_pScrollBar->AddActionSignalTarget( this ); -} - -void ScrollableEditablePanel::ApplySettings( KeyValues *pInResourceData ) -{ - BaseClass::ApplySettings( pInResourceData ); - - KeyValues *pScrollbarKV = pInResourceData->FindKey( "Scrollbar" ); - if ( pScrollbarKV ) - { - m_pScrollBar->ApplySettings( pScrollbarKV ); - } -} - -void ScrollableEditablePanel::PerformLayout() -{ - BaseClass::PerformLayout(); - - m_pChild->SetWide( GetWide() - m_pScrollBar->GetWide() ); - m_pScrollBar->SetRange( 0, m_pChild->GetTall() ); - m_pScrollBar->SetRangeWindow( GetTall() ); - - if ( m_pScrollBar->GetSlider() ) - { - m_pScrollBar->GetSlider()->SetFgColor( GetFgColor() ); - } - if ( m_pScrollBar->GetButton(0) ) - { - m_pScrollBar->GetButton(0)->SetFgColor( GetFgColor() ); - } - if ( m_pScrollBar->GetButton(1) ) - { - m_pScrollBar->GetButton(1)->SetFgColor( GetFgColor() ); - } -} - - -//----------------------------------------------------------------------------- -// Called when the scroll bar moves -//----------------------------------------------------------------------------- -void ScrollableEditablePanel::OnScrollBarSliderMoved() -{ - InvalidateLayout(); - - int nScrollAmount = m_pScrollBar->GetValue(); - m_pChild->SetPos( 0, -nScrollAmount ); -} - -//----------------------------------------------------------------------------- -// respond to mouse wheel events -//----------------------------------------------------------------------------- -void ScrollableEditablePanel::OnMouseWheeled(int delta) -{ - int val = m_pScrollBar->GetValue(); - val -= (delta * 50); - m_pScrollBar->SetValue( val ); -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#include "vgui_controls/ScrollableEditablePanel.h" +#include "vgui_controls/ScrollBar.h" +#include "vgui_controls/ScrollBarSlider.h" +#include "vgui_controls/Button.h" +#include "KeyValues.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +using namespace vgui; + +ScrollableEditablePanel::ScrollableEditablePanel( vgui::Panel *pParent, vgui::EditablePanel *pChild, const char *pName ) : + BaseClass( pParent, pName ) +{ + m_pChild = pChild; + m_pChild->SetParent( this ); + + m_pScrollBar = new vgui::ScrollBar( this, "VerticalScrollBar", true ); + m_pScrollBar->SetWide( 16 ); + m_pScrollBar->SetAutoResize( PIN_TOPRIGHT, AUTORESIZE_DOWN, 0, 0, -16, 0 ); + m_pScrollBar->AddActionSignalTarget( this ); +} + +void ScrollableEditablePanel::ApplySettings( KeyValues *pInResourceData ) +{ + BaseClass::ApplySettings( pInResourceData ); + + KeyValues *pScrollbarKV = pInResourceData->FindKey( "Scrollbar" ); + if ( pScrollbarKV ) + { + m_pScrollBar->ApplySettings( pScrollbarKV ); + } +} + +void ScrollableEditablePanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + m_pChild->SetWide( GetWide() - m_pScrollBar->GetWide() ); + m_pScrollBar->SetRange( 0, m_pChild->GetTall() ); + m_pScrollBar->SetRangeWindow( GetTall() ); + + if ( m_pScrollBar->GetSlider() ) + { + m_pScrollBar->GetSlider()->SetFgColor( GetFgColor() ); + } + if ( m_pScrollBar->GetButton(0) ) + { + m_pScrollBar->GetButton(0)->SetFgColor( GetFgColor() ); + } + if ( m_pScrollBar->GetButton(1) ) + { + m_pScrollBar->GetButton(1)->SetFgColor( GetFgColor() ); + } +} + + +//----------------------------------------------------------------------------- +// Called when the scroll bar moves +//----------------------------------------------------------------------------- +void ScrollableEditablePanel::OnScrollBarSliderMoved() +{ + InvalidateLayout(); + + int nScrollAmount = m_pScrollBar->GetValue(); + m_pChild->SetPos( 0, -nScrollAmount ); +} + +//----------------------------------------------------------------------------- +// respond to mouse wheel events +//----------------------------------------------------------------------------- +void ScrollableEditablePanel::OnMouseWheeled(int delta) +{ + int val = m_pScrollBar->GetValue(); + val -= (delta * 50); + m_pScrollBar->SetValue( val ); +} + diff --git a/mp/src/vgui2/vgui_controls/SectionedListPanel.cpp b/mp/src/vgui2/vgui_controls/SectionedListPanel.cpp index 120de368..65e90239 100644 --- a/mp/src/vgui2/vgui_controls/SectionedListPanel.cpp +++ b/mp/src/vgui2/vgui_controls/SectionedListPanel.cpp @@ -1,2169 +1,2169 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "utlvector.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -enum -{ - BUTTON_HEIGHT_DEFAULT = 20, - BUTTON_HEIGHT_SPACER = 7, - DEFAULT_LINE_SPACING = 20, - DEFAULT_SECTION_GAP = 8, - COLUMN_DATA_INDENT = 6, - COLUMN_DATA_GAP = 2, -}; - -namespace vgui -{ - -//----------------------------------------------------------------------------- -// Purpose: header label that separates and names each section -//----------------------------------------------------------------------------- -SectionedListPanelHeader::SectionedListPanelHeader(SectionedListPanel *parent, const char *name, int sectionID) : Label(parent, name, "") -{ - m_pListPanel = parent; - m_iSectionID = sectionID; - SetTextImageIndex(-1); - ClearImages(); - SetPaintBackgroundEnabled( false ); -} - -SectionedListPanelHeader::SectionedListPanelHeader(SectionedListPanel *parent, const wchar_t *name, int sectionID) : Label(parent, "SectionHeader", "") -{ - SetText(name); - SetVisible(false); - m_pListPanel = parent; - m_iSectionID = sectionID; - SetTextImageIndex(-1); - ClearImages(); -} - -void SectionedListPanelHeader::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetFgColor(GetSchemeColor("SectionedListPanel.HeaderTextColor", pScheme)); - m_SectionDividerColor = GetSchemeColor("SectionedListPanel.DividerColor", pScheme); - SetBgColor(GetSchemeColor("SectionedListPanelHeader.BgColor", GetBgColor(), pScheme)); - SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional())); - ClearImages(); - - HFont hFont = m_pListPanel->GetHeaderFont(); - if ( hFont != INVALID_FONT ) - { - SetFont( hFont ); - } - else - { - SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional())); - } -} - -void SectionedListPanelHeader::Paint() -{ - BaseClass::Paint(); - - int x, y, wide, tall; - GetBounds(x, y, wide, tall); - - y = (tall - 2); // draw the line under the panel - - surface()->DrawSetColor(m_SectionDividerColor); - surface()->DrawFilledRect(1, y, GetWide() - 2, y + 1); -} - -void SectionedListPanelHeader::SetColor(Color col) -{ - m_SectionDividerColor = col; - SetFgColor(col); -} -void SectionedListPanelHeader::SetDividerColor(Color col ) -{ - m_SectionDividerColor = col; -} - -void SectionedListPanelHeader::PerformLayout() -{ - BaseClass::PerformLayout(); - - // set up the text in the header - int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID); - if (colCount != GetImageCount()) - { - // rebuild the image list - for (int i = 0; i < colCount; i++) - { - int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i); - IImage *image = NULL; - if (columnFlags & SectionedListPanel::HEADER_IMAGE) - { - //!! need some kind of image reference - image = NULL; - } - else - { - TextImage *textImage = new TextImage(""); - textImage->SetFont(GetFont()); - HFont fallback = m_pListPanel->GetColumnFallbackFontBySection( m_iSectionID, i ); - if ( INVALID_FONT != fallback ) - { - textImage->SetUseFallbackFont( true, fallback ); - } - textImage->SetColor(GetFgColor()); - image = textImage; - } - - SetImageAtIndex(i, image, 0); - } - } - - for (int repeat = 0; repeat <= 1; repeat++) - { - int xpos = 0; - for (int i = 0; i < colCount; i++) - { - int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i); - int columnWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i); - int maxWidth = columnWidth; - - IImage *image = GetImageAtIndex(i); - if (!image) - { - xpos += columnWidth; - continue; - } - - // set the image position within the label - int contentWide, wide, tall; - image->GetContentSize(wide, tall); - contentWide = wide; - - // see if we can draw over the next few column headers (if we're left-aligned) - if (!(columnFlags & SectionedListPanel::COLUMN_RIGHT)) - { - for (int j = i + 1; j < colCount; j++) - { - // see if this column header has anything for a header - int iwide = 0, itall = 0; - if (GetImageAtIndex(j)) - { - GetImageAtIndex(j)->GetContentSize(iwide, itall); - } - - if (iwide == 0) - { - // it's a blank header, ok to draw over it - maxWidth += m_pListPanel->GetColumnWidthBySection(m_iSectionID, j); - } - } - } - if (maxWidth >= 0) - { - wide = maxWidth; - } - - if (columnFlags & SectionedListPanel::COLUMN_RIGHT) - { - SetImageBounds(i, xpos + wide - contentWide, wide - COLUMN_DATA_GAP); - } - else - { - SetImageBounds(i, xpos, wide - COLUMN_DATA_GAP); - } - xpos += columnWidth; - - if (!(columnFlags & SectionedListPanel::HEADER_IMAGE)) - { - Assert(dynamic_cast(image) != NULL); - TextImage *textImage = (TextImage *)image; - textImage->SetFont(GetFont()); - textImage->SetText(m_pListPanel->GetColumnTextBySection(m_iSectionID, i)); - textImage->ResizeImageToContentMaxWidth( maxWidth ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Individual items in the list -//----------------------------------------------------------------------------- -class CItemButton : public Label -{ - DECLARE_CLASS_SIMPLE( CItemButton, Label ); - -public: - CItemButton(SectionedListPanel *parent, int itemID) : Label(parent, NULL, "< item >") - { - m_pListPanel = parent; - m_iID = itemID; - m_pData = NULL; - Clear(); - } - - ~CItemButton() - { - // free all the keyvalues - if (m_pData) - { - m_pData->deleteThis(); - } - - // clear any section data - SetSectionID(-1); - } - - void Clear() - { - m_bSelected = false; - m_bOverrideColors = false; - m_iSectionID = -1; - SetPaintBackgroundEnabled( false ); - SetTextImageIndex(-1); - ClearImages(); - } - - int GetID() - { - return m_iID; - } - - void SetID(int itemID) - { - m_iID = itemID; - } - - int GetSectionID() - { - return m_iSectionID; - } - - void SetSectionID(int sectionID) - { - if (sectionID != m_iSectionID) - { - // free any existing textimage list - ClearImages(); - // delete any images we've created - for (int i = 0; i < m_TextImages.Count(); i++) - { - delete m_TextImages[i]; - } - m_TextImages.RemoveAll(); - // mark the list as needing rebuilding - InvalidateLayout(); - } - m_iSectionID = sectionID; - } - - void SetData(const KeyValues *data) - { - if (m_pData) - { - m_pData->deleteThis(); - } - - m_pData = data->MakeCopy(); - InvalidateLayout(); - } - - KeyValues *GetData() - { - return m_pData; - } - - virtual void PerformLayout() - { - // get our button text - int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID); - if (!m_pData || colCount < 1) - { - SetText("< unset >"); - } - else - { - if (colCount != GetImageCount()) - { - // rebuild the image list - for (int i = 0; i < colCount; i++) - { - int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i); - if (!(columnFlags & SectionedListPanel::COLUMN_IMAGE)) - { - TextImage *image = new TextImage(""); - m_TextImages.AddToTail(image); - image->SetFont( GetFont() ); - HFont fallback = m_pListPanel->GetColumnFallbackFontBySection( m_iSectionID, i ); - if ( INVALID_FONT != fallback ) - { - image->SetUseFallbackFont( true, fallback ); - } - SetImageAtIndex(i, image, 0); - } - } - - {for ( int i = GetImageCount(); i < colCount; i++ ) // make sure we have enough image slots - { - AddImage( NULL, 0 ); - }} - } - - // set the text for each column - int xpos = 0; - for (int i = 0; i < colCount; i++) - { - const char *keyname = m_pListPanel->GetColumnNameBySection(m_iSectionID, i); - - int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i); - int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i); - - IImage *image = NULL; - if (columnFlags & SectionedListPanel::COLUMN_IMAGE) - { - // lookup which image is being referred to - if (m_pListPanel->m_pImageList) - { - int imageIndex = m_pData->GetInt(keyname, 0); - if (m_pListPanel->m_pImageList->IsValidIndex(imageIndex)) - { - // 0 is always the blank image - if (imageIndex > 0) - { - image = m_pListPanel->m_pImageList->GetImage(imageIndex); - SetImageAtIndex(i, image, 0); - } - } - else - { - // this is mildly valid (CGamesList hits it because of the way it uses the image indices) - // Assert(!("Image index out of range for ImageList in SectionedListPanel")); - } - } - else - { - Assert(!("Images columns used in SectionedListPanel with no ImageList set")); - } - } - else - { - TextImage *textImage = dynamic_cast(GetImageAtIndex(i)); - if (textImage) - { - textImage->SetText(m_pData->GetString(keyname, "")); - textImage->ResizeImageToContentMaxWidth( maxWidth ); - - // set the text color based on the selection state - if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected - VPANEL focus = input()->GetFocus(); - if ( !m_bOverrideColors ) - { - if (IsSelected() && !m_pListPanel->IsInEditMode()) - { - if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))) - { - textImage->SetColor(m_ArmedFgColor2); - } - else - { - textImage->SetColor(m_OutOfFocusSelectedTextColor); - } - } - else if (columnFlags & SectionedListPanel::COLUMN_BRIGHT) - { - textImage->SetColor(m_ArmedFgColor1); - } - else - { - textImage->SetColor(m_FgColor2); - } - } - else - { - // custom colors - if (IsSelected() && (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))) - { - textImage->SetColor(m_ArmedFgColor2); - } - else - { - textImage->SetColor(GetFgColor()); - } - } - } - image = textImage; - } - - // set the image position within the label - int imageWide = 0, tall = 0; - int wide; - if (image) - { - image->GetContentSize(imageWide, tall); - } - if (maxWidth >= 0) - { - wide = maxWidth; - } - else - { - wide = imageWide; - } - - if (i == 0 && !(columnFlags & SectionedListPanel::COLUMN_IMAGE)) - { - // first column has an extra indent - SetImageBounds(i, xpos + COLUMN_DATA_INDENT, wide - (COLUMN_DATA_INDENT + COLUMN_DATA_GAP)); - } - else - { - if (columnFlags & SectionedListPanel::COLUMN_CENTER) - { - int offSet = (wide / 2) - (imageWide / 2); - SetImageBounds(i, xpos + offSet, wide - offSet - COLUMN_DATA_GAP); - } - else if (columnFlags & SectionedListPanel::COLUMN_RIGHT) - { - SetImageBounds(i, xpos + wide - imageWide, wide - COLUMN_DATA_GAP); - } - else - { - SetImageBounds(i, xpos, wide - COLUMN_DATA_GAP); - } - } - xpos += wide; - } - } - - BaseClass::PerformLayout(); - } - - virtual void ApplySchemeSettings(IScheme *pScheme) - { - BaseClass::ApplySchemeSettings(pScheme); - - m_ArmedFgColor1 = GetSchemeColor("SectionedListPanel.BrightTextColor", pScheme); - m_ArmedFgColor2 = GetSchemeColor("SectionedListPanel.SelectedTextColor", pScheme); - m_OutOfFocusSelectedTextColor = GetSchemeColor("SectionedListPanel.OutOfFocusSelectedTextColor", pScheme); - m_ArmedBgColor = GetSchemeColor("SectionedListPanel.SelectedBgColor", pScheme); - - m_FgColor2 = GetSchemeColor("SectionedListPanel.TextColor", pScheme); - - m_BgColor = GetSchemeColor("SectionedListPanel.BgColor", GetBgColor(), pScheme); - m_SelectionBG2Color = GetSchemeColor("SectionedListPanel.OutOfFocusSelectedBgColor", pScheme); - - - HFont hFont = m_pListPanel->GetRowFont(); - if ( hFont != INVALID_FONT ) - { - SetFont( hFont ); - } - else - { - const char *fontName = pScheme->GetResourceString( "SectionedListPanel.Font" ); - HFont font = pScheme->GetFont(fontName, IsProportional()); - if ( font != INVALID_FONT ) - { - SetFont( font ); - } - } - - ClearImages(); - } - - virtual void PaintBackground() - { - int wide, tall; - GetSize(wide, tall); - - if (IsSelected() && !m_pListPanel->IsInEditMode()) - { - VPANEL focus = input()->GetFocus(); - // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected - if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))) - { - surface()->DrawSetColor(m_ArmedBgColor); - } - else - { - surface()->DrawSetColor(m_SelectionBG2Color); - } - } - else - { - surface()->DrawSetColor(GetBgColor()); - } - surface()->DrawFilledRect(0, 0, wide, tall); - } - - virtual void Paint() - { - BaseClass::Paint(); - - if ( !m_bShowColumns ) - return; - - // Debugging code to show column widths - int wide, tall; - GetSize(wide, tall); - surface()->DrawSetColor( 255,255,255,255 ); - surface()->DrawOutlinedRect(0, 0, wide, tall); - - int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID); - if (m_pData && colCount >= 0) - { - int xpos = 0; - for (int i = 0; i < colCount; i++) - { - const char *keyname = m_pListPanel->GetColumnNameBySection(m_iSectionID, i); - int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i); - int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i); - - IImage *image = NULL; - if (columnFlags & SectionedListPanel::COLUMN_IMAGE) - { - // lookup which image is being referred to - if (m_pListPanel->m_pImageList) - { - int imageIndex = m_pData->GetInt(keyname, 0); - if (m_pListPanel->m_pImageList->IsValidIndex(imageIndex)) - { - if (imageIndex > 0) - { - image = m_pListPanel->m_pImageList->GetImage(imageIndex); - } - } - } - } - else - { - image = GetImageAtIndex(i); - } - - int imageWide = 0, tall = 0; - int wide; - if (image) - { - image->GetContentSize(imageWide, tall); - } - if (maxWidth >= 0) - { - wide = maxWidth; - } - else - { - wide = imageWide; - } - - xpos += wide;//max(maxWidth,wide); - surface()->DrawOutlinedRect( xpos, 0, xpos, GetTall() ); - } - } - } - - virtual void OnMousePressed(MouseCode code) - { - if ( m_pListPanel && m_pListPanel->IsClickable() && IsEnabled() ) - { - if (code == MOUSE_LEFT) - { - m_pListPanel->PostActionSignal(new KeyValues("ItemLeftClick", "itemID", m_iID)); - } - if (code == MOUSE_RIGHT) - { - KeyValues *msg = new KeyValues("ItemContextMenu", "itemID", m_iID); - msg->SetPtr("SubPanel", this); - m_pListPanel->PostActionSignal(msg); - } - - m_pListPanel->SetSelectedItem(this); - } - } - - void SetSelected(bool state) - { - if (m_bSelected != state) - { - if (state) - { - RequestFocus(); - } - m_bSelected = state; - SetPaintBackgroundEnabled( state ); - InvalidateLayout(); - Repaint(); - } - } - - bool IsSelected() - { - return m_bSelected; - } - - virtual void OnSetFocus() - { - InvalidateLayout(); // force the layout to be redone so we can change text color according to focus - BaseClass::OnSetFocus(); - } - - virtual void OnKillFocus() - { - InvalidateLayout(); // force the layout to be redone so we can change text color according to focus - BaseClass::OnSetFocus(); - } - - virtual void OnMouseDoublePressed(MouseCode code) - { - //============================================================================= - // HPE_BEGIN: - // [tj] Only do this if clicking is enabled. - //============================================================================= - if (m_pListPanel && m_pListPanel->IsClickable()) - { - if (code == MOUSE_LEFT) - { - m_pListPanel->PostActionSignal(new KeyValues("ItemDoubleLeftClick", "itemID", m_iID)); - - // post up an enter key being hit - m_pListPanel->OnKeyCodeTyped(KEY_ENTER); - } - else - { - OnMousePressed(code); - } - - m_pListPanel->SetSelectedItem(this); - } - //============================================================================= - // HPE_END - //============================================================================= - } - - void GetCellBounds(int column, int &xpos, int &columnWide) - { - xpos = 0, columnWide = 0; - int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID); - for (int i = 0; i < colCount; i++) - { - int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i); - - IImage *image = GetImageAtIndex(i); - if (!image) - continue; - - // set the image position within the label - int wide, tall; - image->GetContentSize(wide, tall); - if (maxWidth >= 0) - { - wide = maxWidth; - } - - if (i == column) - { - // found the cell size, bail - columnWide = wide; - return; - } - - xpos += wide; - } - } - - //============================================================================= - // HPE_BEGIN: - // [menglish] gets the local coordinates of a cell using the max width for every column - //============================================================================= - - void GetMaxCellBounds(int column, int &xpos, int &columnWide) - { - xpos = 0, columnWide = 0; - int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID); - for (int i = 0; i < colCount; i++) - { - int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i); - - if (i == column) - { - // found the cell size, bail - columnWide = maxWidth; - return; - } - - xpos += maxWidth; - } - } - - //============================================================================= - // HPE_END - //============================================================================= - - virtual void SetOverrideColors( bool state ) - { - m_bOverrideColors = state; - } - - void SetShowColumns( bool bShow ) - { - m_bShowColumns = bShow; - } - -private: - SectionedListPanel *m_pListPanel; - int m_iID; - int m_iSectionID; - KeyValues *m_pData; - Color m_FgColor2; - Color m_BgColor; - Color m_ArmedFgColor1; - Color m_ArmedFgColor2; - Color m_OutOfFocusSelectedTextColor; - Color m_ArmedBgColor; - Color m_SelectionBG2Color; - CUtlVector m_TextImages; - - bool m_bSelected; - bool m_bOverrideColors; - bool m_bShowColumns; -}; - -}; // namespace vgui - -DECLARE_BUILD_FACTORY( SectionedListPanel ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -SectionedListPanel::SectionedListPanel(vgui::Panel *parent, const char *name) : BaseClass(parent, name) -{ - m_pScrollBar = new ScrollBar(this, "SectionedScrollBar", true); - m_pScrollBar->SetVisible(false); - m_pScrollBar->AddActionSignalTarget(this); - - m_iEditModeItemID = 0; - m_iEditModeColumn = 0; - m_bSortNeeded = false; - m_bVerticalScrollbarEnabled = true; - m_iLineSpacing = DEFAULT_LINE_SPACING; - m_iSectionGap = DEFAULT_SECTION_GAP; - - m_pImageList = NULL; - m_bDeleteImageListWhenDone = false; - - m_hHeaderFont = INVALID_FONT; - m_hRowFont = INVALID_FONT; - - //============================================================================= - // HPE_BEGIN: - //============================================================================= - // [tj] Default clickability to true so existing controls aren't affected. - m_clickable = true; - // [tj] draw section headers by default so existing controls aren't affected. - m_bDrawSectionHeaders = true; - //============================================================================= - // HPE_END - //============================================================================= -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -SectionedListPanel::~SectionedListPanel() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Sorts the list -//----------------------------------------------------------------------------- -void SectionedListPanel::ReSortList() -{ - m_SortedItems.RemoveAll(); - - int sectionStart = 0; - // layout the buttons - for (int sectionIndex = 0; sectionIndex < m_Sections.Size(); sectionIndex++) - { - section_t §ion = m_Sections[sectionIndex]; - sectionStart = m_SortedItems.Count(); - - // find all the items in this section - for( int i = m_Items.Head(); i != m_Items.InvalidIndex(); i = m_Items.Next( i ) ) - { - if (m_Items[i]->GetSectionID() == m_Sections[sectionIndex].m_iID) - { - // insert the items sorted - if (section.m_pSortFunc) - { - int insertionPoint = sectionStart; - for (;insertionPoint < m_SortedItems.Count(); insertionPoint++) - { - if (section.m_pSortFunc(this, i, m_SortedItems[insertionPoint]->GetID())) - break; - } - - if (insertionPoint == m_SortedItems.Count()) - { - m_SortedItems.AddToTail(m_Items[i]); - } - else - { - m_SortedItems.InsertBefore(insertionPoint, m_Items[i]); - } - } - else - { - // just add to the end - m_SortedItems.AddToTail(m_Items[i]); - } - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: iterates through and sets up the position of all the sections and items -//----------------------------------------------------------------------------- -void SectionedListPanel::PerformLayout() -{ - // lazy resort the list - if (m_bSortNeeded) - { - ReSortList(); - m_bSortNeeded = false; - } - - BaseClass::PerformLayout(); - - LayoutPanels(m_iContentHeight); - - int cx, cy, cwide, ctall; - GetBounds(cx, cy, cwide, ctall); - if (m_iContentHeight > ctall && m_bVerticalScrollbarEnabled) - { - m_pScrollBar->SetVisible(true); - m_pScrollBar->MoveToFront(); - - m_pScrollBar->SetPos(cwide - m_pScrollBar->GetWide() - 2, 0); - m_pScrollBar->SetSize(m_pScrollBar->GetWide(), ctall - 2); - - m_pScrollBar->SetRangeWindow(ctall); - - m_pScrollBar->SetRange(0, m_iContentHeight); - m_pScrollBar->InvalidateLayout(); - m_pScrollBar->Repaint(); - - // since we're just about to make the scrollbar visible, we need to re-layout - // the buttons since they depend on the scrollbar size - LayoutPanels(m_iContentHeight); - } - else - { - m_pScrollBar->SetValue(0); - - bool bWasVisible = m_pScrollBar->IsVisible(); - m_pScrollBar->SetVisible(false); - - // When we hide the scrollbar, we need to layout the buttons because they'll have more width to work with - if ( bWasVisible ) - { - LayoutPanels(m_iContentHeight); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: lays out the sections and rows in the panel -//----------------------------------------------------------------------------- -void SectionedListPanel::LayoutPanels(int &contentTall) -{ - int tall = GetSectionTall(); - int x = 5, wide = GetWide() - 10; - int y = 5; - - if (m_pScrollBar->IsVisible()) - { - y -= m_pScrollBar->GetValue(); - wide -= m_pScrollBar->GetWide(); - } - - int iStart = -1; - int iEnd = -1; - - // layout the buttons - bool bFirstVisibleSection = true; - for (int sectionIndex = 0; sectionIndex < m_Sections.Size(); sectionIndex++) - { - section_t §ion = m_Sections[sectionIndex]; - - iStart = -1; - iEnd = -1; - for (int i = 0; i < m_SortedItems.Count(); i++) - { - if (m_SortedItems[i]->GetSectionID() == m_Sections[sectionIndex].m_iID) - { - if (iStart == -1) - iStart = i; - iEnd = i; - } - } - - // don't draw this section at all if their are no item in it - if (iStart == -1 && !section.m_bAlwaysVisible) - { - section.m_pHeader->SetVisible(false); - continue; - } - - // Skip down a bit if this is not the first section to be drawn - if ( bFirstVisibleSection ) - bFirstVisibleSection = false; - else - y += m_iSectionGap; - - //============================================================================= - // HPE_BEGIN: - // [tj] Only draw the header if it is enabled - //============================================================================= - int nMinNextSectionY = y + section.m_iMinimumHeight; - if (m_bDrawSectionHeaders) - { - // draw the header - section.m_pHeader->SetBounds(x, y, wide, tall); - section.m_pHeader->SetVisible(true); - y += tall; - } - else - { - section.m_pHeader->SetVisible(false); - } - //============================================================================= - // HPE_END - //============================================================================= - - if (iStart == -1 && section.m_bAlwaysVisible) - { - } - else - { - // arrange all the items in this section underneath - for (int i = iStart; i <= iEnd; i++) - { - CItemButton *item = m_SortedItems[i]; //items[i]; - item->SetBounds(x, y, wide, m_iLineSpacing); - - // setup edit mode - if (m_hEditModePanel.Get() && m_iEditModeItemID == item->GetID()) - { - int cx, cwide; - item->GetCellBounds(1, cx, cwide); - m_hEditModePanel->SetBounds(cx, y, cwide, tall); - } - - y += m_iLineSpacing; - } - } - - // Add space, if needed to fulfill minimum requested content height - if ( y < nMinNextSectionY ) - y = nMinNextSectionY; - } - - // calculate height - contentTall = y; - if (m_pScrollBar->IsVisible()) - { - contentTall += m_pScrollBar->GetValue(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Ensures that the specified item is visible in the display -//----------------------------------------------------------------------------- -void SectionedListPanel::ScrollToItem(int iItem) -{ - int tall = GetSectionTall(); - int itemX, itemY ; - int nCurrentValue = m_pScrollBar->GetValue(); - - // find out where the item is - m_Items[iItem]->GetPos(itemX, itemY); - // add in the current scrollbar position - itemY += nCurrentValue; - - // compare that in the list - int cx, cy, cwide, ctall; - GetBounds(cx, cy, cwide, ctall); - if (m_iContentHeight > ctall) - { - if (itemY < nCurrentValue) - { - // scroll up - m_pScrollBar->SetValue(itemY); - } - else if (itemY > nCurrentValue + ctall - tall) - { - // scroll down - m_pScrollBar->SetValue(itemY - ctall + tall); - } - else - { - // keep the current value - } - } - else - { - // area isn't big enough, just remove the scrollbar - m_pScrollBar->SetValue(0); - } - - // reset scrollbar - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: sets background color & border -//----------------------------------------------------------------------------- -void SectionedListPanel::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetBgColor(GetSchemeColor("SectionedListPanel.BgColor", GetBgColor(), pScheme)); - SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); - - FOR_EACH_LL( m_Items, j ) - { - m_Items[j]->SetShowColumns( m_bShowColumns ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void SectionedListPanel::SetHeaderFont( HFont hFont ) -{ - m_hHeaderFont = hFont; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -HFont SectionedListPanel::GetHeaderFont( void ) const -{ - return m_hHeaderFont; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void SectionedListPanel::SetRowFont( HFont hFont ) -{ - m_hRowFont = hFont; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -HFont SectionedListPanel::GetRowFont( void ) const -{ - return m_hRowFont; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void SectionedListPanel::ApplySettings(KeyValues *inResourceData) -{ - BaseClass::ApplySettings(inResourceData); - m_iLineSpacing = inResourceData->GetInt("linespacing", 0); - if (!m_iLineSpacing) - { - m_iLineSpacing = DEFAULT_LINE_SPACING; - } - if (IsProportional()) - { - m_iLineSpacing = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iLineSpacing); - } - - m_iSectionGap = inResourceData->GetInt("sectiongap", 0); - if (!m_iSectionGap) - { - m_iSectionGap = DEFAULT_SECTION_GAP; - } - if (IsProportional()) - { - m_iSectionGap = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iSectionGap); - } -} - -//----------------------------------------------------------------------------- -// Purpose: passes on proportional state to children -//----------------------------------------------------------------------------- -void SectionedListPanel::SetProportional(bool state) -{ - BaseClass::SetProportional(state); - - // now setup the section headers and items - int i; - for (i = 0; i < m_Sections.Count(); i++) - { - m_Sections[i].m_pHeader->SetProportional(state); - } - FOR_EACH_LL( m_Items, j ) - { - m_Items[j]->SetProportional(state); - } -} - -//----------------------------------------------------------------------------- -// Purpose: sets whether or not the vertical scrollbar should ever be displayed -//----------------------------------------------------------------------------- -void SectionedListPanel::SetVerticalScrollbar(bool state) -{ - m_bVerticalScrollbarEnabled = state; -} - -//----------------------------------------------------------------------------- -// Purpose: adds a new section -//----------------------------------------------------------------------------- -void SectionedListPanel::AddSection(int sectionID, const char *name, SectionSortFunc_t sortFunc) -{ - SectionedListPanelHeader *header = new SectionedListPanelHeader(this, name, sectionID); - AddSection(sectionID, header, sortFunc); -} - -//----------------------------------------------------------------------------- -// Purpose: adds a new section -//----------------------------------------------------------------------------- -void SectionedListPanel::AddSection(int sectionID, const wchar_t *name, SectionSortFunc_t sortFunc) -{ - SectionedListPanelHeader *header = new SectionedListPanelHeader(this, name, sectionID); - AddSection(sectionID, header, sortFunc); -} - -//----------------------------------------------------------------------------- -// Purpose: Adds a new section, given object -//----------------------------------------------------------------------------- -void SectionedListPanel::AddSection(int sectionID, SectionedListPanelHeader *header, SectionSortFunc_t sortFunc) -{ - header = SETUP_PANEL( header ); - int index = m_Sections.AddToTail(); - m_Sections[index].m_iID = sectionID; - m_Sections[index].m_pHeader = header; - m_Sections[index].m_pSortFunc = sortFunc; - m_Sections[index].m_bAlwaysVisible = false; - m_Sections[index].m_iMinimumHeight = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: removes all the sections from the current panel -//----------------------------------------------------------------------------- -void SectionedListPanel::RemoveAllSections() -{ - for (int i = 0; i < m_Sections.Count(); i++) - { - if (!m_Sections.IsValidIndex(i)) - continue; - - m_Sections[i].m_pHeader->SetVisible(false); - m_Sections[i].m_pHeader->MarkForDeletion(); - } - - m_Sections.RemoveAll(); - m_Sections.Purge(); - m_SortedItems.RemoveAll(); - - InvalidateLayout(); - ReSortList(); -} - -//----------------------------------------------------------------------------- -// Purpose: adds a new column to a section -//----------------------------------------------------------------------------- -bool SectionedListPanel::AddColumnToSection(int sectionID, const char *columnName, const char *columnText, int columnFlags, int width, HFont fallbackFont /*= INVALID_FONT*/ ) -{ - wchar_t wtext[64]; - wchar_t *pwtext = g_pVGuiLocalize->Find(columnText); - if (!pwtext) - { - g_pVGuiLocalize->ConvertANSIToUnicode(columnText, wtext, sizeof(wtext)); - pwtext = wtext; - } - return AddColumnToSection(sectionID, columnName, pwtext, columnFlags, width, fallbackFont ); -} - -//----------------------------------------------------------------------------- -// Purpose: as above but with wchar_t's -//----------------------------------------------------------------------------- -bool SectionedListPanel::AddColumnToSection(int sectionID, const char *columnName, const wchar_t *columnText, int columnFlags, int width, HFont fallbackFont /*= INVALID_FONT*/ ) -{ - int index = FindSectionIndexByID(sectionID); - if (index < 0) - return false; - section_t §ion = m_Sections[index]; - - // add the new column to the sections' list - index = section.m_Columns.AddToTail(); - column_t &column = section.m_Columns[index]; - - Q_strncpy(column.m_szColumnName, columnName, sizeof(column.m_szColumnName)); - wcsncpy(column.m_szColumnText, columnText, sizeof(column.m_szColumnText) / sizeof(wchar_t)); - column.m_szColumnText[sizeof(column.m_szColumnText) / sizeof(wchar_t) - 1] = 0; - column.m_iColumnFlags = columnFlags; - column.m_iWidth = width; - column.m_hFallbackFont = fallbackFont; - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: modifies the text in an existing column -//----------------------------------------------------------------------------- -bool SectionedListPanel::ModifyColumn(int sectionID, const char *columnName, const wchar_t *columnText) -{ - int index = FindSectionIndexByID(sectionID); - if (index < 0) - return false; - section_t §ion = m_Sections[index]; - - // find the specified column - int columnIndex; - for (columnIndex = 0; columnIndex < section.m_Columns.Count(); columnIndex++) - { - if (!stricmp(section.m_Columns[columnIndex].m_szColumnName, columnName)) - break; - } - if (!section.m_Columns.IsValidIndex(columnIndex)) - return false; - column_t &column = section.m_Columns[columnIndex]; - - // modify the text - wcsncpy(column.m_szColumnText, columnText, sizeof(column.m_szColumnText) / sizeof(wchar_t)); - column.m_szColumnText[sizeof(column.m_szColumnText) / sizeof(wchar_t) - 1] = 0; - section.m_pHeader->InvalidateLayout(); - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: adds an item to the list; returns itemID -//----------------------------------------------------------------------------- -int SectionedListPanel::AddItem(int sectionID, const KeyValues *data) -{ - int itemID = GetNewItemButton(); - ModifyItem(itemID, sectionID, data); - - // not sorted but in list - m_SortedItems.AddToTail(m_Items[itemID]); - m_bSortNeeded = true; - - return itemID; -} - -//----------------------------------------------------------------------------- -// Purpose: modifies an existing item; returns false if the item does not exist -//----------------------------------------------------------------------------- -bool SectionedListPanel::ModifyItem(int itemID, int sectionID, const KeyValues *data) -{ - if ( !m_Items.IsValidIndex(itemID) ) - return false; - - InvalidateLayout(); - m_Items[itemID]->SetSectionID(sectionID); - m_Items[itemID]->SetData(data); - m_Items[itemID]->InvalidateLayout(); - m_bSortNeeded = true; - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void SectionedListPanel::SetItemFgColor( int itemID, Color color ) -{ - Assert( m_Items.IsValidIndex(itemID) ); - if ( !m_Items.IsValidIndex(itemID) ) - return; - - m_Items[itemID]->SetFgColor( color ); - m_Items[itemID]->SetOverrideColors( true ); - m_Items[itemID]->InvalidateLayout(); -} - -//============================================================================= -// HPE_BEGIN: -// [menglish] Setter for the background color similar to the foreground color -//============================================================================= - -void SectionedListPanel::SetItemBgColor( int itemID, Color color ) -{ - Assert( m_Items.IsValidIndex(itemID) ); - if ( !m_Items.IsValidIndex(itemID) ) - return; - - m_Items[itemID]->SetBgColor( color ); - m_Items[itemID]->SetPaintBackgroundEnabled( true ); - m_Items[itemID]->SetOverrideColors( true ); - m_Items[itemID]->InvalidateLayout(); -} - -void SectionedListPanel::SetItemFont( int itemID, HFont font ) -{ - Assert( m_Items.IsValidIndex(itemID) ); - if ( !m_Items.IsValidIndex(itemID) ) - return; - - m_Items[itemID]->SetFont( font ); -} - -void SectionedListPanel::SetItemEnabled( int itemID, bool bEnabled ) -{ - Assert( m_Items.IsValidIndex(itemID) ); - if ( !m_Items.IsValidIndex(itemID) ) - return; - - m_Items[itemID]->SetEnabled( bEnabled ); -} - -//============================================================================= -// HPE_END -//============================================================================= -//----------------------------------------------------------------------------- -// Purpose: sets the color of a section text & underline -//----------------------------------------------------------------------------- -void SectionedListPanel::SetSectionFgColor(int sectionID, Color color) -{ - if (!m_Sections.IsValidIndex(sectionID)) - return; - - m_Sections[sectionID].m_pHeader->SetColor(color); -} -//----------------------------------------------------------------------------- -// Purpose: added so you can change the divider color AFTER the main color. -//----------------------------------------------------------------------------- -void SectionedListPanel::SetSectionDividerColor( int sectionID, Color color) -{ - if (!m_Sections.IsValidIndex(sectionID)) - return; - - m_Sections[sectionID].m_pHeader->SetDividerColor(color); -} -//----------------------------------------------------------------------------- -// Purpose: forces a section to always be visible -//----------------------------------------------------------------------------- -void SectionedListPanel::SetSectionAlwaysVisible(int sectionID, bool visible) -{ - if (!m_Sections.IsValidIndex(sectionID)) - return; - - m_Sections[sectionID].m_bAlwaysVisible = visible; -} -void SectionedListPanel::SetFontSection(int sectionID, HFont font) -{ - if (!m_Sections.IsValidIndex(sectionID)) - return; - - m_Sections[sectionID].m_pHeader->SetFont(font); -} -void SectionedListPanel::SetSectionMinimumHeight(int sectionID, int iMinimumHeight) -{ - if (!m_Sections.IsValidIndex(sectionID)) - return; - - m_Sections[sectionID].m_iMinimumHeight = iMinimumHeight; - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: removes an item from the list; returns false if the item does not exist or is already removed -//----------------------------------------------------------------------------- -bool SectionedListPanel::RemoveItem(int itemID) -{ - if ( !m_Items.IsValidIndex(itemID) ) - return false; - - m_SortedItems.FindAndRemove(m_Items[itemID]); - m_bSortNeeded = true; - - m_Items[itemID]->MarkForDeletion(); - m_Items.Remove(itemID); - - InvalidateLayout(); - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: returns the number of columns in a section -//----------------------------------------------------------------------------- -int SectionedListPanel::GetColumnCountBySection(int sectionID) -{ - int index = FindSectionIndexByID(sectionID); - if (index < 0) - return NULL; - - return m_Sections[index].m_Columns.Size(); -} - -//----------------------------------------------------------------------------- -// Purpose: returns the name of a column by section and column index; returns NULL if there are no more columns -// valid range of columnIndex is [0, GetColumnCountBySection) -//----------------------------------------------------------------------------- -const char *SectionedListPanel::GetColumnNameBySection(int sectionID, int columnIndex) -{ - int index = FindSectionIndexByID(sectionID); - if (index < 0 || columnIndex >= m_Sections[index].m_Columns.Size()) - return NULL; - - return m_Sections[index].m_Columns[columnIndex].m_szColumnName; -} - -//----------------------------------------------------------------------------- -// Purpose: returns the text for a column by section and column index -//----------------------------------------------------------------------------- -const wchar_t *SectionedListPanel::GetColumnTextBySection(int sectionID, int columnIndex) -{ - int index = FindSectionIndexByID(sectionID); - if (index < 0 || columnIndex >= m_Sections[index].m_Columns.Size()) - return NULL; - - return m_Sections[index].m_Columns[columnIndex].m_szColumnText; -} - -//----------------------------------------------------------------------------- -// Purpose: returns the type of a column by section and column index -//----------------------------------------------------------------------------- -int SectionedListPanel::GetColumnFlagsBySection(int sectionID, int columnIndex) -{ - int index = FindSectionIndexByID(sectionID); - if (index < 0) - return 0; - - if (columnIndex >= m_Sections[index].m_Columns.Size()) - return 0; - - return m_Sections[index].m_Columns[columnIndex].m_iColumnFlags; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int SectionedListPanel::GetColumnWidthBySection(int sectionID, int columnIndex) -{ - int index = FindSectionIndexByID(sectionID); - if (index < 0) - return 0; - - if (columnIndex >= m_Sections[index].m_Columns.Size()) - return 0; - - return m_Sections[index].m_Columns[columnIndex].m_iWidth; -} - -//============================================================================= -// HPE_BEGIN: -// [menglish] Gets the column index by the string identifier -//============================================================================= - -int SectionedListPanel::GetColumnIndexByName(int sectionID, char* name) -{ - int index = FindSectionIndexByID(sectionID); - if (index < 0) - return 0; - - for ( int columnIndex = 0; columnIndex < m_Sections[index].m_Columns.Count(); ++ columnIndex) - { - if( !V_strcmp( m_Sections[index].m_Columns[columnIndex].m_szColumnName, name ) ) - return columnIndex; - } - - return -1; -} - -//============================================================================= -// HPE_END -//============================================================================= - -//----------------------------------------------------------------------------- -// Purpose: returns -1 if section not found -//----------------------------------------------------------------------------- -int SectionedListPanel::FindSectionIndexByID(int sectionID) -{ - for (int i = 0; i < m_Sections.Size(); i++) - { - if (m_Sections[i].m_iID == sectionID) - { - return i; - } - } - - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: Called when the scrollbar is moved -//----------------------------------------------------------------------------- -void SectionedListPanel::OnSliderMoved() -{ - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Scrolls the list according to the mouse wheel movement -//----------------------------------------------------------------------------- -void SectionedListPanel::OnMouseWheeled(int delta) -{ - if (m_hEditModePanel.Get()) - { - // ignore mouse wheel in edit mode, forward right up to parent - CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); - return; - } - - // scroll the window based on the delta - int val = m_pScrollBar->GetValue(); - val -= (delta * BUTTON_HEIGHT_DEFAULT * 3); - m_pScrollBar->SetValue(val); -} - -//----------------------------------------------------------------------------- -// Purpose: Resets the scrollbar position on size change -//----------------------------------------------------------------------------- -void SectionedListPanel::OnSizeChanged(int wide, int tall) -{ - BaseClass::OnSizeChanged(wide, tall); - m_pScrollBar->SetValue(0); - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: deselects any items -//----------------------------------------------------------------------------- -void SectionedListPanel::OnMousePressed(MouseCode code) -{ - //============================================================================= - // HPE_BEGIN: - // [tj] Only do this if clicking is enabled. - //============================================================================= - if (m_clickable){ - ClearSelection(); - } - //============================================================================= - // HPE_END - //=============================================================================} -} -//----------------------------------------------------------------------------- -// Purpose: deselects any items -//----------------------------------------------------------------------------- -void SectionedListPanel::ClearSelection( void ) -{ - SetSelectedItem((CItemButton *)NULL); -} - -void SectionedListPanel::MoveSelectionDown( void ) -{ - int itemID = GetSelectedItem(); - if (itemID == -1) - return; - - if (!m_SortedItems.Count()) // if the list has been emptied - return; - - int i; - for (i = 0; i < m_SortedItems.Count(); i++) - { - if (m_SortedItems[i]->GetID() == itemID) - break; - } - - Assert(i != m_SortedItems.Count()); - - // we're already on the end - if (i >= m_SortedItems.Count() - 1) - return; - - int newItemID = m_SortedItems[i + 1]->GetID(); - SetSelectedItem(m_Items[newItemID]); - ScrollToItem(newItemID); -} - -void SectionedListPanel::MoveSelectionUp( void ) -{ - int itemID = GetSelectedItem(); - if (itemID == -1) - return; - - if (!m_SortedItems.Count()) // if the list has been emptied - return; - - int i; - for (i = 0; i < m_SortedItems.Count(); i++) - { - if (m_SortedItems[i]->GetID() == itemID) - break; - } - - Assert(i != m_SortedItems.Count()); - - // we're already on the end - if (i == 0 || i >= m_SortedItems.Count() ) - return; - - int newItemID = m_SortedItems[i - 1]->GetID(); - SetSelectedItem(m_Items[newItemID]); - ScrollToItem(newItemID); -} - -void SectionedListPanel::NavigateTo( void ) -{ - BaseClass::NavigateTo(); - - if ( m_SortedItems.Count() ) - { - int nItemID = m_SortedItems[ 0 ]->GetID(); - SetSelectedItem( m_Items[ nItemID ] ); - ScrollToItem( nItemID ); - } - - RequestFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: arrow key movement handler -//----------------------------------------------------------------------------- -void SectionedListPanel::OnKeyCodePressed( KeyCode code ) -{ - if (m_hEditModePanel.Get()) - { - // ignore arrow keys in edit mode - // forward right up to parent so that tab focus change doesn't occur - CallParentFunction(new KeyValues("KeyCodePressed", "code", code)); - return; - } - - int buttonTall = GetSectionTall(); - - ButtonCode_t nButtonCode = GetBaseButtonCode( code ); - - if ( nButtonCode == KEY_XBUTTON_DOWN || - nButtonCode == KEY_XSTICK1_DOWN || - nButtonCode == KEY_XSTICK2_DOWN || - code == KEY_DOWN ) - { - int itemID = GetSelectedItem(); - MoveSelectionDown(); - if ( itemID != GetSelectedItem() ) - { - // Only eat the input if it did something - return; - } - } - else if ( nButtonCode == KEY_XBUTTON_UP || - nButtonCode == KEY_XSTICK1_UP || - nButtonCode == KEY_XSTICK2_UP || - code == KEY_UP) - { - int itemID = GetSelectedItem(); - MoveSelectionUp(); - if ( itemID != GetSelectedItem() ) - { - // Only eat the input if it did something - return; - } - } - else if (code == KEY_PAGEDOWN) - { - // calculate info for # of rows - int cx, cy, cwide, ctall; - GetBounds(cx, cy, cwide, ctall); - - int rowsperpage = ctall/buttonTall; - - int itemID = GetSelectedItem(); - int lastValidItem = itemID; - int secID = m_Items[itemID]->GetSectionID(); - int i=0; - int row = m_SortedItems.Find(m_Items[itemID]); - - while ( i < rowsperpage ) - { - if ( m_SortedItems.IsValidIndex(++row) ) - { - itemID = m_SortedItems[row]->GetID(); - lastValidItem = itemID; - i++; - - // if we switched sections, then count the section header as a row - if (m_Items[itemID]->GetSectionID() != secID) - { - secID = m_Items[itemID]->GetSectionID(); - i++; - } - } - else - { - itemID = lastValidItem; - break; - } - } - SetSelectedItem(m_Items[itemID]); - ScrollToItem(itemID); - return; - } - else if (code == KEY_PAGEUP) - { - // calculate info for # of rows - int cx, cy, cwide, ctall; - GetBounds(cx, cy, cwide, ctall); - int rowsperpage = ctall/buttonTall; - - int itemID = GetSelectedItem(); - int lastValidItem = itemID; - int secID = m_Items[itemID]->GetSectionID(); - int i=0; - int row = m_SortedItems.Find(m_Items[itemID]); - while ( i < rowsperpage ) - { - if ( m_SortedItems.IsValidIndex(--row) ) - { - itemID = m_SortedItems[row]->GetID(); - lastValidItem = itemID; - i++; - - // if we switched sections, then count the section header as a row - if (m_Items[itemID]->GetSectionID() != secID) - { - secID = m_Items[itemID]->GetSectionID(); - i++; - } - } - else - { - SetSelectedItem(m_Items[lastValidItem]); - m_pScrollBar->SetValue(0); - return; - } - } - SetSelectedItem(m_Items[itemID]); - ScrollToItem(itemID); - return; - } - else if ( code == KEY_ENTER || nButtonCode == KEY_XBUTTON_A ) - { - Panel *pSelectedItem = m_hSelectedItem; - if ( pSelectedItem ) - { - pSelectedItem->OnMousePressed( MOUSE_LEFT ); - } - return; - } - - BaseClass::OnKeyCodePressed( code ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Clears the list -//----------------------------------------------------------------------------- -void SectionedListPanel::DeleteAllItems() -{ - FOR_EACH_LL( m_Items, i ) - { - m_Items[i]->SetVisible(false); - m_Items[i]->Clear(); - - // don't delete, move to free list - int freeIndex = m_FreeItems.AddToTail(); - m_FreeItems[freeIndex] = m_Items[i]; - } - - m_Items.RemoveAll(); - m_SortedItems.RemoveAll(); - m_hSelectedItem = NULL; - InvalidateLayout(); - m_bSortNeeded = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Changes the current list selection -//----------------------------------------------------------------------------- -void SectionedListPanel::SetSelectedItem(CItemButton *item) -{ - if (m_hSelectedItem.Get() == item) - return; - - // deselect the current item - if (m_hSelectedItem.Get()) - { - m_hSelectedItem->SetSelected(false); - } - - // set the new item - m_hSelectedItem = item; - if (m_hSelectedItem.Get()) - { - m_hSelectedItem->SetSelected(true); - } - - Repaint(); - PostActionSignal(new KeyValues("ItemSelected", "itemID", m_hSelectedItem.Get() ? m_hSelectedItem->GetID() : -1)); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int SectionedListPanel::GetSelectedItem() -{ - if (m_hSelectedItem.Get()) - { - return m_hSelectedItem->GetID(); - } - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: sets which item is currently selected -//----------------------------------------------------------------------------- -void SectionedListPanel::SetSelectedItem(int itemID) -{ - if ( m_Items.IsValidIndex(itemID) ) - { - SetSelectedItem(m_Items[itemID]); - } -} - -//----------------------------------------------------------------------------- -// Purpose: returns the data of a selected item -//----------------------------------------------------------------------------- -KeyValues *SectionedListPanel::GetItemData(int itemID) -{ - Assert(m_Items.IsValidIndex(itemID)); - if ( !m_Items.IsValidIndex(itemID) ) - return NULL; - - return m_Items[itemID]->GetData(); -} - -//----------------------------------------------------------------------------- -// Purpose: returns what section an item is in -//----------------------------------------------------------------------------- -int SectionedListPanel::GetItemSection(int itemID) -{ - if ( !m_Items.IsValidIndex(itemID) ) - return -1; - - return m_Items[itemID]->GetSectionID(); -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if the itemID is valid for use -//----------------------------------------------------------------------------- -bool SectionedListPanel::IsItemIDValid(int itemID) -{ - return m_Items.IsValidIndex(itemID); -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if the itemID is valid for use -//----------------------------------------------------------------------------- -int SectionedListPanel::GetHighestItemID() -{ - return m_Items.MaxElementIndex(); -} - -//----------------------------------------------------------------------------- -// Purpose: item iterators -//----------------------------------------------------------------------------- -int SectionedListPanel::GetItemCount() -{ - return m_SortedItems.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: item iterators -//----------------------------------------------------------------------------- -int SectionedListPanel::GetItemIDFromRow(int row) -{ - if ( !m_SortedItems.IsValidIndex(row) ) - return -1; - - return m_SortedItems[row]->GetID(); -} - -//----------------------------------------------------------------------------- -// Purpose: returns the row that this itemID occupies. -1 if the itemID is invalid -//----------------------------------------------------------------------------- -int SectionedListPanel::GetRowFromItemID(int itemID) -{ - for (int i = 0; i < m_SortedItems.Count(); i++) - { - if ( m_SortedItems[i]->GetID() == itemID ) - { - return i; - } - } - - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: gets the local coordinates of a cell -//----------------------------------------------------------------------------- -bool SectionedListPanel::GetCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall) -{ - x = y = wide = tall = 0; - if ( !IsItemIDValid(itemID) ) - return false; - - // get the item - CItemButton *item = m_Items[itemID]; - - if ( !item->IsVisible() ) - return false; - - //!! ignores column for now - item->GetBounds(x, y, wide, tall); - item->GetCellBounds(column, x, wide); - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: gets the local coordinates of a section header -//----------------------------------------------------------------------------- -bool SectionedListPanel::GetSectionHeaderBounds(int sectionID, int &x, int &y, int &wide, int &tall) -{ - x = y = wide = tall = 0; - int index = FindSectionIndexByID(sectionID); - if (index < 0 || !m_Sections[index].m_pHeader ) - return false; - - m_Sections[index].m_pHeader->GetBounds( x, y, wide, tall ); - return true; -} - -//============================================================================= -// HPE_BEGIN: -// [menglish] Gets the local coordinates of a cell using the max width for every column -// Gets the local coordinates of a cell -//============================================================================= - -bool SectionedListPanel::GetMaxCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall) -{ - x = y = wide = tall = 0; - if ( !IsItemIDValid(itemID) ) - return false; - - // get the item - CItemButton *item = m_Items[itemID]; - - if ( !item->IsVisible() ) - return false; - - //!! ignores column for now - item->GetBounds(x, y, wide, tall); - item->GetMaxCellBounds(column, x, wide); - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: gets the local coordinates of a cell -//----------------------------------------------------------------------------- -bool SectionedListPanel::GetItemBounds(int itemID, int &x, int &y, int &wide, int &tall) -{ - x = y = wide = tall = 0; - if ( !IsItemIDValid(itemID) ) - return false; - - // get the item - CItemButton *item = m_Items[itemID]; - - if ( !item->IsVisible() ) - return false; - - //!! ignores column for now - item->GetBounds(x, y, wide, tall); - return true; -} - -//============================================================================= -// HPE_END -//============================================================================= - -//----------------------------------------------------------------------------- -// Purpose: forces an item to redraw -//----------------------------------------------------------------------------- -void SectionedListPanel::InvalidateItem(int itemID) -{ - if ( !IsItemIDValid(itemID) ) - return; - - m_Items[itemID]->InvalidateLayout(); - m_Items[itemID]->Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: set up a field for editing -//----------------------------------------------------------------------------- -void SectionedListPanel::EnterEditMode(int itemID, int column, vgui::Panel *editPanel) -{ - m_hEditModePanel = editPanel; - m_iEditModeItemID = itemID; - m_iEditModeColumn = column; - editPanel->SetParent(this); - editPanel->SetVisible(true); - editPanel->RequestFocus(); - editPanel->MoveToFront(); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: leaves editing mode -//----------------------------------------------------------------------------- -void SectionedListPanel::LeaveEditMode() -{ - if (m_hEditModePanel.Get()) - { - InvalidateItem(m_iEditModeItemID); - m_hEditModePanel->SetVisible(false); - m_hEditModePanel->SetParent((Panel *)NULL); - m_hEditModePanel = NULL; - } -} - -//----------------------------------------------------------------------------- -// Purpose: returns true if we are currently in inline editing mode -//----------------------------------------------------------------------------- -bool SectionedListPanel::IsInEditMode() -{ - return (m_hEditModePanel.Get() != NULL); -} - -//----------------------------------------------------------------------------- -// Purpose: list used to match indexes in image columns to image pointers -//----------------------------------------------------------------------------- -void SectionedListPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone) -{ - m_bDeleteImageListWhenDone = deleteImageListWhenDone; - m_pImageList = imageList; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void SectionedListPanel::OnSetFocus() -{ - if (m_hSelectedItem.Get()) - { - m_hSelectedItem->RequestFocus(); - } - else - { - BaseClass::OnSetFocus(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int SectionedListPanel::GetSectionTall() -{ - if (m_Sections.Count()) - { - HFont font = m_Sections[0].m_pHeader->GetFont(); - if (font != INVALID_FONT) - { - return surface()->GetFontTall(font) + BUTTON_HEIGHT_SPACER; - } - } - - return BUTTON_HEIGHT_DEFAULT; -} - -//----------------------------------------------------------------------------- -// Purpose: returns the size required to fully draw the contents of the panel -//----------------------------------------------------------------------------- -void SectionedListPanel::GetContentSize(int &wide, int &tall) -{ - // make sure our layout is done - if (IsLayoutInvalid()) - { - if (m_bSortNeeded) - { - ReSortList(); - m_bSortNeeded = false; - } - LayoutPanels(m_iContentHeight); - } - - wide = GetWide(); - tall = m_iContentHeight; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the index of a new item button -//----------------------------------------------------------------------------- -int SectionedListPanel::GetNewItemButton() -{ - int itemID = m_Items.AddToTail(); - if (m_FreeItems.Count()) - { - // reusing an existing CItemButton - m_Items[itemID] = m_FreeItems[m_FreeItems.Head()]; - m_Items[itemID]->SetID(itemID); - m_Items[itemID]->SetVisible(true); - m_FreeItems.Remove(m_FreeItems.Head()); - } - else - { - // create a new CItemButton - m_Items[itemID] = SETUP_PANEL(new CItemButton(this, itemID)); - m_Items[itemID]->SetShowColumns( m_bShowColumns ); - } - - // Gross. Le's hope this isn't the only property that doesn't get defaulted - // properly when an item is recycled..... - m_Items[itemID]->SetEnabled( true ); - - return itemID; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns fallback font to use for text image for this column -// Input : sectionID - -// columnIndex - -// Output : virtual HFont -//----------------------------------------------------------------------------- -HFont SectionedListPanel::GetColumnFallbackFontBySection( int sectionID, int columnIndex ) -{ - int index = FindSectionIndexByID(sectionID); - if (index < 0) - return INVALID_FONT; - - if (columnIndex >= m_Sections[index].m_Columns.Size()) - return INVALID_FONT; - - return m_Sections[index].m_Columns[columnIndex].m_hFallbackFont; -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "utlvector.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +enum +{ + BUTTON_HEIGHT_DEFAULT = 20, + BUTTON_HEIGHT_SPACER = 7, + DEFAULT_LINE_SPACING = 20, + DEFAULT_SECTION_GAP = 8, + COLUMN_DATA_INDENT = 6, + COLUMN_DATA_GAP = 2, +}; + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: header label that separates and names each section +//----------------------------------------------------------------------------- +SectionedListPanelHeader::SectionedListPanelHeader(SectionedListPanel *parent, const char *name, int sectionID) : Label(parent, name, "") +{ + m_pListPanel = parent; + m_iSectionID = sectionID; + SetTextImageIndex(-1); + ClearImages(); + SetPaintBackgroundEnabled( false ); +} + +SectionedListPanelHeader::SectionedListPanelHeader(SectionedListPanel *parent, const wchar_t *name, int sectionID) : Label(parent, "SectionHeader", "") +{ + SetText(name); + SetVisible(false); + m_pListPanel = parent; + m_iSectionID = sectionID; + SetTextImageIndex(-1); + ClearImages(); +} + +void SectionedListPanelHeader::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetFgColor(GetSchemeColor("SectionedListPanel.HeaderTextColor", pScheme)); + m_SectionDividerColor = GetSchemeColor("SectionedListPanel.DividerColor", pScheme); + SetBgColor(GetSchemeColor("SectionedListPanelHeader.BgColor", GetBgColor(), pScheme)); + SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional())); + ClearImages(); + + HFont hFont = m_pListPanel->GetHeaderFont(); + if ( hFont != INVALID_FONT ) + { + SetFont( hFont ); + } + else + { + SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional())); + } +} + +void SectionedListPanelHeader::Paint() +{ + BaseClass::Paint(); + + int x, y, wide, tall; + GetBounds(x, y, wide, tall); + + y = (tall - 2); // draw the line under the panel + + surface()->DrawSetColor(m_SectionDividerColor); + surface()->DrawFilledRect(1, y, GetWide() - 2, y + 1); +} + +void SectionedListPanelHeader::SetColor(Color col) +{ + m_SectionDividerColor = col; + SetFgColor(col); +} +void SectionedListPanelHeader::SetDividerColor(Color col ) +{ + m_SectionDividerColor = col; +} + +void SectionedListPanelHeader::PerformLayout() +{ + BaseClass::PerformLayout(); + + // set up the text in the header + int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID); + if (colCount != GetImageCount()) + { + // rebuild the image list + for (int i = 0; i < colCount; i++) + { + int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i); + IImage *image = NULL; + if (columnFlags & SectionedListPanel::HEADER_IMAGE) + { + //!! need some kind of image reference + image = NULL; + } + else + { + TextImage *textImage = new TextImage(""); + textImage->SetFont(GetFont()); + HFont fallback = m_pListPanel->GetColumnFallbackFontBySection( m_iSectionID, i ); + if ( INVALID_FONT != fallback ) + { + textImage->SetUseFallbackFont( true, fallback ); + } + textImage->SetColor(GetFgColor()); + image = textImage; + } + + SetImageAtIndex(i, image, 0); + } + } + + for (int repeat = 0; repeat <= 1; repeat++) + { + int xpos = 0; + for (int i = 0; i < colCount; i++) + { + int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i); + int columnWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i); + int maxWidth = columnWidth; + + IImage *image = GetImageAtIndex(i); + if (!image) + { + xpos += columnWidth; + continue; + } + + // set the image position within the label + int contentWide, wide, tall; + image->GetContentSize(wide, tall); + contentWide = wide; + + // see if we can draw over the next few column headers (if we're left-aligned) + if (!(columnFlags & SectionedListPanel::COLUMN_RIGHT)) + { + for (int j = i + 1; j < colCount; j++) + { + // see if this column header has anything for a header + int iwide = 0, itall = 0; + if (GetImageAtIndex(j)) + { + GetImageAtIndex(j)->GetContentSize(iwide, itall); + } + + if (iwide == 0) + { + // it's a blank header, ok to draw over it + maxWidth += m_pListPanel->GetColumnWidthBySection(m_iSectionID, j); + } + } + } + if (maxWidth >= 0) + { + wide = maxWidth; + } + + if (columnFlags & SectionedListPanel::COLUMN_RIGHT) + { + SetImageBounds(i, xpos + wide - contentWide, wide - COLUMN_DATA_GAP); + } + else + { + SetImageBounds(i, xpos, wide - COLUMN_DATA_GAP); + } + xpos += columnWidth; + + if (!(columnFlags & SectionedListPanel::HEADER_IMAGE)) + { + Assert(dynamic_cast(image) != NULL); + TextImage *textImage = (TextImage *)image; + textImage->SetFont(GetFont()); + textImage->SetText(m_pListPanel->GetColumnTextBySection(m_iSectionID, i)); + textImage->ResizeImageToContentMaxWidth( maxWidth ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Individual items in the list +//----------------------------------------------------------------------------- +class CItemButton : public Label +{ + DECLARE_CLASS_SIMPLE( CItemButton, Label ); + +public: + CItemButton(SectionedListPanel *parent, int itemID) : Label(parent, NULL, "< item >") + { + m_pListPanel = parent; + m_iID = itemID; + m_pData = NULL; + Clear(); + } + + ~CItemButton() + { + // free all the keyvalues + if (m_pData) + { + m_pData->deleteThis(); + } + + // clear any section data + SetSectionID(-1); + } + + void Clear() + { + m_bSelected = false; + m_bOverrideColors = false; + m_iSectionID = -1; + SetPaintBackgroundEnabled( false ); + SetTextImageIndex(-1); + ClearImages(); + } + + int GetID() + { + return m_iID; + } + + void SetID(int itemID) + { + m_iID = itemID; + } + + int GetSectionID() + { + return m_iSectionID; + } + + void SetSectionID(int sectionID) + { + if (sectionID != m_iSectionID) + { + // free any existing textimage list + ClearImages(); + // delete any images we've created + for (int i = 0; i < m_TextImages.Count(); i++) + { + delete m_TextImages[i]; + } + m_TextImages.RemoveAll(); + // mark the list as needing rebuilding + InvalidateLayout(); + } + m_iSectionID = sectionID; + } + + void SetData(const KeyValues *data) + { + if (m_pData) + { + m_pData->deleteThis(); + } + + m_pData = data->MakeCopy(); + InvalidateLayout(); + } + + KeyValues *GetData() + { + return m_pData; + } + + virtual void PerformLayout() + { + // get our button text + int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID); + if (!m_pData || colCount < 1) + { + SetText("< unset >"); + } + else + { + if (colCount != GetImageCount()) + { + // rebuild the image list + for (int i = 0; i < colCount; i++) + { + int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i); + if (!(columnFlags & SectionedListPanel::COLUMN_IMAGE)) + { + TextImage *image = new TextImage(""); + m_TextImages.AddToTail(image); + image->SetFont( GetFont() ); + HFont fallback = m_pListPanel->GetColumnFallbackFontBySection( m_iSectionID, i ); + if ( INVALID_FONT != fallback ) + { + image->SetUseFallbackFont( true, fallback ); + } + SetImageAtIndex(i, image, 0); + } + } + + {for ( int i = GetImageCount(); i < colCount; i++ ) // make sure we have enough image slots + { + AddImage( NULL, 0 ); + }} + } + + // set the text for each column + int xpos = 0; + for (int i = 0; i < colCount; i++) + { + const char *keyname = m_pListPanel->GetColumnNameBySection(m_iSectionID, i); + + int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i); + int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i); + + IImage *image = NULL; + if (columnFlags & SectionedListPanel::COLUMN_IMAGE) + { + // lookup which image is being referred to + if (m_pListPanel->m_pImageList) + { + int imageIndex = m_pData->GetInt(keyname, 0); + if (m_pListPanel->m_pImageList->IsValidIndex(imageIndex)) + { + // 0 is always the blank image + if (imageIndex > 0) + { + image = m_pListPanel->m_pImageList->GetImage(imageIndex); + SetImageAtIndex(i, image, 0); + } + } + else + { + // this is mildly valid (CGamesList hits it because of the way it uses the image indices) + // Assert(!("Image index out of range for ImageList in SectionedListPanel")); + } + } + else + { + Assert(!("Images columns used in SectionedListPanel with no ImageList set")); + } + } + else + { + TextImage *textImage = dynamic_cast(GetImageAtIndex(i)); + if (textImage) + { + textImage->SetText(m_pData->GetString(keyname, "")); + textImage->ResizeImageToContentMaxWidth( maxWidth ); + + // set the text color based on the selection state - if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected + VPANEL focus = input()->GetFocus(); + if ( !m_bOverrideColors ) + { + if (IsSelected() && !m_pListPanel->IsInEditMode()) + { + if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))) + { + textImage->SetColor(m_ArmedFgColor2); + } + else + { + textImage->SetColor(m_OutOfFocusSelectedTextColor); + } + } + else if (columnFlags & SectionedListPanel::COLUMN_BRIGHT) + { + textImage->SetColor(m_ArmedFgColor1); + } + else + { + textImage->SetColor(m_FgColor2); + } + } + else + { + // custom colors + if (IsSelected() && (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))) + { + textImage->SetColor(m_ArmedFgColor2); + } + else + { + textImage->SetColor(GetFgColor()); + } + } + } + image = textImage; + } + + // set the image position within the label + int imageWide = 0, tall = 0; + int wide; + if (image) + { + image->GetContentSize(imageWide, tall); + } + if (maxWidth >= 0) + { + wide = maxWidth; + } + else + { + wide = imageWide; + } + + if (i == 0 && !(columnFlags & SectionedListPanel::COLUMN_IMAGE)) + { + // first column has an extra indent + SetImageBounds(i, xpos + COLUMN_DATA_INDENT, wide - (COLUMN_DATA_INDENT + COLUMN_DATA_GAP)); + } + else + { + if (columnFlags & SectionedListPanel::COLUMN_CENTER) + { + int offSet = (wide / 2) - (imageWide / 2); + SetImageBounds(i, xpos + offSet, wide - offSet - COLUMN_DATA_GAP); + } + else if (columnFlags & SectionedListPanel::COLUMN_RIGHT) + { + SetImageBounds(i, xpos + wide - imageWide, wide - COLUMN_DATA_GAP); + } + else + { + SetImageBounds(i, xpos, wide - COLUMN_DATA_GAP); + } + } + xpos += wide; + } + } + + BaseClass::PerformLayout(); + } + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + BaseClass::ApplySchemeSettings(pScheme); + + m_ArmedFgColor1 = GetSchemeColor("SectionedListPanel.BrightTextColor", pScheme); + m_ArmedFgColor2 = GetSchemeColor("SectionedListPanel.SelectedTextColor", pScheme); + m_OutOfFocusSelectedTextColor = GetSchemeColor("SectionedListPanel.OutOfFocusSelectedTextColor", pScheme); + m_ArmedBgColor = GetSchemeColor("SectionedListPanel.SelectedBgColor", pScheme); + + m_FgColor2 = GetSchemeColor("SectionedListPanel.TextColor", pScheme); + + m_BgColor = GetSchemeColor("SectionedListPanel.BgColor", GetBgColor(), pScheme); + m_SelectionBG2Color = GetSchemeColor("SectionedListPanel.OutOfFocusSelectedBgColor", pScheme); + + + HFont hFont = m_pListPanel->GetRowFont(); + if ( hFont != INVALID_FONT ) + { + SetFont( hFont ); + } + else + { + const char *fontName = pScheme->GetResourceString( "SectionedListPanel.Font" ); + HFont font = pScheme->GetFont(fontName, IsProportional()); + if ( font != INVALID_FONT ) + { + SetFont( font ); + } + } + + ClearImages(); + } + + virtual void PaintBackground() + { + int wide, tall; + GetSize(wide, tall); + + if (IsSelected() && !m_pListPanel->IsInEditMode()) + { + VPANEL focus = input()->GetFocus(); + // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected + if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))) + { + surface()->DrawSetColor(m_ArmedBgColor); + } + else + { + surface()->DrawSetColor(m_SelectionBG2Color); + } + } + else + { + surface()->DrawSetColor(GetBgColor()); + } + surface()->DrawFilledRect(0, 0, wide, tall); + } + + virtual void Paint() + { + BaseClass::Paint(); + + if ( !m_bShowColumns ) + return; + + // Debugging code to show column widths + int wide, tall; + GetSize(wide, tall); + surface()->DrawSetColor( 255,255,255,255 ); + surface()->DrawOutlinedRect(0, 0, wide, tall); + + int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID); + if (m_pData && colCount >= 0) + { + int xpos = 0; + for (int i = 0; i < colCount; i++) + { + const char *keyname = m_pListPanel->GetColumnNameBySection(m_iSectionID, i); + int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i); + int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i); + + IImage *image = NULL; + if (columnFlags & SectionedListPanel::COLUMN_IMAGE) + { + // lookup which image is being referred to + if (m_pListPanel->m_pImageList) + { + int imageIndex = m_pData->GetInt(keyname, 0); + if (m_pListPanel->m_pImageList->IsValidIndex(imageIndex)) + { + if (imageIndex > 0) + { + image = m_pListPanel->m_pImageList->GetImage(imageIndex); + } + } + } + } + else + { + image = GetImageAtIndex(i); + } + + int imageWide = 0, tall = 0; + int wide; + if (image) + { + image->GetContentSize(imageWide, tall); + } + if (maxWidth >= 0) + { + wide = maxWidth; + } + else + { + wide = imageWide; + } + + xpos += wide;//max(maxWidth,wide); + surface()->DrawOutlinedRect( xpos, 0, xpos, GetTall() ); + } + } + } + + virtual void OnMousePressed(MouseCode code) + { + if ( m_pListPanel && m_pListPanel->IsClickable() && IsEnabled() ) + { + if (code == MOUSE_LEFT) + { + m_pListPanel->PostActionSignal(new KeyValues("ItemLeftClick", "itemID", m_iID)); + } + if (code == MOUSE_RIGHT) + { + KeyValues *msg = new KeyValues("ItemContextMenu", "itemID", m_iID); + msg->SetPtr("SubPanel", this); + m_pListPanel->PostActionSignal(msg); + } + + m_pListPanel->SetSelectedItem(this); + } + } + + void SetSelected(bool state) + { + if (m_bSelected != state) + { + if (state) + { + RequestFocus(); + } + m_bSelected = state; + SetPaintBackgroundEnabled( state ); + InvalidateLayout(); + Repaint(); + } + } + + bool IsSelected() + { + return m_bSelected; + } + + virtual void OnSetFocus() + { + InvalidateLayout(); // force the layout to be redone so we can change text color according to focus + BaseClass::OnSetFocus(); + } + + virtual void OnKillFocus() + { + InvalidateLayout(); // force the layout to be redone so we can change text color according to focus + BaseClass::OnSetFocus(); + } + + virtual void OnMouseDoublePressed(MouseCode code) + { + //============================================================================= + // HPE_BEGIN: + // [tj] Only do this if clicking is enabled. + //============================================================================= + if (m_pListPanel && m_pListPanel->IsClickable()) + { + if (code == MOUSE_LEFT) + { + m_pListPanel->PostActionSignal(new KeyValues("ItemDoubleLeftClick", "itemID", m_iID)); + + // post up an enter key being hit + m_pListPanel->OnKeyCodeTyped(KEY_ENTER); + } + else + { + OnMousePressed(code); + } + + m_pListPanel->SetSelectedItem(this); + } + //============================================================================= + // HPE_END + //============================================================================= + } + + void GetCellBounds(int column, int &xpos, int &columnWide) + { + xpos = 0, columnWide = 0; + int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID); + for (int i = 0; i < colCount; i++) + { + int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i); + + IImage *image = GetImageAtIndex(i); + if (!image) + continue; + + // set the image position within the label + int wide, tall; + image->GetContentSize(wide, tall); + if (maxWidth >= 0) + { + wide = maxWidth; + } + + if (i == column) + { + // found the cell size, bail + columnWide = wide; + return; + } + + xpos += wide; + } + } + + //============================================================================= + // HPE_BEGIN: + // [menglish] gets the local coordinates of a cell using the max width for every column + //============================================================================= + + void GetMaxCellBounds(int column, int &xpos, int &columnWide) + { + xpos = 0, columnWide = 0; + int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID); + for (int i = 0; i < colCount; i++) + { + int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i); + + if (i == column) + { + // found the cell size, bail + columnWide = maxWidth; + return; + } + + xpos += maxWidth; + } + } + + //============================================================================= + // HPE_END + //============================================================================= + + virtual void SetOverrideColors( bool state ) + { + m_bOverrideColors = state; + } + + void SetShowColumns( bool bShow ) + { + m_bShowColumns = bShow; + } + +private: + SectionedListPanel *m_pListPanel; + int m_iID; + int m_iSectionID; + KeyValues *m_pData; + Color m_FgColor2; + Color m_BgColor; + Color m_ArmedFgColor1; + Color m_ArmedFgColor2; + Color m_OutOfFocusSelectedTextColor; + Color m_ArmedBgColor; + Color m_SelectionBG2Color; + CUtlVector m_TextImages; + + bool m_bSelected; + bool m_bOverrideColors; + bool m_bShowColumns; +}; + +}; // namespace vgui + +DECLARE_BUILD_FACTORY( SectionedListPanel ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +SectionedListPanel::SectionedListPanel(vgui::Panel *parent, const char *name) : BaseClass(parent, name) +{ + m_pScrollBar = new ScrollBar(this, "SectionedScrollBar", true); + m_pScrollBar->SetVisible(false); + m_pScrollBar->AddActionSignalTarget(this); + + m_iEditModeItemID = 0; + m_iEditModeColumn = 0; + m_bSortNeeded = false; + m_bVerticalScrollbarEnabled = true; + m_iLineSpacing = DEFAULT_LINE_SPACING; + m_iSectionGap = DEFAULT_SECTION_GAP; + + m_pImageList = NULL; + m_bDeleteImageListWhenDone = false; + + m_hHeaderFont = INVALID_FONT; + m_hRowFont = INVALID_FONT; + + //============================================================================= + // HPE_BEGIN: + //============================================================================= + // [tj] Default clickability to true so existing controls aren't affected. + m_clickable = true; + // [tj] draw section headers by default so existing controls aren't affected. + m_bDrawSectionHeaders = true; + //============================================================================= + // HPE_END + //============================================================================= +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +SectionedListPanel::~SectionedListPanel() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Sorts the list +//----------------------------------------------------------------------------- +void SectionedListPanel::ReSortList() +{ + m_SortedItems.RemoveAll(); + + int sectionStart = 0; + // layout the buttons + for (int sectionIndex = 0; sectionIndex < m_Sections.Size(); sectionIndex++) + { + section_t §ion = m_Sections[sectionIndex]; + sectionStart = m_SortedItems.Count(); + + // find all the items in this section + for( int i = m_Items.Head(); i != m_Items.InvalidIndex(); i = m_Items.Next( i ) ) + { + if (m_Items[i]->GetSectionID() == m_Sections[sectionIndex].m_iID) + { + // insert the items sorted + if (section.m_pSortFunc) + { + int insertionPoint = sectionStart; + for (;insertionPoint < m_SortedItems.Count(); insertionPoint++) + { + if (section.m_pSortFunc(this, i, m_SortedItems[insertionPoint]->GetID())) + break; + } + + if (insertionPoint == m_SortedItems.Count()) + { + m_SortedItems.AddToTail(m_Items[i]); + } + else + { + m_SortedItems.InsertBefore(insertionPoint, m_Items[i]); + } + } + else + { + // just add to the end + m_SortedItems.AddToTail(m_Items[i]); + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: iterates through and sets up the position of all the sections and items +//----------------------------------------------------------------------------- +void SectionedListPanel::PerformLayout() +{ + // lazy resort the list + if (m_bSortNeeded) + { + ReSortList(); + m_bSortNeeded = false; + } + + BaseClass::PerformLayout(); + + LayoutPanels(m_iContentHeight); + + int cx, cy, cwide, ctall; + GetBounds(cx, cy, cwide, ctall); + if (m_iContentHeight > ctall && m_bVerticalScrollbarEnabled) + { + m_pScrollBar->SetVisible(true); + m_pScrollBar->MoveToFront(); + + m_pScrollBar->SetPos(cwide - m_pScrollBar->GetWide() - 2, 0); + m_pScrollBar->SetSize(m_pScrollBar->GetWide(), ctall - 2); + + m_pScrollBar->SetRangeWindow(ctall); + + m_pScrollBar->SetRange(0, m_iContentHeight); + m_pScrollBar->InvalidateLayout(); + m_pScrollBar->Repaint(); + + // since we're just about to make the scrollbar visible, we need to re-layout + // the buttons since they depend on the scrollbar size + LayoutPanels(m_iContentHeight); + } + else + { + m_pScrollBar->SetValue(0); + + bool bWasVisible = m_pScrollBar->IsVisible(); + m_pScrollBar->SetVisible(false); + + // When we hide the scrollbar, we need to layout the buttons because they'll have more width to work with + if ( bWasVisible ) + { + LayoutPanels(m_iContentHeight); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: lays out the sections and rows in the panel +//----------------------------------------------------------------------------- +void SectionedListPanel::LayoutPanels(int &contentTall) +{ + int tall = GetSectionTall(); + int x = 5, wide = GetWide() - 10; + int y = 5; + + if (m_pScrollBar->IsVisible()) + { + y -= m_pScrollBar->GetValue(); + wide -= m_pScrollBar->GetWide(); + } + + int iStart = -1; + int iEnd = -1; + + // layout the buttons + bool bFirstVisibleSection = true; + for (int sectionIndex = 0; sectionIndex < m_Sections.Size(); sectionIndex++) + { + section_t §ion = m_Sections[sectionIndex]; + + iStart = -1; + iEnd = -1; + for (int i = 0; i < m_SortedItems.Count(); i++) + { + if (m_SortedItems[i]->GetSectionID() == m_Sections[sectionIndex].m_iID) + { + if (iStart == -1) + iStart = i; + iEnd = i; + } + } + + // don't draw this section at all if their are no item in it + if (iStart == -1 && !section.m_bAlwaysVisible) + { + section.m_pHeader->SetVisible(false); + continue; + } + + // Skip down a bit if this is not the first section to be drawn + if ( bFirstVisibleSection ) + bFirstVisibleSection = false; + else + y += m_iSectionGap; + + //============================================================================= + // HPE_BEGIN: + // [tj] Only draw the header if it is enabled + //============================================================================= + int nMinNextSectionY = y + section.m_iMinimumHeight; + if (m_bDrawSectionHeaders) + { + // draw the header + section.m_pHeader->SetBounds(x, y, wide, tall); + section.m_pHeader->SetVisible(true); + y += tall; + } + else + { + section.m_pHeader->SetVisible(false); + } + //============================================================================= + // HPE_END + //============================================================================= + + if (iStart == -1 && section.m_bAlwaysVisible) + { + } + else + { + // arrange all the items in this section underneath + for (int i = iStart; i <= iEnd; i++) + { + CItemButton *item = m_SortedItems[i]; //items[i]; + item->SetBounds(x, y, wide, m_iLineSpacing); + + // setup edit mode + if (m_hEditModePanel.Get() && m_iEditModeItemID == item->GetID()) + { + int cx, cwide; + item->GetCellBounds(1, cx, cwide); + m_hEditModePanel->SetBounds(cx, y, cwide, tall); + } + + y += m_iLineSpacing; + } + } + + // Add space, if needed to fulfill minimum requested content height + if ( y < nMinNextSectionY ) + y = nMinNextSectionY; + } + + // calculate height + contentTall = y; + if (m_pScrollBar->IsVisible()) + { + contentTall += m_pScrollBar->GetValue(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Ensures that the specified item is visible in the display +//----------------------------------------------------------------------------- +void SectionedListPanel::ScrollToItem(int iItem) +{ + int tall = GetSectionTall(); + int itemX, itemY ; + int nCurrentValue = m_pScrollBar->GetValue(); + + // find out where the item is + m_Items[iItem]->GetPos(itemX, itemY); + // add in the current scrollbar position + itemY += nCurrentValue; + + // compare that in the list + int cx, cy, cwide, ctall; + GetBounds(cx, cy, cwide, ctall); + if (m_iContentHeight > ctall) + { + if (itemY < nCurrentValue) + { + // scroll up + m_pScrollBar->SetValue(itemY); + } + else if (itemY > nCurrentValue + ctall - tall) + { + // scroll down + m_pScrollBar->SetValue(itemY - ctall + tall); + } + else + { + // keep the current value + } + } + else + { + // area isn't big enough, just remove the scrollbar + m_pScrollBar->SetValue(0); + } + + // reset scrollbar + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: sets background color & border +//----------------------------------------------------------------------------- +void SectionedListPanel::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetBgColor(GetSchemeColor("SectionedListPanel.BgColor", GetBgColor(), pScheme)); + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); + + FOR_EACH_LL( m_Items, j ) + { + m_Items[j]->SetShowColumns( m_bShowColumns ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void SectionedListPanel::SetHeaderFont( HFont hFont ) +{ + m_hHeaderFont = hFont; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +HFont SectionedListPanel::GetHeaderFont( void ) const +{ + return m_hHeaderFont; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void SectionedListPanel::SetRowFont( HFont hFont ) +{ + m_hRowFont = hFont; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +HFont SectionedListPanel::GetRowFont( void ) const +{ + return m_hRowFont; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void SectionedListPanel::ApplySettings(KeyValues *inResourceData) +{ + BaseClass::ApplySettings(inResourceData); + m_iLineSpacing = inResourceData->GetInt("linespacing", 0); + if (!m_iLineSpacing) + { + m_iLineSpacing = DEFAULT_LINE_SPACING; + } + if (IsProportional()) + { + m_iLineSpacing = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iLineSpacing); + } + + m_iSectionGap = inResourceData->GetInt("sectiongap", 0); + if (!m_iSectionGap) + { + m_iSectionGap = DEFAULT_SECTION_GAP; + } + if (IsProportional()) + { + m_iSectionGap = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iSectionGap); + } +} + +//----------------------------------------------------------------------------- +// Purpose: passes on proportional state to children +//----------------------------------------------------------------------------- +void SectionedListPanel::SetProportional(bool state) +{ + BaseClass::SetProportional(state); + + // now setup the section headers and items + int i; + for (i = 0; i < m_Sections.Count(); i++) + { + m_Sections[i].m_pHeader->SetProportional(state); + } + FOR_EACH_LL( m_Items, j ) + { + m_Items[j]->SetProportional(state); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sets whether or not the vertical scrollbar should ever be displayed +//----------------------------------------------------------------------------- +void SectionedListPanel::SetVerticalScrollbar(bool state) +{ + m_bVerticalScrollbarEnabled = state; +} + +//----------------------------------------------------------------------------- +// Purpose: adds a new section +//----------------------------------------------------------------------------- +void SectionedListPanel::AddSection(int sectionID, const char *name, SectionSortFunc_t sortFunc) +{ + SectionedListPanelHeader *header = new SectionedListPanelHeader(this, name, sectionID); + AddSection(sectionID, header, sortFunc); +} + +//----------------------------------------------------------------------------- +// Purpose: adds a new section +//----------------------------------------------------------------------------- +void SectionedListPanel::AddSection(int sectionID, const wchar_t *name, SectionSortFunc_t sortFunc) +{ + SectionedListPanelHeader *header = new SectionedListPanelHeader(this, name, sectionID); + AddSection(sectionID, header, sortFunc); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds a new section, given object +//----------------------------------------------------------------------------- +void SectionedListPanel::AddSection(int sectionID, SectionedListPanelHeader *header, SectionSortFunc_t sortFunc) +{ + header = SETUP_PANEL( header ); + int index = m_Sections.AddToTail(); + m_Sections[index].m_iID = sectionID; + m_Sections[index].m_pHeader = header; + m_Sections[index].m_pSortFunc = sortFunc; + m_Sections[index].m_bAlwaysVisible = false; + m_Sections[index].m_iMinimumHeight = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: removes all the sections from the current panel +//----------------------------------------------------------------------------- +void SectionedListPanel::RemoveAllSections() +{ + for (int i = 0; i < m_Sections.Count(); i++) + { + if (!m_Sections.IsValidIndex(i)) + continue; + + m_Sections[i].m_pHeader->SetVisible(false); + m_Sections[i].m_pHeader->MarkForDeletion(); + } + + m_Sections.RemoveAll(); + m_Sections.Purge(); + m_SortedItems.RemoveAll(); + + InvalidateLayout(); + ReSortList(); +} + +//----------------------------------------------------------------------------- +// Purpose: adds a new column to a section +//----------------------------------------------------------------------------- +bool SectionedListPanel::AddColumnToSection(int sectionID, const char *columnName, const char *columnText, int columnFlags, int width, HFont fallbackFont /*= INVALID_FONT*/ ) +{ + wchar_t wtext[64]; + wchar_t *pwtext = g_pVGuiLocalize->Find(columnText); + if (!pwtext) + { + g_pVGuiLocalize->ConvertANSIToUnicode(columnText, wtext, sizeof(wtext)); + pwtext = wtext; + } + return AddColumnToSection(sectionID, columnName, pwtext, columnFlags, width, fallbackFont ); +} + +//----------------------------------------------------------------------------- +// Purpose: as above but with wchar_t's +//----------------------------------------------------------------------------- +bool SectionedListPanel::AddColumnToSection(int sectionID, const char *columnName, const wchar_t *columnText, int columnFlags, int width, HFont fallbackFont /*= INVALID_FONT*/ ) +{ + int index = FindSectionIndexByID(sectionID); + if (index < 0) + return false; + section_t §ion = m_Sections[index]; + + // add the new column to the sections' list + index = section.m_Columns.AddToTail(); + column_t &column = section.m_Columns[index]; + + Q_strncpy(column.m_szColumnName, columnName, sizeof(column.m_szColumnName)); + wcsncpy(column.m_szColumnText, columnText, sizeof(column.m_szColumnText) / sizeof(wchar_t)); + column.m_szColumnText[sizeof(column.m_szColumnText) / sizeof(wchar_t) - 1] = 0; + column.m_iColumnFlags = columnFlags; + column.m_iWidth = width; + column.m_hFallbackFont = fallbackFont; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: modifies the text in an existing column +//----------------------------------------------------------------------------- +bool SectionedListPanel::ModifyColumn(int sectionID, const char *columnName, const wchar_t *columnText) +{ + int index = FindSectionIndexByID(sectionID); + if (index < 0) + return false; + section_t §ion = m_Sections[index]; + + // find the specified column + int columnIndex; + for (columnIndex = 0; columnIndex < section.m_Columns.Count(); columnIndex++) + { + if (!stricmp(section.m_Columns[columnIndex].m_szColumnName, columnName)) + break; + } + if (!section.m_Columns.IsValidIndex(columnIndex)) + return false; + column_t &column = section.m_Columns[columnIndex]; + + // modify the text + wcsncpy(column.m_szColumnText, columnText, sizeof(column.m_szColumnText) / sizeof(wchar_t)); + column.m_szColumnText[sizeof(column.m_szColumnText) / sizeof(wchar_t) - 1] = 0; + section.m_pHeader->InvalidateLayout(); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: adds an item to the list; returns itemID +//----------------------------------------------------------------------------- +int SectionedListPanel::AddItem(int sectionID, const KeyValues *data) +{ + int itemID = GetNewItemButton(); + ModifyItem(itemID, sectionID, data); + + // not sorted but in list + m_SortedItems.AddToTail(m_Items[itemID]); + m_bSortNeeded = true; + + return itemID; +} + +//----------------------------------------------------------------------------- +// Purpose: modifies an existing item; returns false if the item does not exist +//----------------------------------------------------------------------------- +bool SectionedListPanel::ModifyItem(int itemID, int sectionID, const KeyValues *data) +{ + if ( !m_Items.IsValidIndex(itemID) ) + return false; + + InvalidateLayout(); + m_Items[itemID]->SetSectionID(sectionID); + m_Items[itemID]->SetData(data); + m_Items[itemID]->InvalidateLayout(); + m_bSortNeeded = true; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void SectionedListPanel::SetItemFgColor( int itemID, Color color ) +{ + Assert( m_Items.IsValidIndex(itemID) ); + if ( !m_Items.IsValidIndex(itemID) ) + return; + + m_Items[itemID]->SetFgColor( color ); + m_Items[itemID]->SetOverrideColors( true ); + m_Items[itemID]->InvalidateLayout(); +} + +//============================================================================= +// HPE_BEGIN: +// [menglish] Setter for the background color similar to the foreground color +//============================================================================= + +void SectionedListPanel::SetItemBgColor( int itemID, Color color ) +{ + Assert( m_Items.IsValidIndex(itemID) ); + if ( !m_Items.IsValidIndex(itemID) ) + return; + + m_Items[itemID]->SetBgColor( color ); + m_Items[itemID]->SetPaintBackgroundEnabled( true ); + m_Items[itemID]->SetOverrideColors( true ); + m_Items[itemID]->InvalidateLayout(); +} + +void SectionedListPanel::SetItemFont( int itemID, HFont font ) +{ + Assert( m_Items.IsValidIndex(itemID) ); + if ( !m_Items.IsValidIndex(itemID) ) + return; + + m_Items[itemID]->SetFont( font ); +} + +void SectionedListPanel::SetItemEnabled( int itemID, bool bEnabled ) +{ + Assert( m_Items.IsValidIndex(itemID) ); + if ( !m_Items.IsValidIndex(itemID) ) + return; + + m_Items[itemID]->SetEnabled( bEnabled ); +} + +//============================================================================= +// HPE_END +//============================================================================= +//----------------------------------------------------------------------------- +// Purpose: sets the color of a section text & underline +//----------------------------------------------------------------------------- +void SectionedListPanel::SetSectionFgColor(int sectionID, Color color) +{ + if (!m_Sections.IsValidIndex(sectionID)) + return; + + m_Sections[sectionID].m_pHeader->SetColor(color); +} +//----------------------------------------------------------------------------- +// Purpose: added so you can change the divider color AFTER the main color. +//----------------------------------------------------------------------------- +void SectionedListPanel::SetSectionDividerColor( int sectionID, Color color) +{ + if (!m_Sections.IsValidIndex(sectionID)) + return; + + m_Sections[sectionID].m_pHeader->SetDividerColor(color); +} +//----------------------------------------------------------------------------- +// Purpose: forces a section to always be visible +//----------------------------------------------------------------------------- +void SectionedListPanel::SetSectionAlwaysVisible(int sectionID, bool visible) +{ + if (!m_Sections.IsValidIndex(sectionID)) + return; + + m_Sections[sectionID].m_bAlwaysVisible = visible; +} +void SectionedListPanel::SetFontSection(int sectionID, HFont font) +{ + if (!m_Sections.IsValidIndex(sectionID)) + return; + + m_Sections[sectionID].m_pHeader->SetFont(font); +} +void SectionedListPanel::SetSectionMinimumHeight(int sectionID, int iMinimumHeight) +{ + if (!m_Sections.IsValidIndex(sectionID)) + return; + + m_Sections[sectionID].m_iMinimumHeight = iMinimumHeight; + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: removes an item from the list; returns false if the item does not exist or is already removed +//----------------------------------------------------------------------------- +bool SectionedListPanel::RemoveItem(int itemID) +{ + if ( !m_Items.IsValidIndex(itemID) ) + return false; + + m_SortedItems.FindAndRemove(m_Items[itemID]); + m_bSortNeeded = true; + + m_Items[itemID]->MarkForDeletion(); + m_Items.Remove(itemID); + + InvalidateLayout(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the number of columns in a section +//----------------------------------------------------------------------------- +int SectionedListPanel::GetColumnCountBySection(int sectionID) +{ + int index = FindSectionIndexByID(sectionID); + if (index < 0) + return NULL; + + return m_Sections[index].m_Columns.Size(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the name of a column by section and column index; returns NULL if there are no more columns +// valid range of columnIndex is [0, GetColumnCountBySection) +//----------------------------------------------------------------------------- +const char *SectionedListPanel::GetColumnNameBySection(int sectionID, int columnIndex) +{ + int index = FindSectionIndexByID(sectionID); + if (index < 0 || columnIndex >= m_Sections[index].m_Columns.Size()) + return NULL; + + return m_Sections[index].m_Columns[columnIndex].m_szColumnName; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the text for a column by section and column index +//----------------------------------------------------------------------------- +const wchar_t *SectionedListPanel::GetColumnTextBySection(int sectionID, int columnIndex) +{ + int index = FindSectionIndexByID(sectionID); + if (index < 0 || columnIndex >= m_Sections[index].m_Columns.Size()) + return NULL; + + return m_Sections[index].m_Columns[columnIndex].m_szColumnText; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the type of a column by section and column index +//----------------------------------------------------------------------------- +int SectionedListPanel::GetColumnFlagsBySection(int sectionID, int columnIndex) +{ + int index = FindSectionIndexByID(sectionID); + if (index < 0) + return 0; + + if (columnIndex >= m_Sections[index].m_Columns.Size()) + return 0; + + return m_Sections[index].m_Columns[columnIndex].m_iColumnFlags; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int SectionedListPanel::GetColumnWidthBySection(int sectionID, int columnIndex) +{ + int index = FindSectionIndexByID(sectionID); + if (index < 0) + return 0; + + if (columnIndex >= m_Sections[index].m_Columns.Size()) + return 0; + + return m_Sections[index].m_Columns[columnIndex].m_iWidth; +} + +//============================================================================= +// HPE_BEGIN: +// [menglish] Gets the column index by the string identifier +//============================================================================= + +int SectionedListPanel::GetColumnIndexByName(int sectionID, char* name) +{ + int index = FindSectionIndexByID(sectionID); + if (index < 0) + return 0; + + for ( int columnIndex = 0; columnIndex < m_Sections[index].m_Columns.Count(); ++ columnIndex) + { + if( !V_strcmp( m_Sections[index].m_Columns[columnIndex].m_szColumnName, name ) ) + return columnIndex; + } + + return -1; +} + +//============================================================================= +// HPE_END +//============================================================================= + +//----------------------------------------------------------------------------- +// Purpose: returns -1 if section not found +//----------------------------------------------------------------------------- +int SectionedListPanel::FindSectionIndexByID(int sectionID) +{ + for (int i = 0; i < m_Sections.Size(); i++) + { + if (m_Sections[i].m_iID == sectionID) + { + return i; + } + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the scrollbar is moved +//----------------------------------------------------------------------------- +void SectionedListPanel::OnSliderMoved() +{ + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Scrolls the list according to the mouse wheel movement +//----------------------------------------------------------------------------- +void SectionedListPanel::OnMouseWheeled(int delta) +{ + if (m_hEditModePanel.Get()) + { + // ignore mouse wheel in edit mode, forward right up to parent + CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); + return; + } + + // scroll the window based on the delta + int val = m_pScrollBar->GetValue(); + val -= (delta * BUTTON_HEIGHT_DEFAULT * 3); + m_pScrollBar->SetValue(val); +} + +//----------------------------------------------------------------------------- +// Purpose: Resets the scrollbar position on size change +//----------------------------------------------------------------------------- +void SectionedListPanel::OnSizeChanged(int wide, int tall) +{ + BaseClass::OnSizeChanged(wide, tall); + m_pScrollBar->SetValue(0); + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: deselects any items +//----------------------------------------------------------------------------- +void SectionedListPanel::OnMousePressed(MouseCode code) +{ + //============================================================================= + // HPE_BEGIN: + // [tj] Only do this if clicking is enabled. + //============================================================================= + if (m_clickable){ + ClearSelection(); + } + //============================================================================= + // HPE_END + //=============================================================================} +} +//----------------------------------------------------------------------------- +// Purpose: deselects any items +//----------------------------------------------------------------------------- +void SectionedListPanel::ClearSelection( void ) +{ + SetSelectedItem((CItemButton *)NULL); +} + +void SectionedListPanel::MoveSelectionDown( void ) +{ + int itemID = GetSelectedItem(); + if (itemID == -1) + return; + + if (!m_SortedItems.Count()) // if the list has been emptied + return; + + int i; + for (i = 0; i < m_SortedItems.Count(); i++) + { + if (m_SortedItems[i]->GetID() == itemID) + break; + } + + Assert(i != m_SortedItems.Count()); + + // we're already on the end + if (i >= m_SortedItems.Count() - 1) + return; + + int newItemID = m_SortedItems[i + 1]->GetID(); + SetSelectedItem(m_Items[newItemID]); + ScrollToItem(newItemID); +} + +void SectionedListPanel::MoveSelectionUp( void ) +{ + int itemID = GetSelectedItem(); + if (itemID == -1) + return; + + if (!m_SortedItems.Count()) // if the list has been emptied + return; + + int i; + for (i = 0; i < m_SortedItems.Count(); i++) + { + if (m_SortedItems[i]->GetID() == itemID) + break; + } + + Assert(i != m_SortedItems.Count()); + + // we're already on the end + if (i == 0 || i >= m_SortedItems.Count() ) + return; + + int newItemID = m_SortedItems[i - 1]->GetID(); + SetSelectedItem(m_Items[newItemID]); + ScrollToItem(newItemID); +} + +void SectionedListPanel::NavigateTo( void ) +{ + BaseClass::NavigateTo(); + + if ( m_SortedItems.Count() ) + { + int nItemID = m_SortedItems[ 0 ]->GetID(); + SetSelectedItem( m_Items[ nItemID ] ); + ScrollToItem( nItemID ); + } + + RequestFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: arrow key movement handler +//----------------------------------------------------------------------------- +void SectionedListPanel::OnKeyCodePressed( KeyCode code ) +{ + if (m_hEditModePanel.Get()) + { + // ignore arrow keys in edit mode + // forward right up to parent so that tab focus change doesn't occur + CallParentFunction(new KeyValues("KeyCodePressed", "code", code)); + return; + } + + int buttonTall = GetSectionTall(); + + ButtonCode_t nButtonCode = GetBaseButtonCode( code ); + + if ( nButtonCode == KEY_XBUTTON_DOWN || + nButtonCode == KEY_XSTICK1_DOWN || + nButtonCode == KEY_XSTICK2_DOWN || + code == KEY_DOWN ) + { + int itemID = GetSelectedItem(); + MoveSelectionDown(); + if ( itemID != GetSelectedItem() ) + { + // Only eat the input if it did something + return; + } + } + else if ( nButtonCode == KEY_XBUTTON_UP || + nButtonCode == KEY_XSTICK1_UP || + nButtonCode == KEY_XSTICK2_UP || + code == KEY_UP) + { + int itemID = GetSelectedItem(); + MoveSelectionUp(); + if ( itemID != GetSelectedItem() ) + { + // Only eat the input if it did something + return; + } + } + else if (code == KEY_PAGEDOWN) + { + // calculate info for # of rows + int cx, cy, cwide, ctall; + GetBounds(cx, cy, cwide, ctall); + + int rowsperpage = ctall/buttonTall; + + int itemID = GetSelectedItem(); + int lastValidItem = itemID; + int secID = m_Items[itemID]->GetSectionID(); + int i=0; + int row = m_SortedItems.Find(m_Items[itemID]); + + while ( i < rowsperpage ) + { + if ( m_SortedItems.IsValidIndex(++row) ) + { + itemID = m_SortedItems[row]->GetID(); + lastValidItem = itemID; + i++; + + // if we switched sections, then count the section header as a row + if (m_Items[itemID]->GetSectionID() != secID) + { + secID = m_Items[itemID]->GetSectionID(); + i++; + } + } + else + { + itemID = lastValidItem; + break; + } + } + SetSelectedItem(m_Items[itemID]); + ScrollToItem(itemID); + return; + } + else if (code == KEY_PAGEUP) + { + // calculate info for # of rows + int cx, cy, cwide, ctall; + GetBounds(cx, cy, cwide, ctall); + int rowsperpage = ctall/buttonTall; + + int itemID = GetSelectedItem(); + int lastValidItem = itemID; + int secID = m_Items[itemID]->GetSectionID(); + int i=0; + int row = m_SortedItems.Find(m_Items[itemID]); + while ( i < rowsperpage ) + { + if ( m_SortedItems.IsValidIndex(--row) ) + { + itemID = m_SortedItems[row]->GetID(); + lastValidItem = itemID; + i++; + + // if we switched sections, then count the section header as a row + if (m_Items[itemID]->GetSectionID() != secID) + { + secID = m_Items[itemID]->GetSectionID(); + i++; + } + } + else + { + SetSelectedItem(m_Items[lastValidItem]); + m_pScrollBar->SetValue(0); + return; + } + } + SetSelectedItem(m_Items[itemID]); + ScrollToItem(itemID); + return; + } + else if ( code == KEY_ENTER || nButtonCode == KEY_XBUTTON_A ) + { + Panel *pSelectedItem = m_hSelectedItem; + if ( pSelectedItem ) + { + pSelectedItem->OnMousePressed( MOUSE_LEFT ); + } + return; + } + + BaseClass::OnKeyCodePressed( code ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Clears the list +//----------------------------------------------------------------------------- +void SectionedListPanel::DeleteAllItems() +{ + FOR_EACH_LL( m_Items, i ) + { + m_Items[i]->SetVisible(false); + m_Items[i]->Clear(); + + // don't delete, move to free list + int freeIndex = m_FreeItems.AddToTail(); + m_FreeItems[freeIndex] = m_Items[i]; + } + + m_Items.RemoveAll(); + m_SortedItems.RemoveAll(); + m_hSelectedItem = NULL; + InvalidateLayout(); + m_bSortNeeded = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Changes the current list selection +//----------------------------------------------------------------------------- +void SectionedListPanel::SetSelectedItem(CItemButton *item) +{ + if (m_hSelectedItem.Get() == item) + return; + + // deselect the current item + if (m_hSelectedItem.Get()) + { + m_hSelectedItem->SetSelected(false); + } + + // set the new item + m_hSelectedItem = item; + if (m_hSelectedItem.Get()) + { + m_hSelectedItem->SetSelected(true); + } + + Repaint(); + PostActionSignal(new KeyValues("ItemSelected", "itemID", m_hSelectedItem.Get() ? m_hSelectedItem->GetID() : -1)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int SectionedListPanel::GetSelectedItem() +{ + if (m_hSelectedItem.Get()) + { + return m_hSelectedItem->GetID(); + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: sets which item is currently selected +//----------------------------------------------------------------------------- +void SectionedListPanel::SetSelectedItem(int itemID) +{ + if ( m_Items.IsValidIndex(itemID) ) + { + SetSelectedItem(m_Items[itemID]); + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns the data of a selected item +//----------------------------------------------------------------------------- +KeyValues *SectionedListPanel::GetItemData(int itemID) +{ + Assert(m_Items.IsValidIndex(itemID)); + if ( !m_Items.IsValidIndex(itemID) ) + return NULL; + + return m_Items[itemID]->GetData(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns what section an item is in +//----------------------------------------------------------------------------- +int SectionedListPanel::GetItemSection(int itemID) +{ + if ( !m_Items.IsValidIndex(itemID) ) + return -1; + + return m_Items[itemID]->GetSectionID(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the itemID is valid for use +//----------------------------------------------------------------------------- +bool SectionedListPanel::IsItemIDValid(int itemID) +{ + return m_Items.IsValidIndex(itemID); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the itemID is valid for use +//----------------------------------------------------------------------------- +int SectionedListPanel::GetHighestItemID() +{ + return m_Items.MaxElementIndex(); +} + +//----------------------------------------------------------------------------- +// Purpose: item iterators +//----------------------------------------------------------------------------- +int SectionedListPanel::GetItemCount() +{ + return m_SortedItems.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: item iterators +//----------------------------------------------------------------------------- +int SectionedListPanel::GetItemIDFromRow(int row) +{ + if ( !m_SortedItems.IsValidIndex(row) ) + return -1; + + return m_SortedItems[row]->GetID(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the row that this itemID occupies. -1 if the itemID is invalid +//----------------------------------------------------------------------------- +int SectionedListPanel::GetRowFromItemID(int itemID) +{ + for (int i = 0; i < m_SortedItems.Count(); i++) + { + if ( m_SortedItems[i]->GetID() == itemID ) + { + return i; + } + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the local coordinates of a cell +//----------------------------------------------------------------------------- +bool SectionedListPanel::GetCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall) +{ + x = y = wide = tall = 0; + if ( !IsItemIDValid(itemID) ) + return false; + + // get the item + CItemButton *item = m_Items[itemID]; + + if ( !item->IsVisible() ) + return false; + + //!! ignores column for now + item->GetBounds(x, y, wide, tall); + item->GetCellBounds(column, x, wide); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the local coordinates of a section header +//----------------------------------------------------------------------------- +bool SectionedListPanel::GetSectionHeaderBounds(int sectionID, int &x, int &y, int &wide, int &tall) +{ + x = y = wide = tall = 0; + int index = FindSectionIndexByID(sectionID); + if (index < 0 || !m_Sections[index].m_pHeader ) + return false; + + m_Sections[index].m_pHeader->GetBounds( x, y, wide, tall ); + return true; +} + +//============================================================================= +// HPE_BEGIN: +// [menglish] Gets the local coordinates of a cell using the max width for every column +// Gets the local coordinates of a cell +//============================================================================= + +bool SectionedListPanel::GetMaxCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall) +{ + x = y = wide = tall = 0; + if ( !IsItemIDValid(itemID) ) + return false; + + // get the item + CItemButton *item = m_Items[itemID]; + + if ( !item->IsVisible() ) + return false; + + //!! ignores column for now + item->GetBounds(x, y, wide, tall); + item->GetMaxCellBounds(column, x, wide); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the local coordinates of a cell +//----------------------------------------------------------------------------- +bool SectionedListPanel::GetItemBounds(int itemID, int &x, int &y, int &wide, int &tall) +{ + x = y = wide = tall = 0; + if ( !IsItemIDValid(itemID) ) + return false; + + // get the item + CItemButton *item = m_Items[itemID]; + + if ( !item->IsVisible() ) + return false; + + //!! ignores column for now + item->GetBounds(x, y, wide, tall); + return true; +} + +//============================================================================= +// HPE_END +//============================================================================= + +//----------------------------------------------------------------------------- +// Purpose: forces an item to redraw +//----------------------------------------------------------------------------- +void SectionedListPanel::InvalidateItem(int itemID) +{ + if ( !IsItemIDValid(itemID) ) + return; + + m_Items[itemID]->InvalidateLayout(); + m_Items[itemID]->Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: set up a field for editing +//----------------------------------------------------------------------------- +void SectionedListPanel::EnterEditMode(int itemID, int column, vgui::Panel *editPanel) +{ + m_hEditModePanel = editPanel; + m_iEditModeItemID = itemID; + m_iEditModeColumn = column; + editPanel->SetParent(this); + editPanel->SetVisible(true); + editPanel->RequestFocus(); + editPanel->MoveToFront(); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: leaves editing mode +//----------------------------------------------------------------------------- +void SectionedListPanel::LeaveEditMode() +{ + if (m_hEditModePanel.Get()) + { + InvalidateItem(m_iEditModeItemID); + m_hEditModePanel->SetVisible(false); + m_hEditModePanel->SetParent((Panel *)NULL); + m_hEditModePanel = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if we are currently in inline editing mode +//----------------------------------------------------------------------------- +bool SectionedListPanel::IsInEditMode() +{ + return (m_hEditModePanel.Get() != NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: list used to match indexes in image columns to image pointers +//----------------------------------------------------------------------------- +void SectionedListPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone) +{ + m_bDeleteImageListWhenDone = deleteImageListWhenDone; + m_pImageList = imageList; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void SectionedListPanel::OnSetFocus() +{ + if (m_hSelectedItem.Get()) + { + m_hSelectedItem->RequestFocus(); + } + else + { + BaseClass::OnSetFocus(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int SectionedListPanel::GetSectionTall() +{ + if (m_Sections.Count()) + { + HFont font = m_Sections[0].m_pHeader->GetFont(); + if (font != INVALID_FONT) + { + return surface()->GetFontTall(font) + BUTTON_HEIGHT_SPACER; + } + } + + return BUTTON_HEIGHT_DEFAULT; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the size required to fully draw the contents of the panel +//----------------------------------------------------------------------------- +void SectionedListPanel::GetContentSize(int &wide, int &tall) +{ + // make sure our layout is done + if (IsLayoutInvalid()) + { + if (m_bSortNeeded) + { + ReSortList(); + m_bSortNeeded = false; + } + LayoutPanels(m_iContentHeight); + } + + wide = GetWide(); + tall = m_iContentHeight; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the index of a new item button +//----------------------------------------------------------------------------- +int SectionedListPanel::GetNewItemButton() +{ + int itemID = m_Items.AddToTail(); + if (m_FreeItems.Count()) + { + // reusing an existing CItemButton + m_Items[itemID] = m_FreeItems[m_FreeItems.Head()]; + m_Items[itemID]->SetID(itemID); + m_Items[itemID]->SetVisible(true); + m_FreeItems.Remove(m_FreeItems.Head()); + } + else + { + // create a new CItemButton + m_Items[itemID] = SETUP_PANEL(new CItemButton(this, itemID)); + m_Items[itemID]->SetShowColumns( m_bShowColumns ); + } + + // Gross. Le's hope this isn't the only property that doesn't get defaulted + // properly when an item is recycled..... + m_Items[itemID]->SetEnabled( true ); + + return itemID; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns fallback font to use for text image for this column +// Input : sectionID - +// columnIndex - +// Output : virtual HFont +//----------------------------------------------------------------------------- +HFont SectionedListPanel::GetColumnFallbackFontBySection( int sectionID, int columnIndex ) +{ + int index = FindSectionIndexByID(sectionID); + if (index < 0) + return INVALID_FONT; + + if (columnIndex >= m_Sections[index].m_Columns.Size()) + return INVALID_FONT; + + return m_Sections[index].m_Columns[columnIndex].m_hFallbackFont; +} diff --git a/mp/src/vgui2/vgui_controls/Slider.cpp b/mp/src/vgui2/vgui_controls/Slider.cpp index 9a620448..a05454a2 100644 --- a/mp/src/vgui2/vgui_controls/Slider.cpp +++ b/mp/src/vgui2/vgui_controls/Slider.cpp @@ -1,954 +1,954 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#define PROTECTED_THINGS_DISABLE - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY( Slider ); - -static const float NOB_SIZE = 8.0f; - -//----------------------------------------------------------------------------- -// Purpose: Create a slider bar with ticks underneath it -//----------------------------------------------------------------------------- -Slider::Slider(Panel *parent, const char *panelName ) : BaseClass(parent, panelName) -{ - m_bIsDragOnRepositionNob = false; - _dragging = false; - _value = 0; - _range[0] = 0; - _range[1] = 0; - _buttonOffset = 0; - _sliderBorder = NULL; - _insetBorder = NULL; - m_nNumTicks = 10; - _leftCaption = NULL; - _rightCaption = NULL; - - _subrange[ 0 ] = 0; - _subrange[ 1 ] = 0; - m_bUseSubRange = false; - m_bInverted = false; - - SetThumbWidth( 8 ); - RecomputeNobPosFromValue(); - AddActionSignalTarget(this); - SetBlockDragChaining( true ); -} - -// This allows the slider to behave like it's larger than what's actually being drawn -//----------------------------------------------------------------------------- -// Purpose: -// Input : bEnable - -// 0 - -// 100 - -//----------------------------------------------------------------------------- -void Slider::SetSliderThumbSubRange( bool bEnable, int nMin /*= 0*/, int nMax /*= 100*/ ) -{ - m_bUseSubRange = bEnable; - _subrange[ 0 ] = nMin; - _subrange[ 1 ] = nMax; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the size of the slider bar. -// Warning less than 30 pixels tall and everything probably won't fit. -//----------------------------------------------------------------------------- -void Slider::OnSizeChanged(int wide,int tall) -{ - BaseClass::OnSizeChanged(wide,tall); - - RecomputeNobPosFromValue(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the value of the slider to one of the ticks. -//----------------------------------------------------------------------------- -void Slider::SetValue(int value, bool bTriggerChangeMessage) -{ - int oldValue=_value; - - if ( _range[0] < _range[1] ) - { - if(value<_range[0]) - { - value=_range[0]; - } - if(value>_range[1]) - { - value=_range[1]; - } - } - else - { - if(value<_range[1]) - { - value=_range[1]; - } - if(value>_range[0]) - { - value=_range[0]; - } - } - - _value = value; - - RecomputeNobPosFromValue(); - - if (_value != oldValue && bTriggerChangeMessage) - { - SendSliderMovedMessage(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Return the value of the slider -//----------------------------------------------------------------------------- -int Slider::GetValue() -{ - return _value; -} - -//----------------------------------------------------------------------------- -// Purpose: Layout the slider before drawing it on screen. -//----------------------------------------------------------------------------- -void Slider::PerformLayout() -{ - BaseClass::PerformLayout(); - RecomputeNobPosFromValue(); - - if (_leftCaption) - { - _leftCaption->ResizeImageToContent(); - } - if (_rightCaption) - { - _rightCaption->ResizeImageToContent(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Move the nob on the slider in response to changing its value. -//----------------------------------------------------------------------------- -void Slider::RecomputeNobPosFromValue() -{ - //int wide,tall; - //GetPaintSize(wide,tall); - int x, y, wide, tall; - GetTrackRect( x, y, wide, tall ); - - float usevalue = _value; - int *userange = &_range[ 0 ]; - if ( m_bUseSubRange ) - { - userange = &_subrange[ 0 ]; - usevalue = clamp( _value, _subrange[ 0 ], _subrange[ 1 ] ); - } - - float fwide=(float)wide; - float frange=(float)(userange[1] -userange[0]); - float fvalue=(float)(usevalue -userange[0]); - float fper = (frange != 0.0f) ? fvalue / frange : 0.0f; - - if ( m_bInverted ) - fper = 1.0f - fper; - - float freepixels = fwide - _nobSize; - float leftpixel = (float)x; - float firstpixel = leftpixel + freepixels * fper + 0.5f; - - _nobPos[0]=(int)( firstpixel ); - _nobPos[1]=(int)( firstpixel + _nobSize ); - - - int rightEdge = x + wide; - - if(_nobPos[1]> rightEdge ) - { - _nobPos[0]=rightEdge-((int)_nobSize); - _nobPos[1]=rightEdge; - } - - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sync the slider's value up with the nob's position. -//----------------------------------------------------------------------------- -void Slider::RecomputeValueFromNobPos() -{ - int value = EstimateValueAtPos( _nobPos[ 0 ], 0 ); - SetValue( value ); -} - -int Slider::EstimateValueAtPos( int localMouseX, int /*localMouseY*/ ) -{ - int x, y, wide, tall; - GetTrackRect( x, y, wide, tall ); - - int *userange = &_range[ 0 ]; - if ( m_bUseSubRange ) - { - userange = &_subrange[ 0 ]; - } - - float fwide = (float)wide; - float fvalue = (float)( _value - userange[0] ); - float fnob = (float)( localMouseX - x ); - float freepixels = fwide - _nobSize; - - // Map into reduced range - fvalue = freepixels != 0.0f ? fnob / freepixels : 0.0f; - - return (int) (RemapVal( fvalue, 0.0, 1.0, userange[0], userange[1] )); -} - -void Slider::SetInverted( bool bInverted ) -{ - m_bInverted = bInverted; -} - - -//----------------------------------------------------------------------------- -// Purpose: Send a message to interested parties when the slider moves -//----------------------------------------------------------------------------- -void Slider::SendSliderMovedMessage() -{ - // send a changed message - KeyValues *pParams = new KeyValues("SliderMoved", "position", _value); - pParams->SetPtr( "panel", this ); - PostActionSignal( pParams ); -} - -//----------------------------------------------------------------------------- -// Purpose: Send a message to interested parties when the user begins dragging the slider -//----------------------------------------------------------------------------- -void Slider::SendSliderDragStartMessage() -{ - // send a message - KeyValues *pParams = new KeyValues("SliderDragStart", "position", _value); - pParams->SetPtr( "panel", this ); - PostActionSignal( pParams ); -} - -//----------------------------------------------------------------------------- -// Purpose: Send a message to interested parties when the user ends dragging the slider -//----------------------------------------------------------------------------- -void Slider::SendSliderDragEndMessage() -{ - // send a message - KeyValues *pParams = new KeyValues("SliderDragEnd", "position", _value); - pParams->SetPtr( "panel", this ); - PostActionSignal( pParams ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Slider::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetFgColor(GetSchemeColor("Slider.NobColor", pScheme)); - // this line is useful for debugging - //SetBgColor(GetSchemeColor("0 0 0 255")); - - m_TickColor = pScheme->GetColor( "Slider.TextColor", GetFgColor() ); - m_TrackColor = pScheme->GetColor( "Slider.TrackColor", GetFgColor() ); - -#ifdef _X360 - m_DepressedBgColor = GetSchemeColor("Slider.NobFocusColor", pScheme); -#endif - - m_DisabledTextColor1 = pScheme->GetColor( "Slider.DisabledTextColor1", GetFgColor() ); - m_DisabledTextColor2 = pScheme->GetColor( "Slider.DisabledTextColor2", GetFgColor() ); - - _sliderBorder = pScheme->GetBorder("ButtonBorder"); - _insetBorder = pScheme->GetBorder("ButtonDepressedBorder"); - - if ( _leftCaption ) - { - _leftCaption->SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional() )); - } - - if ( _rightCaption ) - { - _rightCaption->SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional() )); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Slider::GetSettings(KeyValues *outResourceData) -{ - BaseClass::GetSettings(outResourceData); - - char buf[256]; - if (_leftCaption) - { - _leftCaption->GetUnlocalizedText(buf, sizeof(buf)); - outResourceData->SetString("leftText", buf); - } - - if (_rightCaption) - { - _rightCaption->GetUnlocalizedText(buf, sizeof(buf)); - outResourceData->SetString("rightText", buf); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Slider::ApplySettings(KeyValues *inResourceData) -{ - BaseClass::ApplySettings(inResourceData); - - const char *left = inResourceData->GetString("leftText", NULL); - const char *right = inResourceData->GetString("rightText", NULL); - - int thumbWidth = inResourceData->GetInt("thumbwidth", 0); - if (thumbWidth != 0) - { - SetThumbWidth(thumbWidth); - } - - SetTickCaptions(left, right); - - int nNumTicks = inResourceData->GetInt( "numTicks", -1 ); - if ( nNumTicks >= 0 ) - { - SetNumTicks( nNumTicks ); - } - - int nCurrentRange[2]; - GetRange( nCurrentRange[0], nCurrentRange[1] ); - KeyValues *pRangeMin = inResourceData->FindKey( "rangeMin", false ); - KeyValues *pRangeMax = inResourceData->FindKey( "rangeMax", false ); - bool bDoClamp = false; - if ( pRangeMin ) - { - _range[0] = inResourceData->GetInt( "rangeMin" ); - bDoClamp = true; - } - if ( pRangeMax ) - { - _range[1] = inResourceData->GetInt( "rangeMax" ); - bDoClamp = true; - } - - if ( bDoClamp ) - { - ClampRange(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -const char *Slider::GetDescription() -{ - static char buf[1024]; - Q_snprintf(buf, sizeof(buf), "%s, string leftText, string rightText", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the rectangle to draw the slider track in. -//----------------------------------------------------------------------------- -void Slider::GetTrackRect( int& x, int& y, int& w, int& h ) -{ - int wide, tall; - GetPaintSize( wide, tall ); - - x = 0; - y = 8; - w = wide - (int)_nobSize; - h = 4; -} - -//----------------------------------------------------------------------------- -// Purpose: Draw everything on screen -//----------------------------------------------------------------------------- -void Slider::Paint() -{ - DrawTicks(); - - DrawTickLabels(); - - // Draw nob last so it draws over ticks. - DrawNob(); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw the ticks below the slider. -//----------------------------------------------------------------------------- -void Slider::DrawTicks() -{ - int x, y; - int wide,tall; - GetTrackRect( x, y, wide, tall ); - - // Figure out how to draw the ticks -// GetPaintSize( wide, tall ); - - float fwide = (float)wide; - float freepixels = fwide - _nobSize; - - float leftpixel = _nobSize / 2.0f; - - float pixelspertick = freepixels / ( m_nNumTicks ); - - y += (int)_nobSize; - int tickHeight = 5; - - if (IsEnabled()) - { - surface()->DrawSetColor( m_TickColor ); //vgui::Color( 127, 140, 127, 255 ) ); - for ( int i = 0; i <= m_nNumTicks; i++ ) - { - int xpos = (int)( leftpixel + i * pixelspertick ); - - surface()->DrawFilledRect( xpos, y, xpos + 1, y + tickHeight ); - } - } - else - { - surface()->DrawSetColor( m_DisabledTextColor1 ); //vgui::Color( 127, 140, 127, 255 ) ); - for ( int i = 0; i <= m_nNumTicks; i++ ) - { - int xpos = (int)( leftpixel + i * pixelspertick ); - surface()->DrawFilledRect( xpos+1, y+1, xpos + 2, y + tickHeight + 1 ); - } - surface()->DrawSetColor( m_DisabledTextColor2 ); //vgui::Color( 127, 140, 127, 255 ) ); - for ( int i = 0; i <= m_nNumTicks; i++ ) - { - int xpos = (int)( leftpixel + i * pixelspertick ); - surface()->DrawFilledRect( xpos, y, xpos + 1, y + tickHeight ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Draw Tick labels under the ticks. -//----------------------------------------------------------------------------- -void Slider::DrawTickLabels() -{ - int x, y; - int wide,tall; - GetTrackRect( x, y, wide, tall ); - - // Figure out how to draw the ticks -// GetPaintSize( wide, tall ); - y += (int)NOB_SIZE + 4; - - // Draw Start and end range values - if (IsEnabled()) - surface()->DrawSetTextColor( m_TickColor ); //vgui::Color( 127, 140, 127, 255 ) ); - else - surface()->DrawSetTextColor( m_DisabledTextColor1 ); //vgui::Color( 127, 140, 127, 255 ) ); - - - if ( _leftCaption != NULL ) - { - _leftCaption->SetPos(0, y); - if (IsEnabled()) - { - _leftCaption->SetColor( m_TickColor ); - } - else - { - _leftCaption->SetColor( m_DisabledTextColor1 ); - } - - _leftCaption->Paint(); - } - - if ( _rightCaption != NULL) - { - int rwide, rtall; - _rightCaption->GetSize(rwide, rtall); - _rightCaption->SetPos((int)(wide - rwide) , y); - if (IsEnabled()) - { - _rightCaption->SetColor( m_TickColor ); - } - else - { - _rightCaption->SetColor( m_DisabledTextColor1 ); - } - - _rightCaption->Paint(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Draw the nob part of the slider. -//----------------------------------------------------------------------------- -void Slider::DrawNob() -{ - // horizontal nob - int x, y; - int wide,tall; - GetTrackRect( x, y, wide, tall ); - Color col = GetFgColor(); -#ifdef _X360 - if(HasFocus()) - { - col = m_DepressedBgColor; - } -#endif - surface()->DrawSetColor(col); - - int nobheight = 16; - - surface()->DrawFilledRect( - _nobPos[0], - y + tall / 2 - nobheight / 2, - _nobPos[1], - y + tall / 2 + nobheight / 2); - // border - if (_sliderBorder) - { - _sliderBorder->Paint( - _nobPos[0], - y + tall / 2 - nobheight / 2, - _nobPos[1], - y + tall / 2 + nobheight / 2); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set the text labels of the Start and end ticks. -//----------------------------------------------------------------------------- -void Slider::SetTickCaptions( const char *left, const char *right ) -{ - if (left) - { - if (_leftCaption) - { - _leftCaption->SetText(left); - } - else - { - _leftCaption = new TextImage(left); - } - } - if (right) - { - if (_rightCaption) - { - _rightCaption->SetText(right); - } - else - { - _rightCaption = new TextImage(right); - } - } - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the text labels of the Start and end ticks. -//----------------------------------------------------------------------------- -void Slider::SetTickCaptions( const wchar_t *left, const wchar_t *right ) -{ - if (left) - { - if (_leftCaption) - { - _leftCaption->SetText(left); - } - else - { - _leftCaption = new TextImage(left); - } - } - if (right) - { - if (_rightCaption) - { - _rightCaption->SetText(right); - } - else - { - _rightCaption = new TextImage(right); - } - } - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw the slider track -//----------------------------------------------------------------------------- -void Slider::PaintBackground() -{ - BaseClass::PaintBackground(); - - int x, y; - int wide,tall; - - GetTrackRect( x, y, wide, tall ); - - surface()->DrawSetColor( m_TrackColor ); - surface()->DrawFilledRect( x, y, x + wide, y + tall ); - if (_insetBorder) - { - _insetBorder->Paint( x, y, x + wide, y + tall ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set the range of the slider. -//----------------------------------------------------------------------------- -void Slider::SetRange(int min,int max) -{ - _range[0]=min; - _range[1]=max; - - ClampRange(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sanity check and clamp the range if necessary. -//----------------------------------------------------------------------------- -void Slider::ClampRange() -{ - if ( _range[0] < _range[1] ) - { - if(_value<_range[0]) - { - SetValue( _range[0], false ); - } - else if( _value>_range[1]) - { - SetValue( _range[1], false ); - } - } - else - { - if(_value<_range[1]) - { - SetValue( _range[1], false ); - } - else if( _value>_range[0]) - { - SetValue( _range[0], false ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Get the max and min values of the slider -//----------------------------------------------------------------------------- -void Slider::GetRange(int& min,int& max) -{ - min=_range[0]; - max=_range[1]; -} - -//----------------------------------------------------------------------------- -// Purpose: Respond when the cursor is moved in our window if we are clicking -// and dragging. -//----------------------------------------------------------------------------- -void Slider::OnCursorMoved(int x,int y) -{ - if(!_dragging) - { - return; - } - -// input()->GetCursorPos(x,y); - input()->GetCursorPosition( x, y ); - ScreenToLocal(x,y); - -// int wide,tall; -// GetPaintSize(wide,tall); - int _x, _y, wide, tall; - GetTrackRect( _x, _y, wide, tall ); - - _nobPos[0]=_nobDragStartPos[0]+(x-_dragStartPos[0]); - _nobPos[1]=_nobDragStartPos[1]+(x-_dragStartPos[0]); - - int rightEdge = _x +wide; - int unclamped = _nobPos[ 0 ]; - - if(_nobPos[1]>rightEdge) - { - _nobPos[0]=rightEdge-(_nobPos[1]-_nobPos[0]); - _nobPos[1]=rightEdge; - } - - if(_nobPos[0]<_x) - { - int offset = _x - _nobPos[0]; - _nobPos[1]=_nobPos[1]-offset; - _nobPos[0]=0; - } - - int value = EstimateValueAtPos( unclamped, 0 ); - SetValue( value ); - - // RecomputeValueFromNobPos(); - Repaint(); - SendSliderMovedMessage(); -} - -//----------------------------------------------------------------------------- -// Purpose: If you click on the slider outside of the nob, the nob jumps -// to the click position, and if this setting is enabled, the nob -// is then draggable from the new position until the mouse is released -// Input : state - -//----------------------------------------------------------------------------- -void Slider::SetDragOnRepositionNob( bool state ) -{ - m_bIsDragOnRepositionNob = state; -} - -bool Slider::IsDragOnRepositionNob() const -{ - return m_bIsDragOnRepositionNob; -} - -bool Slider::IsDragged( void ) const -{ - return _dragging; -} - -//----------------------------------------------------------------------------- -// Purpose: Respond to mouse presses. Trigger Record staring positon. -//----------------------------------------------------------------------------- -void Slider::OnMousePressed(MouseCode code) -{ - int x,y; - - if (!IsEnabled()) - return; - -// input()->GetCursorPos(x,y); - input()->GetCursorPosition( x, y ); - - ScreenToLocal(x,y); - RequestFocus(); - - bool startdragging = false, bPostDragStartSignal = false; - - if ((x >= _nobPos[0]) && (x < _nobPos[1])) - { - startdragging = true; - bPostDragStartSignal = true; - } - else - { - // we clicked elsewhere on the slider; move the nob to that position - int min, max; - GetRange(min, max); - if ( m_bUseSubRange ) - { - min = _subrange[ 0 ]; - max = _subrange[ 1 ]; - } - -// int wide = GetWide(); - int _x, _y, wide, tall; - GetTrackRect( _x, _y, wide, tall ); - if ( wide > 0 ) - { - float frange = ( float )( max - min ); - float clickFrac = clamp( ( float )( x - _x ) / (float)( wide - 1 ), 0.0f, 1.0f ); - - float value = (float)min + clickFrac * frange; - - startdragging = IsDragOnRepositionNob(); - - if ( startdragging ) - { - _dragging = true; // Required when as - SendSliderDragStartMessage(); - } - - SetValue( ( int )( value + 0.5f ) ); - } - } - - if ( startdragging ) - { - // drag the nob - _dragging = true; - input()->SetMouseCapture(GetVPanel()); - _nobDragStartPos[0] = _nobPos[0]; - _nobDragStartPos[1] = _nobPos[1]; - _dragStartPos[0] = x; - _dragStartPos[1] = y; - } - - if ( bPostDragStartSignal ) - SendSliderDragStartMessage(); -} - -//----------------------------------------------------------------------------- -// Purpose: Just handle double presses like mouse presses -//----------------------------------------------------------------------------- -void Slider::OnMouseDoublePressed(MouseCode code) -{ - OnMousePressed(code); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -#ifdef _X360 -void Slider::OnKeyCodePressed(KeyCode code) -{ - switch ( GetBaseButtonCode( code ) ) - { - case KEY_XBUTTON_LEFT: - case KEY_XSTICK1_LEFT: - case KEY_XSTICK2_LEFT: - SetValue(GetValue() - 1); - break; - case KEY_XBUTTON_RIGHT: - case KEY_XSTICK1_RIGHT: - case KEY_XSTICK2_RIGHT: - SetValue(GetValue() + 1); - break; - default: - BaseClass::OnKeyCodePressed(code); - break; - } -} -#endif - -//----------------------------------------------------------------------------- -// Purpose: Handle key presses -//----------------------------------------------------------------------------- -void Slider::OnKeyCodeTyped(KeyCode code) -{ - switch (code) - { - // for now left and right arrows just open or close submenus if they are there. - case KEY_LEFT: - case KEY_DOWN: - { - int val = GetValue(); - SetValue(val-1); - break; - } - case KEY_RIGHT: - case KEY_UP: - { - int val = GetValue(); - SetValue(val+1); - break; - } - case KEY_PAGEDOWN: - { - int min, max; - GetRange(min, max); - float range = (float) max-min; - float pertick = range/m_nNumTicks; - int val = GetValue(); - SetValue(val - (int) pertick); - break; - } - case KEY_PAGEUP: - { - int min, max; - GetRange(min, max); - float range = (float) max-min; - float pertick = range/m_nNumTicks; - int val = GetValue(); - SetValue(val + (int) pertick); - break; - } - case KEY_HOME: - { - int min, max; - GetRange(min, max); - SetValue(min); - break; - } - case KEY_END: - { - int min, max; - GetRange(min, max); - SetValue(max); - break; - } - default: - BaseClass::OnKeyCodeTyped(code); - break; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Stop dragging when the mouse is released. -//----------------------------------------------------------------------------- -void Slider::OnMouseReleased(MouseCode code) -{ - if ( _dragging ) - { - _dragging=false; - input()->SetMouseCapture(null); - } - - if ( IsEnabled() ) - { - SendSliderDragEndMessage(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Get the nob's position (the ends of each side of the nob) -//----------------------------------------------------------------------------- -void Slider::GetNobPos(int& min, int& max) -{ - min=_nobPos[0]; - max=_nobPos[1]; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Slider::SetButtonOffset(int buttonOffset) -{ - _buttonOffset=buttonOffset; -} - -void Slider::SetThumbWidth( int width ) -{ - _nobSize = (float)width; -} - - -//----------------------------------------------------------------------------- -// Purpose: Set the number of ticks that appear under the slider. -//----------------------------------------------------------------------------- -void Slider::SetNumTicks( int ticks ) -{ - m_nNumTicks = ticks; -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#define PROTECTED_THINGS_DISABLE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY( Slider ); + +static const float NOB_SIZE = 8.0f; + +//----------------------------------------------------------------------------- +// Purpose: Create a slider bar with ticks underneath it +//----------------------------------------------------------------------------- +Slider::Slider(Panel *parent, const char *panelName ) : BaseClass(parent, panelName) +{ + m_bIsDragOnRepositionNob = false; + _dragging = false; + _value = 0; + _range[0] = 0; + _range[1] = 0; + _buttonOffset = 0; + _sliderBorder = NULL; + _insetBorder = NULL; + m_nNumTicks = 10; + _leftCaption = NULL; + _rightCaption = NULL; + + _subrange[ 0 ] = 0; + _subrange[ 1 ] = 0; + m_bUseSubRange = false; + m_bInverted = false; + + SetThumbWidth( 8 ); + RecomputeNobPosFromValue(); + AddActionSignalTarget(this); + SetBlockDragChaining( true ); +} + +// This allows the slider to behave like it's larger than what's actually being drawn +//----------------------------------------------------------------------------- +// Purpose: +// Input : bEnable - +// 0 - +// 100 - +//----------------------------------------------------------------------------- +void Slider::SetSliderThumbSubRange( bool bEnable, int nMin /*= 0*/, int nMax /*= 100*/ ) +{ + m_bUseSubRange = bEnable; + _subrange[ 0 ] = nMin; + _subrange[ 1 ] = nMax; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the size of the slider bar. +// Warning less than 30 pixels tall and everything probably won't fit. +//----------------------------------------------------------------------------- +void Slider::OnSizeChanged(int wide,int tall) +{ + BaseClass::OnSizeChanged(wide,tall); + + RecomputeNobPosFromValue(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the value of the slider to one of the ticks. +//----------------------------------------------------------------------------- +void Slider::SetValue(int value, bool bTriggerChangeMessage) +{ + int oldValue=_value; + + if ( _range[0] < _range[1] ) + { + if(value<_range[0]) + { + value=_range[0]; + } + if(value>_range[1]) + { + value=_range[1]; + } + } + else + { + if(value<_range[1]) + { + value=_range[1]; + } + if(value>_range[0]) + { + value=_range[0]; + } + } + + _value = value; + + RecomputeNobPosFromValue(); + + if (_value != oldValue && bTriggerChangeMessage) + { + SendSliderMovedMessage(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Return the value of the slider +//----------------------------------------------------------------------------- +int Slider::GetValue() +{ + return _value; +} + +//----------------------------------------------------------------------------- +// Purpose: Layout the slider before drawing it on screen. +//----------------------------------------------------------------------------- +void Slider::PerformLayout() +{ + BaseClass::PerformLayout(); + RecomputeNobPosFromValue(); + + if (_leftCaption) + { + _leftCaption->ResizeImageToContent(); + } + if (_rightCaption) + { + _rightCaption->ResizeImageToContent(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Move the nob on the slider in response to changing its value. +//----------------------------------------------------------------------------- +void Slider::RecomputeNobPosFromValue() +{ + //int wide,tall; + //GetPaintSize(wide,tall); + int x, y, wide, tall; + GetTrackRect( x, y, wide, tall ); + + float usevalue = _value; + int *userange = &_range[ 0 ]; + if ( m_bUseSubRange ) + { + userange = &_subrange[ 0 ]; + usevalue = clamp( _value, _subrange[ 0 ], _subrange[ 1 ] ); + } + + float fwide=(float)wide; + float frange=(float)(userange[1] -userange[0]); + float fvalue=(float)(usevalue -userange[0]); + float fper = (frange != 0.0f) ? fvalue / frange : 0.0f; + + if ( m_bInverted ) + fper = 1.0f - fper; + + float freepixels = fwide - _nobSize; + float leftpixel = (float)x; + float firstpixel = leftpixel + freepixels * fper + 0.5f; + + _nobPos[0]=(int)( firstpixel ); + _nobPos[1]=(int)( firstpixel + _nobSize ); + + + int rightEdge = x + wide; + + if(_nobPos[1]> rightEdge ) + { + _nobPos[0]=rightEdge-((int)_nobSize); + _nobPos[1]=rightEdge; + } + + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sync the slider's value up with the nob's position. +//----------------------------------------------------------------------------- +void Slider::RecomputeValueFromNobPos() +{ + int value = EstimateValueAtPos( _nobPos[ 0 ], 0 ); + SetValue( value ); +} + +int Slider::EstimateValueAtPos( int localMouseX, int /*localMouseY*/ ) +{ + int x, y, wide, tall; + GetTrackRect( x, y, wide, tall ); + + int *userange = &_range[ 0 ]; + if ( m_bUseSubRange ) + { + userange = &_subrange[ 0 ]; + } + + float fwide = (float)wide; + float fvalue = (float)( _value - userange[0] ); + float fnob = (float)( localMouseX - x ); + float freepixels = fwide - _nobSize; + + // Map into reduced range + fvalue = freepixels != 0.0f ? fnob / freepixels : 0.0f; + + return (int) (RemapVal( fvalue, 0.0, 1.0, userange[0], userange[1] )); +} + +void Slider::SetInverted( bool bInverted ) +{ + m_bInverted = bInverted; +} + + +//----------------------------------------------------------------------------- +// Purpose: Send a message to interested parties when the slider moves +//----------------------------------------------------------------------------- +void Slider::SendSliderMovedMessage() +{ + // send a changed message + KeyValues *pParams = new KeyValues("SliderMoved", "position", _value); + pParams->SetPtr( "panel", this ); + PostActionSignal( pParams ); +} + +//----------------------------------------------------------------------------- +// Purpose: Send a message to interested parties when the user begins dragging the slider +//----------------------------------------------------------------------------- +void Slider::SendSliderDragStartMessage() +{ + // send a message + KeyValues *pParams = new KeyValues("SliderDragStart", "position", _value); + pParams->SetPtr( "panel", this ); + PostActionSignal( pParams ); +} + +//----------------------------------------------------------------------------- +// Purpose: Send a message to interested parties when the user ends dragging the slider +//----------------------------------------------------------------------------- +void Slider::SendSliderDragEndMessage() +{ + // send a message + KeyValues *pParams = new KeyValues("SliderDragEnd", "position", _value); + pParams->SetPtr( "panel", this ); + PostActionSignal( pParams ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Slider::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetFgColor(GetSchemeColor("Slider.NobColor", pScheme)); + // this line is useful for debugging + //SetBgColor(GetSchemeColor("0 0 0 255")); + + m_TickColor = pScheme->GetColor( "Slider.TextColor", GetFgColor() ); + m_TrackColor = pScheme->GetColor( "Slider.TrackColor", GetFgColor() ); + +#ifdef _X360 + m_DepressedBgColor = GetSchemeColor("Slider.NobFocusColor", pScheme); +#endif + + m_DisabledTextColor1 = pScheme->GetColor( "Slider.DisabledTextColor1", GetFgColor() ); + m_DisabledTextColor2 = pScheme->GetColor( "Slider.DisabledTextColor2", GetFgColor() ); + + _sliderBorder = pScheme->GetBorder("ButtonBorder"); + _insetBorder = pScheme->GetBorder("ButtonDepressedBorder"); + + if ( _leftCaption ) + { + _leftCaption->SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional() )); + } + + if ( _rightCaption ) + { + _rightCaption->SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional() )); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Slider::GetSettings(KeyValues *outResourceData) +{ + BaseClass::GetSettings(outResourceData); + + char buf[256]; + if (_leftCaption) + { + _leftCaption->GetUnlocalizedText(buf, sizeof(buf)); + outResourceData->SetString("leftText", buf); + } + + if (_rightCaption) + { + _rightCaption->GetUnlocalizedText(buf, sizeof(buf)); + outResourceData->SetString("rightText", buf); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Slider::ApplySettings(KeyValues *inResourceData) +{ + BaseClass::ApplySettings(inResourceData); + + const char *left = inResourceData->GetString("leftText", NULL); + const char *right = inResourceData->GetString("rightText", NULL); + + int thumbWidth = inResourceData->GetInt("thumbwidth", 0); + if (thumbWidth != 0) + { + SetThumbWidth(thumbWidth); + } + + SetTickCaptions(left, right); + + int nNumTicks = inResourceData->GetInt( "numTicks", -1 ); + if ( nNumTicks >= 0 ) + { + SetNumTicks( nNumTicks ); + } + + int nCurrentRange[2]; + GetRange( nCurrentRange[0], nCurrentRange[1] ); + KeyValues *pRangeMin = inResourceData->FindKey( "rangeMin", false ); + KeyValues *pRangeMax = inResourceData->FindKey( "rangeMax", false ); + bool bDoClamp = false; + if ( pRangeMin ) + { + _range[0] = inResourceData->GetInt( "rangeMin" ); + bDoClamp = true; + } + if ( pRangeMax ) + { + _range[1] = inResourceData->GetInt( "rangeMax" ); + bDoClamp = true; + } + + if ( bDoClamp ) + { + ClampRange(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *Slider::GetDescription() +{ + static char buf[1024]; + Q_snprintf(buf, sizeof(buf), "%s, string leftText, string rightText", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the rectangle to draw the slider track in. +//----------------------------------------------------------------------------- +void Slider::GetTrackRect( int& x, int& y, int& w, int& h ) +{ + int wide, tall; + GetPaintSize( wide, tall ); + + x = 0; + y = 8; + w = wide - (int)_nobSize; + h = 4; +} + +//----------------------------------------------------------------------------- +// Purpose: Draw everything on screen +//----------------------------------------------------------------------------- +void Slider::Paint() +{ + DrawTicks(); + + DrawTickLabels(); + + // Draw nob last so it draws over ticks. + DrawNob(); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw the ticks below the slider. +//----------------------------------------------------------------------------- +void Slider::DrawTicks() +{ + int x, y; + int wide,tall; + GetTrackRect( x, y, wide, tall ); + + // Figure out how to draw the ticks +// GetPaintSize( wide, tall ); + + float fwide = (float)wide; + float freepixels = fwide - _nobSize; + + float leftpixel = _nobSize / 2.0f; + + float pixelspertick = freepixels / ( m_nNumTicks ); + + y += (int)_nobSize; + int tickHeight = 5; + + if (IsEnabled()) + { + surface()->DrawSetColor( m_TickColor ); //vgui::Color( 127, 140, 127, 255 ) ); + for ( int i = 0; i <= m_nNumTicks; i++ ) + { + int xpos = (int)( leftpixel + i * pixelspertick ); + + surface()->DrawFilledRect( xpos, y, xpos + 1, y + tickHeight ); + } + } + else + { + surface()->DrawSetColor( m_DisabledTextColor1 ); //vgui::Color( 127, 140, 127, 255 ) ); + for ( int i = 0; i <= m_nNumTicks; i++ ) + { + int xpos = (int)( leftpixel + i * pixelspertick ); + surface()->DrawFilledRect( xpos+1, y+1, xpos + 2, y + tickHeight + 1 ); + } + surface()->DrawSetColor( m_DisabledTextColor2 ); //vgui::Color( 127, 140, 127, 255 ) ); + for ( int i = 0; i <= m_nNumTicks; i++ ) + { + int xpos = (int)( leftpixel + i * pixelspertick ); + surface()->DrawFilledRect( xpos, y, xpos + 1, y + tickHeight ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draw Tick labels under the ticks. +//----------------------------------------------------------------------------- +void Slider::DrawTickLabels() +{ + int x, y; + int wide,tall; + GetTrackRect( x, y, wide, tall ); + + // Figure out how to draw the ticks +// GetPaintSize( wide, tall ); + y += (int)NOB_SIZE + 4; + + // Draw Start and end range values + if (IsEnabled()) + surface()->DrawSetTextColor( m_TickColor ); //vgui::Color( 127, 140, 127, 255 ) ); + else + surface()->DrawSetTextColor( m_DisabledTextColor1 ); //vgui::Color( 127, 140, 127, 255 ) ); + + + if ( _leftCaption != NULL ) + { + _leftCaption->SetPos(0, y); + if (IsEnabled()) + { + _leftCaption->SetColor( m_TickColor ); + } + else + { + _leftCaption->SetColor( m_DisabledTextColor1 ); + } + + _leftCaption->Paint(); + } + + if ( _rightCaption != NULL) + { + int rwide, rtall; + _rightCaption->GetSize(rwide, rtall); + _rightCaption->SetPos((int)(wide - rwide) , y); + if (IsEnabled()) + { + _rightCaption->SetColor( m_TickColor ); + } + else + { + _rightCaption->SetColor( m_DisabledTextColor1 ); + } + + _rightCaption->Paint(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draw the nob part of the slider. +//----------------------------------------------------------------------------- +void Slider::DrawNob() +{ + // horizontal nob + int x, y; + int wide,tall; + GetTrackRect( x, y, wide, tall ); + Color col = GetFgColor(); +#ifdef _X360 + if(HasFocus()) + { + col = m_DepressedBgColor; + } +#endif + surface()->DrawSetColor(col); + + int nobheight = 16; + + surface()->DrawFilledRect( + _nobPos[0], + y + tall / 2 - nobheight / 2, + _nobPos[1], + y + tall / 2 + nobheight / 2); + // border + if (_sliderBorder) + { + _sliderBorder->Paint( + _nobPos[0], + y + tall / 2 - nobheight / 2, + _nobPos[1], + y + tall / 2 + nobheight / 2); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the text labels of the Start and end ticks. +//----------------------------------------------------------------------------- +void Slider::SetTickCaptions( const char *left, const char *right ) +{ + if (left) + { + if (_leftCaption) + { + _leftCaption->SetText(left); + } + else + { + _leftCaption = new TextImage(left); + } + } + if (right) + { + if (_rightCaption) + { + _rightCaption->SetText(right); + } + else + { + _rightCaption = new TextImage(right); + } + } + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the text labels of the Start and end ticks. +//----------------------------------------------------------------------------- +void Slider::SetTickCaptions( const wchar_t *left, const wchar_t *right ) +{ + if (left) + { + if (_leftCaption) + { + _leftCaption->SetText(left); + } + else + { + _leftCaption = new TextImage(left); + } + } + if (right) + { + if (_rightCaption) + { + _rightCaption->SetText(right); + } + else + { + _rightCaption = new TextImage(right); + } + } + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw the slider track +//----------------------------------------------------------------------------- +void Slider::PaintBackground() +{ + BaseClass::PaintBackground(); + + int x, y; + int wide,tall; + + GetTrackRect( x, y, wide, tall ); + + surface()->DrawSetColor( m_TrackColor ); + surface()->DrawFilledRect( x, y, x + wide, y + tall ); + if (_insetBorder) + { + _insetBorder->Paint( x, y, x + wide, y + tall ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the range of the slider. +//----------------------------------------------------------------------------- +void Slider::SetRange(int min,int max) +{ + _range[0]=min; + _range[1]=max; + + ClampRange(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sanity check and clamp the range if necessary. +//----------------------------------------------------------------------------- +void Slider::ClampRange() +{ + if ( _range[0] < _range[1] ) + { + if(_value<_range[0]) + { + SetValue( _range[0], false ); + } + else if( _value>_range[1]) + { + SetValue( _range[1], false ); + } + } + else + { + if(_value<_range[1]) + { + SetValue( _range[1], false ); + } + else if( _value>_range[0]) + { + SetValue( _range[0], false ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get the max and min values of the slider +//----------------------------------------------------------------------------- +void Slider::GetRange(int& min,int& max) +{ + min=_range[0]; + max=_range[1]; +} + +//----------------------------------------------------------------------------- +// Purpose: Respond when the cursor is moved in our window if we are clicking +// and dragging. +//----------------------------------------------------------------------------- +void Slider::OnCursorMoved(int x,int y) +{ + if(!_dragging) + { + return; + } + +// input()->GetCursorPos(x,y); + input()->GetCursorPosition( x, y ); + ScreenToLocal(x,y); + +// int wide,tall; +// GetPaintSize(wide,tall); + int _x, _y, wide, tall; + GetTrackRect( _x, _y, wide, tall ); + + _nobPos[0]=_nobDragStartPos[0]+(x-_dragStartPos[0]); + _nobPos[1]=_nobDragStartPos[1]+(x-_dragStartPos[0]); + + int rightEdge = _x +wide; + int unclamped = _nobPos[ 0 ]; + + if(_nobPos[1]>rightEdge) + { + _nobPos[0]=rightEdge-(_nobPos[1]-_nobPos[0]); + _nobPos[1]=rightEdge; + } + + if(_nobPos[0]<_x) + { + int offset = _x - _nobPos[0]; + _nobPos[1]=_nobPos[1]-offset; + _nobPos[0]=0; + } + + int value = EstimateValueAtPos( unclamped, 0 ); + SetValue( value ); + + // RecomputeValueFromNobPos(); + Repaint(); + SendSliderMovedMessage(); +} + +//----------------------------------------------------------------------------- +// Purpose: If you click on the slider outside of the nob, the nob jumps +// to the click position, and if this setting is enabled, the nob +// is then draggable from the new position until the mouse is released +// Input : state - +//----------------------------------------------------------------------------- +void Slider::SetDragOnRepositionNob( bool state ) +{ + m_bIsDragOnRepositionNob = state; +} + +bool Slider::IsDragOnRepositionNob() const +{ + return m_bIsDragOnRepositionNob; +} + +bool Slider::IsDragged( void ) const +{ + return _dragging; +} + +//----------------------------------------------------------------------------- +// Purpose: Respond to mouse presses. Trigger Record staring positon. +//----------------------------------------------------------------------------- +void Slider::OnMousePressed(MouseCode code) +{ + int x,y; + + if (!IsEnabled()) + return; + +// input()->GetCursorPos(x,y); + input()->GetCursorPosition( x, y ); + + ScreenToLocal(x,y); + RequestFocus(); + + bool startdragging = false, bPostDragStartSignal = false; + + if ((x >= _nobPos[0]) && (x < _nobPos[1])) + { + startdragging = true; + bPostDragStartSignal = true; + } + else + { + // we clicked elsewhere on the slider; move the nob to that position + int min, max; + GetRange(min, max); + if ( m_bUseSubRange ) + { + min = _subrange[ 0 ]; + max = _subrange[ 1 ]; + } + +// int wide = GetWide(); + int _x, _y, wide, tall; + GetTrackRect( _x, _y, wide, tall ); + if ( wide > 0 ) + { + float frange = ( float )( max - min ); + float clickFrac = clamp( ( float )( x - _x ) / (float)( wide - 1 ), 0.0f, 1.0f ); + + float value = (float)min + clickFrac * frange; + + startdragging = IsDragOnRepositionNob(); + + if ( startdragging ) + { + _dragging = true; // Required when as + SendSliderDragStartMessage(); + } + + SetValue( ( int )( value + 0.5f ) ); + } + } + + if ( startdragging ) + { + // drag the nob + _dragging = true; + input()->SetMouseCapture(GetVPanel()); + _nobDragStartPos[0] = _nobPos[0]; + _nobDragStartPos[1] = _nobPos[1]; + _dragStartPos[0] = x; + _dragStartPos[1] = y; + } + + if ( bPostDragStartSignal ) + SendSliderDragStartMessage(); +} + +//----------------------------------------------------------------------------- +// Purpose: Just handle double presses like mouse presses +//----------------------------------------------------------------------------- +void Slider::OnMouseDoublePressed(MouseCode code) +{ + OnMousePressed(code); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#ifdef _X360 +void Slider::OnKeyCodePressed(KeyCode code) +{ + switch ( GetBaseButtonCode( code ) ) + { + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case KEY_XSTICK2_LEFT: + SetValue(GetValue() - 1); + break; + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + case KEY_XSTICK2_RIGHT: + SetValue(GetValue() + 1); + break; + default: + BaseClass::OnKeyCodePressed(code); + break; + } +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Handle key presses +//----------------------------------------------------------------------------- +void Slider::OnKeyCodeTyped(KeyCode code) +{ + switch (code) + { + // for now left and right arrows just open or close submenus if they are there. + case KEY_LEFT: + case KEY_DOWN: + { + int val = GetValue(); + SetValue(val-1); + break; + } + case KEY_RIGHT: + case KEY_UP: + { + int val = GetValue(); + SetValue(val+1); + break; + } + case KEY_PAGEDOWN: + { + int min, max; + GetRange(min, max); + float range = (float) max-min; + float pertick = range/m_nNumTicks; + int val = GetValue(); + SetValue(val - (int) pertick); + break; + } + case KEY_PAGEUP: + { + int min, max; + GetRange(min, max); + float range = (float) max-min; + float pertick = range/m_nNumTicks; + int val = GetValue(); + SetValue(val + (int) pertick); + break; + } + case KEY_HOME: + { + int min, max; + GetRange(min, max); + SetValue(min); + break; + } + case KEY_END: + { + int min, max; + GetRange(min, max); + SetValue(max); + break; + } + default: + BaseClass::OnKeyCodeTyped(code); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Stop dragging when the mouse is released. +//----------------------------------------------------------------------------- +void Slider::OnMouseReleased(MouseCode code) +{ + if ( _dragging ) + { + _dragging=false; + input()->SetMouseCapture(null); + } + + if ( IsEnabled() ) + { + SendSliderDragEndMessage(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get the nob's position (the ends of each side of the nob) +//----------------------------------------------------------------------------- +void Slider::GetNobPos(int& min, int& max) +{ + min=_nobPos[0]; + max=_nobPos[1]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Slider::SetButtonOffset(int buttonOffset) +{ + _buttonOffset=buttonOffset; +} + +void Slider::SetThumbWidth( int width ) +{ + _nobSize = (float)width; +} + + +//----------------------------------------------------------------------------- +// Purpose: Set the number of ticks that appear under the slider. +//----------------------------------------------------------------------------- +void Slider::SetNumTicks( int ticks ) +{ + m_nNumTicks = ticks; +} diff --git a/mp/src/vgui2/vgui_controls/Splitter.cpp b/mp/src/vgui2/vgui_controls/Splitter.cpp index fc127a38..96bde986 100644 --- a/mp/src/vgui2/vgui_controls/Splitter.cpp +++ b/mp/src/vgui2/vgui_controls/Splitter.cpp @@ -1,765 +1,765 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//===========================================================================// - -#include -#include -#include -#include -#include "tier1/KeyValues.h" -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -using namespace vgui; - - -enum -{ - SPLITTER_HANDLE_WIDTH = 4 -}; - - -//----------------------------------------------------------------------------- -// Splitter handle -//----------------------------------------------------------------------------- -namespace vgui -{ - -class SplitterHandle : public Panel -{ - DECLARE_CLASS_SIMPLE( SplitterHandle, Panel ); - -public: - SplitterHandle( Splitter *parent, const char *name, SplitterMode_t mode, int nIndex ); - ~SplitterHandle(); - - virtual void ApplySchemeSettings( IScheme *pScheme ); - virtual void OnMousePressed( MouseCode code ); - virtual void OnMouseReleased( MouseCode code ); - virtual void OnCursorMoved( int x, int y ); - virtual void OnMouseDoublePressed( MouseCode code ); - -private: - SplitterMode_t m_nMode; - int m_nIndex; - bool m_bDragging; -}; - -} // end namespace vgui - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -SplitterHandle::SplitterHandle( Splitter *parent, const char *name, SplitterMode_t mode, int nIndex ) : BaseClass( parent, name ) -{ - int w, h; - parent->GetSize( w, h ); - - if ( mode == SPLITTER_MODE_HORIZONTAL ) - { - SetSize( w, SPLITTER_HANDLE_WIDTH ); - SetCursor( dc_sizens ); - } - else - { - SetSize( SPLITTER_HANDLE_WIDTH, h ); - SetCursor( dc_sizewe ); - } - - SetVisible( true ); - SetPaintBackgroundEnabled( false ); - SetPaintEnabled( false ); - SetPaintBorderEnabled( true ); - m_bDragging = false; - m_nIndex = nIndex; - m_nMode = mode; -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -SplitterHandle::~SplitterHandle() -{ -} - - -//----------------------------------------------------------------------------- -// Scheme settings -//----------------------------------------------------------------------------- -void SplitterHandle::ApplySchemeSettings(IScheme *pScheme) -{ - // Cache off background color stored in SetSplitterColor - Color c = GetBgColor(); - SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); - BaseClass::ApplySchemeSettings(pScheme); - SetBgColor( c ); -} - - -//----------------------------------------------------------------------------- -// Capture mouse when dragging -//----------------------------------------------------------------------------- -void SplitterHandle::OnMousePressed(MouseCode code) -{ - if ( !m_bDragging ) - { - input()->SetMouseCapture(GetVPanel()); - m_bDragging = true; - } -} - - -//----------------------------------------------------------------------------- -// Release mouse capture when finished dragging -//----------------------------------------------------------------------------- -void SplitterHandle::OnMouseReleased(MouseCode code) -{ - if ( m_bDragging ) - { - input()->SetMouseCapture(NULL); - m_bDragging = false; - } -} - - -//----------------------------------------------------------------------------- -// While dragging, update the splitter position -//----------------------------------------------------------------------------- -void SplitterHandle::OnCursorMoved(int x, int y) -{ - if (m_bDragging) - { - input()->GetCursorPos( x, y ); - Splitter *pSplitter = assert_cast( GetParent() ); - pSplitter->ScreenToLocal( x,y ); - pSplitter->SetSplitterPosition( m_nIndex, (m_nMode == SPLITTER_MODE_HORIZONTAL) ? y : x ); - } -} - - -//----------------------------------------------------------------------------- -// Double-click: make both panels on either side of the splitter equal size -//----------------------------------------------------------------------------- -void SplitterHandle::OnMouseDoublePressed( MouseCode code ) -{ - Splitter *pSplitter = assert_cast( GetParent() ); - pSplitter->EvenlyRespaceSplitters(); -} - - - -//----------------------------------------------------------------------------- -// Returns a panel that chains user configs -//----------------------------------------------------------------------------- -namespace vgui -{ - -class SplitterChildPanel : public EditablePanel -{ - DECLARE_CLASS_SIMPLE( SplitterChildPanel, EditablePanel ); - -public: - SplitterChildPanel( Panel *parent, const char *panelName ) : BaseClass( parent, panelName ) - { - SetPaintBackgroundEnabled( false ); - SetPaintEnabled( false ); - SetPaintBorderEnabled( false ); - } - - virtual ~SplitterChildPanel() {} - - // Children may have user config settings - bool HasUserConfigSettings() - { - return true; - } -}; - -} // end namespace vgui - -//----------------------------------------------------------------------------- -// -// Splitter panel -// -//----------------------------------------------------------------------------- -vgui::Panel *Splitter_V_Factory() -{ - return new Splitter( NULL, NULL, SPLITTER_MODE_VERTICAL, 1 ); -} - -vgui::Panel *Splitter_H_Factory() -{ - return new Splitter( NULL, NULL, SPLITTER_MODE_HORIZONTAL, 1 ); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -Splitter::Splitter( Panel *parent, const char *name, SplitterMode_t mode, int nCount ) : BaseClass( parent, name ) -{ - Assert( nCount >= 1 ); - m_Mode = mode; - - SetPaintBackgroundEnabled( false ); - SetPaintEnabled( false ); - SetPaintBorderEnabled( false ); - - RecreateSplitters( nCount ); - - EvenlyRespaceSplitters(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -Splitter::~Splitter() -{ - m_Splitters.RemoveAll(); -} - -void Splitter::RecreateSplitters( int nCount ) -{ - int i; - int c = m_Splitters.Count(); - for ( i = 0; i < c; ++i ) - { - delete m_Splitters[ i ].m_pPanel; - delete m_Splitters[ i ].m_pHandle; - } - m_Splitters.RemoveAll(); - - for ( i = 0; i < (nCount + 1); ++i ) - { - char pBuffer[512]; - Q_snprintf( pBuffer, sizeof(pBuffer), "child%d", i ); - - int nIndex = m_Splitters.AddToTail( ); - SplitterChildPanel *pEditablePanel = new SplitterChildPanel( this, pBuffer ); - m_Splitters[nIndex].m_pPanel = pEditablePanel; - m_Splitters[nIndex].m_bLocked = false; - m_Splitters[nIndex].m_nLockedSize = 0; - } - - // We do this in 2 loops so that the first N children are actual child panels - for ( i = 0; i < nCount; ++i ) - { - SplitterHandle *pHandle = new SplitterHandle( this, "SplitterHandle", m_Mode, i ); - m_Splitters[i].m_pHandle = pHandle; - pHandle->MoveToFront(); - } - m_Splitters[nCount].m_pHandle = NULL; -} - - -//----------------------------------------------------------------------------- -// Sets the splitter color -//----------------------------------------------------------------------------- -void Splitter::SetSplitterColor( Color c ) -{ - int nCount = m_Splitters.Count() - 1; - if ( c.a() != 0 ) - { - for ( int i = 0; i < nCount; ++i ) - { - m_Splitters[i].m_pHandle->SetBgColor( c ); - m_Splitters[i].m_pHandle->SetPaintBackgroundEnabled( true ); - } - } - else - { - for ( int i = 0; i < nCount; ++i ) - { - m_Splitters[i].m_pHandle->SetPaintBackgroundEnabled( false ); - } - } -} - - -//----------------------------------------------------------------------------- -// Enables borders on the splitters -//----------------------------------------------------------------------------- -void Splitter::EnableBorders( bool bEnable ) -{ - int nCount = m_Splitters.Count() - 1; - for ( int i = 0; i < nCount; ++i ) - { - m_Splitters[i].m_pHandle->SetPaintBorderEnabled( bEnable ); - } -} - - -//----------------------------------------------------------------------------- -// controls splitters -//----------------------------------------------------------------------------- -int Splitter::GetSplitterCount() const -{ - return m_Splitters.Count() - 1; -} - - -//----------------------------------------------------------------------------- -// controls splitters -//----------------------------------------------------------------------------- -int Splitter::GetSubPanelCount() const -{ - return m_Splitters.Count(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Applies resouce settings -//----------------------------------------------------------------------------- -void Splitter::ApplySettings(KeyValues *inResourceData) -{ - BaseClass::ApplySettings(inResourceData); - - // Look for splitter positions - int nSplitterCount = GetSplitterCount(); - for ( int i = 0; i < nSplitterCount; ++i ) - { - char pBuffer[512]; - Q_snprintf( pBuffer, sizeof(pBuffer), "splitter%d", i ); - - int nSplitterPos = inResourceData->GetInt( pBuffer , -1 ); - if ( nSplitterPos >= 0 ) - { - SetSplitterPosition( i, nSplitterPos ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int Splitter::GetPosRange() -{ - int w, h; - GetSize( w, h ); - int nPosRange = (m_Mode == SPLITTER_MODE_HORIZONTAL) ? h : w; - return nPosRange; -} - - -//----------------------------------------------------------------------------- -// Locks the size of a particular child in pixels. -//----------------------------------------------------------------------------- -void Splitter::LockChildSize( int nChildIndex, int nSize ) -{ - Assert( nChildIndex < m_Splitters.Count() ); - SplitterInfo_t &info = m_Splitters[nChildIndex]; - nSize += SPLITTER_HANDLE_WIDTH; - if ( !info.m_bLocked || (info.m_nLockedSize != nSize) ) - { - float flPrevPos = (nChildIndex > 0) ? m_Splitters[nChildIndex-1].m_flPos : 0.0f; - float flOldSize = info.m_flPos - flPrevPos; - float flDelta = nSize - flOldSize; - int nCount = m_Splitters.Count(); - for ( int i = nChildIndex; i < nCount-1; ++i ) - { - m_Splitters[i].m_flPos += flDelta; - } - m_Splitters[nCount-1].m_flPos = GetPosRange(); - - info.m_bLocked = true; - info.m_nLockedSize = nSize; - InvalidateLayout(); - } -} - -void Splitter::UnlockChildSize( int nChildIndex ) -{ - Assert( nChildIndex < m_Splitters.Count() ); - SplitterInfo_t &info = m_Splitters[nChildIndex]; - if ( info.m_bLocked ) - { - info.m_bLocked = false; - - float flPrevPos = (nChildIndex > 0) ? m_Splitters[nChildIndex-1].m_flPos : 0.0f; - float flBelowSize = GetPosRange() - flPrevPos; - - int nLockedSize = ComputeLockedSize( nChildIndex + 1 ); - int nUnlockedCount = 1; - int nCount = m_Splitters.Count(); - for ( int i = nChildIndex + 1; i < nCount; ++i ) - { - if ( !m_Splitters[i].m_bLocked ) - { - ++nUnlockedCount; - } - } - - float flUnlockedSize = ( flBelowSize - nLockedSize ) / nUnlockedCount; - - for ( int i = nChildIndex; i < nCount; ++i ) - { - if ( !m_Splitters[i].m_bLocked ) - { - m_Splitters[i].m_flPos = flPrevPos + flUnlockedSize; - } - else - { - m_Splitters[i].m_flPos = flPrevPos + m_Splitters[i].m_nLockedSize; - } - flPrevPos = m_Splitters[i].m_flPos; - } - InvalidateLayout(); - } -} - - -//----------------------------------------------------------------------------- -// Called when size changes -//----------------------------------------------------------------------------- -void Splitter::OnSizeChanged( int newWide, int newTall ) -{ - BaseClass::OnSizeChanged( newWide, newTall ); - - // Don't resize if it's degenerate and won't show up anyway... - if ( newTall <= 0 || newWide <= 0 ) - return; - - int nLockedSize = 0; - float flUnlockedSize = 0.0f; - int nCount = m_Splitters.Count(); - float flLastPos = 0.0f; - int nUnlockedCount = 0; - for ( int i = 0; i < nCount; ++i ) - { - SplitterInfo_t &info = m_Splitters[i]; - if ( info.m_bLocked ) - { - nLockedSize += info.m_nLockedSize; - } - else - { - ++nUnlockedCount; - flUnlockedSize += info.m_flPos - flLastPos; - } - flLastPos = info.m_flPos; - } - - int nNewTotalSize = (m_Mode == SPLITTER_MODE_HORIZONTAL) ? newTall : newWide; - int nNewUnlockedSize = nNewTotalSize - nLockedSize; - if ( nNewUnlockedSize < nUnlockedCount * SPLITTER_HANDLE_WIDTH ) - { - nNewUnlockedSize = nUnlockedCount * SPLITTER_HANDLE_WIDTH; - } - - float flRatio = nNewUnlockedSize / flUnlockedSize; - float flLastPrevPos = 0.0f; - flLastPos = 0.0f; - for ( int i = 0; i < nCount - 1; ++i ) - { - SplitterInfo_t &info = m_Splitters[i]; - if ( info.m_bLocked ) - { - flLastPrevPos = info.m_flPos; - info.m_flPos = flLastPos + info.m_nLockedSize; - } - else - { - float flNewSize = info.m_flPos - flLastPrevPos; - flNewSize *= flRatio; - flLastPrevPos = info.m_flPos; - info.m_flPos = flLastPos + flNewSize; - } - flLastPos = info.m_flPos; - } - - // Clamp the bottom to 1.0 - m_Splitters[nCount-1].m_flPos = nNewTotalSize; -} - - -//----------------------------------------------------------------------------- -// Splitter position -//----------------------------------------------------------------------------- -int Splitter::GetSplitterPosition( int nIndex ) -{ - return (int)( m_Splitters[nIndex].m_flPos + 0.5f ); -} - -void Splitter::SetSplitterPosition( int nIndex, int nPos ) -{ - int nPosRange = GetPosRange(); - if ( nPosRange == 0 ) - return; - - // If we're locked to a sibling, move the previous sibling first - while ( ( nIndex >= 0 ) && m_Splitters[nIndex].m_bLocked ) - { - nPos -= m_Splitters[nIndex].m_nLockedSize; - --nIndex; - } - if ( nIndex < 0 ) - return; - - // Clamp to the valid positional range - int i; - int nMinPos = 0; - for ( i = 0; i < nIndex; ++i ) - { - if ( !m_Splitters[i].m_bLocked ) - { - nMinPos += SPLITTER_HANDLE_WIDTH; - } - else - { - nMinPos += m_Splitters[i].m_nLockedSize; - } - } - - int nMaxPos = nPosRange - SPLITTER_HANDLE_WIDTH; - int c = GetSplitterCount(); - for ( i = nIndex + 1; i < c; ++i ) - { - if ( !m_Splitters[i].m_bLocked ) - { - nMaxPos -= SPLITTER_HANDLE_WIDTH; - } - else - { - nMaxPos -= m_Splitters[i].m_nLockedSize; - } - } - nPos = clamp( nPos, nMinPos, nMaxPos ); - - m_Splitters[nIndex].m_flPos = nPos; - int p = nPos; - for ( i = nIndex - 1 ; i >= 0; --i ) - { - int nMinPrevPos; - int nMaxPrevPos; - if ( !m_Splitters[i+1].m_bLocked ) - { - nMinPrevPos = -INT_MAX; - nMaxPrevPos = nPos - SPLITTER_HANDLE_WIDTH; - } - else - { - nMinPrevPos = nMaxPrevPos = p - m_Splitters[i+1].m_nLockedSize; - } - - int nCurPos = GetSplitterPosition( i ); - if ( nMaxPrevPos < nCurPos || nMinPrevPos > nCurPos ) - { - m_Splitters[ i ].m_flPos = nMaxPrevPos; - p = nMaxPrevPos; - } - else - { - p = m_Splitters[ i ].m_flPos; - } - } - - for ( i = nIndex + 1 ; i < c; ++i ) - { - int nMinNextPos; - int nMaxNextPos; - if ( !m_Splitters[i].m_bLocked ) - { - nMinNextPos = nPos + SPLITTER_HANDLE_WIDTH; - nMaxNextPos = INT_MAX; - } - else - { - nMinNextPos = nMaxNextPos = nPos + m_Splitters[i].m_nLockedSize; - } - - int nCurPos = GetSplitterPosition( i ); - if ( nMinNextPos > nCurPos || nMaxNextPos < nCurPos ) - { - m_Splitters[ i ].m_flPos = nMinNextPos; - nPos = nMinNextPos; - } - else - { - nPos = m_Splitters[ i ].m_flPos; - } - } - - InvalidateLayout(); -} - - -//----------------------------------------------------------------------------- -// Computes the locked size -//----------------------------------------------------------------------------- -int Splitter::ComputeLockedSize( int nStartingIndex ) -{ - int nLockedSize = 0; - int nCount = m_Splitters.Count(); - for ( int i = nStartingIndex; i < nCount; ++i ) - { - if ( m_Splitters[i].m_bLocked ) - { - nLockedSize += m_Splitters[i].m_nLockedSize; - } - } - return nLockedSize; -} - - -//----------------------------------------------------------------------------- -// Evenly respaces all the splitters -//----------------------------------------------------------------------------- -void Splitter::EvenlyRespaceSplitters( ) -{ - int nSplitterCount = GetSubPanelCount(); - if ( nSplitterCount == 0 ) - return; - - int nLockedSize = ComputeLockedSize( 0 ); - float flUnlockedSize = (float)( GetPosRange() - nLockedSize ); - float flDPos = flUnlockedSize / (float)nSplitterCount; - if ( flDPos < SPLITTER_HANDLE_WIDTH ) - { - flDPos = SPLITTER_HANDLE_WIDTH; - } - float flPos = 0.0f; - for ( int i = 0; i < nSplitterCount; ++i ) - { - if ( !m_Splitters[i].m_bLocked ) - { - flPos += flDPos; - } - else - { - flPos += m_Splitters[i].m_nLockedSize; - } - m_Splitters[i].m_flPos = flPos; - } - - InvalidateLayout(); -} - -void Splitter::RespaceSplitters( float *flFractions ) -{ - int nSplitterCount = GetSubPanelCount(); - if ( nSplitterCount == 0 ) - return; - - float flPos = 0.0f; - int nPosRange = GetPosRange(); - for ( int i = 0; i < nSplitterCount; ++i ) - { - flPos += flFractions[i]; - m_Splitters[i].m_flPos = flPos * nPosRange; - } - - Assert( flPos == 1.0f ); - - InvalidateLayout(); -} - - -//----------------------------------------------------------------------------- -// Purpose: sets user settings -//----------------------------------------------------------------------------- -void Splitter::ApplyUserConfigSettings(KeyValues *userConfig) -{ - BaseClass::ApplyUserConfigSettings( userConfig ); - - // read the splitter sizes - int c = m_Splitters.Count(); - float *pFractions = (float*)_alloca( c * sizeof(float) ); - float flTotalSize = 0.0f; - for ( int i = 0; i < c; i++ ) - { - char name[128]; - _snprintf(name, sizeof(name), "%d_splitter_pos", i); - pFractions[i] = userConfig->GetFloat( name, flTotalSize + SPLITTER_HANDLE_WIDTH + 1 ); - flTotalSize = pFractions[i]; - } - - if ( flTotalSize != 0.0f ) - { - int nPosRange = GetPosRange(); - for ( int i = 0; i < c; ++i ) - { - pFractions[i] /= flTotalSize; - m_Splitters[i].m_flPos = pFractions[i] * nPosRange; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: returns user config settings for this control -//----------------------------------------------------------------------------- -void Splitter::GetUserConfigSettings(KeyValues *userConfig) -{ - BaseClass::GetUserConfigSettings( userConfig ); - - // save which columns are hidden - int c = m_Splitters.Count(); - for ( int i = 0 ; i < c; i++ ) - { - char name[128]; - _snprintf(name, sizeof(name), "%d_splitter_pos", i); - userConfig->SetFloat( name, m_Splitters[i].m_flPos ); - } -} - - -//----------------------------------------------------------------------------- -// Called to perform layout -//----------------------------------------------------------------------------- -void Splitter::PerformLayout( ) -{ - BaseClass::PerformLayout(); - - int nSplitterCount = GetSubPanelCount(); - if ( nSplitterCount == 0 ) - return; - - int w, h; - GetSize( w, h ); - - int nLastPos = 0; - for ( int i = 0; i < nSplitterCount; ++i ) - { - Panel *pChild = m_Splitters[i].m_pPanel; - SplitterHandle *pHandle = m_Splitters[i].m_pHandle; - int nSplitterPos = (int)( m_Splitters[i].m_flPos + 0.5f ); - - if ( m_Mode == SPLITTER_MODE_HORIZONTAL ) - { - pChild->SetPos( 0, nLastPos ); - pChild->SetSize( w, nSplitterPos - nLastPos ); - if ( pHandle ) - { - pHandle->SetPos( 0, nSplitterPos ); - pHandle->SetSize( w, SPLITTER_HANDLE_WIDTH ); - } - } - else - { - pChild->SetPos( nLastPos, 0 ); - pChild->SetSize( nSplitterPos - nLastPos, h ); - if ( pHandle ) - { - pHandle->SetPos( nSplitterPos, 0 ); - pHandle->SetSize( SPLITTER_HANDLE_WIDTH, h ); - } - } - - nLastPos = nSplitterPos + SPLITTER_HANDLE_WIDTH; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Splitter::GetSettings( KeyValues *outResourceData ) -{ - BaseClass::GetSettings( outResourceData ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include +#include +#include +#include +#include "tier1/KeyValues.h" +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + + +enum +{ + SPLITTER_HANDLE_WIDTH = 4 +}; + + +//----------------------------------------------------------------------------- +// Splitter handle +//----------------------------------------------------------------------------- +namespace vgui +{ + +class SplitterHandle : public Panel +{ + DECLARE_CLASS_SIMPLE( SplitterHandle, Panel ); + +public: + SplitterHandle( Splitter *parent, const char *name, SplitterMode_t mode, int nIndex ); + ~SplitterHandle(); + + virtual void ApplySchemeSettings( IScheme *pScheme ); + virtual void OnMousePressed( MouseCode code ); + virtual void OnMouseReleased( MouseCode code ); + virtual void OnCursorMoved( int x, int y ); + virtual void OnMouseDoublePressed( MouseCode code ); + +private: + SplitterMode_t m_nMode; + int m_nIndex; + bool m_bDragging; +}; + +} // end namespace vgui + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +SplitterHandle::SplitterHandle( Splitter *parent, const char *name, SplitterMode_t mode, int nIndex ) : BaseClass( parent, name ) +{ + int w, h; + parent->GetSize( w, h ); + + if ( mode == SPLITTER_MODE_HORIZONTAL ) + { + SetSize( w, SPLITTER_HANDLE_WIDTH ); + SetCursor( dc_sizens ); + } + else + { + SetSize( SPLITTER_HANDLE_WIDTH, h ); + SetCursor( dc_sizewe ); + } + + SetVisible( true ); + SetPaintBackgroundEnabled( false ); + SetPaintEnabled( false ); + SetPaintBorderEnabled( true ); + m_bDragging = false; + m_nIndex = nIndex; + m_nMode = mode; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +SplitterHandle::~SplitterHandle() +{ +} + + +//----------------------------------------------------------------------------- +// Scheme settings +//----------------------------------------------------------------------------- +void SplitterHandle::ApplySchemeSettings(IScheme *pScheme) +{ + // Cache off background color stored in SetSplitterColor + Color c = GetBgColor(); + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); + BaseClass::ApplySchemeSettings(pScheme); + SetBgColor( c ); +} + + +//----------------------------------------------------------------------------- +// Capture mouse when dragging +//----------------------------------------------------------------------------- +void SplitterHandle::OnMousePressed(MouseCode code) +{ + if ( !m_bDragging ) + { + input()->SetMouseCapture(GetVPanel()); + m_bDragging = true; + } +} + + +//----------------------------------------------------------------------------- +// Release mouse capture when finished dragging +//----------------------------------------------------------------------------- +void SplitterHandle::OnMouseReleased(MouseCode code) +{ + if ( m_bDragging ) + { + input()->SetMouseCapture(NULL); + m_bDragging = false; + } +} + + +//----------------------------------------------------------------------------- +// While dragging, update the splitter position +//----------------------------------------------------------------------------- +void SplitterHandle::OnCursorMoved(int x, int y) +{ + if (m_bDragging) + { + input()->GetCursorPos( x, y ); + Splitter *pSplitter = assert_cast( GetParent() ); + pSplitter->ScreenToLocal( x,y ); + pSplitter->SetSplitterPosition( m_nIndex, (m_nMode == SPLITTER_MODE_HORIZONTAL) ? y : x ); + } +} + + +//----------------------------------------------------------------------------- +// Double-click: make both panels on either side of the splitter equal size +//----------------------------------------------------------------------------- +void SplitterHandle::OnMouseDoublePressed( MouseCode code ) +{ + Splitter *pSplitter = assert_cast( GetParent() ); + pSplitter->EvenlyRespaceSplitters(); +} + + + +//----------------------------------------------------------------------------- +// Returns a panel that chains user configs +//----------------------------------------------------------------------------- +namespace vgui +{ + +class SplitterChildPanel : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( SplitterChildPanel, EditablePanel ); + +public: + SplitterChildPanel( Panel *parent, const char *panelName ) : BaseClass( parent, panelName ) + { + SetPaintBackgroundEnabled( false ); + SetPaintEnabled( false ); + SetPaintBorderEnabled( false ); + } + + virtual ~SplitterChildPanel() {} + + // Children may have user config settings + bool HasUserConfigSettings() + { + return true; + } +}; + +} // end namespace vgui + +//----------------------------------------------------------------------------- +// +// Splitter panel +// +//----------------------------------------------------------------------------- +vgui::Panel *Splitter_V_Factory() +{ + return new Splitter( NULL, NULL, SPLITTER_MODE_VERTICAL, 1 ); +} + +vgui::Panel *Splitter_H_Factory() +{ + return new Splitter( NULL, NULL, SPLITTER_MODE_HORIZONTAL, 1 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Splitter::Splitter( Panel *parent, const char *name, SplitterMode_t mode, int nCount ) : BaseClass( parent, name ) +{ + Assert( nCount >= 1 ); + m_Mode = mode; + + SetPaintBackgroundEnabled( false ); + SetPaintEnabled( false ); + SetPaintBorderEnabled( false ); + + RecreateSplitters( nCount ); + + EvenlyRespaceSplitters(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +Splitter::~Splitter() +{ + m_Splitters.RemoveAll(); +} + +void Splitter::RecreateSplitters( int nCount ) +{ + int i; + int c = m_Splitters.Count(); + for ( i = 0; i < c; ++i ) + { + delete m_Splitters[ i ].m_pPanel; + delete m_Splitters[ i ].m_pHandle; + } + m_Splitters.RemoveAll(); + + for ( i = 0; i < (nCount + 1); ++i ) + { + char pBuffer[512]; + Q_snprintf( pBuffer, sizeof(pBuffer), "child%d", i ); + + int nIndex = m_Splitters.AddToTail( ); + SplitterChildPanel *pEditablePanel = new SplitterChildPanel( this, pBuffer ); + m_Splitters[nIndex].m_pPanel = pEditablePanel; + m_Splitters[nIndex].m_bLocked = false; + m_Splitters[nIndex].m_nLockedSize = 0; + } + + // We do this in 2 loops so that the first N children are actual child panels + for ( i = 0; i < nCount; ++i ) + { + SplitterHandle *pHandle = new SplitterHandle( this, "SplitterHandle", m_Mode, i ); + m_Splitters[i].m_pHandle = pHandle; + pHandle->MoveToFront(); + } + m_Splitters[nCount].m_pHandle = NULL; +} + + +//----------------------------------------------------------------------------- +// Sets the splitter color +//----------------------------------------------------------------------------- +void Splitter::SetSplitterColor( Color c ) +{ + int nCount = m_Splitters.Count() - 1; + if ( c.a() != 0 ) + { + for ( int i = 0; i < nCount; ++i ) + { + m_Splitters[i].m_pHandle->SetBgColor( c ); + m_Splitters[i].m_pHandle->SetPaintBackgroundEnabled( true ); + } + } + else + { + for ( int i = 0; i < nCount; ++i ) + { + m_Splitters[i].m_pHandle->SetPaintBackgroundEnabled( false ); + } + } +} + + +//----------------------------------------------------------------------------- +// Enables borders on the splitters +//----------------------------------------------------------------------------- +void Splitter::EnableBorders( bool bEnable ) +{ + int nCount = m_Splitters.Count() - 1; + for ( int i = 0; i < nCount; ++i ) + { + m_Splitters[i].m_pHandle->SetPaintBorderEnabled( bEnable ); + } +} + + +//----------------------------------------------------------------------------- +// controls splitters +//----------------------------------------------------------------------------- +int Splitter::GetSplitterCount() const +{ + return m_Splitters.Count() - 1; +} + + +//----------------------------------------------------------------------------- +// controls splitters +//----------------------------------------------------------------------------- +int Splitter::GetSubPanelCount() const +{ + return m_Splitters.Count(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Applies resouce settings +//----------------------------------------------------------------------------- +void Splitter::ApplySettings(KeyValues *inResourceData) +{ + BaseClass::ApplySettings(inResourceData); + + // Look for splitter positions + int nSplitterCount = GetSplitterCount(); + for ( int i = 0; i < nSplitterCount; ++i ) + { + char pBuffer[512]; + Q_snprintf( pBuffer, sizeof(pBuffer), "splitter%d", i ); + + int nSplitterPos = inResourceData->GetInt( pBuffer , -1 ); + if ( nSplitterPos >= 0 ) + { + SetSplitterPosition( i, nSplitterPos ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int Splitter::GetPosRange() +{ + int w, h; + GetSize( w, h ); + int nPosRange = (m_Mode == SPLITTER_MODE_HORIZONTAL) ? h : w; + return nPosRange; +} + + +//----------------------------------------------------------------------------- +// Locks the size of a particular child in pixels. +//----------------------------------------------------------------------------- +void Splitter::LockChildSize( int nChildIndex, int nSize ) +{ + Assert( nChildIndex < m_Splitters.Count() ); + SplitterInfo_t &info = m_Splitters[nChildIndex]; + nSize += SPLITTER_HANDLE_WIDTH; + if ( !info.m_bLocked || (info.m_nLockedSize != nSize) ) + { + float flPrevPos = (nChildIndex > 0) ? m_Splitters[nChildIndex-1].m_flPos : 0.0f; + float flOldSize = info.m_flPos - flPrevPos; + float flDelta = nSize - flOldSize; + int nCount = m_Splitters.Count(); + for ( int i = nChildIndex; i < nCount-1; ++i ) + { + m_Splitters[i].m_flPos += flDelta; + } + m_Splitters[nCount-1].m_flPos = GetPosRange(); + + info.m_bLocked = true; + info.m_nLockedSize = nSize; + InvalidateLayout(); + } +} + +void Splitter::UnlockChildSize( int nChildIndex ) +{ + Assert( nChildIndex < m_Splitters.Count() ); + SplitterInfo_t &info = m_Splitters[nChildIndex]; + if ( info.m_bLocked ) + { + info.m_bLocked = false; + + float flPrevPos = (nChildIndex > 0) ? m_Splitters[nChildIndex-1].m_flPos : 0.0f; + float flBelowSize = GetPosRange() - flPrevPos; + + int nLockedSize = ComputeLockedSize( nChildIndex + 1 ); + int nUnlockedCount = 1; + int nCount = m_Splitters.Count(); + for ( int i = nChildIndex + 1; i < nCount; ++i ) + { + if ( !m_Splitters[i].m_bLocked ) + { + ++nUnlockedCount; + } + } + + float flUnlockedSize = ( flBelowSize - nLockedSize ) / nUnlockedCount; + + for ( int i = nChildIndex; i < nCount; ++i ) + { + if ( !m_Splitters[i].m_bLocked ) + { + m_Splitters[i].m_flPos = flPrevPos + flUnlockedSize; + } + else + { + m_Splitters[i].m_flPos = flPrevPos + m_Splitters[i].m_nLockedSize; + } + flPrevPos = m_Splitters[i].m_flPos; + } + InvalidateLayout(); + } +} + + +//----------------------------------------------------------------------------- +// Called when size changes +//----------------------------------------------------------------------------- +void Splitter::OnSizeChanged( int newWide, int newTall ) +{ + BaseClass::OnSizeChanged( newWide, newTall ); + + // Don't resize if it's degenerate and won't show up anyway... + if ( newTall <= 0 || newWide <= 0 ) + return; + + int nLockedSize = 0; + float flUnlockedSize = 0.0f; + int nCount = m_Splitters.Count(); + float flLastPos = 0.0f; + int nUnlockedCount = 0; + for ( int i = 0; i < nCount; ++i ) + { + SplitterInfo_t &info = m_Splitters[i]; + if ( info.m_bLocked ) + { + nLockedSize += info.m_nLockedSize; + } + else + { + ++nUnlockedCount; + flUnlockedSize += info.m_flPos - flLastPos; + } + flLastPos = info.m_flPos; + } + + int nNewTotalSize = (m_Mode == SPLITTER_MODE_HORIZONTAL) ? newTall : newWide; + int nNewUnlockedSize = nNewTotalSize - nLockedSize; + if ( nNewUnlockedSize < nUnlockedCount * SPLITTER_HANDLE_WIDTH ) + { + nNewUnlockedSize = nUnlockedCount * SPLITTER_HANDLE_WIDTH; + } + + float flRatio = nNewUnlockedSize / flUnlockedSize; + float flLastPrevPos = 0.0f; + flLastPos = 0.0f; + for ( int i = 0; i < nCount - 1; ++i ) + { + SplitterInfo_t &info = m_Splitters[i]; + if ( info.m_bLocked ) + { + flLastPrevPos = info.m_flPos; + info.m_flPos = flLastPos + info.m_nLockedSize; + } + else + { + float flNewSize = info.m_flPos - flLastPrevPos; + flNewSize *= flRatio; + flLastPrevPos = info.m_flPos; + info.m_flPos = flLastPos + flNewSize; + } + flLastPos = info.m_flPos; + } + + // Clamp the bottom to 1.0 + m_Splitters[nCount-1].m_flPos = nNewTotalSize; +} + + +//----------------------------------------------------------------------------- +// Splitter position +//----------------------------------------------------------------------------- +int Splitter::GetSplitterPosition( int nIndex ) +{ + return (int)( m_Splitters[nIndex].m_flPos + 0.5f ); +} + +void Splitter::SetSplitterPosition( int nIndex, int nPos ) +{ + int nPosRange = GetPosRange(); + if ( nPosRange == 0 ) + return; + + // If we're locked to a sibling, move the previous sibling first + while ( ( nIndex >= 0 ) && m_Splitters[nIndex].m_bLocked ) + { + nPos -= m_Splitters[nIndex].m_nLockedSize; + --nIndex; + } + if ( nIndex < 0 ) + return; + + // Clamp to the valid positional range + int i; + int nMinPos = 0; + for ( i = 0; i < nIndex; ++i ) + { + if ( !m_Splitters[i].m_bLocked ) + { + nMinPos += SPLITTER_HANDLE_WIDTH; + } + else + { + nMinPos += m_Splitters[i].m_nLockedSize; + } + } + + int nMaxPos = nPosRange - SPLITTER_HANDLE_WIDTH; + int c = GetSplitterCount(); + for ( i = nIndex + 1; i < c; ++i ) + { + if ( !m_Splitters[i].m_bLocked ) + { + nMaxPos -= SPLITTER_HANDLE_WIDTH; + } + else + { + nMaxPos -= m_Splitters[i].m_nLockedSize; + } + } + nPos = clamp( nPos, nMinPos, nMaxPos ); + + m_Splitters[nIndex].m_flPos = nPos; + int p = nPos; + for ( i = nIndex - 1 ; i >= 0; --i ) + { + int nMinPrevPos; + int nMaxPrevPos; + if ( !m_Splitters[i+1].m_bLocked ) + { + nMinPrevPos = -INT_MAX; + nMaxPrevPos = nPos - SPLITTER_HANDLE_WIDTH; + } + else + { + nMinPrevPos = nMaxPrevPos = p - m_Splitters[i+1].m_nLockedSize; + } + + int nCurPos = GetSplitterPosition( i ); + if ( nMaxPrevPos < nCurPos || nMinPrevPos > nCurPos ) + { + m_Splitters[ i ].m_flPos = nMaxPrevPos; + p = nMaxPrevPos; + } + else + { + p = m_Splitters[ i ].m_flPos; + } + } + + for ( i = nIndex + 1 ; i < c; ++i ) + { + int nMinNextPos; + int nMaxNextPos; + if ( !m_Splitters[i].m_bLocked ) + { + nMinNextPos = nPos + SPLITTER_HANDLE_WIDTH; + nMaxNextPos = INT_MAX; + } + else + { + nMinNextPos = nMaxNextPos = nPos + m_Splitters[i].m_nLockedSize; + } + + int nCurPos = GetSplitterPosition( i ); + if ( nMinNextPos > nCurPos || nMaxNextPos < nCurPos ) + { + m_Splitters[ i ].m_flPos = nMinNextPos; + nPos = nMinNextPos; + } + else + { + nPos = m_Splitters[ i ].m_flPos; + } + } + + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Computes the locked size +//----------------------------------------------------------------------------- +int Splitter::ComputeLockedSize( int nStartingIndex ) +{ + int nLockedSize = 0; + int nCount = m_Splitters.Count(); + for ( int i = nStartingIndex; i < nCount; ++i ) + { + if ( m_Splitters[i].m_bLocked ) + { + nLockedSize += m_Splitters[i].m_nLockedSize; + } + } + return nLockedSize; +} + + +//----------------------------------------------------------------------------- +// Evenly respaces all the splitters +//----------------------------------------------------------------------------- +void Splitter::EvenlyRespaceSplitters( ) +{ + int nSplitterCount = GetSubPanelCount(); + if ( nSplitterCount == 0 ) + return; + + int nLockedSize = ComputeLockedSize( 0 ); + float flUnlockedSize = (float)( GetPosRange() - nLockedSize ); + float flDPos = flUnlockedSize / (float)nSplitterCount; + if ( flDPos < SPLITTER_HANDLE_WIDTH ) + { + flDPos = SPLITTER_HANDLE_WIDTH; + } + float flPos = 0.0f; + for ( int i = 0; i < nSplitterCount; ++i ) + { + if ( !m_Splitters[i].m_bLocked ) + { + flPos += flDPos; + } + else + { + flPos += m_Splitters[i].m_nLockedSize; + } + m_Splitters[i].m_flPos = flPos; + } + + InvalidateLayout(); +} + +void Splitter::RespaceSplitters( float *flFractions ) +{ + int nSplitterCount = GetSubPanelCount(); + if ( nSplitterCount == 0 ) + return; + + float flPos = 0.0f; + int nPosRange = GetPosRange(); + for ( int i = 0; i < nSplitterCount; ++i ) + { + flPos += flFractions[i]; + m_Splitters[i].m_flPos = flPos * nPosRange; + } + + Assert( flPos == 1.0f ); + + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Purpose: sets user settings +//----------------------------------------------------------------------------- +void Splitter::ApplyUserConfigSettings(KeyValues *userConfig) +{ + BaseClass::ApplyUserConfigSettings( userConfig ); + + // read the splitter sizes + int c = m_Splitters.Count(); + float *pFractions = (float*)_alloca( c * sizeof(float) ); + float flTotalSize = 0.0f; + for ( int i = 0; i < c; i++ ) + { + char name[128]; + _snprintf(name, sizeof(name), "%d_splitter_pos", i); + pFractions[i] = userConfig->GetFloat( name, flTotalSize + SPLITTER_HANDLE_WIDTH + 1 ); + flTotalSize = pFractions[i]; + } + + if ( flTotalSize != 0.0f ) + { + int nPosRange = GetPosRange(); + for ( int i = 0; i < c; ++i ) + { + pFractions[i] /= flTotalSize; + m_Splitters[i].m_flPos = pFractions[i] * nPosRange; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns user config settings for this control +//----------------------------------------------------------------------------- +void Splitter::GetUserConfigSettings(KeyValues *userConfig) +{ + BaseClass::GetUserConfigSettings( userConfig ); + + // save which columns are hidden + int c = m_Splitters.Count(); + for ( int i = 0 ; i < c; i++ ) + { + char name[128]; + _snprintf(name, sizeof(name), "%d_splitter_pos", i); + userConfig->SetFloat( name, m_Splitters[i].m_flPos ); + } +} + + +//----------------------------------------------------------------------------- +// Called to perform layout +//----------------------------------------------------------------------------- +void Splitter::PerformLayout( ) +{ + BaseClass::PerformLayout(); + + int nSplitterCount = GetSubPanelCount(); + if ( nSplitterCount == 0 ) + return; + + int w, h; + GetSize( w, h ); + + int nLastPos = 0; + for ( int i = 0; i < nSplitterCount; ++i ) + { + Panel *pChild = m_Splitters[i].m_pPanel; + SplitterHandle *pHandle = m_Splitters[i].m_pHandle; + int nSplitterPos = (int)( m_Splitters[i].m_flPos + 0.5f ); + + if ( m_Mode == SPLITTER_MODE_HORIZONTAL ) + { + pChild->SetPos( 0, nLastPos ); + pChild->SetSize( w, nSplitterPos - nLastPos ); + if ( pHandle ) + { + pHandle->SetPos( 0, nSplitterPos ); + pHandle->SetSize( w, SPLITTER_HANDLE_WIDTH ); + } + } + else + { + pChild->SetPos( nLastPos, 0 ); + pChild->SetSize( nSplitterPos - nLastPos, h ); + if ( pHandle ) + { + pHandle->SetPos( nSplitterPos, 0 ); + pHandle->SetSize( SPLITTER_HANDLE_WIDTH, h ); + } + } + + nLastPos = nSplitterPos + SPLITTER_HANDLE_WIDTH; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Splitter::GetSettings( KeyValues *outResourceData ) +{ + BaseClass::GetSettings( outResourceData ); +} diff --git a/mp/src/vgui2/vgui_controls/TextEntry.cpp b/mp/src/vgui2/vgui_controls/TextEntry.cpp index cecf3e04..7a4f2d8c 100644 --- a/mp/src/vgui2/vgui_controls/TextEntry.cpp +++ b/mp/src/vgui2/vgui_controls/TextEntry.cpp @@ -1,4279 +1,4279 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -// memdbgon must be the last include file in a .cpp file!!! -#include - -enum -{ - // maximum size of text buffer - BUFFER_SIZE=999999, -}; - -using namespace vgui; - -static const int DRAW_OFFSET_X = 3,DRAW_OFFSET_Y = 1; - -DECLARE_BUILD_FACTORY( TextEntry ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -TextEntry::TextEntry(Panel *parent, const char *panelName) : BaseClass(parent, panelName) -{ - SetTriplePressAllowed( true ); - - _font = INVALID_FONT; - _smallfont = INVALID_FONT; - - m_szComposition[ 0 ] = L'\0'; - - m_bAllowNumericInputOnly = false; - m_bAllowNonAsciiCharacters = false; - _hideText = false; - _editable = false; - _verticalScrollbar = false; - _cursorPos = 0; - _currentStartIndex = 0; - _horizScrollingAllowed = true; - _cursorIsAtEnd = false; - _putCursorAtEnd = false; - _multiline = false; - _cursorBlinkRate = 400; - _mouseSelection = false; - _mouseDragSelection = false; - _vertScrollBar=NULL; - _catchEnterKey = false; - _maxCharCount = -1; - _charCount = 0; - _wrap = false; // don't wrap by default - _sendNewLines = false; // don't pass on a newline msg by default - _drawWidth = 0; - m_bAutoProgressOnHittingCharLimit = false; - m_pIMECandidates = NULL; - m_hPreviousIME = input()->GetEnglishIMEHandle(); - m_bDrawLanguageIDAtLeft = false; - m_nLangInset = 0; - m_bUseFallbackFont = false; - m_hFallbackFont = INVALID_FONT; - - //a -1 for _select[0] means that the selection is empty - _select[0] = -1; - _select[1] = -1; - m_pEditMenu = NULL; - - //this really just inits it when in here - ResetCursorBlink(); - - SetCursor(dc_ibeam); - - SetEditable(true); - - // initialize the line break array - m_LineBreaks.AddToTail(BUFFER_SIZE); - - _recalculateBreaksIndex = 0; - - _selectAllOnFirstFocus = false; - _selectAllOnFocusAlways = false; - - //position the cursor so it is at the end of the text - GotoTextEnd(); - - // If keyboard focus is in an edit control, don't chain keyboard mappings up to parents since it could mess with typing in text. - SetAllowKeyBindingChainToParent( false ); - - REGISTER_COLOR_AS_OVERRIDABLE( _disabledFgColor, "disabledFgColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _disabledBgColor, "disabledBgColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _selectionColor, "selectionColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _selectionTextColor, "selectionTextColor_override" ); - REGISTER_COLOR_AS_OVERRIDABLE( _defaultSelectionBG2Color, "defaultSelectionBG2Color_override" ); -} - - -TextEntry::~TextEntry() -{ - delete m_pEditMenu; - delete m_pIMECandidates; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TextEntry::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetFgColor(GetSchemeColor("TextEntry.TextColor", pScheme)); - SetBgColor(GetSchemeColor("TextEntry.BgColor", pScheme)); - - _cursorColor = GetSchemeColor("TextEntry.CursorColor", pScheme); - _disabledFgColor = GetSchemeColor("TextEntry.DisabledTextColor", pScheme); - _disabledBgColor = GetSchemeColor("TextEntry.DisabledBgColor", pScheme); - - _selectionTextColor = GetSchemeColor("TextEntry.SelectedTextColor", GetFgColor(), pScheme); - _selectionColor = GetSchemeColor("TextEntry.SelectedBgColor", pScheme); - _defaultSelectionBG2Color = GetSchemeColor("TextEntry.OutOfFocusSelectedBgColor", pScheme); - _focusEdgeColor = GetSchemeColor("TextEntry.FocusEdgeColor", Color(0, 0, 0, 0), pScheme); - - SetBorder( pScheme->GetBorder("ButtonDepressedBorder")); - - if ( _font == INVALID_FONT ) _font = pScheme->GetFont("Default", IsProportional() ); - if ( _smallfont == INVALID_FONT ) _smallfont = pScheme->GetFont( "DefaultVerySmall", IsProportional() ); - - SetFont( _font ); -} - -void TextEntry::SetSelectionTextColor( const Color& clr ) -{ - _selectionTextColor = clr; -} - -void TextEntry::SetSelectionBgColor( const Color& clr ) -{ - _selectionColor = clr; -} - -void TextEntry::SetSelectionUnfocusedBgColor( const Color& clr ) -{ - _defaultSelectionBG2Color = clr; -} - -//----------------------------------------------------------------------------- -// Purpose: sets the color of the background when the control is disabled -//----------------------------------------------------------------------------- -void TextEntry::SetDisabledBgColor(Color col) -{ - _disabledBgColor = col; -} - - -//----------------------------------------------------------------------------- -// Purpose: Sends a message if the data has changed -// Turns off any selected text in the window if we are not using the edit menu -//----------------------------------------------------------------------------- -void TextEntry::OnKillFocus() -{ - m_szComposition[ 0 ] = L'\0'; - HideIMECandidates(); - - if (_dataChanged) - { - FireActionSignal(); - _dataChanged = false; - } - - // check if we clicked the right mouse button or if it is down - bool mouseRightClicked = input()->WasMousePressed(MOUSE_RIGHT); - bool mouseRightUp = input()->WasMouseReleased(MOUSE_RIGHT); - bool mouseRightDown = input()->IsMouseDown(MOUSE_RIGHT); - - if (mouseRightClicked || mouseRightDown || mouseRightUp ) - { - int cursorX, cursorY; - input()->GetCursorPos(cursorX, cursorY); - - // if we're right clicking within our window, we don't actually kill focus - if (IsWithin(cursorX, cursorY)) - return; - } - - // clear any selection - SelectNone(); - - // move the cursor to the start -// GotoTextStart(); - - PostActionSignal( new KeyValues( "TextKillFocus" ) ); - - // chain - BaseClass::OnKillFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: Wipe line breaks after the size of a panel has been changed -//----------------------------------------------------------------------------- -void TextEntry::OnSizeChanged(int newWide, int newTall) -{ - BaseClass::OnSizeChanged(newWide, newTall); - - // blow away the line breaks list - _recalculateBreaksIndex = 0; - m_LineBreaks.RemoveAll(); - m_LineBreaks.AddToTail(BUFFER_SIZE); - - // if we're bigger, see if we can scroll left to put more text in the window - if (newWide > _drawWidth) - { - ScrollLeftForResize(); - } - - _drawWidth = newWide; - InvalidateLayout(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Set the text array - convert ANSI text to unicode and pass to unicode function -//----------------------------------------------------------------------------- -void TextEntry::SetText(const char *text) -{ - if (!text) - { - text = ""; - } - - if (text[0] == '#') - { - // check for localization - wchar_t *wsz = g_pVGuiLocalize->Find(text); - if (wsz) - { - SetText(wsz); - return; - } - } - - size_t len = strlen( text ); - if ( len < 1023 ) - { - wchar_t unicode[ 1024 ]; - g_pVGuiLocalize->ConvertANSIToUnicode( text, unicode, sizeof( unicode ) ); - SetText( unicode ); - } - else - { - size_t lenUnicode = ( len * sizeof( wchar_t ) + 4 ); - wchar_t *unicode = ( wchar_t * ) malloc( lenUnicode ); - g_pVGuiLocalize->ConvertANSIToUnicode( text, unicode, lenUnicode ); - SetText( unicode ); - free( unicode ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set the text array -// Using this function will cause all lineBreaks to be discarded. -// This is because this fxn replaces the contents of the text buffer. -// For modifying large buffers use insert functions. -//----------------------------------------------------------------------------- -void TextEntry::SetText(const wchar_t *wszText) -{ - if (!wszText) - { - wszText = L""; - } - int textLen = wcslen(wszText); - m_TextStream.RemoveAll(); - m_TextStream.EnsureCapacity(textLen); - - int missed_count = 0; - for (int i = 0; i < textLen; i++) - { - if(wszText[i]=='\r') // don't insert \r characters - { - missed_count++; - continue; - } - m_TextStream.AddToTail(wszText[i]); - SetCharAt(wszText[i], i-missed_count); - } - - GotoTextStart(); - SelectNone(); - - // reset the data changed flag - _dataChanged = false; - - // blow away the line breaks list - _recalculateBreaksIndex = 0; - m_LineBreaks.RemoveAll(); - m_LineBreaks.AddToTail(BUFFER_SIZE); - - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the value of char at index position. -//----------------------------------------------------------------------------- -void TextEntry::SetCharAt(wchar_t ch, int index) -{ - if ((ch == '\n') || (ch == '\0')) - { - // if its not at the end of the buffer it matters. - // redo the linebreaks - //if (index != m_TextStream.Count()) - { - _recalculateBreaksIndex = 0; - m_LineBreaks.RemoveAll(); - m_LineBreaks.AddToTail(BUFFER_SIZE); - } - } - - if (index < 0) - return; - - if (index >= m_TextStream.Count()) - { - m_TextStream.AddMultipleToTail(index - m_TextStream.Count() + 1); - } - m_TextStream[index] = ch; - _dataChanged = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Restarts the time of the next cursor blink -//----------------------------------------------------------------------------- -void TextEntry::ResetCursorBlink() -{ - _cursorBlink=false; - _cursorNextBlinkTime=system()->GetTimeMillis()+_cursorBlinkRate; -} - -//----------------------------------------------------------------------------- -// Purpose: Hides the text buffer so it will not be drawn -//----------------------------------------------------------------------------- -void TextEntry::SetTextHidden(bool bHideText) -{ - _hideText = bHideText; - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: return character width -//----------------------------------------------------------------------------- -int getCharWidth(HFont font, wchar_t ch) -{ - if (!iswcntrl(ch)) - { - int a, b, c; - surface()->GetCharABCwide(font, ch, a, b, c); - return (a + b + c); - } - return 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Given cursor's position in the text buffer, convert it to -// the local window's x and y pixel coordinates -// Input: cursorPos: cursor index -// Output: cx, cy, the corresponding coords in the local window -//----------------------------------------------------------------------------- -void TextEntry::CursorToPixelSpace(int cursorPos, int &cx, int &cy) -{ - int yStart = GetYStart(); - - int x = DRAW_OFFSET_X, y = yStart; - _pixelsIndent = 0; - int lineBreakIndexIndex = 0; - - for (int i = GetStartDrawIndex(lineBreakIndexIndex); i < m_TextStream.Count(); i++) - { - wchar_t ch = m_TextStream[i]; - if (_hideText) - { - ch = '*'; - } - - // if we've found the position, break - if (cursorPos == i) - { - // even if this is a line break entry for the cursor, the next insert - // will be at this position, which will push the line break forward one - // so don't push the cursor down a line here... - /*if (!_putCursorAtEnd) - { - // if we've passed a line break go to that - if (m_LineBreaks[lineBreakIndexIndex] == i) - { - // add another line - AddAnotherLine(x,y); - lineBreakIndexIndex++; - } - }*/ - break; - } - - // if we've passed a line break go to that - if (m_LineBreaks.Count() && - lineBreakIndexIndex < m_LineBreaks.Count() && - m_LineBreaks[lineBreakIndexIndex] == i) - { - // add another line - AddAnotherLine(x,y); - lineBreakIndexIndex++; - } - - // add to the current position - x += getCharWidth(_font, ch); - } - - if ( m_bDrawLanguageIDAtLeft ) - { - x += m_nLangInset; - } - - cx = x; - cy = y; -} - -//----------------------------------------------------------------------------- -// Purpose: Converts local pixel coordinates to an index in the text buffer -// This function appears to be used only in response to mouse clicking -// Input : cx - -// cy - pixel location -//----------------------------------------------------------------------------- -int TextEntry::PixelToCursorSpace(int cx, int cy) -{ - - int w, h; - GetSize(w, h); - cx = clamp(cx, 0, w+100); - cy = clamp(cy, 0, h); - - _putCursorAtEnd = false; // Start off assuming we clicked somewhere in the text - - int fontTall = surface()->GetFontTall(_font); - - // where to Start reading - int yStart = GetYStart(); - int x = DRAW_OFFSET_X, y = yStart; - _pixelsIndent = 0; - int lineBreakIndexIndex = 0; - - int startIndex = GetStartDrawIndex(lineBreakIndexIndex); - bool onRightLine = false; - int i; - for (i = startIndex; i < m_TextStream.Count(); i++) - { - wchar_t ch = m_TextStream[i]; - if (_hideText) - { - ch = '*'; - } - - // if we are on the right line but off the end of if put the cursor at the end of the line - if (m_LineBreaks[lineBreakIndexIndex] == i ) - { - // add another line - AddAnotherLine(x,y); - lineBreakIndexIndex++; - - if (onRightLine) - { - _putCursorAtEnd = true; - return i; - } - } - - // check to see if we're on the right line - if (cy < yStart) - { - // cursor is above panel - onRightLine = true; - _putCursorAtEnd = true; // this will make the text scroll up if needed - } - else if (cy >= y && (cy < (y + fontTall + DRAW_OFFSET_Y))) - { - onRightLine = true; - } - - int wide = getCharWidth(_font, ch); - - // if we've found the position, break - if (onRightLine) - { - if (cx > GetWide()) // off right side of window - { - } - else if (cx < (DRAW_OFFSET_X + _pixelsIndent) || cy < yStart) // off left side of window - { - return i; // move cursor one to left - } - - if (cx >= x && cx < (x + wide)) - { - // check which side of the letter they're on - if (cx < (x + (wide * 0.5))) // left side - { - return i; - } - else // right side - { - return i + 1; - } - } - } - x += wide; - } - - return i; -} - -//----------------------------------------------------------------------------- -// Purpose: Draws a character in the panel -// Input: ch - character to draw -// font - font to use -// x, y - pixel location to draw char at -// Output: returns the width of the character drawn -//----------------------------------------------------------------------------- -int TextEntry::DrawChar(wchar_t ch, HFont font, int index, int x, int y) -{ - // add to the current position - int charWide = getCharWidth(font, ch); - int fontTall=surface()->GetFontTall(font); - if (!iswcntrl(ch)) - { - // draw selection, if any - int selection0 = -1, selection1 = -1; - GetSelectedRange(selection0, selection1); - - if (index >= selection0 && index < selection1) - { - // draw background selection color - VPANEL focus = input()->GetFocus(); - Color bgColor; - bool hasFocus = HasFocus(); - bool childOfFocus = focus && ipanel()->HasParent(focus, GetVPanel()); - - // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected - if ( hasFocus || childOfFocus ) - { - bgColor = _selectionColor; - } - else - { - bgColor =_defaultSelectionBG2Color; - } - - surface()->DrawSetColor(bgColor); - - surface()->DrawFilledRect(x, y, x + charWide, y + 1 + fontTall); - - // reset text color - surface()->DrawSetTextColor(_selectionTextColor); - } - if (index == selection1) - { - // we've come out of selection, reset the color - surface()->DrawSetTextColor(GetFgColor()); - } - - surface()->DrawSetTextPos(x, y); - surface()->DrawUnicodeChar(ch); - - return charWide; - } - - return 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Draw the cursor, cursor is not drawn when it is blinked gone -// Input: x,y where to draw cursor -// Output: returns true if cursor was drawn. -//----------------------------------------------------------------------------- -bool TextEntry::DrawCursor(int x, int y) -{ - if (!_cursorBlink) - { - int cx, cy; - CursorToPixelSpace(_cursorPos, cx, cy); - surface()->DrawSetColor(_cursorColor); - int fontTall=surface()->GetFontTall(_font); - surface()->DrawFilledRect(cx, cy, cx + 1, cy + fontTall); - return true; - } - return false; -} - -bool TextEntry::NeedsEllipses( HFont font, int *pIndex ) -{ - Assert( pIndex ); - *pIndex = -1; - int wide = DRAW_OFFSET_X; // buffer on left and right end of text. - for ( int i = 0; i < m_TextStream.Count(); ++i ) - { - wide += getCharWidth( font , m_TextStream[i] ); - if (wide > _drawWidth) - { - *pIndex = i; - return true; - } - } - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Draws the text in the panel -//----------------------------------------------------------------------------- -void TextEntry::PaintBackground() -{ - BaseClass::PaintBackground(); - - // draw background - Color col; - if (IsEnabled()) - { - col = GetBgColor(); - } - else - { - col = _disabledBgColor; - } - Color saveBgColor = col; - - int wide, tall; - GetSize( wide, tall ); - -// surface()->DrawSetColor(col); -// surface()->DrawFilledRect(0, 0, wide, tall); - - // where to Start drawing - int x = DRAW_OFFSET_X + _pixelsIndent, y = GetYStart(); - - m_nLangInset = 0; - - int langlen = 0; - wchar_t shortcode[ 5 ]; - shortcode[ 0 ] = L'\0'; - - if ( m_bAllowNonAsciiCharacters ) - { - input()->GetIMELanguageShortCode( shortcode, sizeof( shortcode ) ); - - if ( shortcode[ 0 ] != L'\0' && - wcsicmp( shortcode, L"EN" ) ) - { - m_nLangInset = 0; - langlen = wcslen( shortcode ); - for ( int i = 0; i < langlen; ++i ) - { - m_nLangInset += getCharWidth( _smallfont, shortcode[ i ] ); - } - - m_nLangInset += 4; - - if ( m_bDrawLanguageIDAtLeft ) - { - x += m_nLangInset; - } - - wide -= m_nLangInset; - } - } - - HFont useFont = _font; - - surface()->DrawSetTextFont(useFont); - if (IsEnabled()) - { - col = GetFgColor(); - } - else - { - col = _disabledFgColor; - } - surface()->DrawSetTextColor(col); - _pixelsIndent = 0; - - int lineBreakIndexIndex = 0; - int startIndex = GetStartDrawIndex(lineBreakIndexIndex); - int remembery = y; - - int oldEnd = m_TextStream.Count(); - int oldCursorPos = _cursorPos; - int nCompStart = -1; - int nCompEnd = -1; - - // FIXME: Should insert at cursor pos instead - bool composing = m_bAllowNonAsciiCharacters && wcslen( m_szComposition ) > 0; - bool invertcomposition = input()->GetShouldInvertCompositionString(); - - if ( composing ) - { - nCompStart = _cursorPos; - - wchar_t *s = m_szComposition; - while ( *s != L'\0' ) - { - m_TextStream.InsertBefore( _cursorPos, *s ); - ++s; - ++_cursorPos; - } - - nCompEnd = _cursorPos; - } - - bool highlight_composition = ( nCompStart != -1 && nCompEnd != -1 ) ? true : false; - - // draw text with an elipsis - if ( (!_multiline) && (!_horizScrollingAllowed) ) - { - int endIndex = m_TextStream.Count(); - // In editable windows only do the ellipsis if we don't have focus. - // In non editable windows do it all the time. - if ( (!HasFocus() && (IsEditable())) || (!IsEditable()) ) - { - int i = -1; - - // loop through all the characters and sum their widths - bool addEllipses = NeedsEllipses( useFont, &i ); - if ( addEllipses && - !IsEditable() && - m_bUseFallbackFont && - INVALID_FONT != m_hFallbackFont ) - { - // Switch to small font!!! - useFont = m_hFallbackFont; - surface()->DrawSetTextFont(useFont); - addEllipses = NeedsEllipses( useFont, &i ); - } - if (addEllipses) - { - int elipsisWidth = 3 * getCharWidth(useFont, '.'); - while (elipsisWidth > 0 && i >= 0) - { - elipsisWidth -= getCharWidth(useFont, m_TextStream[i]); - i--; - } - endIndex = i + 1; - } - - // if we take off less than the last 3 chars we have to make sure - // we take off the last 3 chars so selected text will look right. - if (m_TextStream.Count() - endIndex < 3 && m_TextStream.Count() - endIndex > 0 ) - { - endIndex = m_TextStream.Count() - 3; - } - } - // draw the text - int i; - for (i = startIndex; i < endIndex; i++) - { - wchar_t ch = m_TextStream[i]; - if (_hideText) - { - ch = '*'; - } - - bool iscompositionchar = false; - - if ( highlight_composition ) - { - iscompositionchar = ( i >= nCompStart && i < nCompEnd ) ? true : false; - if ( iscompositionchar ) - { - // Set the underline color to the text color - surface()->DrawSetColor( col ); - - int w = getCharWidth( useFont, ch ); - - if ( invertcomposition ) - { - // Invert color - surface()->DrawSetTextColor( saveBgColor ); - surface()->DrawSetColor( col ); - - surface()->DrawFilledRect(x, 0, x+w, tall); - // Set the underline color to the text color - surface()->DrawSetColor( saveBgColor ); - } - - surface()->DrawFilledRect( x, tall - 2, x + w, tall - 1 ); - } - } - - - // draw the character and update xposition - x += DrawChar(ch, useFont, i, x, y); - - // Restore color - surface()->DrawSetTextColor(col); - - } - if (endIndex < m_TextStream.Count()) // add an elipsis - { - x += DrawChar('.', useFont, i, x, y); - i++; - x += DrawChar('.', useFont, i, x, y); - i++; - x += DrawChar('.', useFont, i, x, y); - i++; - } - } - else - { - // draw the text - for ( int i = startIndex; i < m_TextStream.Count(); i++) - { - wchar_t ch = m_TextStream[i]; - if (_hideText) - { - ch = '*'; - } - - // if we've passed a line break go to that - if ( _multiline && m_LineBreaks[lineBreakIndexIndex] == i) - { - // add another line - AddAnotherLine(x, y); - lineBreakIndexIndex++; - } - - bool iscompositionchar = false; - - if ( highlight_composition ) - { - iscompositionchar = ( i >= nCompStart && i < nCompEnd ) ? true : false; - if ( iscompositionchar ) - { - // Set the underline color to the text color - surface()->DrawSetColor( col ); - - int w = getCharWidth( useFont, ch ); - - if ( invertcomposition ) - { - // Invert color - surface()->DrawSetTextColor( saveBgColor ); - surface()->DrawFilledRect(x, 0, x+w, tall); - // Set the underline color to the text color - surface()->DrawSetColor( saveBgColor ); - } - - surface()->DrawFilledRect( x, tall - 2, x + w, tall - 1 ); - } - } - - // draw the character and update xposition - x += DrawChar(ch, useFont, i, x, y); - - // Restore color - surface()->DrawSetTextColor(col); - } - } - - // custom border - //!! need to replace this with scheme stuff (TextEntryBorder/TextEntrySelectedBorder) - surface()->DrawSetColor(50, 50, 50, 255); - - if (IsEnabled() && IsEditable() && HasFocus()) - { - // set a more distinct border color - surface()->DrawSetColor(0, 0, 0, 255); - - DrawCursor (x, y); - - if ( composing ) - { - LocalToScreen( x, y ); - input()->SetCandidateWindowPos( x, y ); - } - } - - int newEnd = m_TextStream.Count(); - int remove = newEnd - oldEnd; - if ( remove > 0 ) - { - m_TextStream.RemoveMultiple( oldCursorPos, remove ); - } - _cursorPos = oldCursorPos; - - if ( HasFocus() && m_bAllowNonAsciiCharacters && langlen > 0 ) - { - wide += m_nLangInset; - - if ( m_bDrawLanguageIDAtLeft ) - { - x = 0; - } - else - { - // Draw language identififer - x = wide - m_nLangInset; - } - - surface()->DrawSetColor( col ); - - surface()->DrawFilledRect( x, 2, x + m_nLangInset-2, tall - 2 ); - - saveBgColor[ 3 ] = 255; - surface()->DrawSetTextColor( saveBgColor ); - - x += 1; - - surface()->DrawSetTextFont(_smallfont); - for ( int i = 0; i < langlen; ++i ) - { - x += DrawChar( shortcode[ i ], _smallfont, i, x, remembery ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Called when data changes or panel size changes -//----------------------------------------------------------------------------- -void TextEntry::PerformLayout() -{ - BaseClass::PerformLayout(); - - RecalculateLineBreaks(); - - // recalculate scrollbar position - if (_verticalScrollbar) - { - LayoutVerticalScrollBarSlider(); - } - - // force a Repaint - Repaint(); -} - -// moves x,y to the Start of the next line of text -void TextEntry::AddAnotherLine(int &cx, int &cy) -{ - cx = DRAW_OFFSET_X + _pixelsIndent; - cy += (surface()->GetFontTall(_font) + DRAW_OFFSET_Y); -} - - -//----------------------------------------------------------------------------- -// Purpose: Recalculates line breaks -//----------------------------------------------------------------------------- -void TextEntry::RecalculateLineBreaks() -{ - if (!_multiline || _hideText) - return; - - if (m_TextStream.Count() < 1) - return; - - HFont font = _font; - - // line break to our width -2 pixel to keep cursor blinking in window - // (assumes borders are 1 pixel) - int wide = GetWide()-2; - - // subtract the scrollbar width - if (_vertScrollBar) - { - wide -= _vertScrollBar->GetWide(); - } - - int charWidth; - int x = DRAW_OFFSET_X, y = DRAW_OFFSET_Y; - - int wordStartIndex = 0; - int wordLength = 0; - bool hasWord = false; - bool justStartedNewLine = true; - bool wordStartedOnNewLine = true; - - int startChar; - if (_recalculateBreaksIndex <= 0) - { - m_LineBreaks.RemoveAll(); - startChar=0; - } - else - { - // remove the rest of the linebreaks list since its out of date. - for (int i=_recalculateBreaksIndex+1; i < m_LineBreaks.Count(); ++i) - { - m_LineBreaks.Remove((int)i); - --i; // removing shrinks the list! - } - startChar = m_LineBreaks[_recalculateBreaksIndex]; - } - - // handle the case where this char is a new line, in that case - // we have already taken its break index into account above so skip it. - if (m_TextStream[startChar] == '\r' || m_TextStream[startChar] == '\n') - { - startChar++; - } - - // loop through all the characters - int i; - for (i = startChar; i < m_TextStream.Count(); ++i) - { - wchar_t ch = m_TextStream[i]; - - // line break only on whitespace characters - if (!iswspace(ch)) - { - if (hasWord) - { - // append to the current word - } - else - { - // Start a new word - wordStartIndex = i; - hasWord = true; - wordStartedOnNewLine = justStartedNewLine; - wordLength = 0; - } - } - else - { - // whitespace/punctuation character - // end the word - hasWord = false; - } - - // get the width - charWidth = getCharWidth(font, ch); - if (!iswcntrl(ch)) - { - justStartedNewLine = false; - } - - // check to see if the word is past the end of the line [wordStartIndex, i) - if ((x + charWidth) >= wide || ch == '\r' || ch == '\n') - { - // add another line - AddAnotherLine(x,y); - - justStartedNewLine = true; - hasWord = false; - - if (ch == '\r' || ch == '\n') - { - // set the break at the current character - m_LineBreaks.AddToTail(i); - } - else if (wordStartedOnNewLine) - { - // word is longer than a line, so set the break at the current cursor - m_LineBreaks.AddToTail(i); - } - else - { - // set it at the last word Start - m_LineBreaks.AddToTail(wordStartIndex); - - // just back to reparse the next line of text - i = wordStartIndex; - } - - // reset word length - wordLength = 0; - } - - // add to the size - x += charWidth; - wordLength += charWidth; - } - - _charCount = i-1; - - // end the list - m_LineBreaks.AddToTail(BUFFER_SIZE); - - // set up the scrollbar - LayoutVerticalScrollBarSlider(); -} - -//----------------------------------------------------------------------------- -// Purpose: Recalculate where the vertical scroll bar slider should be -// based on the current cursor line we are on. -//----------------------------------------------------------------------------- -void TextEntry::LayoutVerticalScrollBarSlider() -{ - // set up the scrollbar - if (_vertScrollBar) - { - int wide, tall; - GetSize (wide, tall); - - // make sure we factor in insets - int ileft, iright, itop, ibottom; - GetInset(ileft, iright, itop, ibottom); - - // with a scroll bar we take off the inset - wide -= iright; - - _vertScrollBar->SetPos(wide - _vertScrollBar->GetWide(), 0); - // scrollbar is inside the borders. - _vertScrollBar->SetSize(_vertScrollBar->GetWide(), tall - ibottom - itop); - - // calculate how many lines we can fully display - int displayLines = tall / (surface()->GetFontTall(_font) + DRAW_OFFSET_Y); - int numLines = m_LineBreaks.Count(); - - if (numLines <= displayLines) - { - // disable the scrollbar - _vertScrollBar->SetEnabled(false); - _vertScrollBar->SetRange(0, numLines); - _vertScrollBar->SetRangeWindow(numLines); - _vertScrollBar->SetValue(0); - } - else - { - // set the scrollbars range - _vertScrollBar->SetRange(0, numLines); - _vertScrollBar->SetRangeWindow(displayLines); - - _vertScrollBar->SetEnabled(true); - - // this should make it scroll one line at a time - _vertScrollBar->SetButtonPressedScrollValue(1); - - // set the value to view the last entries - int val = _vertScrollBar->GetValue(); - int maxval = _vertScrollBar->GetValue() + displayLines; - if (GetCursorLine() < val ) - { - while (GetCursorLine() < val) - { - val--; - } - } - else if (GetCursorLine() >= maxval) - { - while (GetCursorLine() >= maxval) - { - maxval++; - } - maxval -= displayLines; - val = maxval; - } - else - { - //val = GetCursorLine(); - } - - _vertScrollBar->SetValue(val); - _vertScrollBar->InvalidateLayout(); - _vertScrollBar->Repaint(); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Set boolean value of baseclass variables. -//----------------------------------------------------------------------------- -void TextEntry::SetEnabled(bool state) -{ - BaseClass::SetEnabled(state); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets whether text wraps around multiple lines or not -// Input : state - true or false -//----------------------------------------------------------------------------- -void TextEntry::SetMultiline(bool state) -{ - _multiline = state; -} - -bool TextEntry::IsMultiline() -{ - return _multiline; -} - -//----------------------------------------------------------------------------- -// Purpose: sets whether or not the edit catches and stores ENTER key presses -//----------------------------------------------------------------------------- -void TextEntry::SetCatchEnterKey(bool state) -{ - _catchEnterKey = state; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets whether a vertical scrollbar is visible -// Input : state - true or false -//----------------------------------------------------------------------------- -void TextEntry::SetVerticalScrollbar(bool state) -{ - _verticalScrollbar = state; - - if (_verticalScrollbar) - { - if (!_vertScrollBar) - { - _vertScrollBar = new ScrollBar(this, "ScrollBar", true); - _vertScrollBar->AddActionSignalTarget(this); - } - - _vertScrollBar->SetVisible(true); - } - else if (_vertScrollBar) - { - _vertScrollBar->SetVisible(false); - } - - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: sets _editable flag -// Input : state - true or false -//----------------------------------------------------------------------------- -void TextEntry::SetEditable(bool state) -{ - if ( state ) - { - SetDropEnabled( true, 1.0f ); - } - else - { - SetDropEnabled( false ); - } - _editable = state; -} - -const wchar_t *UnlocalizeUnicode( wchar_t *unicode ) -{ - if ( !unicode ) - return L""; - - if ( *unicode == L'#' ) - { - char lookup[ 512 ]; - g_pVGuiLocalize->ConvertUnicodeToANSI( unicode + 1, lookup, sizeof( lookup ) ); - return g_pVGuiLocalize->Find( lookup ); - } - return unicode; -} - -Menu * TextEntry::GetEditMenu() -{ - return m_pEditMenu; -} - -//----------------------------------------------------------------------------- -// Purpose: Create cut/copy/paste dropdown menu -//----------------------------------------------------------------------------- -void TextEntry::CreateEditMenu() -{ - // create a drop down cut/copy/paste menu appropriate for this object's states - if (m_pEditMenu) - delete m_pEditMenu; - m_pEditMenu = new Menu(this, "EditMenu"); - - m_pEditMenu->SetFont( _font ); - - // add cut/copy/paste drop down options if its editable, just copy if it is not - if (_editable && !_hideText) - { - m_pEditMenu->AddMenuItem("#TextEntry_Cut", new KeyValues("DoCutSelected"), this); - } - - if ( !_hideText ) - { - m_pEditMenu->AddMenuItem("#TextEntry_Copy", new KeyValues("DoCopySelected"), this); - } - - if (_editable) - { - m_pEditMenu->AddMenuItem("#TextEntry_Paste", new KeyValues("DoPaste"), this); - } - - - if ( m_bAllowNonAsciiCharacters ) - { - IInput::LanguageItem *langs = NULL; - - int count = input()->GetIMELanguageList( NULL, 0 ); - if ( count > 0 ) - { - langs = new IInput::LanguageItem[ count ]; - input()->GetIMELanguageList( langs, count ); - - // Create a submenu - Menu *subMenu = new Menu( this, "LanguageMenu" ); - - subMenu->SetFont( _font ); - - for ( int i = 0; i < count; ++i ) - { - int id = subMenu->AddCheckableMenuItem( "Language", UnlocalizeUnicode( langs[ i ].menuname ), new KeyValues( "DoLanguageChanged", "handle", langs[ i ].handleValue ), this ); - if ( langs[ i ].active ) - { - subMenu->SetMenuItemChecked( id, true ); - } - } - - m_pEditMenu->AddCascadingMenuItem( "Language", "#TextEntry_Language", "", this, subMenu ); - - delete[] langs; - } - - IInput::ConversionModeItem *modes = NULL; - - count = input()->GetIMEConversionModes( NULL, 0 ); - // if count == 0 then native mode is the only mode... - if ( count > 0 ) - { - modes = new IInput::ConversionModeItem[ count ]; - input()->GetIMEConversionModes( modes, count ); - - // Create a submenu - Menu *subMenu = new Menu( this, "ConversionModeMenu" ); - - subMenu->SetFont( _font ); - - for ( int i = 0; i < count; ++i ) - { - int id = subMenu->AddCheckableMenuItem( "ConversionMode", UnlocalizeUnicode( modes[ i ].menuname ), new KeyValues( "DoConversionModeChanged", "handle", modes[ i ].handleValue ), this ); - if ( modes[ i ].active ) - { - subMenu->SetMenuItemChecked( id, true ); - } - } - - m_pEditMenu->AddCascadingMenuItem( "ConversionMode", "#TextEntry_ConversionMode", "", this, subMenu ); - - delete[] modes; - } - - IInput::SentenceModeItem *sentencemodes = NULL; - - count = input()->GetIMESentenceModes( NULL, 0 ); - // if count == 0 then native mode is the only mode... - if ( count > 0 ) - { - sentencemodes = new IInput::SentenceModeItem[ count ]; - input()->GetIMESentenceModes( sentencemodes, count ); - - // Create a submenu - Menu *subMenu = new Menu( this, "SentenceModeMenu" ); - - subMenu->SetFont( _font ); - - for ( int i = 0; i < count; ++i ) - { - int id = subMenu->AddCheckableMenuItem( "SentenceMode", UnlocalizeUnicode( sentencemodes[ i ].menuname ), new KeyValues( "DoConversionModeChanged", "handle", modes[ i ].handleValue ), this ); - if ( modes[ i ].active ) - { - subMenu->SetMenuItemChecked( id, true ); - } - } - - m_pEditMenu->AddCascadingMenuItem( "SentenceMode", "#TextEntry_SentenceMode", "", this, subMenu ); - - delete[] sentencemodes; - } - } - - - m_pEditMenu->SetVisible(false); - m_pEditMenu->SetParent(this); - m_pEditMenu->AddActionSignalTarget(this); -} - -//----------------------------------------------------------------------------- -// Purpsoe: Returns state of _editable flag -//----------------------------------------------------------------------------- -bool TextEntry::IsEditable() -{ - return _editable && IsEnabled(); -} - -//----------------------------------------------------------------------------- -// Purpose: We want single line windows to scroll horizontally and select text -// in response to clicking and holding outside window -//----------------------------------------------------------------------------- -void TextEntry::OnMouseFocusTicked() -{ - // if a button is down move the scrollbar slider the appropriate direction - if (_mouseDragSelection) // text is being selected via mouse clicking and dragging - { - OnCursorMoved(0,0); // we want the text to scroll as if we were dragging - } -} - -//----------------------------------------------------------------------------- -// Purpose: If a cursor enters the window, we are not elegible for -// MouseFocusTicked events -//----------------------------------------------------------------------------- -void TextEntry::OnCursorEntered() -{ - _mouseDragSelection = false; // outside of window dont recieve drag scrolling ticks -} - -//----------------------------------------------------------------------------- -// Purpose: When the cursor is outside the window, if we are holding the mouse -// button down, then we want the window to scroll the text one char at a time -// using Ticks -//----------------------------------------------------------------------------- -void TextEntry::OnCursorExited() // outside of window recieve drag scrolling ticks -{ - if (_mouseSelection) - _mouseDragSelection = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Handle selection of text by mouse -//----------------------------------------------------------------------------- -void TextEntry::OnCursorMoved(int x, int y) -{ - if (_mouseSelection) - { - // update the cursor position - int x, y; - input()->GetCursorPos(x, y); - ScreenToLocal(x, y); - _cursorPos = PixelToCursorSpace(x, y); - - // if we are at Start of buffer don't put cursor at end, this will keep - // window from scrolling up to a blank line - if (_cursorPos == 0) - _putCursorAtEnd = false; - - // scroll if we went off left side - if (_cursorPos == _currentStartIndex) - { - if (_cursorPos > 0) - _cursorPos--; - - ScrollLeft(); - _cursorPos = _currentStartIndex; - } - if ( _cursorPos != _select[1]) - { - _select[1] = _cursorPos; - Repaint(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handle Mouse button down events. -//----------------------------------------------------------------------------- -void TextEntry::OnMousePressed(MouseCode code) -{ - if (code == MOUSE_LEFT) - { - bool keepChecking = SelectCheck( true ); - if ( !keepChecking ) - { - BaseClass::OnMousePressed( code ); - return; - } - - // move the cursor to where the mouse was pressed - int x, y; - input()->GetCursorPos(x, y); - ScreenToLocal(x, y); - - _cursorIsAtEnd = _putCursorAtEnd; // save this off before calling PixelToCursorSpace() - _cursorPos = PixelToCursorSpace(x, y); - // if we are at Start of buffer don't put cursor at end, this will keep - // window from scrolling up to a blank line - if (_cursorPos == 0) - _putCursorAtEnd = false; - - // enter selection mode - input()->SetMouseCapture(GetVPanel()); - _mouseSelection = true; - - if (_select[0] < 0) - { - // if no initial selection position, Start selection position at cursor - _select[0] = _cursorPos; - } - _select[1] = _cursorPos; - - ResetCursorBlink(); - RequestFocus(); - Repaint(); - } - else if (code == MOUSE_RIGHT) // check for context menu open - { - CreateEditMenu(); - Assert(m_pEditMenu); - - OpenEditMenu(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handle mouse button up events -//----------------------------------------------------------------------------- -void TextEntry::OnMouseReleased(MouseCode code) -{ - _mouseSelection = false; - - input()->SetMouseCapture(NULL); - - // make sure something has been selected - int cx0, cx1; - if (GetSelectedRange(cx0, cx1)) - { - if (cx1 - cx0 == 0) - { - // nullify selection - _select[0] = -1; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : code - -//----------------------------------------------------------------------------- -void TextEntry::OnMouseTriplePressed( MouseCode code ) -{ - BaseClass::OnMouseTriplePressed( code ); - - // left triple clicking on a word selects all - if (code == MOUSE_LEFT) - { - GotoTextEnd(); - - SelectAllText( false ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Handle mouse double clicks -//----------------------------------------------------------------------------- -void TextEntry::OnMouseDoublePressed(MouseCode code) -{ - // left double clicking on a word selects the word - if (code == MOUSE_LEFT) - { - // move the cursor just as if you single clicked. - OnMousePressed(code); - // then find the start and end of the word we are in to highlight it. - int selectSpot[2]; - GotoWordLeft(); - selectSpot[0] = _cursorPos; - GotoWordRight(); - selectSpot[1] = _cursorPos; - - if (_cursorPos > 0) - { - if (iswspace(m_TextStream[_cursorPos - 1])) - { - selectSpot[1]--; - _cursorPos--; - } - - _select[0] = selectSpot[0]; - _select[1] = selectSpot[1]; - _mouseSelection = true; - } - } - -} - -//----------------------------------------------------------------------------- -// Purpose: Turn off text selection code when mouse button is not down -//----------------------------------------------------------------------------- -void TextEntry::OnMouseCaptureLost() -{ - _mouseSelection = false; -} - -//----------------------------------------------------------------------------- -// Purpose: Only pass some keys upwards -// everything else we don't relay to the parent -//----------------------------------------------------------------------------- -void TextEntry::OnKeyCodePressed(KeyCode code) -{ - // Pass enter on only if _catchEnterKey isn't set - if ( code == KEY_ENTER ) - { - if ( !_catchEnterKey ) - { - Panel::OnKeyCodePressed( code ); - return; - } - } - - // Forward on just a few key codes, everything else can be handled by TextEntry itself - switch ( code ) - { - case KEY_F1: - case KEY_F2: - case KEY_F3: - case KEY_F4: - case KEY_F5: - case KEY_F6: - case KEY_F7: - case KEY_F8: - case KEY_F9: - case KEY_F10: - case KEY_F11: - case KEY_F12: - case KEY_ESCAPE: - case KEY_APP: - Panel::OnKeyCodePressed( code ); - return; - } - - // Pass on the joystick and mouse codes - if ( IsMouseCode(code) || IsNovintButtonCode(code) || IsJoystickCode(code) || IsJoystickButtonCode(code) || - IsJoystickPOVCode(code) || IsJoystickPOVCode(code) || IsJoystickAxisCode(code) ) - { - Panel::OnKeyCodePressed( code ); - return; - } - -} - - -//----------------------------------------------------------------------------- -// Purpose: Masks which keys get chained up -// Maps keyboard input to text window functions. -//----------------------------------------------------------------------------- -void TextEntry::OnKeyCodeTyped(KeyCode code) -{ - _cursorIsAtEnd = _putCursorAtEnd; - _putCursorAtEnd = false; - - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - bool winkey = (input()->IsKeyDown(KEY_LWIN) || input()->IsKeyDown(KEY_RWIN)); - bool fallThrough = false; - - if ( ( ctrl || ( winkey && IsOSX() ) ) && !alt) - { - switch(code) - { - case KEY_A: - SelectAllText(false); - // move the cursor to the end - _cursorPos = _select[1]; - break; - - case KEY_INSERT: - case KEY_C: - { - CopySelected(); - break; - } - case KEY_V: - { - DeleteSelected(); - Paste(); - break; - } - case KEY_X: - { - CopySelected(); - DeleteSelected(); - break; - } - case KEY_Z: - { - Undo(); - break; - } - case KEY_RIGHT: - { - GotoWordRight(); - break; - } - case KEY_LEFT: - { - GotoWordLeft(); - break; - } - case KEY_ENTER: - { - // insert a newline - if (_multiline) - { - DeleteSelected(); - SaveUndoState(); - InsertChar('\n'); - } - // fire newlines back to the main target if asked to - if(_sendNewLines) - { - PostActionSignal(new KeyValues("TextNewLine")); - } - break; - } - case KEY_HOME: - { - GotoTextStart(); - break; - } - case KEY_END: - { - GotoTextEnd(); - break; - } - case KEY_PAGEUP: - { - OnChangeIME( true ); - } - break; - case KEY_PAGEDOWN: - { - OnChangeIME( false ); - } - break; - case KEY_UP: - case KEY_DOWN: - if ( m_bAllowNonAsciiCharacters ) - { - FlipToLastIME(); - } - else - { - fallThrough = true; - } - break; - default: - { - fallThrough = true; - break; - } - } - } - else if (alt) - { - // do nothing with ALT-x keys - if ( !m_bAllowNonAsciiCharacters || ( code != KEY_BACKQUOTE ) ) - { - fallThrough = true; - } - } - else - { - switch(code) - { - case KEY_TAB: - case KEY_LSHIFT: - case KEY_RSHIFT: - case KEY_ESCAPE: - { - fallThrough = true; - break; - } - case KEY_INSERT: - { - if (shift) - { - DeleteSelected(); - Paste(); - } - else - { - fallThrough = true; - } - - break; - } - case KEY_DELETE: - { - if (shift) - { - // shift-delete is cut - CopySelected(); - DeleteSelected(); - } - else - { - Delete(); - } - break; - } - case KEY_LEFT: - { - GotoLeft(); - break; - } - case KEY_RIGHT: - { - GotoRight(); - break; - } - case KEY_UP: - { - if (_multiline) - { - GotoUp(); - } - else - { - fallThrough = true; - } - break; - } - case KEY_DOWN: - { - if (_multiline) - { - GotoDown(); - } - else - { - fallThrough = true; - } - break; - } - case KEY_HOME: - { - if (_multiline) - { - GotoFirstOfLine(); - } - else - { - GotoTextStart(); - } - break; - } - case KEY_END: - { - GotoEndOfLine(); - break; - } - case KEY_BACKSPACE: - { - int x0, x1; - if (GetSelectedRange(x0, x1)) - { - // act just like delete if there is a selection - DeleteSelected(); - } - else - { - Backspace(); - } - break; - } - case KEY_ENTER: - { - // insert a newline - if (_multiline && _catchEnterKey) - { - DeleteSelected(); - SaveUndoState(); - InsertChar('\n'); - } - else - { - fallThrough = true; - } - // fire newlines back to the main target if asked to - if(_sendNewLines) - { - PostActionSignal(new KeyValues("TextNewLine")); - } - break; - } - case KEY_PAGEUP: - { - int val = 0; - fallThrough = (!_multiline) && (!_vertScrollBar); - if (_vertScrollBar) - { - val = _vertScrollBar->GetValue(); - } - - // if there is a scroll bar scroll down one rangewindow - if (_multiline) - { - int displayLines = GetTall() / (surface()->GetFontTall(_font) + DRAW_OFFSET_Y); - // move the cursor down - for (int i=0; i < displayLines; i++) - { - GotoUp(); - } - } - - // if there is a scroll bar scroll down one rangewindow - if (_vertScrollBar) - { - int window = _vertScrollBar->GetRangeWindow(); - int newval = _vertScrollBar->GetValue(); - int linesToMove = window - (val - newval); - _vertScrollBar->SetValue(val - linesToMove - 1); - } - break; - - } - case KEY_PAGEDOWN: - { - int val = 0; - fallThrough = (!_multiline) && (!_vertScrollBar); - if (_vertScrollBar) - { - val = _vertScrollBar->GetValue(); - } - - if (_multiline) - { - int displayLines = GetTall() / (surface()->GetFontTall(_font) + DRAW_OFFSET_Y); - // move the cursor down - for (int i=0; i < displayLines; i++) - { - GotoDown(); - } - } - - // if there is a scroll bar scroll down one rangewindow - if (_vertScrollBar) - { - int window = _vertScrollBar->GetRangeWindow(); - int newval = _vertScrollBar->GetValue(); - int linesToMove = window - (newval - val); - _vertScrollBar->SetValue(val + linesToMove + 1); - } - break; - } - - case KEY_F1: - case KEY_F2: - case KEY_F3: - case KEY_F4: - case KEY_F5: - case KEY_F6: - case KEY_F7: - case KEY_F8: - case KEY_F9: - case KEY_F10: - case KEY_F11: - case KEY_F12: - { - fallThrough = true; - break; - } - - default: - { - // return if any other char is pressed. - // as it will be a unicode char. - // and we don't want select[1] changed unless a char was pressed that this fxn handles - return; - } - } - } - - // select[1] is the location in the line where the blinking cursor started - _select[1] = _cursorPos; - - if (_dataChanged) - { - FireActionSignal(); - } - - // chain back on some keys - if (fallThrough) - { - _putCursorAtEnd=_cursorIsAtEnd; // keep state of cursor on fallthroughs - BaseClass::OnKeyCodeTyped(code); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Masks which keys get chained up -// Maps keyboard input to text window functions. -//----------------------------------------------------------------------------- -void TextEntry::OnKeyTyped(wchar_t unichar) -{ - _cursorIsAtEnd = _putCursorAtEnd; - _putCursorAtEnd=false; - - bool fallThrough = false; - - // KeyCodes handle all non printable chars - if (iswcntrl(unichar) || unichar == 9 ) // tab key (code 9) is printable but handled elsewhere - return; - - // do readonly keys - if (!IsEditable()) - { - BaseClass::OnKeyTyped(unichar); - return; - } - - if (unichar != 0) - { - DeleteSelected(); - SaveUndoState(); - InsertChar(unichar); - } - - // select[1] is the location in the line where the blinking cursor started - _select[1] = _cursorPos; - - if (_dataChanged) - { - FireActionSignal(); - } - - // chain back on some keys - if (fallThrough) - { - _putCursorAtEnd=_cursorIsAtEnd; // keep state of cursor on fallthroughs - BaseClass::OnKeyTyped(unichar); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Scrolls the list according to the mouse wheel movement -//----------------------------------------------------------------------------- -void TextEntry::OnMouseWheeled(int delta) -{ - if (_vertScrollBar) - { - MoveScrollBar(delta); - } - else - { - // if we don't use the input, chain back - BaseClass::OnMouseWheeled(delta); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Scrolls the list -// Input : delta - amount to move scrollbar up -//----------------------------------------------------------------------------- -void TextEntry::MoveScrollBar(int delta) -{ - if (_vertScrollBar) - { - int val = _vertScrollBar->GetValue(); - val -= (delta * 3); - _vertScrollBar->SetValue(val); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Called every frame the entry has keyboard focus; -// blinks the text cursor -//----------------------------------------------------------------------------- -void TextEntry::OnKeyFocusTicked() -{ - int time=system()->GetTimeMillis(); - if(time>_cursorNextBlinkTime) - { - _cursorBlink=!_cursorBlink; - _cursorNextBlinkTime=time+_cursorBlinkRate; - Repaint(); - } -} - -Panel *TextEntry::GetDragPanel() -{ - if ( input()->IsMouseDown( MOUSE_LEFT ) ) - { - int x, y; - input()->GetCursorPos(x, y); - ScreenToLocal(x, y); - int cursor = PixelToCursorSpace(x, y); - - int cx0, cx1; - bool check = GetSelectedRange( cx0, cx1 ); - - if ( check && cursor >= cx0 && cursor < cx1 ) - { - // Don't deselect in this case!!! - return BaseClass::GetDragPanel(); - } - return NULL; - } - - return BaseClass::GetDragPanel(); -} - -void TextEntry::OnCreateDragData( KeyValues *msg ) -{ - BaseClass::OnCreateDragData( msg ); - - char txt[ 256 ]; - GetText( txt, sizeof( txt ) ); - - int r0, r1; - if ( GetSelectedRange( r0, r1 ) && r0 != r1 ) - { - int len = r1 - r0; - if ( len > 0 && r0 < 1024 ) - { - char selection[ 512 ]; - Q_strncpy( selection, &txt[ r0 ], len + 1 ); - selection[ len ] = 0; - msg->SetString( "text", selection ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Check if we are selecting text (so we can highlight it) -//----------------------------------------------------------------------------- -bool TextEntry::SelectCheck( bool fromMouse /*=false*/ ) -{ - bool bret = true; - if (!HasFocus() || !(input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT))) - { - bool deselect = true; - int cx0, cx1; - if ( fromMouse && - GetDragPanel() != NULL ) - { - // move the cursor to where the mouse was pressed - int x, y; - input()->GetCursorPos(x, y); - ScreenToLocal(x, y); - int cursor = PixelToCursorSpace(x, y); - - bool check = GetSelectedRange( cx0, cx1 ); - - if ( check && cursor >= cx0 && cursor < cx1 ) - { - // Don't deselect in this case!!! - deselect = false; - bret = false; - } - } - - if ( deselect ) - { - _select[0] = -1; - } - } - else if (_select[0] == -1) - { - _select[0] = _cursorPos; - } - return bret; -} - -//----------------------------------------------------------------------------- -// Purpose: set the maximum number of chars in the text buffer -//----------------------------------------------------------------------------- -void TextEntry::SetMaximumCharCount(int maxChars) -{ - _maxCharCount = maxChars; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -int TextEntry::GetMaximumCharCount() -{ - return _maxCharCount; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void TextEntry::SetAutoProgressOnHittingCharLimit(bool state) -{ - m_bAutoProgressOnHittingCharLimit = state; -} - -//----------------------------------------------------------------------------- -// Purpose: set whether to wrap the text buffer -//----------------------------------------------------------------------------- -void TextEntry::SetWrap(bool wrap) -{ - _wrap = wrap; -} - -//----------------------------------------------------------------------------- -// Purpose: set whether to pass newline msgs to parent -//----------------------------------------------------------------------------- -void TextEntry::SendNewLine(bool send) -{ - _sendNewLines = send; -} - -//----------------------------------------------------------------------------- -// Purpose: Tell if an index is a linebreakindex -//----------------------------------------------------------------------------- -bool TextEntry::IsLineBreak(int index) -{ - for (int i=0; i 0) - { - _cursorPos--; - } - - ScrollLeft(); - - ResetCursorBlink(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Move the cursor one character to the right, scroll the text -// horizontally if needed -//----------------------------------------------------------------------------- -void TextEntry::GotoRight() -{ - SelectCheck(); - - // if we are on a line break just move the cursor to the next line - if (IsLineBreak(_cursorPos)) - { - if (_cursorIsAtEnd) - { - _putCursorAtEnd = false; - } - else - { - // if we are not at end increment cursor - if (_cursorPos < m_TextStream.Count()) - { - _cursorPos++; - } - } - } - else - { - // if we are not at end increment cursor - if (_cursorPos < m_TextStream.Count()) - { - _cursorPos++; - } - - // if we are on a line break move the cursor to end of line - if (IsLineBreak(_cursorPos)) - { - if (!_cursorIsAtEnd) - _putCursorAtEnd = true; - } - } - // scroll right if we need to - ScrollRight(); - - ResetCursorBlink(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Find out what line the cursor is on -//----------------------------------------------------------------------------- -int TextEntry::GetCursorLine() -{ - // find which line the cursor is on - int cursorLine; - for (cursorLine = 0; cursorLine < m_LineBreaks.Count(); cursorLine++) - { - if (_cursorPos < m_LineBreaks[cursorLine]) - break; - } - - if (_putCursorAtEnd) // correct for when cursor is at end of line rather than Start of next - { - // we are not at end of buffer, in which case there is no next line to be at the Start of - if (_cursorPos != m_TextStream.Count() ) - cursorLine--; - } - - return cursorLine; -} - -//----------------------------------------------------------------------------- -// Purpose: Move the cursor one line up -//----------------------------------------------------------------------------- -void TextEntry::GotoUp() -{ - SelectCheck(); - - if (_cursorIsAtEnd) - { - if ( (GetCursorLine() - 1 ) == 0) // we are on first line - { - // stay at end of line - _putCursorAtEnd = true; - return; // dont move the cursor - } - else - _cursorPos--; - } - - int cx, cy; - CursorToPixelSpace(_cursorPos, cx, cy); - - // move the cursor to the previous line - MoveCursor(GetCursorLine() - 1, cx); -} - - -//----------------------------------------------------------------------------- -// Purpose: Move the cursor one line down -//----------------------------------------------------------------------------- -void TextEntry::GotoDown() -{ - SelectCheck(); - - if (_cursorIsAtEnd) - { - _cursorPos--; - if (_cursorPos < 0) - _cursorPos = 0; - } - - int cx, cy; - CursorToPixelSpace(_cursorPos, cx, cy); - - // move the cursor to the next line - MoveCursor(GetCursorLine() + 1, cx); - if (!_putCursorAtEnd && _cursorIsAtEnd ) - { - _cursorPos++; - if (_cursorPos > m_TextStream.Count()) - { - _cursorPos = m_TextStream.Count(); - } - } - LayoutVerticalScrollBarSlider(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the starting ypixel positon for a walk through the window -//----------------------------------------------------------------------------- -int TextEntry::GetYStart() -{ - if (_multiline) - { - // just Start from the top - return DRAW_OFFSET_Y; - } - - int fontTall = surface()->GetFontTall(_font); - return (GetTall() / 2) - (fontTall / 2); -} - -//----------------------------------------------------------------------------- -// Purpose: Move the cursor to a line, need to know how many pixels are in a line -//----------------------------------------------------------------------------- -void TextEntry::MoveCursor(int line, int pixelsAcross) -{ - // clamp to a valid line - if (line < 0) - line = 0; - if (line >= m_LineBreaks.Count()) - line = m_LineBreaks.Count() -1; - - // walk the whole text set looking for our place - // work out where to Start checking - - int yStart = GetYStart(); - - int x = DRAW_OFFSET_X, y = yStart; - int lineBreakIndexIndex = 0; - _pixelsIndent = 0; - int i; - for ( i = 0; i < m_TextStream.Count(); i++) - { - wchar_t ch = m_TextStream[i]; - - if (_hideText) - { - ch = '*'; - } - - // if we've passed a line break go to that - if (m_LineBreaks[lineBreakIndexIndex] == i) - { - if (lineBreakIndexIndex == line) - { - _putCursorAtEnd = true; - _cursorPos = i; - break; - } - - // add another line - AddAnotherLine(x,y); - lineBreakIndexIndex++; - - } - - // add to the current position - int charWidth = getCharWidth(_font, ch); - - if (line == lineBreakIndexIndex) - { - // check to see if we're in range - if ((x + (charWidth / 2)) > pixelsAcross) - { - // found position - _cursorPos = i; - break; - } - } - - x += charWidth; - } - - // if we never find the cursor it must be past the end - // of the text buffer, to let's just slap it on the end of the text buffer then. - if (i == m_TextStream.Count()) - { - GotoTextEnd(); - } - - LayoutVerticalScrollBarSlider(); - ResetCursorBlink(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Turn horizontal scrolling on or off. -// Horizontal scrolling is disabled in multline windows. -// Toggling this will disable it in single line windows as well. -//----------------------------------------------------------------------------- -void TextEntry::SetHorizontalScrolling(bool status) -{ - _horizScrollingAllowed = status; -} - -//----------------------------------------------------------------------------- -// Purpose: Horizontal scrolling function, not used in multiline windows -// Function will scroll the buffer to the left if the cursor is not in the window -// scroll left if we need to -//----------------------------------------------------------------------------- -void TextEntry::ScrollLeft() -{ - if (_multiline) // early out - { - return; - } - - if (!_horizScrollingAllowed) //early out - { - return; - } - - if(_cursorPos < _currentStartIndex) // scroll left if we need to - { - if (_cursorPos < 0)// dont scroll past the Start of buffer - { - _cursorPos=0; - } - _currentStartIndex = _cursorPos; - } - - LayoutVerticalScrollBarSlider(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TextEntry::ScrollLeftForResize() -{ - if (_multiline) // early out - { - return; - } - - if (!_horizScrollingAllowed) //early out - { - return; - } - - while (_currentStartIndex > 0) // go until we hit leftmost - { - _currentStartIndex--; - int nVal = _currentStartIndex; - - // check if the cursor is now off the screen - if (IsCursorOffRightSideOfWindow(_cursorPos)) - { - _currentStartIndex++; // we've gone too far, return it - break; - } - - // IsCursorOffRightSideOfWindow actually fixes the _currentStartIndex, - // so if our value changed that menas we really are off the screen - if (nVal != _currentStartIndex) - break; - } - LayoutVerticalScrollBarSlider(); -} - -//----------------------------------------------------------------------------- -// Purpose: Horizontal scrolling function, not used in multiline windows -// Scroll one char right until the cursor is visible in the window. -// We do this one char at a time because char width isn't a constant. -//----------------------------------------------------------------------------- -void TextEntry::ScrollRight() -{ - if (!_horizScrollingAllowed) - return; - - if (_multiline) - { - } - // check if cursor is off the right side of window - else if (IsCursorOffRightSideOfWindow(_cursorPos)) - { - _currentStartIndex++; //scroll over - ScrollRight(); // scroll again, check if cursor is in window yet - } - - LayoutVerticalScrollBarSlider(); -} - -//----------------------------------------------------------------------------- -// Purpose: Check and see if cursor position is off the right side of the window -// just compare cursor's pixel coords with the window size coords. -// Input: an integer cursor Position, if you pass _cursorPos fxn will tell you -// if current cursor is outside window. -// Output: true: cursor is outside right edge or window -// false: cursor is inside right edge -//----------------------------------------------------------------------------- -bool TextEntry::IsCursorOffRightSideOfWindow(int cursorPos) -{ - int cx, cy; - CursorToPixelSpace(cursorPos, cx, cy); - int wx=GetWide()-1; //width of inside of window is GetWide()-1 - if ( wx <= 0 ) - return false; - - return (cx >= wx); -} - -//----------------------------------------------------------------------------- -// Purpose: Check and see if cursor position is off the left side of the window -// just compare cursor's pixel coords with the window size coords. -// Input: an integer cursor Position, if you pass _cursorPos fxn will tell you -// if current cursor is outside window. -// Output: true - cursor is outside left edge or window -// false - cursor is inside left edge -//----------------------------------------------------------------------------- -bool TextEntry::IsCursorOffLeftSideOfWindow(int cursorPos) -{ - int cx, cy; - CursorToPixelSpace(cursorPos, cx, cy); - return (cx <= 0); -} - -//----------------------------------------------------------------------------- -// Purpose: Move the cursor over to the Start of the next word to the right -//----------------------------------------------------------------------------- -void TextEntry::GotoWordRight() -{ - SelectCheck(); - - // search right until we hit a whitespace character or a newline - while (++_cursorPos < m_TextStream.Count()) - { - if (iswspace(m_TextStream[_cursorPos])) - break; - } - - // search right until we hit an nonspace character - while (++_cursorPos < m_TextStream.Count()) - { - if (!iswspace(m_TextStream[_cursorPos])) - break; - } - - if (_cursorPos > m_TextStream.Count()) - _cursorPos = m_TextStream.Count(); - - // now we are at the start of the next word - - // scroll right if we need to - ScrollRight(); - - LayoutVerticalScrollBarSlider(); - ResetCursorBlink(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Move the cursor over to the Start of the next word to the left -//----------------------------------------------------------------------------- -void TextEntry::GotoWordLeft() -{ - SelectCheck(); - - if (_cursorPos < 1) - return; - - // search left until we hit an nonspace character - while (--_cursorPos >= 0) - { - if (!iswspace(m_TextStream[_cursorPos])) - break; - } - - // search left until we hit a whitespace character - while (--_cursorPos >= 0) - { - if (iswspace(m_TextStream[_cursorPos])) - { - break; - } - } - - // we end one character off - _cursorPos++; - // now we are at the Start of the previous word - - - // scroll left if we need to - ScrollLeft(); - - LayoutVerticalScrollBarSlider(); - ResetCursorBlink(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Move cursor to the Start of the text buffer -//----------------------------------------------------------------------------- -void TextEntry::GotoTextStart() -{ - SelectCheck(); - _cursorPos = 0; // set cursor to Start - _putCursorAtEnd = false; - _currentStartIndex=0; // scroll over to Start - - LayoutVerticalScrollBarSlider(); - ResetCursorBlink(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Move cursor to the end of the text buffer -//----------------------------------------------------------------------------- -void TextEntry::GotoTextEnd() -{ - SelectCheck(); - _cursorPos=m_TextStream.Count(); // set cursor to end of buffer - _putCursorAtEnd = true; // move cursor Start of next line - ScrollRight(); // scroll over until cursor is on screen - - LayoutVerticalScrollBarSlider(); - ResetCursorBlink(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Move cursor to the Start of the current line -//----------------------------------------------------------------------------- -void TextEntry::GotoFirstOfLine() -{ - SelectCheck(); - // to get to the Start of the line you have to take into account line wrap - // we have to figure out at which point the line wraps - // given the current cursor position, select[1], find the index that is the - // line Start to the left of the cursor - //_cursorPos = 0; //TODO: this is wrong, should go to first non-whitespace first, then to zero - _cursorPos = GetCurrentLineStart(); - _putCursorAtEnd = false; - - _currentStartIndex=_cursorPos; - - LayoutVerticalScrollBarSlider(); - ResetCursorBlink(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the index of the first char on the current line -//----------------------------------------------------------------------------- -int TextEntry::GetCurrentLineStart() -{ - if (!_multiline) // quick out for non multline buffers - return _currentStartIndex; - - int i; - if (IsLineBreak(_cursorPos)) - { - for (i = 0; i < m_LineBreaks.Count(); ++i ) - { - if (_cursorPos == m_LineBreaks[i]) - break; - } - if (_cursorIsAtEnd) - { - if (i > 0) - { - return m_LineBreaks[i-1]; - } - return m_LineBreaks[0]; - } - else - return _cursorPos; // we are already at Start - } - - for ( i = 0; i < m_LineBreaks.Count(); ++i ) - { - if (_cursorPos < m_LineBreaks[i]) - { - if (i == 0) - return 0; - else - return m_LineBreaks[i-1]; - } - } - // if there were no line breaks, the first char in the line is the Start of the buffer - return 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Move cursor to the end of the current line -//----------------------------------------------------------------------------- -void TextEntry::GotoEndOfLine() -{ - SelectCheck(); - // to get to the end of the line you have to take into account line wrap in the buffer - // we have to figure out at which point the line wraps - // given the current cursor position, select[1], find the index that is the - // line end to the right of the cursor - //_cursorPos=m_TextStream.Count(); //TODO: this is wrong, should go to last non-whitespace, then to true EOL - _cursorPos = GetCurrentLineEnd(); - _putCursorAtEnd = true; - - ScrollRight(); - - LayoutVerticalScrollBarSlider(); - ResetCursorBlink(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the index of the last char on the current line -//----------------------------------------------------------------------------- -int TextEntry::GetCurrentLineEnd() -{ - int i; - if (IsLineBreak(_cursorPos) ) - { - for ( i = 0; i < m_LineBreaks.Count()-1; ++i ) - { - if (_cursorPos == m_LineBreaks[i]) - break; - } - if (!_cursorIsAtEnd) - { - if (i == m_LineBreaks.Count()-2 ) - m_TextStream.Count(); - else - return m_LineBreaks[i+1]; - } - else - return _cursorPos; // we are already at end - } - - for ( i = 0; i < m_LineBreaks.Count()-1; i++ ) - { - if ( _cursorPos < m_LineBreaks[i]) - { - return m_LineBreaks[i]; - } - } - return m_TextStream.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: Insert a character into the text buffer -//----------------------------------------------------------------------------- -void TextEntry::InsertChar(wchar_t ch) -{ - // throw away redundant linefeed characters - if (ch == '\r') - return; - - // no newline characters in single-line dialogs - if (!_multiline && ch == '\n') - return; - - // no tab characters - if (ch == '\t') - return; - - if (m_bAllowNumericInputOnly) - { - if (!iswdigit(ch) && ((char)ch != '.')) - { - surface()->PlaySound("Resource\\warning.wav"); - return; - } - } - - // check against unicode characters - if (!m_bAllowNonAsciiCharacters) - { - if (ch > 127) - return; - } - - // don't add characters if the max char count has been reached - // ding at the user - if (_maxCharCount > -1 && m_TextStream.Count() >= _maxCharCount) - { - if (_maxCharCount>0 && _multiline && _wrap) - { - // if we wrap lines rather than stopping - while (m_TextStream.Count() > _maxCharCount) - { - if (_recalculateBreaksIndex==0) - { - // we can get called before this has been run for the first time :) - RecalculateLineBreaks(); - } - if (m_LineBreaks[0]> m_TextStream.Count()) - { - // if the line break is the past the end of the buffer recalc - _recalculateBreaksIndex=-1; - RecalculateLineBreaks(); - } - - if (m_LineBreaks[0]+1 < m_TextStream.Count()) - { - // delete the line - m_TextStream.RemoveMultiple(0, m_LineBreaks[0]); - - // in case we just deleted text from where the cursor is - if (_cursorPos> m_TextStream.Count()) - { - _cursorPos = m_TextStream.Count(); - } - else - { // shift the cursor up. don't let it wander past zero - _cursorPos-=m_LineBreaks[0]+1; - if (_cursorPos<0) - { - _cursorPos=0; - } - } - - // move any selection area up - if(_select[0]>-1) - { - _select[0] -=m_LineBreaks[0]+1; - - if(_select[0] <=0) - { - _select[0] =-1; - } - - _select[1] -=m_LineBreaks[0]+1; - if(_select[1] <=0) - { - _select[1] =-1; - } - - } - - // now redraw the buffer - for (int i = m_TextStream.Count() - 1; i >= 0; i--) - { - SetCharAt(m_TextStream[i], i+1); - } - - // redo all the line breaks - _recalculateBreaksIndex=-1; - RecalculateLineBreaks(); - - } - } - - } - else - { - // make a sound - // we've hit the max character limit - surface()->PlaySound("Resource\\warning.wav"); - return; - } - } - - - if (_wrap) - { - // when wrapping you always insert the new char at the end of the buffer - SetCharAt(ch, m_TextStream.Count()); - _cursorPos=m_TextStream.Count(); - } - else - { - // move chars right 1 starting from cursor, then replace cursorPos with char and increment cursor - for (int i = m_TextStream.Count()- 1; i >= _cursorPos; i--) - { - SetCharAt(m_TextStream[i], i+1); - } - - SetCharAt(ch, _cursorPos); - _cursorPos++; - } - - // if its a newline char we can't do the slider until we recalc the line breaks - if (ch == '\n') - { - RecalculateLineBreaks(); - } - - // see if we've hit the char limit - if (m_bAutoProgressOnHittingCharLimit && m_TextStream.Count() == _maxCharCount) - { - // move the next panel (most likely another TextEntry) - RequestFocusNext(); - } - - // scroll right if this pushed the cursor off screen - ScrollRight(); - - _dataChanged = true; - - CalcBreakIndex(); - LayoutVerticalScrollBarSlider(); - ResetCursorBlink(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the lineBreakIndex index of the line before the cursor -// note _recalculateBreaksIndex < 0 flags RecalculateLineBreaks -// to figure it all out from scratch -//----------------------------------------------------------------------------- -void TextEntry::CalcBreakIndex() -{ - // an optimization to handle when the cursor is at the end of the buffer. - // pays off if the buffer is large, and the search loop would be long. - if (_cursorPos == m_TextStream.Count()) - { - // we know m_LineBreaks array always has at least one element in it (99999 sentinel) - // when there is just one line this will make recalc = -1 which is ok. - _recalculateBreaksIndex = m_LineBreaks.Count()-2; - return; - } - - _recalculateBreaksIndex=0; - // find the line break just before the cursor position - while (_cursorPos > m_LineBreaks[_recalculateBreaksIndex]) - ++_recalculateBreaksIndex; - - // -1 is ok. - --_recalculateBreaksIndex; -} - -//----------------------------------------------------------------------------- -// Purpose: Insert a string into the text buffer, this is just a series -// of char inserts because we have to check each char is ok to insert -//----------------------------------------------------------------------------- -void TextEntry::InsertString(const wchar_t *wszText) -{ - SaveUndoState(); - - for (const wchar_t *ch = wszText; *ch != 0; ++ch) - { - InsertChar(*ch); - } - - if (_dataChanged) - { - FireActionSignal(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Converts an ansi string to unicode and inserts it into the text stream -//----------------------------------------------------------------------------- -void TextEntry::InsertString(const char *text) -{ - // check for to see if the string is in the localization tables - if (text[0] == '#') - { - wchar_t *wsz = g_pVGuiLocalize->Find(text); - if (wsz) - { - InsertString(wsz); - return; - } - } - - // straight convert the ansi to unicode and insert - wchar_t unicode[1024]; - g_pVGuiLocalize->ConvertANSIToUnicode(text, unicode, sizeof(unicode)); - InsertString(unicode); -} - -//----------------------------------------------------------------------------- -// Purpose: Handle the effect of user hitting backspace key -// we delete the char before the cursor and reformat the text so it -// behaves like in windows. -//----------------------------------------------------------------------------- -void TextEntry::Backspace() -{ - if (!IsEditable()) - return; - - //if you are at the first position don't do anything - if(_cursorPos==0) - { - return; - } - - //if the line is empty, don't do anything - if(m_TextStream.Count()==0) - { - return; - } - - SaveUndoState(); - - //shift chars left one, starting at the cursor position, then make the line one smaller - for(int i=_cursorPos;i= 0) // dont scroll if there are not enough chars to scroll - { - _currentStartIndex-=6; - } - else - _currentStartIndex=0; - } - - //move the cursor left one - _cursorPos--; - - _dataChanged = true; - - // recalculate linebreaks (the fast incremental linebreak function doesn't work in this case) - _recalculateBreaksIndex = 0; - m_LineBreaks.RemoveAll(); - m_LineBreaks.AddToTail(BUFFER_SIZE); - - LayoutVerticalScrollBarSlider(); - ResetCursorBlink(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Deletes the current selection, if any, moving the cursor to the Start -// of the selection -//----------------------------------------------------------------------------- -void TextEntry::DeleteSelected() -{ - if (!IsEditable()) - return; - - // if the line is empty, don't do anything - if (m_TextStream.Count() == 0) - return; - - // get the range to delete - int x0, x1; - if (!GetSelectedRange(x0, x1)) - { - // no selection, don't touch anything - return; - } - - SaveUndoState(); - - // shift chars left one starting after cursor position, then make the line one smaller - int dif = x1 - x0; - for (int i = 0; i < dif; ++i) - { - m_TextStream.Remove(x0); - } - - // clear any selection - SelectNone(); - ResetCursorBlink(); - - // move the cursor to just after the deleted section - _cursorPos = x0; - - _dataChanged = true; - - _recalculateBreaksIndex = 0; - m_LineBreaks.RemoveAll(); - m_LineBreaks.AddToTail(BUFFER_SIZE); - - CalcBreakIndex(); - - LayoutVerticalScrollBarSlider(); -} - -//----------------------------------------------------------------------------- -// Purpose: Handle the effect of the user hitting the delete key -// removes the char in front of the cursor -//----------------------------------------------------------------------------- -void TextEntry::Delete() -{ - if (!IsEditable()) - return; - - // if the line is empty, don't do anything - if (m_TextStream.Count() == 0) - return; - - // get the range to delete - int x0, x1; - if (!GetSelectedRange(x0, x1)) - { - // no selection, so just delete the one character - x0 = _cursorPos; - x1 = x0 + 1; - - // if we're at the end of the line don't do anything - if (_cursorPos >= m_TextStream.Count()) - return; - } - - SaveUndoState(); - - // shift chars left one starting after cursor position, then make the line one smaller - int dif = x1 - x0; - for (int i = 0; i < dif; i++) - { - m_TextStream.Remove((int)x0); - } - - ResetCursorBlink(); - - // clear any selection - SelectNone(); - - // move the cursor to just after the deleted section - _cursorPos = x0; - - _dataChanged = true; - - _recalculateBreaksIndex = 0; - m_LineBreaks.RemoveAll(); - m_LineBreaks.AddToTail(BUFFER_SIZE); - - CalcBreakIndex(); - - LayoutVerticalScrollBarSlider(); -} - -//----------------------------------------------------------------------------- -// Purpose: Declare a selection empty -//----------------------------------------------------------------------------- -void TextEntry::SelectNone() -{ - // tag the selection as empty - _select[0] = -1; - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Load in the selection range so cx0 is the Start and cx1 is the end -// from smallest to highest (right to left) -//----------------------------------------------------------------------------- -bool TextEntry::GetSelectedRange(int& cx0,int& cx1) -{ - // if there is nothing selected return false - if (_select[0] == -1) - { - return false; - } - - // sort the two position so cx0 is the smallest - cx0=_select[0]; - cx1=_select[1]; - int temp; - if(cx1GetCursorPos(cursorX, cursorY); - - /* !! disabled since it recursively gets panel pointers, potentially across dll boundaries, - and doesn't need to be necessary (it's just for handling windowed mode) - - // find the frame that has no parent (the one on the desktop) - Panel *panel = this; - while ( panel->GetParent() != NULL) - { - panel = panel->GetParent(); - } - panel->ScreenToLocal(cursorX, cursorY); - int x, y; - // get base panel's postition - panel->GetPos(x, y); - - // adjust our cursor position accordingly - cursorX += x; - cursorY += y; - */ - - int x0, x1; - if (GetSelectedRange(x0, x1)) // there is something selected - { - m_pEditMenu->SetItemEnabled("&Cut", true); - m_pEditMenu->SetItemEnabled("C&opy", true); - } - else // there is nothing selected, disable cut/copy options - { - m_pEditMenu->SetItemEnabled("&Cut", false); - m_pEditMenu->SetItemEnabled("C&opy", false); - } - m_pEditMenu->SetVisible(true); - m_pEditMenu->RequestFocus(); - - // relayout the menu immediately so that we know it's size - m_pEditMenu->InvalidateLayout(true); - int menuWide, menuTall; - m_pEditMenu->GetSize(menuWide, menuTall); - - // work out where the cursor is and therefore the best place to put the menu - int wide, tall; - surface()->GetScreenSize(wide, tall); - - if (wide - menuWide > cursorX) - { - // menu hanging right - if (tall - menuTall > cursorY) - { - // menu hanging down - m_pEditMenu->SetPos(cursorX, cursorY); - } - else - { - // menu hanging up - m_pEditMenu->SetPos(cursorX, cursorY - menuTall); - } - } - else - { - // menu hanging left - if (tall - menuTall > cursorY) - { - // menu hanging down - m_pEditMenu->SetPos(cursorX - menuWide, cursorY); - } - else - { - // menu hanging up - m_pEditMenu->SetPos(cursorX - menuWide, cursorY - menuTall); - } - } - - m_pEditMenu->RequestFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: Cuts the selected chars from the buffer and -// copies them into the clipboard -//----------------------------------------------------------------------------- -void TextEntry::CutSelected() -{ - CopySelected(); - DeleteSelected(); - // have to request focus if we used the menu - RequestFocus(); - - if ( _dataChanged ) - { - FireActionSignal(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Copies the selected chars into the clipboard -//----------------------------------------------------------------------------- -void TextEntry::CopySelected() -{ - if (_hideText) - return; - - int x0, x1; - if (GetSelectedRange(x0, x1)) - { - CUtlVector buf; - for (int i = x0; i < x1; i++) - { - if ( m_TextStream[i]=='\n') - { - buf.AddToTail( '\r' ); - } - buf.AddToTail(m_TextStream[i]); - } - buf.AddToTail('\0'); - system()->SetClipboardText(buf.Base(), buf.Count()); - } - - // have to request focus if we used the menu - RequestFocus(); - - if ( _dataChanged ) - { - FireActionSignal(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Pastes the selected chars from the clipboard into the text buffer -// truncates if text is longer than our _maxCharCount -//----------------------------------------------------------------------------- -void TextEntry::Paste() -{ - if (!IsEditable()) - return; - - CUtlVector buf; - int bufferSize = system()->GetClipboardTextCount(); - if (!m_bAutoProgressOnHittingCharLimit) - { - bufferSize = _maxCharCount > 0 ? _maxCharCount + 1 : system()->GetClipboardTextCount(); // +1 for terminator - } - - buf.AddMultipleToTail(bufferSize); - int len = system()->GetClipboardText(0, buf.Base(), bufferSize * sizeof(wchar_t)); - if (len < 1) - return; - - SaveUndoState(); - bool bHaveMovedFocusAwayFromCurrentEntry = false; - - // insert all the characters - for (int i = 0; i < len && buf[i] != 0; i++) - { - if (m_bAutoProgressOnHittingCharLimit) - { - // see if we're about to hit the char limit - if (m_TextStream.Count() == _maxCharCount) - { - // move the next panel (most likely another TextEntry) - RequestFocusNext(); - // copy the remainder into the clipboard - wchar_t *remainingText = &buf[i]; - system()->SetClipboardText(remainingText, len - i - 1); - // set the next entry to paste - if (GetVParent() && ipanel()->GetCurrentKeyFocus(GetVParent()) != GetVPanel()) - { - bHaveMovedFocusAwayFromCurrentEntry = true; - ipanel()->SendMessage(ipanel()->GetCurrentKeyFocus(GetVParent()), new KeyValues("DoPaste"), GetVPanel()); - } - break; - } - } - - // insert the character - InsertChar(buf[i]); - } - - // restore the original clipboard text if neccessary - if (m_bAutoProgressOnHittingCharLimit) - { - system()->SetClipboardText(buf.Base(), bufferSize); - } - - _dataChanged = true; - FireActionSignal(); - - if (!bHaveMovedFocusAwayFromCurrentEntry) - { - // have to request focus if we used the menu - RequestFocus(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Reverts back to last saved changes -//----------------------------------------------------------------------------- -void TextEntry::Undo() -{ - _cursorPos = _undoCursorPos; - m_TextStream.CopyArray(m_UndoTextStream.Base(), m_UndoTextStream.Count()); - - InvalidateLayout(); - Repaint(); - SelectNone(); -} - -//----------------------------------------------------------------------------- -// Purpose: Saves the current state to the undo stack -//----------------------------------------------------------------------------- -void TextEntry::SaveUndoState() -{ - _undoCursorPos = _cursorPos; - m_UndoTextStream.CopyArray(m_TextStream.Base(), m_TextStream.Count()); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the index in the text buffer of the -// character the drawing should Start at -//----------------------------------------------------------------------------- -int TextEntry::GetStartDrawIndex(int &lineBreakIndexIndex) -{ - int startIndex = 0; - - int numLines = m_LineBreaks.Count(); - int startLine = 0; - - // determine the Start point from the scroll bar - // do this only if we are not selecting text in the window with the mouse - if (_vertScrollBar && !_mouseDragSelection) - { - // skip to line indicated by scrollbar - startLine = _vertScrollBar->GetValue(); - } - else - { - // check to see if the cursor is off the screen-multiline case - HFont font = _font; - int displayLines = GetTall() / (surface()->GetFontTall(font) + DRAW_OFFSET_Y); - if (displayLines < 1) - { - displayLines = 1; - } - if (numLines > displayLines) - { - int cursorLine = GetCursorLine(); - - startLine = _currentStartLine; - - // see if that is visible - if (cursorLine < _currentStartLine) - { - // cursor is above visible area; scroll back - startLine = cursorLine; - if (_vertScrollBar) - { - MoveScrollBar( 1 ); // should be calibrated for speed - // adjust startline incase we hit a limit - startLine = _vertScrollBar->GetValue(); - } - } - else if (cursorLine > (_currentStartLine + displayLines - 1)) - { - // cursor is down below visible area; scroll forward - startLine = cursorLine - displayLines + 1; - if (_vertScrollBar) - { - MoveScrollBar( -1 ); - startLine = _vertScrollBar->GetValue(); - } - } - } - else if (!_multiline) - { - // check to see if cursor is off the right side of screen-single line case - // get cursor's x coordinate in pixel space - bool done = false; - while ( !done ) - { - done = true; - int x = DRAW_OFFSET_X; - for (int i = _currentStartIndex; i < m_TextStream.Count(); i++) - { - done = false; - wchar_t ch = m_TextStream[i]; - if (_hideText) - { - ch = '*'; - } - - // if we've found the position, break - if (_cursorPos == i) - { - break; - } - - // add to the current position - x += getCharWidth(font, ch); - } - - if ( x >= GetWide() ) - { - _currentStartIndex++; - // Keep searching... - continue; - } - - if ( x <= 0 ) - { - // dont go past the Start of buffer - if (_currentStartIndex > 0) - _currentStartIndex--; - } - - break; - } - } - } - - if (startLine > 0) - { - lineBreakIndexIndex = startLine; - if (startLine && startLine < m_LineBreaks.Count()) - { - startIndex = m_LineBreaks[startLine - 1]; - } - } - - if (!_horizScrollingAllowed) - return 0; - - _currentStartLine = startLine; - if (_multiline) - return startIndex; - else - return _currentStartIndex; - - -} - -// helper accessors for common gets -float TextEntry::GetValueAsFloat() -{ - int nTextLength = GetTextLength() + 1; - char* txt = ( char* )_alloca( nTextLength * sizeof( char ) ); - GetText( txt, nTextLength ); - - return V_atof( txt ); -} - -int TextEntry::GetValueAsInt() -{ - int nTextLength = GetTextLength() + 1; - char* txt = ( char* )_alloca( nTextLength * sizeof( char ) ); - GetText( txt, nTextLength ); - - return V_atoi( txt ); -} - -//----------------------------------------------------------------------------- -// Purpose: Get a string from text buffer -// Input: offset - index to Start reading from -// bufLenInBytes - length of string -//----------------------------------------------------------------------------- -void TextEntry::GetText(OUT_Z_BYTECAP(bufLenInBytes) char *buf, int bufLenInBytes) -{ - Assert(bufLenInBytes >= sizeof(buf[0])); - if (m_TextStream.Count()) - { - // temporarily null terminate the text stream so we can use the conversion function - int nullTerminatorIndex = m_TextStream.AddToTail((wchar_t)0); - g_pVGuiLocalize->ConvertUnicodeToANSI(m_TextStream.Base(), buf, bufLenInBytes); - m_TextStream.FastRemove(nullTerminatorIndex); - } - else - { - // no characters in the stream - buf[0] = 0; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Get a string from text buffer -// Input: offset - index to Start reading from -// bufLen - length of string -//----------------------------------------------------------------------------- -void TextEntry::GetText(OUT_Z_BYTECAP(bufLenInBytes) wchar_t *wbuf, int bufLenInBytes) -{ - Assert(bufLenInBytes >= sizeof(wbuf[0])); - int len = m_TextStream.Count(); - if (m_TextStream.Count()) - { - int terminator = min(len, (bufLenInBytes / (int)sizeof(wchar_t)) - 1); - wcsncpy(wbuf, m_TextStream.Base(), terminator); - wbuf[terminator] = 0; - } - else - { - wbuf[0] = 0; - } -} - -void TextEntry::GetTextRange( wchar_t *buf, int from, int numchars ) -{ - int len = m_TextStream.Count(); - int cpChars = max( 0, min( numchars, len - from ) ); - - wcsncpy( buf, m_TextStream.Base() + max( 0, min( len, from ) ), cpChars ); - buf[ cpChars ] = 0; -} - -void TextEntry::GetTextRange( char *buf, int from, int numchars ) -{ - int len = m_TextStream.Count(); - int cpChars = max( 0, min( numchars, len - from ) ); - - g_pVGuiLocalize->ConvertUnicodeToANSI( m_TextStream.Base() + max( 0, min( len, from ) ), buf, cpChars + 1 ); - buf[ cpChars ] = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Sends a message that the text has changed -//----------------------------------------------------------------------------- -void TextEntry::FireActionSignal() -{ - PostActionSignal(new KeyValues("TextChanged")); - _dataChanged = false; // reset the data changed flag - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the font of the buffer text -// Input: font to change to -//----------------------------------------------------------------------------- -void TextEntry::SetFont(HFont font) -{ - _font = font; - InvalidateLayout(); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Called when the scrollbar slider is moved -//----------------------------------------------------------------------------- -void TextEntry::OnSliderMoved() -{ - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool TextEntry::RequestInfo(KeyValues *outputData) -{ - if (!stricmp(outputData->GetName(), "GetText")) - { - wchar_t wbuf[256]; - GetText(wbuf, 255); - outputData->SetWString("text", wbuf); - return true; - } - else if (!stricmp(outputData->GetName(), "GetState")) - { - char buf[64]; - GetText(buf, sizeof(buf)); - outputData->SetInt("state", atoi(buf)); - return true; - } - return BaseClass::RequestInfo(outputData); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TextEntry::OnSetText(const wchar_t *text) -{ - SetText(text); -} - -//----------------------------------------------------------------------------- -// Purpose: as above, but sets an integer -//----------------------------------------------------------------------------- -void TextEntry::OnSetState(int state) -{ - char buf[64]; - Q_snprintf(buf, sizeof(buf), "%d", state); - SetText(buf); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TextEntry::ApplySettings( KeyValues *inResourceData ) -{ - BaseClass::ApplySettings( inResourceData ); - - _font = scheme()->GetIScheme( GetScheme() )->GetFont( inResourceData->GetString( "font", "Default" ), IsProportional() ); - SetFont( _font ); - - SetTextHidden((bool)inResourceData->GetInt("textHidden", 0)); - SetEditable((bool)inResourceData->GetInt("editable", 1)); - SetMaximumCharCount(inResourceData->GetInt("maxchars", -1)); - SetAllowNumericInputOnly(inResourceData->GetInt("NumericInputOnly", 0)); - SetAllowNonAsciiCharacters(inResourceData->GetInt("unicode", 0)); - SelectAllOnFirstFocus(inResourceData->GetInt("selectallonfirstfocus", 0)); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TextEntry::GetSettings( KeyValues *outResourceData ) -{ - BaseClass::GetSettings( outResourceData ); - outResourceData->SetInt("textHidden", _hideText); - outResourceData->SetInt("editable", IsEditable()); - outResourceData->SetInt("maxchars", GetMaximumCharCount()); - outResourceData->SetInt("NumericInputOnly", m_bAllowNumericInputOnly); - outResourceData->SetInt("unicode", m_bAllowNonAsciiCharacters); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -const char *TextEntry::GetDescription() -{ - static char buf[1024]; - Q_snprintf(buf, sizeof(buf), "%s, bool textHidden, bool editable, bool unicode, bool NumericInputOnly, int maxchars", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the number of lines in the window -//----------------------------------------------------------------------------- -int TextEntry::GetNumLines() -{ - return m_LineBreaks.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the height of the text entry window so all text will fit inside -//----------------------------------------------------------------------------- -void TextEntry::SetToFullHeight() -{ - PerformLayout(); - int wide, tall; - GetSize(wide, tall); - - tall = GetNumLines() * (surface()->GetFontTall(_font) + DRAW_OFFSET_Y) + DRAW_OFFSET_Y + 2; - SetSize (wide, tall); - PerformLayout(); - -} - -//----------------------------------------------------------------------------- -// Purpose: Select all the text. -//----------------------------------------------------------------------------- -void TextEntry::SelectAllText( bool bResetCursorPos ) -{ - // if there's no text at all, select none - if ( m_TextStream.Count() == 0 ) - { - _select[0] = -1; - } - else - { - _select[0] = 0; - } - - _select[1] = m_TextStream.Count(); - - if ( bResetCursorPos ) - { - _cursorPos = _select[1]; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Select no text. -//----------------------------------------------------------------------------- -void TextEntry::SelectNoText() -{ - _select[0] = -1; - _select[1] = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the width of the text entry window so all text will fit inside -//----------------------------------------------------------------------------- -void TextEntry::SetToFullWidth() -{ - // probably be problems if you try using this on multi line buffers - // or buffers with clickable text in them. - if (_multiline) - return; - - PerformLayout(); - int wide = 2*DRAW_OFFSET_X; // buffer on left and right end of text. - - // loop through all the characters and sum their widths - for (int i = 0; i < m_TextStream.Count(); ++i) - { - wide += getCharWidth(_font, m_TextStream[i]); - } - - // height of one line of text - int tall = (surface()->GetFontTall(_font) + DRAW_OFFSET_Y) + DRAW_OFFSET_Y + 2; - - SetSize (wide, tall); - PerformLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TextEntry::SelectAllOnFirstFocus( bool status ) -{ - _selectAllOnFirstFocus = status; -} - -void TextEntry::SelectAllOnFocusAlways( bool status ) -{ - _selectAllOnFirstFocus = status; - _selectAllOnFocusAlways = status; -} - -//----------------------------------------------------------------------------- -// Purpose: called when the text entry receives focus -//----------------------------------------------------------------------------- -void TextEntry::OnSetFocus() -{ - // see if we should highlight all on selection - if (_selectAllOnFirstFocus) - { - _select[1] = m_TextStream.Count(); - _select[0] = _select[1] > 0 ? 0 : -1; - _cursorPos = _select[1]; // cursor at end of line - if ( !_selectAllOnFocusAlways ) - { - _selectAllOnFirstFocus = false; - } - } - else if (input()->IsKeyDown(KEY_TAB) || input()->WasKeyReleased(KEY_TAB)) - { - // if we've tabbed to this field then move to the end of the text - GotoTextEnd(); - // clear any selection - SelectNone(); - } - - BaseClass::OnSetFocus(); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the width we have to draw text in. -// Do not use in multiline windows. -//----------------------------------------------------------------------------- -void TextEntry::SetDrawWidth(int width) -{ - _drawWidth = width; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the width we have to draw text in. -//----------------------------------------------------------------------------- -int TextEntry::GetDrawWidth() -{ - return _drawWidth; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void TextEntry::SetAllowNonAsciiCharacters(bool state) -{ - m_bAllowNonAsciiCharacters = state; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void TextEntry::SetAllowNumericInputOnly(bool state) -{ - m_bAllowNumericInputOnly = state; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : forward - -//----------------------------------------------------------------------------- -void TextEntry::OnChangeIME( bool forward ) -{ - // Only change ime if Unicode aware - if ( m_bAllowNonAsciiCharacters ) - { - input()->OnChangeIME( forward ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : handleValue - -//----------------------------------------------------------------------------- -void TextEntry::LanguageChanged( int handleValue ) -{ - input()->OnChangeIMEByHandle( handleValue ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : handleValue - -//----------------------------------------------------------------------------- -void TextEntry::ConversionModeChanged( int handleValue ) -{ - input()->OnChangeIMEConversionModeByHandle( handleValue ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : handleValue - -//----------------------------------------------------------------------------- -void TextEntry::SentenceModeChanged( int handleValue ) -{ - input()->OnChangeIMESentenceModeByHandle( handleValue ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *compstr - -//----------------------------------------------------------------------------- -void TextEntry::CompositionString( const wchar_t *compstr ) -{ - wcsncpy( m_szComposition, compstr, sizeof( m_szComposition ) / sizeof( wchar_t ) - 1 ); - m_szComposition[ sizeof( m_szComposition ) / sizeof( wchar_t ) - 1 ] = L'\0'; -} - -void TextEntry::ShowIMECandidates() -{ - HideIMECandidates(); - - int c = input()->GetCandidateListCount(); - if ( c == 0 ) - { - return; - } - - m_pIMECandidates = new Menu( this, "IMECandidatesMenu" ); - - int pageStart = input()->GetCandidateListPageStart(); - int pageSize = input()->GetCandidateListPageSize(); - int selected = input()->GetCandidateListSelectedItem(); - - int startAtOne = input()->CandidateListStartsAtOne() ? 1 : 0; - - if ( ( selected < pageStart ) || ( selected >= pageStart + pageSize ) ) - { - pageStart = ( selected / pageSize ) * pageSize; - input()->SetCandidateListPageStart( pageStart ); - } - - for ( int i = pageStart; i < pageStart + pageSize; ++i ) - { - if ( i >= c ) - continue; - - bool isSelected = ( i == selected ) ? true : false; - - wchar_t unicode[ 32 ]; - input()->GetCandidate( i, unicode, sizeof( unicode ) ); - - wchar_t label[ 64 ]; - _snwprintf( label, sizeof( label ) / sizeof( wchar_t ) - 1, L"%i %s", i - pageStart + startAtOne, unicode ); - label[ sizeof( label ) / sizeof( wchar_t ) - 1 ] = L'\0'; - - int id = m_pIMECandidates->AddMenuItem( "Candidate", label, (KeyValues *)NULL, this ); - if ( isSelected ) - { - m_pIMECandidates->SetCurrentlyHighlightedItem( id ); - } - } - - m_pIMECandidates->SetVisible(true); - m_pIMECandidates->SetParent(this); - m_pIMECandidates->AddActionSignalTarget(this); - m_pIMECandidates->SetKeyBoardInputEnabled( false ); - - int cx, cy; - CursorToPixelSpace(_cursorPos, cx, cy); - cy = GetTall(); - - LocalToScreen( cx, cy ); - - //m_pIMECandidates->SetPos( cx, cy ); - - // relayout the menu immediately so that we know it's size - m_pIMECandidates->InvalidateLayout(true); - int menuWide, menuTall; - m_pIMECandidates->GetSize(menuWide, menuTall); - - // work out where the cursor is and therefore the best place to put the menu - int wide, tall; - surface()->GetScreenSize(wide, tall); - - if (wide - menuWide > cx) - { - // menu hanging right - if (tall - menuTall > cy) - { - // menu hanging down - m_pIMECandidates->SetPos(cx, cy); - } - else - { - // menu hanging up - m_pIMECandidates->SetPos(cx, cy - menuTall - GetTall()); - } - } - else - { - // menu hanging left - if (tall - menuTall > cy) - { - // menu hanging down - m_pIMECandidates->SetPos(cx - menuWide, cy); - } - else - { - // menu hanging up - m_pIMECandidates->SetPos(cx - menuWide, cy - menuTall-GetTall()); - } - } -} - -void TextEntry::HideIMECandidates() -{ - if ( m_pIMECandidates ) - { - m_pIMECandidates->SetVisible( false ); - } - delete m_pIMECandidates; - m_pIMECandidates = NULL; -} - -void TextEntry::UpdateIMECandidates() -{ - if ( !m_pIMECandidates ) - return; - - int c = input()->GetCandidateListCount(); - if ( c == 0 ) - { - HideIMECandidates(); - return; - } - - int oldCount = m_pIMECandidates->GetItemCount(); - int newCount = input()->GetCandidateListPageSize(); - - if ( oldCount != newCount ) - { - // Recreate the entire menu - ShowIMECandidates(); - return; - } - - int pageSize = input()->GetCandidateListPageSize(); - int selected = input()->GetCandidateListSelectedItem(); - int pageStart = input()->GetCandidateListPageStart(); - - if ( ( selected < pageStart ) || selected >= pageStart + pageSize ) - { - pageStart = ( selected / pageSize ) * pageSize; - input()->SetCandidateListPageStart( pageStart ); - } - - int startAtOne = input()->CandidateListStartsAtOne() ? 1 : 0; - - for ( int i = pageStart; i < pageStart + pageSize; ++i ) - { - int id = m_pIMECandidates->GetMenuID( i - pageStart ); - - MenuItem *item = m_pIMECandidates->GetMenuItem( id ); - if ( !item ) - continue; - - if ( i >= c ) - { - item->SetVisible( false ); - continue; - } - else - { - item->SetVisible( true ); - } - - bool isSelected = ( i == selected ) ? true : false; - - wchar_t unicode[ 32 ]; - input()->GetCandidate( i, unicode, sizeof( unicode ) ); - - wchar_t label[ 64 ]; - _snwprintf( label, sizeof( label ) / sizeof( wchar_t ) - 1, L"%i %s", i - pageStart + startAtOne, unicode ); - label[ sizeof( label ) / sizeof( wchar_t ) - 1 ] = L'\0'; - item->SetText( label ); - if ( isSelected ) - { - m_pIMECandidates->SetCurrentlyHighlightedItem( id ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TextEntry::FlipToLastIME() -{ - int hCurrentIME = input()->GetCurrentIMEHandle(); - int hEnglishIME = input()->GetEnglishIMEHandle(); - - bool isEnglish = ( hCurrentIME == hEnglishIME ) ? true : false; - - // If in english, flip back to previous - if ( isEnglish ) - { - input()->OnChangeIMEByHandle( m_hPreviousIME ); - } - else - { - // If not, remember language and flip to english... - m_hPreviousIME = hCurrentIME; - input()->OnChangeIMEByHandle( hEnglishIME ); - } -} - -void TextEntry::SetDrawLanguageIDAtLeft( bool state ) -{ - m_bDrawLanguageIDAtLeft = state; -} - -bool TextEntry::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ) -{ - menu->AddMenuItem( "replace", "#TextEntry_ReplaceText", "replace", this ); - menu->AddMenuItem( "append", "#TextEntry_AppendText", "append", this ); - menu->AddMenuItem( "prepend", "#TextEntry_PrependText", "prepend", this ); - return true; -} - -bool TextEntry::IsDroppable( CUtlVector< KeyValues * >& msglist ) -{ - if ( msglist.Count() != 1 ) - return false; - - if ( !IsEnabled() ) - return false; - - KeyValues *msg = msglist[ 0 ]; - - const wchar_t *txt = msg->GetWString( "text", L"" ); - if ( !txt || txt[ 0 ] == L'\0' ) - return false; - - return true; -} - -void TextEntry::OnPanelDropped( CUtlVector< KeyValues * >& msglist ) -{ - if ( msglist.Count() != 1 ) - return; - - KeyValues *data = msglist[ 0 ]; - - const wchar_t *newText = data->GetWString( "text" ); - if ( !newText || newText[ 0 ] == L'\0' ) - return; - - char const *cmd = data->GetString( "command" ); - if ( !Q_stricmp( cmd, "replace" ) || - !Q_stricmp( cmd, "default" ) ) - { - SetText( newText ); - _dataChanged = true; - FireActionSignal(); - } - else if ( !Q_stricmp( cmd, "append" ) ) - { - int newLen = wcslen( newText ); - int curLen = m_TextStream.Count(); - - size_t outsize = sizeof( wchar_t ) * ( newLen + curLen + 1 ); - wchar_t *out = (wchar_t *)_alloca( outsize ); - Q_memset( out, 0, outsize ); - wcsncpy( out, m_TextStream.Base(), curLen ); - wcsncat( out, newText, wcslen( newText ) ); - out[ newLen + curLen ] = L'\0'; - SetText( out ); - _dataChanged = true; - FireActionSignal(); - } - else if ( !Q_stricmp( cmd, "prepend" ) ) - { - int newLen = wcslen( newText ); - int curLen = m_TextStream.Count(); - - size_t outsize = sizeof( wchar_t ) * ( newLen + curLen + 1 ); - wchar_t *out = (wchar_t *)_alloca( outsize ); - Q_memset( out, 0, outsize ); - wcsncpy( out, newText, wcslen( newText ) ); - wcsncat( out, m_TextStream.Base(), curLen ); - out[ newLen + curLen ] = L'\0'; - SetText( out ); - _dataChanged = true; - FireActionSignal(); - } -} - -int TextEntry::GetTextLength() const -{ - return m_TextStream.Count(); -} - -bool TextEntry::IsTextFullySelected() const -{ - if ( _select[ 0 ] != 0 ) - return false; - - if ( _select[ 1 ] != GetTextLength() ) - return false; - - return true; -} - -void TextEntry::SetUseFallbackFont( bool bState, HFont hFallback ) -{ - m_bUseFallbackFont = bState; - m_hFallbackFont = hFallback; -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +// memdbgon must be the last include file in a .cpp file!!! +#include + +enum +{ + // maximum size of text buffer + BUFFER_SIZE=999999, +}; + +using namespace vgui; + +static const int DRAW_OFFSET_X = 3,DRAW_OFFSET_Y = 1; + +DECLARE_BUILD_FACTORY( TextEntry ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +TextEntry::TextEntry(Panel *parent, const char *panelName) : BaseClass(parent, panelName) +{ + SetTriplePressAllowed( true ); + + _font = INVALID_FONT; + _smallfont = INVALID_FONT; + + m_szComposition[ 0 ] = L'\0'; + + m_bAllowNumericInputOnly = false; + m_bAllowNonAsciiCharacters = false; + _hideText = false; + _editable = false; + _verticalScrollbar = false; + _cursorPos = 0; + _currentStartIndex = 0; + _horizScrollingAllowed = true; + _cursorIsAtEnd = false; + _putCursorAtEnd = false; + _multiline = false; + _cursorBlinkRate = 400; + _mouseSelection = false; + _mouseDragSelection = false; + _vertScrollBar=NULL; + _catchEnterKey = false; + _maxCharCount = -1; + _charCount = 0; + _wrap = false; // don't wrap by default + _sendNewLines = false; // don't pass on a newline msg by default + _drawWidth = 0; + m_bAutoProgressOnHittingCharLimit = false; + m_pIMECandidates = NULL; + m_hPreviousIME = input()->GetEnglishIMEHandle(); + m_bDrawLanguageIDAtLeft = false; + m_nLangInset = 0; + m_bUseFallbackFont = false; + m_hFallbackFont = INVALID_FONT; + + //a -1 for _select[0] means that the selection is empty + _select[0] = -1; + _select[1] = -1; + m_pEditMenu = NULL; + + //this really just inits it when in here + ResetCursorBlink(); + + SetCursor(dc_ibeam); + + SetEditable(true); + + // initialize the line break array + m_LineBreaks.AddToTail(BUFFER_SIZE); + + _recalculateBreaksIndex = 0; + + _selectAllOnFirstFocus = false; + _selectAllOnFocusAlways = false; + + //position the cursor so it is at the end of the text + GotoTextEnd(); + + // If keyboard focus is in an edit control, don't chain keyboard mappings up to parents since it could mess with typing in text. + SetAllowKeyBindingChainToParent( false ); + + REGISTER_COLOR_AS_OVERRIDABLE( _disabledFgColor, "disabledFgColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _disabledBgColor, "disabledBgColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _selectionColor, "selectionColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _selectionTextColor, "selectionTextColor_override" ); + REGISTER_COLOR_AS_OVERRIDABLE( _defaultSelectionBG2Color, "defaultSelectionBG2Color_override" ); +} + + +TextEntry::~TextEntry() +{ + delete m_pEditMenu; + delete m_pIMECandidates; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TextEntry::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetFgColor(GetSchemeColor("TextEntry.TextColor", pScheme)); + SetBgColor(GetSchemeColor("TextEntry.BgColor", pScheme)); + + _cursorColor = GetSchemeColor("TextEntry.CursorColor", pScheme); + _disabledFgColor = GetSchemeColor("TextEntry.DisabledTextColor", pScheme); + _disabledBgColor = GetSchemeColor("TextEntry.DisabledBgColor", pScheme); + + _selectionTextColor = GetSchemeColor("TextEntry.SelectedTextColor", GetFgColor(), pScheme); + _selectionColor = GetSchemeColor("TextEntry.SelectedBgColor", pScheme); + _defaultSelectionBG2Color = GetSchemeColor("TextEntry.OutOfFocusSelectedBgColor", pScheme); + _focusEdgeColor = GetSchemeColor("TextEntry.FocusEdgeColor", Color(0, 0, 0, 0), pScheme); + + SetBorder( pScheme->GetBorder("ButtonDepressedBorder")); + + if ( _font == INVALID_FONT ) _font = pScheme->GetFont("Default", IsProportional() ); + if ( _smallfont == INVALID_FONT ) _smallfont = pScheme->GetFont( "DefaultVerySmall", IsProportional() ); + + SetFont( _font ); +} + +void TextEntry::SetSelectionTextColor( const Color& clr ) +{ + _selectionTextColor = clr; +} + +void TextEntry::SetSelectionBgColor( const Color& clr ) +{ + _selectionColor = clr; +} + +void TextEntry::SetSelectionUnfocusedBgColor( const Color& clr ) +{ + _defaultSelectionBG2Color = clr; +} + +//----------------------------------------------------------------------------- +// Purpose: sets the color of the background when the control is disabled +//----------------------------------------------------------------------------- +void TextEntry::SetDisabledBgColor(Color col) +{ + _disabledBgColor = col; +} + + +//----------------------------------------------------------------------------- +// Purpose: Sends a message if the data has changed +// Turns off any selected text in the window if we are not using the edit menu +//----------------------------------------------------------------------------- +void TextEntry::OnKillFocus() +{ + m_szComposition[ 0 ] = L'\0'; + HideIMECandidates(); + + if (_dataChanged) + { + FireActionSignal(); + _dataChanged = false; + } + + // check if we clicked the right mouse button or if it is down + bool mouseRightClicked = input()->WasMousePressed(MOUSE_RIGHT); + bool mouseRightUp = input()->WasMouseReleased(MOUSE_RIGHT); + bool mouseRightDown = input()->IsMouseDown(MOUSE_RIGHT); + + if (mouseRightClicked || mouseRightDown || mouseRightUp ) + { + int cursorX, cursorY; + input()->GetCursorPos(cursorX, cursorY); + + // if we're right clicking within our window, we don't actually kill focus + if (IsWithin(cursorX, cursorY)) + return; + } + + // clear any selection + SelectNone(); + + // move the cursor to the start +// GotoTextStart(); + + PostActionSignal( new KeyValues( "TextKillFocus" ) ); + + // chain + BaseClass::OnKillFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Wipe line breaks after the size of a panel has been changed +//----------------------------------------------------------------------------- +void TextEntry::OnSizeChanged(int newWide, int newTall) +{ + BaseClass::OnSizeChanged(newWide, newTall); + + // blow away the line breaks list + _recalculateBreaksIndex = 0; + m_LineBreaks.RemoveAll(); + m_LineBreaks.AddToTail(BUFFER_SIZE); + + // if we're bigger, see if we can scroll left to put more text in the window + if (newWide > _drawWidth) + { + ScrollLeftForResize(); + } + + _drawWidth = newWide; + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Set the text array - convert ANSI text to unicode and pass to unicode function +//----------------------------------------------------------------------------- +void TextEntry::SetText(const char *text) +{ + if (!text) + { + text = ""; + } + + if (text[0] == '#') + { + // check for localization + wchar_t *wsz = g_pVGuiLocalize->Find(text); + if (wsz) + { + SetText(wsz); + return; + } + } + + size_t len = strlen( text ); + if ( len < 1023 ) + { + wchar_t unicode[ 1024 ]; + g_pVGuiLocalize->ConvertANSIToUnicode( text, unicode, sizeof( unicode ) ); + SetText( unicode ); + } + else + { + size_t lenUnicode = ( len * sizeof( wchar_t ) + 4 ); + wchar_t *unicode = ( wchar_t * ) malloc( lenUnicode ); + g_pVGuiLocalize->ConvertANSIToUnicode( text, unicode, lenUnicode ); + SetText( unicode ); + free( unicode ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the text array +// Using this function will cause all lineBreaks to be discarded. +// This is because this fxn replaces the contents of the text buffer. +// For modifying large buffers use insert functions. +//----------------------------------------------------------------------------- +void TextEntry::SetText(const wchar_t *wszText) +{ + if (!wszText) + { + wszText = L""; + } + int textLen = wcslen(wszText); + m_TextStream.RemoveAll(); + m_TextStream.EnsureCapacity(textLen); + + int missed_count = 0; + for (int i = 0; i < textLen; i++) + { + if(wszText[i]=='\r') // don't insert \r characters + { + missed_count++; + continue; + } + m_TextStream.AddToTail(wszText[i]); + SetCharAt(wszText[i], i-missed_count); + } + + GotoTextStart(); + SelectNone(); + + // reset the data changed flag + _dataChanged = false; + + // blow away the line breaks list + _recalculateBreaksIndex = 0; + m_LineBreaks.RemoveAll(); + m_LineBreaks.AddToTail(BUFFER_SIZE); + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the value of char at index position. +//----------------------------------------------------------------------------- +void TextEntry::SetCharAt(wchar_t ch, int index) +{ + if ((ch == '\n') || (ch == '\0')) + { + // if its not at the end of the buffer it matters. + // redo the linebreaks + //if (index != m_TextStream.Count()) + { + _recalculateBreaksIndex = 0; + m_LineBreaks.RemoveAll(); + m_LineBreaks.AddToTail(BUFFER_SIZE); + } + } + + if (index < 0) + return; + + if (index >= m_TextStream.Count()) + { + m_TextStream.AddMultipleToTail(index - m_TextStream.Count() + 1); + } + m_TextStream[index] = ch; + _dataChanged = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Restarts the time of the next cursor blink +//----------------------------------------------------------------------------- +void TextEntry::ResetCursorBlink() +{ + _cursorBlink=false; + _cursorNextBlinkTime=system()->GetTimeMillis()+_cursorBlinkRate; +} + +//----------------------------------------------------------------------------- +// Purpose: Hides the text buffer so it will not be drawn +//----------------------------------------------------------------------------- +void TextEntry::SetTextHidden(bool bHideText) +{ + _hideText = bHideText; + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: return character width +//----------------------------------------------------------------------------- +int getCharWidth(HFont font, wchar_t ch) +{ + if (!iswcntrl(ch)) + { + int a, b, c; + surface()->GetCharABCwide(font, ch, a, b, c); + return (a + b + c); + } + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Given cursor's position in the text buffer, convert it to +// the local window's x and y pixel coordinates +// Input: cursorPos: cursor index +// Output: cx, cy, the corresponding coords in the local window +//----------------------------------------------------------------------------- +void TextEntry::CursorToPixelSpace(int cursorPos, int &cx, int &cy) +{ + int yStart = GetYStart(); + + int x = DRAW_OFFSET_X, y = yStart; + _pixelsIndent = 0; + int lineBreakIndexIndex = 0; + + for (int i = GetStartDrawIndex(lineBreakIndexIndex); i < m_TextStream.Count(); i++) + { + wchar_t ch = m_TextStream[i]; + if (_hideText) + { + ch = '*'; + } + + // if we've found the position, break + if (cursorPos == i) + { + // even if this is a line break entry for the cursor, the next insert + // will be at this position, which will push the line break forward one + // so don't push the cursor down a line here... + /*if (!_putCursorAtEnd) + { + // if we've passed a line break go to that + if (m_LineBreaks[lineBreakIndexIndex] == i) + { + // add another line + AddAnotherLine(x,y); + lineBreakIndexIndex++; + } + }*/ + break; + } + + // if we've passed a line break go to that + if (m_LineBreaks.Count() && + lineBreakIndexIndex < m_LineBreaks.Count() && + m_LineBreaks[lineBreakIndexIndex] == i) + { + // add another line + AddAnotherLine(x,y); + lineBreakIndexIndex++; + } + + // add to the current position + x += getCharWidth(_font, ch); + } + + if ( m_bDrawLanguageIDAtLeft ) + { + x += m_nLangInset; + } + + cx = x; + cy = y; +} + +//----------------------------------------------------------------------------- +// Purpose: Converts local pixel coordinates to an index in the text buffer +// This function appears to be used only in response to mouse clicking +// Input : cx - +// cy - pixel location +//----------------------------------------------------------------------------- +int TextEntry::PixelToCursorSpace(int cx, int cy) +{ + + int w, h; + GetSize(w, h); + cx = clamp(cx, 0, w+100); + cy = clamp(cy, 0, h); + + _putCursorAtEnd = false; // Start off assuming we clicked somewhere in the text + + int fontTall = surface()->GetFontTall(_font); + + // where to Start reading + int yStart = GetYStart(); + int x = DRAW_OFFSET_X, y = yStart; + _pixelsIndent = 0; + int lineBreakIndexIndex = 0; + + int startIndex = GetStartDrawIndex(lineBreakIndexIndex); + bool onRightLine = false; + int i; + for (i = startIndex; i < m_TextStream.Count(); i++) + { + wchar_t ch = m_TextStream[i]; + if (_hideText) + { + ch = '*'; + } + + // if we are on the right line but off the end of if put the cursor at the end of the line + if (m_LineBreaks[lineBreakIndexIndex] == i ) + { + // add another line + AddAnotherLine(x,y); + lineBreakIndexIndex++; + + if (onRightLine) + { + _putCursorAtEnd = true; + return i; + } + } + + // check to see if we're on the right line + if (cy < yStart) + { + // cursor is above panel + onRightLine = true; + _putCursorAtEnd = true; // this will make the text scroll up if needed + } + else if (cy >= y && (cy < (y + fontTall + DRAW_OFFSET_Y))) + { + onRightLine = true; + } + + int wide = getCharWidth(_font, ch); + + // if we've found the position, break + if (onRightLine) + { + if (cx > GetWide()) // off right side of window + { + } + else if (cx < (DRAW_OFFSET_X + _pixelsIndent) || cy < yStart) // off left side of window + { + return i; // move cursor one to left + } + + if (cx >= x && cx < (x + wide)) + { + // check which side of the letter they're on + if (cx < (x + (wide * 0.5))) // left side + { + return i; + } + else // right side + { + return i + 1; + } + } + } + x += wide; + } + + return i; +} + +//----------------------------------------------------------------------------- +// Purpose: Draws a character in the panel +// Input: ch - character to draw +// font - font to use +// x, y - pixel location to draw char at +// Output: returns the width of the character drawn +//----------------------------------------------------------------------------- +int TextEntry::DrawChar(wchar_t ch, HFont font, int index, int x, int y) +{ + // add to the current position + int charWide = getCharWidth(font, ch); + int fontTall=surface()->GetFontTall(font); + if (!iswcntrl(ch)) + { + // draw selection, if any + int selection0 = -1, selection1 = -1; + GetSelectedRange(selection0, selection1); + + if (index >= selection0 && index < selection1) + { + // draw background selection color + VPANEL focus = input()->GetFocus(); + Color bgColor; + bool hasFocus = HasFocus(); + bool childOfFocus = focus && ipanel()->HasParent(focus, GetVPanel()); + + // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected + if ( hasFocus || childOfFocus ) + { + bgColor = _selectionColor; + } + else + { + bgColor =_defaultSelectionBG2Color; + } + + surface()->DrawSetColor(bgColor); + + surface()->DrawFilledRect(x, y, x + charWide, y + 1 + fontTall); + + // reset text color + surface()->DrawSetTextColor(_selectionTextColor); + } + if (index == selection1) + { + // we've come out of selection, reset the color + surface()->DrawSetTextColor(GetFgColor()); + } + + surface()->DrawSetTextPos(x, y); + surface()->DrawUnicodeChar(ch); + + return charWide; + } + + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Draw the cursor, cursor is not drawn when it is blinked gone +// Input: x,y where to draw cursor +// Output: returns true if cursor was drawn. +//----------------------------------------------------------------------------- +bool TextEntry::DrawCursor(int x, int y) +{ + if (!_cursorBlink) + { + int cx, cy; + CursorToPixelSpace(_cursorPos, cx, cy); + surface()->DrawSetColor(_cursorColor); + int fontTall=surface()->GetFontTall(_font); + surface()->DrawFilledRect(cx, cy, cx + 1, cy + fontTall); + return true; + } + return false; +} + +bool TextEntry::NeedsEllipses( HFont font, int *pIndex ) +{ + Assert( pIndex ); + *pIndex = -1; + int wide = DRAW_OFFSET_X; // buffer on left and right end of text. + for ( int i = 0; i < m_TextStream.Count(); ++i ) + { + wide += getCharWidth( font , m_TextStream[i] ); + if (wide > _drawWidth) + { + *pIndex = i; + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Draws the text in the panel +//----------------------------------------------------------------------------- +void TextEntry::PaintBackground() +{ + BaseClass::PaintBackground(); + + // draw background + Color col; + if (IsEnabled()) + { + col = GetBgColor(); + } + else + { + col = _disabledBgColor; + } + Color saveBgColor = col; + + int wide, tall; + GetSize( wide, tall ); + +// surface()->DrawSetColor(col); +// surface()->DrawFilledRect(0, 0, wide, tall); + + // where to Start drawing + int x = DRAW_OFFSET_X + _pixelsIndent, y = GetYStart(); + + m_nLangInset = 0; + + int langlen = 0; + wchar_t shortcode[ 5 ]; + shortcode[ 0 ] = L'\0'; + + if ( m_bAllowNonAsciiCharacters ) + { + input()->GetIMELanguageShortCode( shortcode, sizeof( shortcode ) ); + + if ( shortcode[ 0 ] != L'\0' && + wcsicmp( shortcode, L"EN" ) ) + { + m_nLangInset = 0; + langlen = wcslen( shortcode ); + for ( int i = 0; i < langlen; ++i ) + { + m_nLangInset += getCharWidth( _smallfont, shortcode[ i ] ); + } + + m_nLangInset += 4; + + if ( m_bDrawLanguageIDAtLeft ) + { + x += m_nLangInset; + } + + wide -= m_nLangInset; + } + } + + HFont useFont = _font; + + surface()->DrawSetTextFont(useFont); + if (IsEnabled()) + { + col = GetFgColor(); + } + else + { + col = _disabledFgColor; + } + surface()->DrawSetTextColor(col); + _pixelsIndent = 0; + + int lineBreakIndexIndex = 0; + int startIndex = GetStartDrawIndex(lineBreakIndexIndex); + int remembery = y; + + int oldEnd = m_TextStream.Count(); + int oldCursorPos = _cursorPos; + int nCompStart = -1; + int nCompEnd = -1; + + // FIXME: Should insert at cursor pos instead + bool composing = m_bAllowNonAsciiCharacters && wcslen( m_szComposition ) > 0; + bool invertcomposition = input()->GetShouldInvertCompositionString(); + + if ( composing ) + { + nCompStart = _cursorPos; + + wchar_t *s = m_szComposition; + while ( *s != L'\0' ) + { + m_TextStream.InsertBefore( _cursorPos, *s ); + ++s; + ++_cursorPos; + } + + nCompEnd = _cursorPos; + } + + bool highlight_composition = ( nCompStart != -1 && nCompEnd != -1 ) ? true : false; + + // draw text with an elipsis + if ( (!_multiline) && (!_horizScrollingAllowed) ) + { + int endIndex = m_TextStream.Count(); + // In editable windows only do the ellipsis if we don't have focus. + // In non editable windows do it all the time. + if ( (!HasFocus() && (IsEditable())) || (!IsEditable()) ) + { + int i = -1; + + // loop through all the characters and sum their widths + bool addEllipses = NeedsEllipses( useFont, &i ); + if ( addEllipses && + !IsEditable() && + m_bUseFallbackFont && + INVALID_FONT != m_hFallbackFont ) + { + // Switch to small font!!! + useFont = m_hFallbackFont; + surface()->DrawSetTextFont(useFont); + addEllipses = NeedsEllipses( useFont, &i ); + } + if (addEllipses) + { + int elipsisWidth = 3 * getCharWidth(useFont, '.'); + while (elipsisWidth > 0 && i >= 0) + { + elipsisWidth -= getCharWidth(useFont, m_TextStream[i]); + i--; + } + endIndex = i + 1; + } + + // if we take off less than the last 3 chars we have to make sure + // we take off the last 3 chars so selected text will look right. + if (m_TextStream.Count() - endIndex < 3 && m_TextStream.Count() - endIndex > 0 ) + { + endIndex = m_TextStream.Count() - 3; + } + } + // draw the text + int i; + for (i = startIndex; i < endIndex; i++) + { + wchar_t ch = m_TextStream[i]; + if (_hideText) + { + ch = '*'; + } + + bool iscompositionchar = false; + + if ( highlight_composition ) + { + iscompositionchar = ( i >= nCompStart && i < nCompEnd ) ? true : false; + if ( iscompositionchar ) + { + // Set the underline color to the text color + surface()->DrawSetColor( col ); + + int w = getCharWidth( useFont, ch ); + + if ( invertcomposition ) + { + // Invert color + surface()->DrawSetTextColor( saveBgColor ); + surface()->DrawSetColor( col ); + + surface()->DrawFilledRect(x, 0, x+w, tall); + // Set the underline color to the text color + surface()->DrawSetColor( saveBgColor ); + } + + surface()->DrawFilledRect( x, tall - 2, x + w, tall - 1 ); + } + } + + + // draw the character and update xposition + x += DrawChar(ch, useFont, i, x, y); + + // Restore color + surface()->DrawSetTextColor(col); + + } + if (endIndex < m_TextStream.Count()) // add an elipsis + { + x += DrawChar('.', useFont, i, x, y); + i++; + x += DrawChar('.', useFont, i, x, y); + i++; + x += DrawChar('.', useFont, i, x, y); + i++; + } + } + else + { + // draw the text + for ( int i = startIndex; i < m_TextStream.Count(); i++) + { + wchar_t ch = m_TextStream[i]; + if (_hideText) + { + ch = '*'; + } + + // if we've passed a line break go to that + if ( _multiline && m_LineBreaks[lineBreakIndexIndex] == i) + { + // add another line + AddAnotherLine(x, y); + lineBreakIndexIndex++; + } + + bool iscompositionchar = false; + + if ( highlight_composition ) + { + iscompositionchar = ( i >= nCompStart && i < nCompEnd ) ? true : false; + if ( iscompositionchar ) + { + // Set the underline color to the text color + surface()->DrawSetColor( col ); + + int w = getCharWidth( useFont, ch ); + + if ( invertcomposition ) + { + // Invert color + surface()->DrawSetTextColor( saveBgColor ); + surface()->DrawFilledRect(x, 0, x+w, tall); + // Set the underline color to the text color + surface()->DrawSetColor( saveBgColor ); + } + + surface()->DrawFilledRect( x, tall - 2, x + w, tall - 1 ); + } + } + + // draw the character and update xposition + x += DrawChar(ch, useFont, i, x, y); + + // Restore color + surface()->DrawSetTextColor(col); + } + } + + // custom border + //!! need to replace this with scheme stuff (TextEntryBorder/TextEntrySelectedBorder) + surface()->DrawSetColor(50, 50, 50, 255); + + if (IsEnabled() && IsEditable() && HasFocus()) + { + // set a more distinct border color + surface()->DrawSetColor(0, 0, 0, 255); + + DrawCursor (x, y); + + if ( composing ) + { + LocalToScreen( x, y ); + input()->SetCandidateWindowPos( x, y ); + } + } + + int newEnd = m_TextStream.Count(); + int remove = newEnd - oldEnd; + if ( remove > 0 ) + { + m_TextStream.RemoveMultiple( oldCursorPos, remove ); + } + _cursorPos = oldCursorPos; + + if ( HasFocus() && m_bAllowNonAsciiCharacters && langlen > 0 ) + { + wide += m_nLangInset; + + if ( m_bDrawLanguageIDAtLeft ) + { + x = 0; + } + else + { + // Draw language identififer + x = wide - m_nLangInset; + } + + surface()->DrawSetColor( col ); + + surface()->DrawFilledRect( x, 2, x + m_nLangInset-2, tall - 2 ); + + saveBgColor[ 3 ] = 255; + surface()->DrawSetTextColor( saveBgColor ); + + x += 1; + + surface()->DrawSetTextFont(_smallfont); + for ( int i = 0; i < langlen; ++i ) + { + x += DrawChar( shortcode[ i ], _smallfont, i, x, remembery ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called when data changes or panel size changes +//----------------------------------------------------------------------------- +void TextEntry::PerformLayout() +{ + BaseClass::PerformLayout(); + + RecalculateLineBreaks(); + + // recalculate scrollbar position + if (_verticalScrollbar) + { + LayoutVerticalScrollBarSlider(); + } + + // force a Repaint + Repaint(); +} + +// moves x,y to the Start of the next line of text +void TextEntry::AddAnotherLine(int &cx, int &cy) +{ + cx = DRAW_OFFSET_X + _pixelsIndent; + cy += (surface()->GetFontTall(_font) + DRAW_OFFSET_Y); +} + + +//----------------------------------------------------------------------------- +// Purpose: Recalculates line breaks +//----------------------------------------------------------------------------- +void TextEntry::RecalculateLineBreaks() +{ + if (!_multiline || _hideText) + return; + + if (m_TextStream.Count() < 1) + return; + + HFont font = _font; + + // line break to our width -2 pixel to keep cursor blinking in window + // (assumes borders are 1 pixel) + int wide = GetWide()-2; + + // subtract the scrollbar width + if (_vertScrollBar) + { + wide -= _vertScrollBar->GetWide(); + } + + int charWidth; + int x = DRAW_OFFSET_X, y = DRAW_OFFSET_Y; + + int wordStartIndex = 0; + int wordLength = 0; + bool hasWord = false; + bool justStartedNewLine = true; + bool wordStartedOnNewLine = true; + + int startChar; + if (_recalculateBreaksIndex <= 0) + { + m_LineBreaks.RemoveAll(); + startChar=0; + } + else + { + // remove the rest of the linebreaks list since its out of date. + for (int i=_recalculateBreaksIndex+1; i < m_LineBreaks.Count(); ++i) + { + m_LineBreaks.Remove((int)i); + --i; // removing shrinks the list! + } + startChar = m_LineBreaks[_recalculateBreaksIndex]; + } + + // handle the case where this char is a new line, in that case + // we have already taken its break index into account above so skip it. + if (m_TextStream[startChar] == '\r' || m_TextStream[startChar] == '\n') + { + startChar++; + } + + // loop through all the characters + int i; + for (i = startChar; i < m_TextStream.Count(); ++i) + { + wchar_t ch = m_TextStream[i]; + + // line break only on whitespace characters + if (!iswspace(ch)) + { + if (hasWord) + { + // append to the current word + } + else + { + // Start a new word + wordStartIndex = i; + hasWord = true; + wordStartedOnNewLine = justStartedNewLine; + wordLength = 0; + } + } + else + { + // whitespace/punctuation character + // end the word + hasWord = false; + } + + // get the width + charWidth = getCharWidth(font, ch); + if (!iswcntrl(ch)) + { + justStartedNewLine = false; + } + + // check to see if the word is past the end of the line [wordStartIndex, i) + if ((x + charWidth) >= wide || ch == '\r' || ch == '\n') + { + // add another line + AddAnotherLine(x,y); + + justStartedNewLine = true; + hasWord = false; + + if (ch == '\r' || ch == '\n') + { + // set the break at the current character + m_LineBreaks.AddToTail(i); + } + else if (wordStartedOnNewLine) + { + // word is longer than a line, so set the break at the current cursor + m_LineBreaks.AddToTail(i); + } + else + { + // set it at the last word Start + m_LineBreaks.AddToTail(wordStartIndex); + + // just back to reparse the next line of text + i = wordStartIndex; + } + + // reset word length + wordLength = 0; + } + + // add to the size + x += charWidth; + wordLength += charWidth; + } + + _charCount = i-1; + + // end the list + m_LineBreaks.AddToTail(BUFFER_SIZE); + + // set up the scrollbar + LayoutVerticalScrollBarSlider(); +} + +//----------------------------------------------------------------------------- +// Purpose: Recalculate where the vertical scroll bar slider should be +// based on the current cursor line we are on. +//----------------------------------------------------------------------------- +void TextEntry::LayoutVerticalScrollBarSlider() +{ + // set up the scrollbar + if (_vertScrollBar) + { + int wide, tall; + GetSize (wide, tall); + + // make sure we factor in insets + int ileft, iright, itop, ibottom; + GetInset(ileft, iright, itop, ibottom); + + // with a scroll bar we take off the inset + wide -= iright; + + _vertScrollBar->SetPos(wide - _vertScrollBar->GetWide(), 0); + // scrollbar is inside the borders. + _vertScrollBar->SetSize(_vertScrollBar->GetWide(), tall - ibottom - itop); + + // calculate how many lines we can fully display + int displayLines = tall / (surface()->GetFontTall(_font) + DRAW_OFFSET_Y); + int numLines = m_LineBreaks.Count(); + + if (numLines <= displayLines) + { + // disable the scrollbar + _vertScrollBar->SetEnabled(false); + _vertScrollBar->SetRange(0, numLines); + _vertScrollBar->SetRangeWindow(numLines); + _vertScrollBar->SetValue(0); + } + else + { + // set the scrollbars range + _vertScrollBar->SetRange(0, numLines); + _vertScrollBar->SetRangeWindow(displayLines); + + _vertScrollBar->SetEnabled(true); + + // this should make it scroll one line at a time + _vertScrollBar->SetButtonPressedScrollValue(1); + + // set the value to view the last entries + int val = _vertScrollBar->GetValue(); + int maxval = _vertScrollBar->GetValue() + displayLines; + if (GetCursorLine() < val ) + { + while (GetCursorLine() < val) + { + val--; + } + } + else if (GetCursorLine() >= maxval) + { + while (GetCursorLine() >= maxval) + { + maxval++; + } + maxval -= displayLines; + val = maxval; + } + else + { + //val = GetCursorLine(); + } + + _vertScrollBar->SetValue(val); + _vertScrollBar->InvalidateLayout(); + _vertScrollBar->Repaint(); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Set boolean value of baseclass variables. +//----------------------------------------------------------------------------- +void TextEntry::SetEnabled(bool state) +{ + BaseClass::SetEnabled(state); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets whether text wraps around multiple lines or not +// Input : state - true or false +//----------------------------------------------------------------------------- +void TextEntry::SetMultiline(bool state) +{ + _multiline = state; +} + +bool TextEntry::IsMultiline() +{ + return _multiline; +} + +//----------------------------------------------------------------------------- +// Purpose: sets whether or not the edit catches and stores ENTER key presses +//----------------------------------------------------------------------------- +void TextEntry::SetCatchEnterKey(bool state) +{ + _catchEnterKey = state; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets whether a vertical scrollbar is visible +// Input : state - true or false +//----------------------------------------------------------------------------- +void TextEntry::SetVerticalScrollbar(bool state) +{ + _verticalScrollbar = state; + + if (_verticalScrollbar) + { + if (!_vertScrollBar) + { + _vertScrollBar = new ScrollBar(this, "ScrollBar", true); + _vertScrollBar->AddActionSignalTarget(this); + } + + _vertScrollBar->SetVisible(true); + } + else if (_vertScrollBar) + { + _vertScrollBar->SetVisible(false); + } + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: sets _editable flag +// Input : state - true or false +//----------------------------------------------------------------------------- +void TextEntry::SetEditable(bool state) +{ + if ( state ) + { + SetDropEnabled( true, 1.0f ); + } + else + { + SetDropEnabled( false ); + } + _editable = state; +} + +const wchar_t *UnlocalizeUnicode( wchar_t *unicode ) +{ + if ( !unicode ) + return L""; + + if ( *unicode == L'#' ) + { + char lookup[ 512 ]; + g_pVGuiLocalize->ConvertUnicodeToANSI( unicode + 1, lookup, sizeof( lookup ) ); + return g_pVGuiLocalize->Find( lookup ); + } + return unicode; +} + +Menu * TextEntry::GetEditMenu() +{ + return m_pEditMenu; +} + +//----------------------------------------------------------------------------- +// Purpose: Create cut/copy/paste dropdown menu +//----------------------------------------------------------------------------- +void TextEntry::CreateEditMenu() +{ + // create a drop down cut/copy/paste menu appropriate for this object's states + if (m_pEditMenu) + delete m_pEditMenu; + m_pEditMenu = new Menu(this, "EditMenu"); + + m_pEditMenu->SetFont( _font ); + + // add cut/copy/paste drop down options if its editable, just copy if it is not + if (_editable && !_hideText) + { + m_pEditMenu->AddMenuItem("#TextEntry_Cut", new KeyValues("DoCutSelected"), this); + } + + if ( !_hideText ) + { + m_pEditMenu->AddMenuItem("#TextEntry_Copy", new KeyValues("DoCopySelected"), this); + } + + if (_editable) + { + m_pEditMenu->AddMenuItem("#TextEntry_Paste", new KeyValues("DoPaste"), this); + } + + + if ( m_bAllowNonAsciiCharacters ) + { + IInput::LanguageItem *langs = NULL; + + int count = input()->GetIMELanguageList( NULL, 0 ); + if ( count > 0 ) + { + langs = new IInput::LanguageItem[ count ]; + input()->GetIMELanguageList( langs, count ); + + // Create a submenu + Menu *subMenu = new Menu( this, "LanguageMenu" ); + + subMenu->SetFont( _font ); + + for ( int i = 0; i < count; ++i ) + { + int id = subMenu->AddCheckableMenuItem( "Language", UnlocalizeUnicode( langs[ i ].menuname ), new KeyValues( "DoLanguageChanged", "handle", langs[ i ].handleValue ), this ); + if ( langs[ i ].active ) + { + subMenu->SetMenuItemChecked( id, true ); + } + } + + m_pEditMenu->AddCascadingMenuItem( "Language", "#TextEntry_Language", "", this, subMenu ); + + delete[] langs; + } + + IInput::ConversionModeItem *modes = NULL; + + count = input()->GetIMEConversionModes( NULL, 0 ); + // if count == 0 then native mode is the only mode... + if ( count > 0 ) + { + modes = new IInput::ConversionModeItem[ count ]; + input()->GetIMEConversionModes( modes, count ); + + // Create a submenu + Menu *subMenu = new Menu( this, "ConversionModeMenu" ); + + subMenu->SetFont( _font ); + + for ( int i = 0; i < count; ++i ) + { + int id = subMenu->AddCheckableMenuItem( "ConversionMode", UnlocalizeUnicode( modes[ i ].menuname ), new KeyValues( "DoConversionModeChanged", "handle", modes[ i ].handleValue ), this ); + if ( modes[ i ].active ) + { + subMenu->SetMenuItemChecked( id, true ); + } + } + + m_pEditMenu->AddCascadingMenuItem( "ConversionMode", "#TextEntry_ConversionMode", "", this, subMenu ); + + delete[] modes; + } + + IInput::SentenceModeItem *sentencemodes = NULL; + + count = input()->GetIMESentenceModes( NULL, 0 ); + // if count == 0 then native mode is the only mode... + if ( count > 0 ) + { + sentencemodes = new IInput::SentenceModeItem[ count ]; + input()->GetIMESentenceModes( sentencemodes, count ); + + // Create a submenu + Menu *subMenu = new Menu( this, "SentenceModeMenu" ); + + subMenu->SetFont( _font ); + + for ( int i = 0; i < count; ++i ) + { + int id = subMenu->AddCheckableMenuItem( "SentenceMode", UnlocalizeUnicode( sentencemodes[ i ].menuname ), new KeyValues( "DoConversionModeChanged", "handle", modes[ i ].handleValue ), this ); + if ( modes[ i ].active ) + { + subMenu->SetMenuItemChecked( id, true ); + } + } + + m_pEditMenu->AddCascadingMenuItem( "SentenceMode", "#TextEntry_SentenceMode", "", this, subMenu ); + + delete[] sentencemodes; + } + } + + + m_pEditMenu->SetVisible(false); + m_pEditMenu->SetParent(this); + m_pEditMenu->AddActionSignalTarget(this); +} + +//----------------------------------------------------------------------------- +// Purpsoe: Returns state of _editable flag +//----------------------------------------------------------------------------- +bool TextEntry::IsEditable() +{ + return _editable && IsEnabled(); +} + +//----------------------------------------------------------------------------- +// Purpose: We want single line windows to scroll horizontally and select text +// in response to clicking and holding outside window +//----------------------------------------------------------------------------- +void TextEntry::OnMouseFocusTicked() +{ + // if a button is down move the scrollbar slider the appropriate direction + if (_mouseDragSelection) // text is being selected via mouse clicking and dragging + { + OnCursorMoved(0,0); // we want the text to scroll as if we were dragging + } +} + +//----------------------------------------------------------------------------- +// Purpose: If a cursor enters the window, we are not elegible for +// MouseFocusTicked events +//----------------------------------------------------------------------------- +void TextEntry::OnCursorEntered() +{ + _mouseDragSelection = false; // outside of window dont recieve drag scrolling ticks +} + +//----------------------------------------------------------------------------- +// Purpose: When the cursor is outside the window, if we are holding the mouse +// button down, then we want the window to scroll the text one char at a time +// using Ticks +//----------------------------------------------------------------------------- +void TextEntry::OnCursorExited() // outside of window recieve drag scrolling ticks +{ + if (_mouseSelection) + _mouseDragSelection = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Handle selection of text by mouse +//----------------------------------------------------------------------------- +void TextEntry::OnCursorMoved(int x, int y) +{ + if (_mouseSelection) + { + // update the cursor position + int x, y; + input()->GetCursorPos(x, y); + ScreenToLocal(x, y); + _cursorPos = PixelToCursorSpace(x, y); + + // if we are at Start of buffer don't put cursor at end, this will keep + // window from scrolling up to a blank line + if (_cursorPos == 0) + _putCursorAtEnd = false; + + // scroll if we went off left side + if (_cursorPos == _currentStartIndex) + { + if (_cursorPos > 0) + _cursorPos--; + + ScrollLeft(); + _cursorPos = _currentStartIndex; + } + if ( _cursorPos != _select[1]) + { + _select[1] = _cursorPos; + Repaint(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle Mouse button down events. +//----------------------------------------------------------------------------- +void TextEntry::OnMousePressed(MouseCode code) +{ + if (code == MOUSE_LEFT) + { + bool keepChecking = SelectCheck( true ); + if ( !keepChecking ) + { + BaseClass::OnMousePressed( code ); + return; + } + + // move the cursor to where the mouse was pressed + int x, y; + input()->GetCursorPos(x, y); + ScreenToLocal(x, y); + + _cursorIsAtEnd = _putCursorAtEnd; // save this off before calling PixelToCursorSpace() + _cursorPos = PixelToCursorSpace(x, y); + // if we are at Start of buffer don't put cursor at end, this will keep + // window from scrolling up to a blank line + if (_cursorPos == 0) + _putCursorAtEnd = false; + + // enter selection mode + input()->SetMouseCapture(GetVPanel()); + _mouseSelection = true; + + if (_select[0] < 0) + { + // if no initial selection position, Start selection position at cursor + _select[0] = _cursorPos; + } + _select[1] = _cursorPos; + + ResetCursorBlink(); + RequestFocus(); + Repaint(); + } + else if (code == MOUSE_RIGHT) // check for context menu open + { + CreateEditMenu(); + Assert(m_pEditMenu); + + OpenEditMenu(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle mouse button up events +//----------------------------------------------------------------------------- +void TextEntry::OnMouseReleased(MouseCode code) +{ + _mouseSelection = false; + + input()->SetMouseCapture(NULL); + + // make sure something has been selected + int cx0, cx1; + if (GetSelectedRange(cx0, cx1)) + { + if (cx1 - cx0 == 0) + { + // nullify selection + _select[0] = -1; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : code - +//----------------------------------------------------------------------------- +void TextEntry::OnMouseTriplePressed( MouseCode code ) +{ + BaseClass::OnMouseTriplePressed( code ); + + // left triple clicking on a word selects all + if (code == MOUSE_LEFT) + { + GotoTextEnd(); + + SelectAllText( false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle mouse double clicks +//----------------------------------------------------------------------------- +void TextEntry::OnMouseDoublePressed(MouseCode code) +{ + // left double clicking on a word selects the word + if (code == MOUSE_LEFT) + { + // move the cursor just as if you single clicked. + OnMousePressed(code); + // then find the start and end of the word we are in to highlight it. + int selectSpot[2]; + GotoWordLeft(); + selectSpot[0] = _cursorPos; + GotoWordRight(); + selectSpot[1] = _cursorPos; + + if (_cursorPos > 0) + { + if (iswspace(m_TextStream[_cursorPos - 1])) + { + selectSpot[1]--; + _cursorPos--; + } + + _select[0] = selectSpot[0]; + _select[1] = selectSpot[1]; + _mouseSelection = true; + } + } + +} + +//----------------------------------------------------------------------------- +// Purpose: Turn off text selection code when mouse button is not down +//----------------------------------------------------------------------------- +void TextEntry::OnMouseCaptureLost() +{ + _mouseSelection = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Only pass some keys upwards +// everything else we don't relay to the parent +//----------------------------------------------------------------------------- +void TextEntry::OnKeyCodePressed(KeyCode code) +{ + // Pass enter on only if _catchEnterKey isn't set + if ( code == KEY_ENTER ) + { + if ( !_catchEnterKey ) + { + Panel::OnKeyCodePressed( code ); + return; + } + } + + // Forward on just a few key codes, everything else can be handled by TextEntry itself + switch ( code ) + { + case KEY_F1: + case KEY_F2: + case KEY_F3: + case KEY_F4: + case KEY_F5: + case KEY_F6: + case KEY_F7: + case KEY_F8: + case KEY_F9: + case KEY_F10: + case KEY_F11: + case KEY_F12: + case KEY_ESCAPE: + case KEY_APP: + Panel::OnKeyCodePressed( code ); + return; + } + + // Pass on the joystick and mouse codes + if ( IsMouseCode(code) || IsNovintButtonCode(code) || IsJoystickCode(code) || IsJoystickButtonCode(code) || + IsJoystickPOVCode(code) || IsJoystickPOVCode(code) || IsJoystickAxisCode(code) ) + { + Panel::OnKeyCodePressed( code ); + return; + } + +} + + +//----------------------------------------------------------------------------- +// Purpose: Masks which keys get chained up +// Maps keyboard input to text window functions. +//----------------------------------------------------------------------------- +void TextEntry::OnKeyCodeTyped(KeyCode code) +{ + _cursorIsAtEnd = _putCursorAtEnd; + _putCursorAtEnd = false; + + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + bool winkey = (input()->IsKeyDown(KEY_LWIN) || input()->IsKeyDown(KEY_RWIN)); + bool fallThrough = false; + + if ( ( ctrl || ( winkey && IsOSX() ) ) && !alt) + { + switch(code) + { + case KEY_A: + SelectAllText(false); + // move the cursor to the end + _cursorPos = _select[1]; + break; + + case KEY_INSERT: + case KEY_C: + { + CopySelected(); + break; + } + case KEY_V: + { + DeleteSelected(); + Paste(); + break; + } + case KEY_X: + { + CopySelected(); + DeleteSelected(); + break; + } + case KEY_Z: + { + Undo(); + break; + } + case KEY_RIGHT: + { + GotoWordRight(); + break; + } + case KEY_LEFT: + { + GotoWordLeft(); + break; + } + case KEY_ENTER: + { + // insert a newline + if (_multiline) + { + DeleteSelected(); + SaveUndoState(); + InsertChar('\n'); + } + // fire newlines back to the main target if asked to + if(_sendNewLines) + { + PostActionSignal(new KeyValues("TextNewLine")); + } + break; + } + case KEY_HOME: + { + GotoTextStart(); + break; + } + case KEY_END: + { + GotoTextEnd(); + break; + } + case KEY_PAGEUP: + { + OnChangeIME( true ); + } + break; + case KEY_PAGEDOWN: + { + OnChangeIME( false ); + } + break; + case KEY_UP: + case KEY_DOWN: + if ( m_bAllowNonAsciiCharacters ) + { + FlipToLastIME(); + } + else + { + fallThrough = true; + } + break; + default: + { + fallThrough = true; + break; + } + } + } + else if (alt) + { + // do nothing with ALT-x keys + if ( !m_bAllowNonAsciiCharacters || ( code != KEY_BACKQUOTE ) ) + { + fallThrough = true; + } + } + else + { + switch(code) + { + case KEY_TAB: + case KEY_LSHIFT: + case KEY_RSHIFT: + case KEY_ESCAPE: + { + fallThrough = true; + break; + } + case KEY_INSERT: + { + if (shift) + { + DeleteSelected(); + Paste(); + } + else + { + fallThrough = true; + } + + break; + } + case KEY_DELETE: + { + if (shift) + { + // shift-delete is cut + CopySelected(); + DeleteSelected(); + } + else + { + Delete(); + } + break; + } + case KEY_LEFT: + { + GotoLeft(); + break; + } + case KEY_RIGHT: + { + GotoRight(); + break; + } + case KEY_UP: + { + if (_multiline) + { + GotoUp(); + } + else + { + fallThrough = true; + } + break; + } + case KEY_DOWN: + { + if (_multiline) + { + GotoDown(); + } + else + { + fallThrough = true; + } + break; + } + case KEY_HOME: + { + if (_multiline) + { + GotoFirstOfLine(); + } + else + { + GotoTextStart(); + } + break; + } + case KEY_END: + { + GotoEndOfLine(); + break; + } + case KEY_BACKSPACE: + { + int x0, x1; + if (GetSelectedRange(x0, x1)) + { + // act just like delete if there is a selection + DeleteSelected(); + } + else + { + Backspace(); + } + break; + } + case KEY_ENTER: + { + // insert a newline + if (_multiline && _catchEnterKey) + { + DeleteSelected(); + SaveUndoState(); + InsertChar('\n'); + } + else + { + fallThrough = true; + } + // fire newlines back to the main target if asked to + if(_sendNewLines) + { + PostActionSignal(new KeyValues("TextNewLine")); + } + break; + } + case KEY_PAGEUP: + { + int val = 0; + fallThrough = (!_multiline) && (!_vertScrollBar); + if (_vertScrollBar) + { + val = _vertScrollBar->GetValue(); + } + + // if there is a scroll bar scroll down one rangewindow + if (_multiline) + { + int displayLines = GetTall() / (surface()->GetFontTall(_font) + DRAW_OFFSET_Y); + // move the cursor down + for (int i=0; i < displayLines; i++) + { + GotoUp(); + } + } + + // if there is a scroll bar scroll down one rangewindow + if (_vertScrollBar) + { + int window = _vertScrollBar->GetRangeWindow(); + int newval = _vertScrollBar->GetValue(); + int linesToMove = window - (val - newval); + _vertScrollBar->SetValue(val - linesToMove - 1); + } + break; + + } + case KEY_PAGEDOWN: + { + int val = 0; + fallThrough = (!_multiline) && (!_vertScrollBar); + if (_vertScrollBar) + { + val = _vertScrollBar->GetValue(); + } + + if (_multiline) + { + int displayLines = GetTall() / (surface()->GetFontTall(_font) + DRAW_OFFSET_Y); + // move the cursor down + for (int i=0; i < displayLines; i++) + { + GotoDown(); + } + } + + // if there is a scroll bar scroll down one rangewindow + if (_vertScrollBar) + { + int window = _vertScrollBar->GetRangeWindow(); + int newval = _vertScrollBar->GetValue(); + int linesToMove = window - (newval - val); + _vertScrollBar->SetValue(val + linesToMove + 1); + } + break; + } + + case KEY_F1: + case KEY_F2: + case KEY_F3: + case KEY_F4: + case KEY_F5: + case KEY_F6: + case KEY_F7: + case KEY_F8: + case KEY_F9: + case KEY_F10: + case KEY_F11: + case KEY_F12: + { + fallThrough = true; + break; + } + + default: + { + // return if any other char is pressed. + // as it will be a unicode char. + // and we don't want select[1] changed unless a char was pressed that this fxn handles + return; + } + } + } + + // select[1] is the location in the line where the blinking cursor started + _select[1] = _cursorPos; + + if (_dataChanged) + { + FireActionSignal(); + } + + // chain back on some keys + if (fallThrough) + { + _putCursorAtEnd=_cursorIsAtEnd; // keep state of cursor on fallthroughs + BaseClass::OnKeyCodeTyped(code); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Masks which keys get chained up +// Maps keyboard input to text window functions. +//----------------------------------------------------------------------------- +void TextEntry::OnKeyTyped(wchar_t unichar) +{ + _cursorIsAtEnd = _putCursorAtEnd; + _putCursorAtEnd=false; + + bool fallThrough = false; + + // KeyCodes handle all non printable chars + if (iswcntrl(unichar) || unichar == 9 ) // tab key (code 9) is printable but handled elsewhere + return; + + // do readonly keys + if (!IsEditable()) + { + BaseClass::OnKeyTyped(unichar); + return; + } + + if (unichar != 0) + { + DeleteSelected(); + SaveUndoState(); + InsertChar(unichar); + } + + // select[1] is the location in the line where the blinking cursor started + _select[1] = _cursorPos; + + if (_dataChanged) + { + FireActionSignal(); + } + + // chain back on some keys + if (fallThrough) + { + _putCursorAtEnd=_cursorIsAtEnd; // keep state of cursor on fallthroughs + BaseClass::OnKeyTyped(unichar); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Scrolls the list according to the mouse wheel movement +//----------------------------------------------------------------------------- +void TextEntry::OnMouseWheeled(int delta) +{ + if (_vertScrollBar) + { + MoveScrollBar(delta); + } + else + { + // if we don't use the input, chain back + BaseClass::OnMouseWheeled(delta); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Scrolls the list +// Input : delta - amount to move scrollbar up +//----------------------------------------------------------------------------- +void TextEntry::MoveScrollBar(int delta) +{ + if (_vertScrollBar) + { + int val = _vertScrollBar->GetValue(); + val -= (delta * 3); + _vertScrollBar->SetValue(val); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called every frame the entry has keyboard focus; +// blinks the text cursor +//----------------------------------------------------------------------------- +void TextEntry::OnKeyFocusTicked() +{ + int time=system()->GetTimeMillis(); + if(time>_cursorNextBlinkTime) + { + _cursorBlink=!_cursorBlink; + _cursorNextBlinkTime=time+_cursorBlinkRate; + Repaint(); + } +} + +Panel *TextEntry::GetDragPanel() +{ + if ( input()->IsMouseDown( MOUSE_LEFT ) ) + { + int x, y; + input()->GetCursorPos(x, y); + ScreenToLocal(x, y); + int cursor = PixelToCursorSpace(x, y); + + int cx0, cx1; + bool check = GetSelectedRange( cx0, cx1 ); + + if ( check && cursor >= cx0 && cursor < cx1 ) + { + // Don't deselect in this case!!! + return BaseClass::GetDragPanel(); + } + return NULL; + } + + return BaseClass::GetDragPanel(); +} + +void TextEntry::OnCreateDragData( KeyValues *msg ) +{ + BaseClass::OnCreateDragData( msg ); + + char txt[ 256 ]; + GetText( txt, sizeof( txt ) ); + + int r0, r1; + if ( GetSelectedRange( r0, r1 ) && r0 != r1 ) + { + int len = r1 - r0; + if ( len > 0 && r0 < 1024 ) + { + char selection[ 512 ]; + Q_strncpy( selection, &txt[ r0 ], len + 1 ); + selection[ len ] = 0; + msg->SetString( "text", selection ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Check if we are selecting text (so we can highlight it) +//----------------------------------------------------------------------------- +bool TextEntry::SelectCheck( bool fromMouse /*=false*/ ) +{ + bool bret = true; + if (!HasFocus() || !(input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT))) + { + bool deselect = true; + int cx0, cx1; + if ( fromMouse && + GetDragPanel() != NULL ) + { + // move the cursor to where the mouse was pressed + int x, y; + input()->GetCursorPos(x, y); + ScreenToLocal(x, y); + int cursor = PixelToCursorSpace(x, y); + + bool check = GetSelectedRange( cx0, cx1 ); + + if ( check && cursor >= cx0 && cursor < cx1 ) + { + // Don't deselect in this case!!! + deselect = false; + bret = false; + } + } + + if ( deselect ) + { + _select[0] = -1; + } + } + else if (_select[0] == -1) + { + _select[0] = _cursorPos; + } + return bret; +} + +//----------------------------------------------------------------------------- +// Purpose: set the maximum number of chars in the text buffer +//----------------------------------------------------------------------------- +void TextEntry::SetMaximumCharCount(int maxChars) +{ + _maxCharCount = maxChars; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +int TextEntry::GetMaximumCharCount() +{ + return _maxCharCount; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void TextEntry::SetAutoProgressOnHittingCharLimit(bool state) +{ + m_bAutoProgressOnHittingCharLimit = state; +} + +//----------------------------------------------------------------------------- +// Purpose: set whether to wrap the text buffer +//----------------------------------------------------------------------------- +void TextEntry::SetWrap(bool wrap) +{ + _wrap = wrap; +} + +//----------------------------------------------------------------------------- +// Purpose: set whether to pass newline msgs to parent +//----------------------------------------------------------------------------- +void TextEntry::SendNewLine(bool send) +{ + _sendNewLines = send; +} + +//----------------------------------------------------------------------------- +// Purpose: Tell if an index is a linebreakindex +//----------------------------------------------------------------------------- +bool TextEntry::IsLineBreak(int index) +{ + for (int i=0; i 0) + { + _cursorPos--; + } + + ScrollLeft(); + + ResetCursorBlink(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Move the cursor one character to the right, scroll the text +// horizontally if needed +//----------------------------------------------------------------------------- +void TextEntry::GotoRight() +{ + SelectCheck(); + + // if we are on a line break just move the cursor to the next line + if (IsLineBreak(_cursorPos)) + { + if (_cursorIsAtEnd) + { + _putCursorAtEnd = false; + } + else + { + // if we are not at end increment cursor + if (_cursorPos < m_TextStream.Count()) + { + _cursorPos++; + } + } + } + else + { + // if we are not at end increment cursor + if (_cursorPos < m_TextStream.Count()) + { + _cursorPos++; + } + + // if we are on a line break move the cursor to end of line + if (IsLineBreak(_cursorPos)) + { + if (!_cursorIsAtEnd) + _putCursorAtEnd = true; + } + } + // scroll right if we need to + ScrollRight(); + + ResetCursorBlink(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Find out what line the cursor is on +//----------------------------------------------------------------------------- +int TextEntry::GetCursorLine() +{ + // find which line the cursor is on + int cursorLine; + for (cursorLine = 0; cursorLine < m_LineBreaks.Count(); cursorLine++) + { + if (_cursorPos < m_LineBreaks[cursorLine]) + break; + } + + if (_putCursorAtEnd) // correct for when cursor is at end of line rather than Start of next + { + // we are not at end of buffer, in which case there is no next line to be at the Start of + if (_cursorPos != m_TextStream.Count() ) + cursorLine--; + } + + return cursorLine; +} + +//----------------------------------------------------------------------------- +// Purpose: Move the cursor one line up +//----------------------------------------------------------------------------- +void TextEntry::GotoUp() +{ + SelectCheck(); + + if (_cursorIsAtEnd) + { + if ( (GetCursorLine() - 1 ) == 0) // we are on first line + { + // stay at end of line + _putCursorAtEnd = true; + return; // dont move the cursor + } + else + _cursorPos--; + } + + int cx, cy; + CursorToPixelSpace(_cursorPos, cx, cy); + + // move the cursor to the previous line + MoveCursor(GetCursorLine() - 1, cx); +} + + +//----------------------------------------------------------------------------- +// Purpose: Move the cursor one line down +//----------------------------------------------------------------------------- +void TextEntry::GotoDown() +{ + SelectCheck(); + + if (_cursorIsAtEnd) + { + _cursorPos--; + if (_cursorPos < 0) + _cursorPos = 0; + } + + int cx, cy; + CursorToPixelSpace(_cursorPos, cx, cy); + + // move the cursor to the next line + MoveCursor(GetCursorLine() + 1, cx); + if (!_putCursorAtEnd && _cursorIsAtEnd ) + { + _cursorPos++; + if (_cursorPos > m_TextStream.Count()) + { + _cursorPos = m_TextStream.Count(); + } + } + LayoutVerticalScrollBarSlider(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the starting ypixel positon for a walk through the window +//----------------------------------------------------------------------------- +int TextEntry::GetYStart() +{ + if (_multiline) + { + // just Start from the top + return DRAW_OFFSET_Y; + } + + int fontTall = surface()->GetFontTall(_font); + return (GetTall() / 2) - (fontTall / 2); +} + +//----------------------------------------------------------------------------- +// Purpose: Move the cursor to a line, need to know how many pixels are in a line +//----------------------------------------------------------------------------- +void TextEntry::MoveCursor(int line, int pixelsAcross) +{ + // clamp to a valid line + if (line < 0) + line = 0; + if (line >= m_LineBreaks.Count()) + line = m_LineBreaks.Count() -1; + + // walk the whole text set looking for our place + // work out where to Start checking + + int yStart = GetYStart(); + + int x = DRAW_OFFSET_X, y = yStart; + int lineBreakIndexIndex = 0; + _pixelsIndent = 0; + int i; + for ( i = 0; i < m_TextStream.Count(); i++) + { + wchar_t ch = m_TextStream[i]; + + if (_hideText) + { + ch = '*'; + } + + // if we've passed a line break go to that + if (m_LineBreaks[lineBreakIndexIndex] == i) + { + if (lineBreakIndexIndex == line) + { + _putCursorAtEnd = true; + _cursorPos = i; + break; + } + + // add another line + AddAnotherLine(x,y); + lineBreakIndexIndex++; + + } + + // add to the current position + int charWidth = getCharWidth(_font, ch); + + if (line == lineBreakIndexIndex) + { + // check to see if we're in range + if ((x + (charWidth / 2)) > pixelsAcross) + { + // found position + _cursorPos = i; + break; + } + } + + x += charWidth; + } + + // if we never find the cursor it must be past the end + // of the text buffer, to let's just slap it on the end of the text buffer then. + if (i == m_TextStream.Count()) + { + GotoTextEnd(); + } + + LayoutVerticalScrollBarSlider(); + ResetCursorBlink(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Turn horizontal scrolling on or off. +// Horizontal scrolling is disabled in multline windows. +// Toggling this will disable it in single line windows as well. +//----------------------------------------------------------------------------- +void TextEntry::SetHorizontalScrolling(bool status) +{ + _horizScrollingAllowed = status; +} + +//----------------------------------------------------------------------------- +// Purpose: Horizontal scrolling function, not used in multiline windows +// Function will scroll the buffer to the left if the cursor is not in the window +// scroll left if we need to +//----------------------------------------------------------------------------- +void TextEntry::ScrollLeft() +{ + if (_multiline) // early out + { + return; + } + + if (!_horizScrollingAllowed) //early out + { + return; + } + + if(_cursorPos < _currentStartIndex) // scroll left if we need to + { + if (_cursorPos < 0)// dont scroll past the Start of buffer + { + _cursorPos=0; + } + _currentStartIndex = _cursorPos; + } + + LayoutVerticalScrollBarSlider(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TextEntry::ScrollLeftForResize() +{ + if (_multiline) // early out + { + return; + } + + if (!_horizScrollingAllowed) //early out + { + return; + } + + while (_currentStartIndex > 0) // go until we hit leftmost + { + _currentStartIndex--; + int nVal = _currentStartIndex; + + // check if the cursor is now off the screen + if (IsCursorOffRightSideOfWindow(_cursorPos)) + { + _currentStartIndex++; // we've gone too far, return it + break; + } + + // IsCursorOffRightSideOfWindow actually fixes the _currentStartIndex, + // so if our value changed that menas we really are off the screen + if (nVal != _currentStartIndex) + break; + } + LayoutVerticalScrollBarSlider(); +} + +//----------------------------------------------------------------------------- +// Purpose: Horizontal scrolling function, not used in multiline windows +// Scroll one char right until the cursor is visible in the window. +// We do this one char at a time because char width isn't a constant. +//----------------------------------------------------------------------------- +void TextEntry::ScrollRight() +{ + if (!_horizScrollingAllowed) + return; + + if (_multiline) + { + } + // check if cursor is off the right side of window + else if (IsCursorOffRightSideOfWindow(_cursorPos)) + { + _currentStartIndex++; //scroll over + ScrollRight(); // scroll again, check if cursor is in window yet + } + + LayoutVerticalScrollBarSlider(); +} + +//----------------------------------------------------------------------------- +// Purpose: Check and see if cursor position is off the right side of the window +// just compare cursor's pixel coords with the window size coords. +// Input: an integer cursor Position, if you pass _cursorPos fxn will tell you +// if current cursor is outside window. +// Output: true: cursor is outside right edge or window +// false: cursor is inside right edge +//----------------------------------------------------------------------------- +bool TextEntry::IsCursorOffRightSideOfWindow(int cursorPos) +{ + int cx, cy; + CursorToPixelSpace(cursorPos, cx, cy); + int wx=GetWide()-1; //width of inside of window is GetWide()-1 + if ( wx <= 0 ) + return false; + + return (cx >= wx); +} + +//----------------------------------------------------------------------------- +// Purpose: Check and see if cursor position is off the left side of the window +// just compare cursor's pixel coords with the window size coords. +// Input: an integer cursor Position, if you pass _cursorPos fxn will tell you +// if current cursor is outside window. +// Output: true - cursor is outside left edge or window +// false - cursor is inside left edge +//----------------------------------------------------------------------------- +bool TextEntry::IsCursorOffLeftSideOfWindow(int cursorPos) +{ + int cx, cy; + CursorToPixelSpace(cursorPos, cx, cy); + return (cx <= 0); +} + +//----------------------------------------------------------------------------- +// Purpose: Move the cursor over to the Start of the next word to the right +//----------------------------------------------------------------------------- +void TextEntry::GotoWordRight() +{ + SelectCheck(); + + // search right until we hit a whitespace character or a newline + while (++_cursorPos < m_TextStream.Count()) + { + if (iswspace(m_TextStream[_cursorPos])) + break; + } + + // search right until we hit an nonspace character + while (++_cursorPos < m_TextStream.Count()) + { + if (!iswspace(m_TextStream[_cursorPos])) + break; + } + + if (_cursorPos > m_TextStream.Count()) + _cursorPos = m_TextStream.Count(); + + // now we are at the start of the next word + + // scroll right if we need to + ScrollRight(); + + LayoutVerticalScrollBarSlider(); + ResetCursorBlink(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Move the cursor over to the Start of the next word to the left +//----------------------------------------------------------------------------- +void TextEntry::GotoWordLeft() +{ + SelectCheck(); + + if (_cursorPos < 1) + return; + + // search left until we hit an nonspace character + while (--_cursorPos >= 0) + { + if (!iswspace(m_TextStream[_cursorPos])) + break; + } + + // search left until we hit a whitespace character + while (--_cursorPos >= 0) + { + if (iswspace(m_TextStream[_cursorPos])) + { + break; + } + } + + // we end one character off + _cursorPos++; + // now we are at the Start of the previous word + + + // scroll left if we need to + ScrollLeft(); + + LayoutVerticalScrollBarSlider(); + ResetCursorBlink(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Move cursor to the Start of the text buffer +//----------------------------------------------------------------------------- +void TextEntry::GotoTextStart() +{ + SelectCheck(); + _cursorPos = 0; // set cursor to Start + _putCursorAtEnd = false; + _currentStartIndex=0; // scroll over to Start + + LayoutVerticalScrollBarSlider(); + ResetCursorBlink(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Move cursor to the end of the text buffer +//----------------------------------------------------------------------------- +void TextEntry::GotoTextEnd() +{ + SelectCheck(); + _cursorPos=m_TextStream.Count(); // set cursor to end of buffer + _putCursorAtEnd = true; // move cursor Start of next line + ScrollRight(); // scroll over until cursor is on screen + + LayoutVerticalScrollBarSlider(); + ResetCursorBlink(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Move cursor to the Start of the current line +//----------------------------------------------------------------------------- +void TextEntry::GotoFirstOfLine() +{ + SelectCheck(); + // to get to the Start of the line you have to take into account line wrap + // we have to figure out at which point the line wraps + // given the current cursor position, select[1], find the index that is the + // line Start to the left of the cursor + //_cursorPos = 0; //TODO: this is wrong, should go to first non-whitespace first, then to zero + _cursorPos = GetCurrentLineStart(); + _putCursorAtEnd = false; + + _currentStartIndex=_cursorPos; + + LayoutVerticalScrollBarSlider(); + ResetCursorBlink(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the index of the first char on the current line +//----------------------------------------------------------------------------- +int TextEntry::GetCurrentLineStart() +{ + if (!_multiline) // quick out for non multline buffers + return _currentStartIndex; + + int i; + if (IsLineBreak(_cursorPos)) + { + for (i = 0; i < m_LineBreaks.Count(); ++i ) + { + if (_cursorPos == m_LineBreaks[i]) + break; + } + if (_cursorIsAtEnd) + { + if (i > 0) + { + return m_LineBreaks[i-1]; + } + return m_LineBreaks[0]; + } + else + return _cursorPos; // we are already at Start + } + + for ( i = 0; i < m_LineBreaks.Count(); ++i ) + { + if (_cursorPos < m_LineBreaks[i]) + { + if (i == 0) + return 0; + else + return m_LineBreaks[i-1]; + } + } + // if there were no line breaks, the first char in the line is the Start of the buffer + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Move cursor to the end of the current line +//----------------------------------------------------------------------------- +void TextEntry::GotoEndOfLine() +{ + SelectCheck(); + // to get to the end of the line you have to take into account line wrap in the buffer + // we have to figure out at which point the line wraps + // given the current cursor position, select[1], find the index that is the + // line end to the right of the cursor + //_cursorPos=m_TextStream.Count(); //TODO: this is wrong, should go to last non-whitespace, then to true EOL + _cursorPos = GetCurrentLineEnd(); + _putCursorAtEnd = true; + + ScrollRight(); + + LayoutVerticalScrollBarSlider(); + ResetCursorBlink(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the index of the last char on the current line +//----------------------------------------------------------------------------- +int TextEntry::GetCurrentLineEnd() +{ + int i; + if (IsLineBreak(_cursorPos) ) + { + for ( i = 0; i < m_LineBreaks.Count()-1; ++i ) + { + if (_cursorPos == m_LineBreaks[i]) + break; + } + if (!_cursorIsAtEnd) + { + if (i == m_LineBreaks.Count()-2 ) + m_TextStream.Count(); + else + return m_LineBreaks[i+1]; + } + else + return _cursorPos; // we are already at end + } + + for ( i = 0; i < m_LineBreaks.Count()-1; i++ ) + { + if ( _cursorPos < m_LineBreaks[i]) + { + return m_LineBreaks[i]; + } + } + return m_TextStream.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: Insert a character into the text buffer +//----------------------------------------------------------------------------- +void TextEntry::InsertChar(wchar_t ch) +{ + // throw away redundant linefeed characters + if (ch == '\r') + return; + + // no newline characters in single-line dialogs + if (!_multiline && ch == '\n') + return; + + // no tab characters + if (ch == '\t') + return; + + if (m_bAllowNumericInputOnly) + { + if (!iswdigit(ch) && ((char)ch != '.')) + { + surface()->PlaySound("Resource\\warning.wav"); + return; + } + } + + // check against unicode characters + if (!m_bAllowNonAsciiCharacters) + { + if (ch > 127) + return; + } + + // don't add characters if the max char count has been reached + // ding at the user + if (_maxCharCount > -1 && m_TextStream.Count() >= _maxCharCount) + { + if (_maxCharCount>0 && _multiline && _wrap) + { + // if we wrap lines rather than stopping + while (m_TextStream.Count() > _maxCharCount) + { + if (_recalculateBreaksIndex==0) + { + // we can get called before this has been run for the first time :) + RecalculateLineBreaks(); + } + if (m_LineBreaks[0]> m_TextStream.Count()) + { + // if the line break is the past the end of the buffer recalc + _recalculateBreaksIndex=-1; + RecalculateLineBreaks(); + } + + if (m_LineBreaks[0]+1 < m_TextStream.Count()) + { + // delete the line + m_TextStream.RemoveMultiple(0, m_LineBreaks[0]); + + // in case we just deleted text from where the cursor is + if (_cursorPos> m_TextStream.Count()) + { + _cursorPos = m_TextStream.Count(); + } + else + { // shift the cursor up. don't let it wander past zero + _cursorPos-=m_LineBreaks[0]+1; + if (_cursorPos<0) + { + _cursorPos=0; + } + } + + // move any selection area up + if(_select[0]>-1) + { + _select[0] -=m_LineBreaks[0]+1; + + if(_select[0] <=0) + { + _select[0] =-1; + } + + _select[1] -=m_LineBreaks[0]+1; + if(_select[1] <=0) + { + _select[1] =-1; + } + + } + + // now redraw the buffer + for (int i = m_TextStream.Count() - 1; i >= 0; i--) + { + SetCharAt(m_TextStream[i], i+1); + } + + // redo all the line breaks + _recalculateBreaksIndex=-1; + RecalculateLineBreaks(); + + } + } + + } + else + { + // make a sound + // we've hit the max character limit + surface()->PlaySound("Resource\\warning.wav"); + return; + } + } + + + if (_wrap) + { + // when wrapping you always insert the new char at the end of the buffer + SetCharAt(ch, m_TextStream.Count()); + _cursorPos=m_TextStream.Count(); + } + else + { + // move chars right 1 starting from cursor, then replace cursorPos with char and increment cursor + for (int i = m_TextStream.Count()- 1; i >= _cursorPos; i--) + { + SetCharAt(m_TextStream[i], i+1); + } + + SetCharAt(ch, _cursorPos); + _cursorPos++; + } + + // if its a newline char we can't do the slider until we recalc the line breaks + if (ch == '\n') + { + RecalculateLineBreaks(); + } + + // see if we've hit the char limit + if (m_bAutoProgressOnHittingCharLimit && m_TextStream.Count() == _maxCharCount) + { + // move the next panel (most likely another TextEntry) + RequestFocusNext(); + } + + // scroll right if this pushed the cursor off screen + ScrollRight(); + + _dataChanged = true; + + CalcBreakIndex(); + LayoutVerticalScrollBarSlider(); + ResetCursorBlink(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the lineBreakIndex index of the line before the cursor +// note _recalculateBreaksIndex < 0 flags RecalculateLineBreaks +// to figure it all out from scratch +//----------------------------------------------------------------------------- +void TextEntry::CalcBreakIndex() +{ + // an optimization to handle when the cursor is at the end of the buffer. + // pays off if the buffer is large, and the search loop would be long. + if (_cursorPos == m_TextStream.Count()) + { + // we know m_LineBreaks array always has at least one element in it (99999 sentinel) + // when there is just one line this will make recalc = -1 which is ok. + _recalculateBreaksIndex = m_LineBreaks.Count()-2; + return; + } + + _recalculateBreaksIndex=0; + // find the line break just before the cursor position + while (_cursorPos > m_LineBreaks[_recalculateBreaksIndex]) + ++_recalculateBreaksIndex; + + // -1 is ok. + --_recalculateBreaksIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: Insert a string into the text buffer, this is just a series +// of char inserts because we have to check each char is ok to insert +//----------------------------------------------------------------------------- +void TextEntry::InsertString(const wchar_t *wszText) +{ + SaveUndoState(); + + for (const wchar_t *ch = wszText; *ch != 0; ++ch) + { + InsertChar(*ch); + } + + if (_dataChanged) + { + FireActionSignal(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Converts an ansi string to unicode and inserts it into the text stream +//----------------------------------------------------------------------------- +void TextEntry::InsertString(const char *text) +{ + // check for to see if the string is in the localization tables + if (text[0] == '#') + { + wchar_t *wsz = g_pVGuiLocalize->Find(text); + if (wsz) + { + InsertString(wsz); + return; + } + } + + // straight convert the ansi to unicode and insert + wchar_t unicode[1024]; + g_pVGuiLocalize->ConvertANSIToUnicode(text, unicode, sizeof(unicode)); + InsertString(unicode); +} + +//----------------------------------------------------------------------------- +// Purpose: Handle the effect of user hitting backspace key +// we delete the char before the cursor and reformat the text so it +// behaves like in windows. +//----------------------------------------------------------------------------- +void TextEntry::Backspace() +{ + if (!IsEditable()) + return; + + //if you are at the first position don't do anything + if(_cursorPos==0) + { + return; + } + + //if the line is empty, don't do anything + if(m_TextStream.Count()==0) + { + return; + } + + SaveUndoState(); + + //shift chars left one, starting at the cursor position, then make the line one smaller + for(int i=_cursorPos;i= 0) // dont scroll if there are not enough chars to scroll + { + _currentStartIndex-=6; + } + else + _currentStartIndex=0; + } + + //move the cursor left one + _cursorPos--; + + _dataChanged = true; + + // recalculate linebreaks (the fast incremental linebreak function doesn't work in this case) + _recalculateBreaksIndex = 0; + m_LineBreaks.RemoveAll(); + m_LineBreaks.AddToTail(BUFFER_SIZE); + + LayoutVerticalScrollBarSlider(); + ResetCursorBlink(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Deletes the current selection, if any, moving the cursor to the Start +// of the selection +//----------------------------------------------------------------------------- +void TextEntry::DeleteSelected() +{ + if (!IsEditable()) + return; + + // if the line is empty, don't do anything + if (m_TextStream.Count() == 0) + return; + + // get the range to delete + int x0, x1; + if (!GetSelectedRange(x0, x1)) + { + // no selection, don't touch anything + return; + } + + SaveUndoState(); + + // shift chars left one starting after cursor position, then make the line one smaller + int dif = x1 - x0; + for (int i = 0; i < dif; ++i) + { + m_TextStream.Remove(x0); + } + + // clear any selection + SelectNone(); + ResetCursorBlink(); + + // move the cursor to just after the deleted section + _cursorPos = x0; + + _dataChanged = true; + + _recalculateBreaksIndex = 0; + m_LineBreaks.RemoveAll(); + m_LineBreaks.AddToTail(BUFFER_SIZE); + + CalcBreakIndex(); + + LayoutVerticalScrollBarSlider(); +} + +//----------------------------------------------------------------------------- +// Purpose: Handle the effect of the user hitting the delete key +// removes the char in front of the cursor +//----------------------------------------------------------------------------- +void TextEntry::Delete() +{ + if (!IsEditable()) + return; + + // if the line is empty, don't do anything + if (m_TextStream.Count() == 0) + return; + + // get the range to delete + int x0, x1; + if (!GetSelectedRange(x0, x1)) + { + // no selection, so just delete the one character + x0 = _cursorPos; + x1 = x0 + 1; + + // if we're at the end of the line don't do anything + if (_cursorPos >= m_TextStream.Count()) + return; + } + + SaveUndoState(); + + // shift chars left one starting after cursor position, then make the line one smaller + int dif = x1 - x0; + for (int i = 0; i < dif; i++) + { + m_TextStream.Remove((int)x0); + } + + ResetCursorBlink(); + + // clear any selection + SelectNone(); + + // move the cursor to just after the deleted section + _cursorPos = x0; + + _dataChanged = true; + + _recalculateBreaksIndex = 0; + m_LineBreaks.RemoveAll(); + m_LineBreaks.AddToTail(BUFFER_SIZE); + + CalcBreakIndex(); + + LayoutVerticalScrollBarSlider(); +} + +//----------------------------------------------------------------------------- +// Purpose: Declare a selection empty +//----------------------------------------------------------------------------- +void TextEntry::SelectNone() +{ + // tag the selection as empty + _select[0] = -1; + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Load in the selection range so cx0 is the Start and cx1 is the end +// from smallest to highest (right to left) +//----------------------------------------------------------------------------- +bool TextEntry::GetSelectedRange(int& cx0,int& cx1) +{ + // if there is nothing selected return false + if (_select[0] == -1) + { + return false; + } + + // sort the two position so cx0 is the smallest + cx0=_select[0]; + cx1=_select[1]; + int temp; + if(cx1GetCursorPos(cursorX, cursorY); + + /* !! disabled since it recursively gets panel pointers, potentially across dll boundaries, + and doesn't need to be necessary (it's just for handling windowed mode) + + // find the frame that has no parent (the one on the desktop) + Panel *panel = this; + while ( panel->GetParent() != NULL) + { + panel = panel->GetParent(); + } + panel->ScreenToLocal(cursorX, cursorY); + int x, y; + // get base panel's postition + panel->GetPos(x, y); + + // adjust our cursor position accordingly + cursorX += x; + cursorY += y; + */ + + int x0, x1; + if (GetSelectedRange(x0, x1)) // there is something selected + { + m_pEditMenu->SetItemEnabled("&Cut", true); + m_pEditMenu->SetItemEnabled("C&opy", true); + } + else // there is nothing selected, disable cut/copy options + { + m_pEditMenu->SetItemEnabled("&Cut", false); + m_pEditMenu->SetItemEnabled("C&opy", false); + } + m_pEditMenu->SetVisible(true); + m_pEditMenu->RequestFocus(); + + // relayout the menu immediately so that we know it's size + m_pEditMenu->InvalidateLayout(true); + int menuWide, menuTall; + m_pEditMenu->GetSize(menuWide, menuTall); + + // work out where the cursor is and therefore the best place to put the menu + int wide, tall; + surface()->GetScreenSize(wide, tall); + + if (wide - menuWide > cursorX) + { + // menu hanging right + if (tall - menuTall > cursorY) + { + // menu hanging down + m_pEditMenu->SetPos(cursorX, cursorY); + } + else + { + // menu hanging up + m_pEditMenu->SetPos(cursorX, cursorY - menuTall); + } + } + else + { + // menu hanging left + if (tall - menuTall > cursorY) + { + // menu hanging down + m_pEditMenu->SetPos(cursorX - menuWide, cursorY); + } + else + { + // menu hanging up + m_pEditMenu->SetPos(cursorX - menuWide, cursorY - menuTall); + } + } + + m_pEditMenu->RequestFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Cuts the selected chars from the buffer and +// copies them into the clipboard +//----------------------------------------------------------------------------- +void TextEntry::CutSelected() +{ + CopySelected(); + DeleteSelected(); + // have to request focus if we used the menu + RequestFocus(); + + if ( _dataChanged ) + { + FireActionSignal(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Copies the selected chars into the clipboard +//----------------------------------------------------------------------------- +void TextEntry::CopySelected() +{ + if (_hideText) + return; + + int x0, x1; + if (GetSelectedRange(x0, x1)) + { + CUtlVector buf; + for (int i = x0; i < x1; i++) + { + if ( m_TextStream[i]=='\n') + { + buf.AddToTail( '\r' ); + } + buf.AddToTail(m_TextStream[i]); + } + buf.AddToTail('\0'); + system()->SetClipboardText(buf.Base(), buf.Count()); + } + + // have to request focus if we used the menu + RequestFocus(); + + if ( _dataChanged ) + { + FireActionSignal(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Pastes the selected chars from the clipboard into the text buffer +// truncates if text is longer than our _maxCharCount +//----------------------------------------------------------------------------- +void TextEntry::Paste() +{ + if (!IsEditable()) + return; + + CUtlVector buf; + int bufferSize = system()->GetClipboardTextCount(); + if (!m_bAutoProgressOnHittingCharLimit) + { + bufferSize = _maxCharCount > 0 ? _maxCharCount + 1 : system()->GetClipboardTextCount(); // +1 for terminator + } + + buf.AddMultipleToTail(bufferSize); + int len = system()->GetClipboardText(0, buf.Base(), bufferSize * sizeof(wchar_t)); + if (len < 1) + return; + + SaveUndoState(); + bool bHaveMovedFocusAwayFromCurrentEntry = false; + + // insert all the characters + for (int i = 0; i < len && buf[i] != 0; i++) + { + if (m_bAutoProgressOnHittingCharLimit) + { + // see if we're about to hit the char limit + if (m_TextStream.Count() == _maxCharCount) + { + // move the next panel (most likely another TextEntry) + RequestFocusNext(); + // copy the remainder into the clipboard + wchar_t *remainingText = &buf[i]; + system()->SetClipboardText(remainingText, len - i - 1); + // set the next entry to paste + if (GetVParent() && ipanel()->GetCurrentKeyFocus(GetVParent()) != GetVPanel()) + { + bHaveMovedFocusAwayFromCurrentEntry = true; + ipanel()->SendMessage(ipanel()->GetCurrentKeyFocus(GetVParent()), new KeyValues("DoPaste"), GetVPanel()); + } + break; + } + } + + // insert the character + InsertChar(buf[i]); + } + + // restore the original clipboard text if neccessary + if (m_bAutoProgressOnHittingCharLimit) + { + system()->SetClipboardText(buf.Base(), bufferSize); + } + + _dataChanged = true; + FireActionSignal(); + + if (!bHaveMovedFocusAwayFromCurrentEntry) + { + // have to request focus if we used the menu + RequestFocus(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Reverts back to last saved changes +//----------------------------------------------------------------------------- +void TextEntry::Undo() +{ + _cursorPos = _undoCursorPos; + m_TextStream.CopyArray(m_UndoTextStream.Base(), m_UndoTextStream.Count()); + + InvalidateLayout(); + Repaint(); + SelectNone(); +} + +//----------------------------------------------------------------------------- +// Purpose: Saves the current state to the undo stack +//----------------------------------------------------------------------------- +void TextEntry::SaveUndoState() +{ + _undoCursorPos = _cursorPos; + m_UndoTextStream.CopyArray(m_TextStream.Base(), m_TextStream.Count()); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the index in the text buffer of the +// character the drawing should Start at +//----------------------------------------------------------------------------- +int TextEntry::GetStartDrawIndex(int &lineBreakIndexIndex) +{ + int startIndex = 0; + + int numLines = m_LineBreaks.Count(); + int startLine = 0; + + // determine the Start point from the scroll bar + // do this only if we are not selecting text in the window with the mouse + if (_vertScrollBar && !_mouseDragSelection) + { + // skip to line indicated by scrollbar + startLine = _vertScrollBar->GetValue(); + } + else + { + // check to see if the cursor is off the screen-multiline case + HFont font = _font; + int displayLines = GetTall() / (surface()->GetFontTall(font) + DRAW_OFFSET_Y); + if (displayLines < 1) + { + displayLines = 1; + } + if (numLines > displayLines) + { + int cursorLine = GetCursorLine(); + + startLine = _currentStartLine; + + // see if that is visible + if (cursorLine < _currentStartLine) + { + // cursor is above visible area; scroll back + startLine = cursorLine; + if (_vertScrollBar) + { + MoveScrollBar( 1 ); // should be calibrated for speed + // adjust startline incase we hit a limit + startLine = _vertScrollBar->GetValue(); + } + } + else if (cursorLine > (_currentStartLine + displayLines - 1)) + { + // cursor is down below visible area; scroll forward + startLine = cursorLine - displayLines + 1; + if (_vertScrollBar) + { + MoveScrollBar( -1 ); + startLine = _vertScrollBar->GetValue(); + } + } + } + else if (!_multiline) + { + // check to see if cursor is off the right side of screen-single line case + // get cursor's x coordinate in pixel space + bool done = false; + while ( !done ) + { + done = true; + int x = DRAW_OFFSET_X; + for (int i = _currentStartIndex; i < m_TextStream.Count(); i++) + { + done = false; + wchar_t ch = m_TextStream[i]; + if (_hideText) + { + ch = '*'; + } + + // if we've found the position, break + if (_cursorPos == i) + { + break; + } + + // add to the current position + x += getCharWidth(font, ch); + } + + if ( x >= GetWide() ) + { + _currentStartIndex++; + // Keep searching... + continue; + } + + if ( x <= 0 ) + { + // dont go past the Start of buffer + if (_currentStartIndex > 0) + _currentStartIndex--; + } + + break; + } + } + } + + if (startLine > 0) + { + lineBreakIndexIndex = startLine; + if (startLine && startLine < m_LineBreaks.Count()) + { + startIndex = m_LineBreaks[startLine - 1]; + } + } + + if (!_horizScrollingAllowed) + return 0; + + _currentStartLine = startLine; + if (_multiline) + return startIndex; + else + return _currentStartIndex; + + +} + +// helper accessors for common gets +float TextEntry::GetValueAsFloat() +{ + int nTextLength = GetTextLength() + 1; + char* txt = ( char* )_alloca( nTextLength * sizeof( char ) ); + GetText( txt, nTextLength ); + + return V_atof( txt ); +} + +int TextEntry::GetValueAsInt() +{ + int nTextLength = GetTextLength() + 1; + char* txt = ( char* )_alloca( nTextLength * sizeof( char ) ); + GetText( txt, nTextLength ); + + return V_atoi( txt ); +} + +//----------------------------------------------------------------------------- +// Purpose: Get a string from text buffer +// Input: offset - index to Start reading from +// bufLenInBytes - length of string +//----------------------------------------------------------------------------- +void TextEntry::GetText(OUT_Z_BYTECAP(bufLenInBytes) char *buf, int bufLenInBytes) +{ + Assert(bufLenInBytes >= sizeof(buf[0])); + if (m_TextStream.Count()) + { + // temporarily null terminate the text stream so we can use the conversion function + int nullTerminatorIndex = m_TextStream.AddToTail((wchar_t)0); + g_pVGuiLocalize->ConvertUnicodeToANSI(m_TextStream.Base(), buf, bufLenInBytes); + m_TextStream.FastRemove(nullTerminatorIndex); + } + else + { + // no characters in the stream + buf[0] = 0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get a string from text buffer +// Input: offset - index to Start reading from +// bufLen - length of string +//----------------------------------------------------------------------------- +void TextEntry::GetText(OUT_Z_BYTECAP(bufLenInBytes) wchar_t *wbuf, int bufLenInBytes) +{ + Assert(bufLenInBytes >= sizeof(wbuf[0])); + int len = m_TextStream.Count(); + if (m_TextStream.Count()) + { + int terminator = min(len, (bufLenInBytes / (int)sizeof(wchar_t)) - 1); + wcsncpy(wbuf, m_TextStream.Base(), terminator); + wbuf[terminator] = 0; + } + else + { + wbuf[0] = 0; + } +} + +void TextEntry::GetTextRange( wchar_t *buf, int from, int numchars ) +{ + int len = m_TextStream.Count(); + int cpChars = max( 0, min( numchars, len - from ) ); + + wcsncpy( buf, m_TextStream.Base() + max( 0, min( len, from ) ), cpChars ); + buf[ cpChars ] = 0; +} + +void TextEntry::GetTextRange( char *buf, int from, int numchars ) +{ + int len = m_TextStream.Count(); + int cpChars = max( 0, min( numchars, len - from ) ); + + g_pVGuiLocalize->ConvertUnicodeToANSI( m_TextStream.Base() + max( 0, min( len, from ) ), buf, cpChars + 1 ); + buf[ cpChars ] = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Sends a message that the text has changed +//----------------------------------------------------------------------------- +void TextEntry::FireActionSignal() +{ + PostActionSignal(new KeyValues("TextChanged")); + _dataChanged = false; // reset the data changed flag + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the font of the buffer text +// Input: font to change to +//----------------------------------------------------------------------------- +void TextEntry::SetFont(HFont font) +{ + _font = font; + InvalidateLayout(); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the scrollbar slider is moved +//----------------------------------------------------------------------------- +void TextEntry::OnSliderMoved() +{ + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool TextEntry::RequestInfo(KeyValues *outputData) +{ + if (!stricmp(outputData->GetName(), "GetText")) + { + wchar_t wbuf[256]; + GetText(wbuf, 255); + outputData->SetWString("text", wbuf); + return true; + } + else if (!stricmp(outputData->GetName(), "GetState")) + { + char buf[64]; + GetText(buf, sizeof(buf)); + outputData->SetInt("state", atoi(buf)); + return true; + } + return BaseClass::RequestInfo(outputData); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TextEntry::OnSetText(const wchar_t *text) +{ + SetText(text); +} + +//----------------------------------------------------------------------------- +// Purpose: as above, but sets an integer +//----------------------------------------------------------------------------- +void TextEntry::OnSetState(int state) +{ + char buf[64]; + Q_snprintf(buf, sizeof(buf), "%d", state); + SetText(buf); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TextEntry::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + _font = scheme()->GetIScheme( GetScheme() )->GetFont( inResourceData->GetString( "font", "Default" ), IsProportional() ); + SetFont( _font ); + + SetTextHidden((bool)inResourceData->GetInt("textHidden", 0)); + SetEditable((bool)inResourceData->GetInt("editable", 1)); + SetMaximumCharCount(inResourceData->GetInt("maxchars", -1)); + SetAllowNumericInputOnly(inResourceData->GetInt("NumericInputOnly", 0)); + SetAllowNonAsciiCharacters(inResourceData->GetInt("unicode", 0)); + SelectAllOnFirstFocus(inResourceData->GetInt("selectallonfirstfocus", 0)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TextEntry::GetSettings( KeyValues *outResourceData ) +{ + BaseClass::GetSettings( outResourceData ); + outResourceData->SetInt("textHidden", _hideText); + outResourceData->SetInt("editable", IsEditable()); + outResourceData->SetInt("maxchars", GetMaximumCharCount()); + outResourceData->SetInt("NumericInputOnly", m_bAllowNumericInputOnly); + outResourceData->SetInt("unicode", m_bAllowNonAsciiCharacters); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *TextEntry::GetDescription() +{ + static char buf[1024]; + Q_snprintf(buf, sizeof(buf), "%s, bool textHidden, bool editable, bool unicode, bool NumericInputOnly, int maxchars", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the number of lines in the window +//----------------------------------------------------------------------------- +int TextEntry::GetNumLines() +{ + return m_LineBreaks.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the height of the text entry window so all text will fit inside +//----------------------------------------------------------------------------- +void TextEntry::SetToFullHeight() +{ + PerformLayout(); + int wide, tall; + GetSize(wide, tall); + + tall = GetNumLines() * (surface()->GetFontTall(_font) + DRAW_OFFSET_Y) + DRAW_OFFSET_Y + 2; + SetSize (wide, tall); + PerformLayout(); + +} + +//----------------------------------------------------------------------------- +// Purpose: Select all the text. +//----------------------------------------------------------------------------- +void TextEntry::SelectAllText( bool bResetCursorPos ) +{ + // if there's no text at all, select none + if ( m_TextStream.Count() == 0 ) + { + _select[0] = -1; + } + else + { + _select[0] = 0; + } + + _select[1] = m_TextStream.Count(); + + if ( bResetCursorPos ) + { + _cursorPos = _select[1]; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Select no text. +//----------------------------------------------------------------------------- +void TextEntry::SelectNoText() +{ + _select[0] = -1; + _select[1] = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the width of the text entry window so all text will fit inside +//----------------------------------------------------------------------------- +void TextEntry::SetToFullWidth() +{ + // probably be problems if you try using this on multi line buffers + // or buffers with clickable text in them. + if (_multiline) + return; + + PerformLayout(); + int wide = 2*DRAW_OFFSET_X; // buffer on left and right end of text. + + // loop through all the characters and sum their widths + for (int i = 0; i < m_TextStream.Count(); ++i) + { + wide += getCharWidth(_font, m_TextStream[i]); + } + + // height of one line of text + int tall = (surface()->GetFontTall(_font) + DRAW_OFFSET_Y) + DRAW_OFFSET_Y + 2; + + SetSize (wide, tall); + PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TextEntry::SelectAllOnFirstFocus( bool status ) +{ + _selectAllOnFirstFocus = status; +} + +void TextEntry::SelectAllOnFocusAlways( bool status ) +{ + _selectAllOnFirstFocus = status; + _selectAllOnFocusAlways = status; +} + +//----------------------------------------------------------------------------- +// Purpose: called when the text entry receives focus +//----------------------------------------------------------------------------- +void TextEntry::OnSetFocus() +{ + // see if we should highlight all on selection + if (_selectAllOnFirstFocus) + { + _select[1] = m_TextStream.Count(); + _select[0] = _select[1] > 0 ? 0 : -1; + _cursorPos = _select[1]; // cursor at end of line + if ( !_selectAllOnFocusAlways ) + { + _selectAllOnFirstFocus = false; + } + } + else if (input()->IsKeyDown(KEY_TAB) || input()->WasKeyReleased(KEY_TAB)) + { + // if we've tabbed to this field then move to the end of the text + GotoTextEnd(); + // clear any selection + SelectNone(); + } + + BaseClass::OnSetFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the width we have to draw text in. +// Do not use in multiline windows. +//----------------------------------------------------------------------------- +void TextEntry::SetDrawWidth(int width) +{ + _drawWidth = width; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the width we have to draw text in. +//----------------------------------------------------------------------------- +int TextEntry::GetDrawWidth() +{ + return _drawWidth; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void TextEntry::SetAllowNonAsciiCharacters(bool state) +{ + m_bAllowNonAsciiCharacters = state; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void TextEntry::SetAllowNumericInputOnly(bool state) +{ + m_bAllowNumericInputOnly = state; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : forward - +//----------------------------------------------------------------------------- +void TextEntry::OnChangeIME( bool forward ) +{ + // Only change ime if Unicode aware + if ( m_bAllowNonAsciiCharacters ) + { + input()->OnChangeIME( forward ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : handleValue - +//----------------------------------------------------------------------------- +void TextEntry::LanguageChanged( int handleValue ) +{ + input()->OnChangeIMEByHandle( handleValue ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : handleValue - +//----------------------------------------------------------------------------- +void TextEntry::ConversionModeChanged( int handleValue ) +{ + input()->OnChangeIMEConversionModeByHandle( handleValue ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : handleValue - +//----------------------------------------------------------------------------- +void TextEntry::SentenceModeChanged( int handleValue ) +{ + input()->OnChangeIMESentenceModeByHandle( handleValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *compstr - +//----------------------------------------------------------------------------- +void TextEntry::CompositionString( const wchar_t *compstr ) +{ + wcsncpy( m_szComposition, compstr, sizeof( m_szComposition ) / sizeof( wchar_t ) - 1 ); + m_szComposition[ sizeof( m_szComposition ) / sizeof( wchar_t ) - 1 ] = L'\0'; +} + +void TextEntry::ShowIMECandidates() +{ + HideIMECandidates(); + + int c = input()->GetCandidateListCount(); + if ( c == 0 ) + { + return; + } + + m_pIMECandidates = new Menu( this, "IMECandidatesMenu" ); + + int pageStart = input()->GetCandidateListPageStart(); + int pageSize = input()->GetCandidateListPageSize(); + int selected = input()->GetCandidateListSelectedItem(); + + int startAtOne = input()->CandidateListStartsAtOne() ? 1 : 0; + + if ( ( selected < pageStart ) || ( selected >= pageStart + pageSize ) ) + { + pageStart = ( selected / pageSize ) * pageSize; + input()->SetCandidateListPageStart( pageStart ); + } + + for ( int i = pageStart; i < pageStart + pageSize; ++i ) + { + if ( i >= c ) + continue; + + bool isSelected = ( i == selected ) ? true : false; + + wchar_t unicode[ 32 ]; + input()->GetCandidate( i, unicode, sizeof( unicode ) ); + + wchar_t label[ 64 ]; + _snwprintf( label, sizeof( label ) / sizeof( wchar_t ) - 1, L"%i %s", i - pageStart + startAtOne, unicode ); + label[ sizeof( label ) / sizeof( wchar_t ) - 1 ] = L'\0'; + + int id = m_pIMECandidates->AddMenuItem( "Candidate", label, (KeyValues *)NULL, this ); + if ( isSelected ) + { + m_pIMECandidates->SetCurrentlyHighlightedItem( id ); + } + } + + m_pIMECandidates->SetVisible(true); + m_pIMECandidates->SetParent(this); + m_pIMECandidates->AddActionSignalTarget(this); + m_pIMECandidates->SetKeyBoardInputEnabled( false ); + + int cx, cy; + CursorToPixelSpace(_cursorPos, cx, cy); + cy = GetTall(); + + LocalToScreen( cx, cy ); + + //m_pIMECandidates->SetPos( cx, cy ); + + // relayout the menu immediately so that we know it's size + m_pIMECandidates->InvalidateLayout(true); + int menuWide, menuTall; + m_pIMECandidates->GetSize(menuWide, menuTall); + + // work out where the cursor is and therefore the best place to put the menu + int wide, tall; + surface()->GetScreenSize(wide, tall); + + if (wide - menuWide > cx) + { + // menu hanging right + if (tall - menuTall > cy) + { + // menu hanging down + m_pIMECandidates->SetPos(cx, cy); + } + else + { + // menu hanging up + m_pIMECandidates->SetPos(cx, cy - menuTall - GetTall()); + } + } + else + { + // menu hanging left + if (tall - menuTall > cy) + { + // menu hanging down + m_pIMECandidates->SetPos(cx - menuWide, cy); + } + else + { + // menu hanging up + m_pIMECandidates->SetPos(cx - menuWide, cy - menuTall-GetTall()); + } + } +} + +void TextEntry::HideIMECandidates() +{ + if ( m_pIMECandidates ) + { + m_pIMECandidates->SetVisible( false ); + } + delete m_pIMECandidates; + m_pIMECandidates = NULL; +} + +void TextEntry::UpdateIMECandidates() +{ + if ( !m_pIMECandidates ) + return; + + int c = input()->GetCandidateListCount(); + if ( c == 0 ) + { + HideIMECandidates(); + return; + } + + int oldCount = m_pIMECandidates->GetItemCount(); + int newCount = input()->GetCandidateListPageSize(); + + if ( oldCount != newCount ) + { + // Recreate the entire menu + ShowIMECandidates(); + return; + } + + int pageSize = input()->GetCandidateListPageSize(); + int selected = input()->GetCandidateListSelectedItem(); + int pageStart = input()->GetCandidateListPageStart(); + + if ( ( selected < pageStart ) || selected >= pageStart + pageSize ) + { + pageStart = ( selected / pageSize ) * pageSize; + input()->SetCandidateListPageStart( pageStart ); + } + + int startAtOne = input()->CandidateListStartsAtOne() ? 1 : 0; + + for ( int i = pageStart; i < pageStart + pageSize; ++i ) + { + int id = m_pIMECandidates->GetMenuID( i - pageStart ); + + MenuItem *item = m_pIMECandidates->GetMenuItem( id ); + if ( !item ) + continue; + + if ( i >= c ) + { + item->SetVisible( false ); + continue; + } + else + { + item->SetVisible( true ); + } + + bool isSelected = ( i == selected ) ? true : false; + + wchar_t unicode[ 32 ]; + input()->GetCandidate( i, unicode, sizeof( unicode ) ); + + wchar_t label[ 64 ]; + _snwprintf( label, sizeof( label ) / sizeof( wchar_t ) - 1, L"%i %s", i - pageStart + startAtOne, unicode ); + label[ sizeof( label ) / sizeof( wchar_t ) - 1 ] = L'\0'; + item->SetText( label ); + if ( isSelected ) + { + m_pIMECandidates->SetCurrentlyHighlightedItem( id ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TextEntry::FlipToLastIME() +{ + int hCurrentIME = input()->GetCurrentIMEHandle(); + int hEnglishIME = input()->GetEnglishIMEHandle(); + + bool isEnglish = ( hCurrentIME == hEnglishIME ) ? true : false; + + // If in english, flip back to previous + if ( isEnglish ) + { + input()->OnChangeIMEByHandle( m_hPreviousIME ); + } + else + { + // If not, remember language and flip to english... + m_hPreviousIME = hCurrentIME; + input()->OnChangeIMEByHandle( hEnglishIME ); + } +} + +void TextEntry::SetDrawLanguageIDAtLeft( bool state ) +{ + m_bDrawLanguageIDAtLeft = state; +} + +bool TextEntry::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ) +{ + menu->AddMenuItem( "replace", "#TextEntry_ReplaceText", "replace", this ); + menu->AddMenuItem( "append", "#TextEntry_AppendText", "append", this ); + menu->AddMenuItem( "prepend", "#TextEntry_PrependText", "prepend", this ); + return true; +} + +bool TextEntry::IsDroppable( CUtlVector< KeyValues * >& msglist ) +{ + if ( msglist.Count() != 1 ) + return false; + + if ( !IsEnabled() ) + return false; + + KeyValues *msg = msglist[ 0 ]; + + const wchar_t *txt = msg->GetWString( "text", L"" ); + if ( !txt || txt[ 0 ] == L'\0' ) + return false; + + return true; +} + +void TextEntry::OnPanelDropped( CUtlVector< KeyValues * >& msglist ) +{ + if ( msglist.Count() != 1 ) + return; + + KeyValues *data = msglist[ 0 ]; + + const wchar_t *newText = data->GetWString( "text" ); + if ( !newText || newText[ 0 ] == L'\0' ) + return; + + char const *cmd = data->GetString( "command" ); + if ( !Q_stricmp( cmd, "replace" ) || + !Q_stricmp( cmd, "default" ) ) + { + SetText( newText ); + _dataChanged = true; + FireActionSignal(); + } + else if ( !Q_stricmp( cmd, "append" ) ) + { + int newLen = wcslen( newText ); + int curLen = m_TextStream.Count(); + + size_t outsize = sizeof( wchar_t ) * ( newLen + curLen + 1 ); + wchar_t *out = (wchar_t *)_alloca( outsize ); + Q_memset( out, 0, outsize ); + wcsncpy( out, m_TextStream.Base(), curLen ); + wcsncat( out, newText, wcslen( newText ) ); + out[ newLen + curLen ] = L'\0'; + SetText( out ); + _dataChanged = true; + FireActionSignal(); + } + else if ( !Q_stricmp( cmd, "prepend" ) ) + { + int newLen = wcslen( newText ); + int curLen = m_TextStream.Count(); + + size_t outsize = sizeof( wchar_t ) * ( newLen + curLen + 1 ); + wchar_t *out = (wchar_t *)_alloca( outsize ); + Q_memset( out, 0, outsize ); + wcsncpy( out, newText, wcslen( newText ) ); + wcsncat( out, m_TextStream.Base(), curLen ); + out[ newLen + curLen ] = L'\0'; + SetText( out ); + _dataChanged = true; + FireActionSignal(); + } +} + +int TextEntry::GetTextLength() const +{ + return m_TextStream.Count(); +} + +bool TextEntry::IsTextFullySelected() const +{ + if ( _select[ 0 ] != 0 ) + return false; + + if ( _select[ 1 ] != GetTextLength() ) + return false; + + return true; +} + +void TextEntry::SetUseFallbackFont( bool bState, HFont hFallback ) +{ + m_bUseFallbackFont = bState; + m_hFallbackFont = hFallback; +} diff --git a/mp/src/vgui2/vgui_controls/TextImage.cpp b/mp/src/vgui2/vgui_controls/TextImage.cpp index d4825ad7..61532126 100644 --- a/mp/src/vgui2/vgui_controls/TextImage.cpp +++ b/mp/src/vgui2/vgui_controls/TextImage.cpp @@ -1,985 +1,985 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: Implementation of vgui::TextImage control -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "tier0/dbg.h" -// memdbgon must be the last include file in a .cpp file!!! -#include - -// enable this define if you want unlocalized strings logged to files unfound.txt and unlocalized.txt -// #define LOG_UNLOCALIZED_STRINGS - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -TextImage::TextImage(const char *text) : Image() -{ - _utext = NULL; - _textBufferLen = 0; - _font = INVALID_FONT; - _fallbackFont = INVALID_FONT; - _unlocalizedTextSymbol = INVALID_LOCALIZE_STRING_INDEX; - _drawWidth = 0; - _textBufferLen = 0; - _textLen = 0; - m_bWrap = false; - m_bWrapCenter = false; - m_LineBreaks.RemoveAll(); - m_LineXIndent.RemoveAll(); - m_pwszEllipsesPosition = NULL; - m_bUseFallbackFont = false; - m_bRenderUsingFallbackFont = false; - m_bAllCaps = false; - - SetText(text); // set the text. -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -TextImage::TextImage(const wchar_t *wszText) : Image() -{ - _utext = NULL; - _textBufferLen = 0; - _font = INVALID_FONT; - _fallbackFont = INVALID_FONT; - _unlocalizedTextSymbol = INVALID_LOCALIZE_STRING_INDEX; - _drawWidth = 0; - _textBufferLen = 0; - _textLen = 0; - m_bWrap = false; - m_bWrapCenter = false; - m_LineBreaks.RemoveAll(); - m_LineXIndent.RemoveAll(); - m_bUseFallbackFont = false; - m_bRenderUsingFallbackFont = false; - m_bAllCaps = false; - - SetText(wszText); // set the text. -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -TextImage::~TextImage() -{ - delete [] _utext; -} - -//----------------------------------------------------------------------------- -// Purpose: takes the string and looks it up in the localization file to convert it to unicode -//----------------------------------------------------------------------------- -void TextImage::SetText(const char *text) -{ - if (!text) - { - text = ""; - } - - // check for localization - if (*text == '#') - { - // try lookup in localization tables - _unlocalizedTextSymbol = g_pVGuiLocalize->FindIndex(text + 1); - - if (_unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX) - { - wchar_t *unicode = g_pVGuiLocalize->GetValueByIndex(_unlocalizedTextSymbol); - SetText(unicode); - return; - } - else - { - // could not find string - // debug code for logging unlocalized strings -#if defined(LOG_UNLOCALIZED_STRINGS) - if (*text) - { - // write out error to unfound.txt log file - static bool first = true; - FILE *f; - if (first) - { - first = false; - f = fopen("unfound.txt", "wt"); - } - else - { - f = fopen("unfound.txt", "at"); - } - - if (f) - { - fprintf(f, "\"%s\"\n", text); - fclose(f); - } - } -#endif // LOG_UNLOCALIZED_STRINGS - } - } - else - { - // debug code for logging unlocalized strings -#if defined(LOG_UNLOCALIZED_STRINGS) - if (text[0]) - { - // setting a label to be ANSI text, write out error to unlocalized.txt log file - static bool first = true; - FILE *f; - if (first) - { - first = false; - f = fopen("unlocalized.txt", "wt"); - } - else - { - f = fopen("unlocalized.txt", "at"); - } - if (f) - { - fprintf(f, "\"%s\"\n", text); - fclose(f); - } - } -#endif // LOG_UNLOCALIZED_STRINGS - } - - // convert the ansi string to unicode and use that - wchar_t unicode[1024]; - g_pVGuiLocalize->ConvertANSIToUnicode(text, unicode, sizeof(unicode)); - SetText(unicode); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the width that the text can be. -//----------------------------------------------------------------------------- -void TextImage::SetDrawWidth(int width) -{ - _drawWidth = width; - m_bRecalculateTruncation = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Gets the width that the text can be. -//----------------------------------------------------------------------------- -void TextImage::GetDrawWidth(int &width) -{ - width = _drawWidth; -} - -//----------------------------------------------------------------------------- -// Purpose: sets unicode text directly -//----------------------------------------------------------------------------- -void TextImage::SetText(const wchar_t *unicode, bool bClearUnlocalizedSymbol) -{ - if ( bClearUnlocalizedSymbol ) - { - // Clear out unlocalized text symbol so that changing dialog variables - // doesn't stomp over the custom unicode string we're being set to. - _unlocalizedTextSymbol = INVALID_LOCALIZE_STRING_INDEX; - } - - if (!unicode) - { - unicode = L""; - } - - // reallocate the buffer if necessary - _textLen = (short)wcslen(unicode); - if (_textLen >= _textBufferLen) - { - delete [] _utext; - _textBufferLen = (short)(_textLen + 1); - _utext = new wchar_t[_textBufferLen]; - } - - m_LineBreaks.RemoveAll(); - m_LineXIndent.RemoveAll(); - - // store the text as unicode - wcscpy(_utext, unicode); - - m_bRecalculateTruncation = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Gets the text in the textImage -//----------------------------------------------------------------------------- -void TextImage::GetText(char *buffer, int bufferSize) -{ - g_pVGuiLocalize->ConvertUnicodeToANSI(_utext, buffer, bufferSize); - - if ( m_bAllCaps ) - { - // Uppercase all the letters - for ( int i = Q_strlen( buffer ); i >= 0; --i ) - { - buffer[ i ] = toupper( buffer[ i ] ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Gets the text in the textImage -//----------------------------------------------------------------------------- -void TextImage::GetText(wchar_t *buffer, int bufLenInBytes) -{ - wcsncpy(buffer, _utext, bufLenInBytes / sizeof(wchar_t)); - - if ( m_bAllCaps ) - { - // Uppercase all the letters - for ( int i = Q_wcslen( buffer ) - 1; i >= 0; --i ) - { - buffer[ i ] = towupper( buffer[ i ] ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void TextImage::GetUnlocalizedText(char *buffer, int bufferSize) -{ - if (_unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX) - { - const char *text = g_pVGuiLocalize->GetNameByIndex(_unlocalizedTextSymbol); - buffer[0] = '#'; - Q_strncpy(buffer + 1, text, bufferSize - 1); - buffer[bufferSize-1] = 0; - } - else - { - GetText(buffer, bufferSize); - } -} - -//----------------------------------------------------------------------------- -// Purpose: unlocalized text symbol -//----------------------------------------------------------------------------- -StringIndex_t TextImage::GetUnlocalizedTextSymbol() -{ - return _unlocalizedTextSymbol; -} - -//----------------------------------------------------------------------------- -// Purpose: Changes the current font -//----------------------------------------------------------------------------- -void TextImage::SetFont(HFont font) -{ - _font = font; - m_bRecalculateTruncation = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Gets the font of the text. -//----------------------------------------------------------------------------- -HFont TextImage::GetFont() -{ - if ( m_bRenderUsingFallbackFont ) - { - return _fallbackFont; - } - - return _font; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the size of the TextImage. This is directly tied to drawWidth -//----------------------------------------------------------------------------- -void TextImage::SetSize(int wide, int tall) -{ - Image::SetSize(wide, tall); - _drawWidth = wide; - m_bRecalculateTruncation = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Draw the Image on screen. -//----------------------------------------------------------------------------- -void TextImage::Paint() -{ - int wide, tall; - GetSize(wide, tall); - - if (!_utext || GetFont() == INVALID_FONT ) - return; - - if (m_bRecalculateTruncation) - { - if ( m_bWrap || m_bWrapCenter ) - { - RecalculateNewLinePositions(); - } - - RecalculateEllipsesPosition(); - } - - DrawSetTextColor(GetColor()); - HFont font = GetFont(); - DrawSetTextFont(font); - - int lineHeight = surface()->GetFontTall(font); - float x = 0.0f; - int y = 0; - int iIndent = 0; - int iNextColorChange = 0; - - int px, py; - GetPos(px, py); - - int currentLineBreak = 0; - - if ( m_bWrapCenter && m_LineXIndent.Count() ) - { - x = m_LineXIndent[0]; - } - - for (wchar_t *wsz = _utext; *wsz != 0; wsz++) - { - wchar_t ch = wsz[0]; - - if ( m_bAllCaps ) - { - ch = towupper( ch ); - } - - if ( m_ColorChangeStream.Count() > iNextColorChange ) - { - if ( m_ColorChangeStream[iNextColorChange].textStreamIndex == (wsz - _utext) ) - { - DrawSetTextColor( m_ColorChangeStream[iNextColorChange].color ); - iNextColorChange++; - } - } - - // check for special characters - if ( ch == '\r' || ch <= 8 ) - { - // ignore, just use \n for newlines - continue; - } - else if (ch == '\n') - { - // newline - iIndent++; - if ( m_bWrapCenter && iIndent < m_LineXIndent.Count() ) - { - x = m_LineXIndent[iIndent]; - } - else - { - x = 0; - } - y += lineHeight; - continue; - } - else if (ch == '&') - { - // "&&" means draw a single ampersand, single one is a shortcut character - if (wsz[1] == '&') - { - // just move on and draw the second ampersand - wsz++; - } - else - { - // draw the underline, then continue to the next character without moving forward -#ifdef VGUI_DRAW_HOTKEYS_ENABLED - surface()->DrawSetTextPos(x + px, y + py); - surface()->DrawUnicodeChar('_'); -#endif - continue; - } - } - - // see if we've hit the truncated portion of the string - if (wsz == m_pwszEllipsesPosition) - { - // string is truncated, draw ellipses - for (int i = 0; i < 3; i++) - { - surface()->DrawSetTextPos(x + px, y + py); - surface()->DrawUnicodeChar('.'); - x += surface()->GetCharacterWidth(font, '.'); - } - break; - } - - if (currentLineBreak != m_LineBreaks.Count()) - { - if (wsz == m_LineBreaks[currentLineBreak]) - { - // newline - iIndent++; - if ( m_bWrapCenter && iIndent < m_LineXIndent.Count() ) - { - x = m_LineXIndent[iIndent]; - } - else - { - x = 0; - } - - y += lineHeight; - currentLineBreak++; - } - } - - // Underlined text wants to draw the spaces anyway -#if USE_GETKERNEDCHARWIDTH - wchar_t chBefore = 0; - wchar_t chAfter = 0; - if ( wsz > _utext ) - chBefore = wsz[-1]; - chAfter = wsz[1]; - float flWide = 0.0f, flabcA = 0.0f; - surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA ); - if ( ch == L' ' ) - x = ceil( x ); - - surface()->DrawSetTextPos( x + px, y + py); - surface()->DrawUnicodeChar(ch); - x += floor(flWide + 0.6); -#else - surface()->DrawSetTextPos( x + px, y + py); - surface()->DrawUnicodeChar(ch); - x += surface()->GetCharacterWidth(font, ch); -#endif - } - - // Useful debugging - /* - DrawSetColor(GetColor()); - DrawOutlinedRect( 0,0, _drawWidth, tall ); - */ -} - -//----------------------------------------------------------------------------- -// Purpose: Get the size of a text string in pixels -//----------------------------------------------------------------------------- -void TextImage::GetTextSize(int &wide, int &tall) -{ - wide = 0; - tall = 0; - int maxWide = 0; - const wchar_t *text = _utext; - - HFont font = _font; - if ( font == INVALID_FONT ) - return; - - if ( m_bWrap || m_bWrapCenter ) - { - RecalculateNewLinePositions(); - } - - // For height, use the remapped font - int fontHeight = surface()->GetFontTall(GetFont()); - tall = fontHeight; - - int textLen = wcslen(text); - for (int i = 0; i < textLen; i++) - { - wchar_t ch = text[i]; - - // handle stupid special characters, these should be removed - if ( ch == '&' ) - { - continue; - } - - if ( m_bAllCaps ) - { - ch = towupper( ch ); - } - -#if USE_GETKERNEDCHARWIDTH - wchar_t chBefore = 0; - wchar_t chAfter = 0; - if ( i > 0 ) - chBefore = text[i-1]; - chAfter = text[i+1]; - float flWide = 0.0f, flabcA; - surface()->GetKernedCharWidth( font, text[i], chBefore, chAfter, flWide, flabcA ); - if ( text[i] == L' ' ) - flWide = ceil( flWide ); - wide += floor( flWide + 0.6); -#else - int a, b, c; - surface()->GetCharABCwide(font, ch, a, b, c); - wide += (a + b + c); -#endif - - - if (ch == '\n') - { - tall += fontHeight; - if(wide>maxWide) - { - maxWide=wide; - } - wide=0; // new line, wide is reset... - } - - if ( m_bWrap || m_bWrapCenter ) - { - for(int j=0; jmaxWide) - { - maxWide=wide; - } - wide=0; // new line, wide is reset... - } - } - } - - } -#ifdef OSX - wide += 2; - if ( textLen < 3 ) - wide += 3; -#endif - if (wide < maxWide) - { - // maxWide only gets set if a newline is in the label - wide = maxWide; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Get the size of the text in the image -//----------------------------------------------------------------------------- -void TextImage::GetContentSize(int &wide, int &tall) -{ - GetTextSize(wide, tall); -} - -//----------------------------------------------------------------------------- -// Purpose: Resize the text image to the content size -//----------------------------------------------------------------------------- -void TextImage::ResizeImageToContent() -{ - int wide, tall; - GetContentSize(wide, tall); - SetSize(wide, tall); -} - -//----------------------------------------------------------------------------- -// Purpose: Recalculates line breaks -//----------------------------------------------------------------------------- -void TextImage::RecalculateNewLinePositions() -{ - HFont font = GetFont(); - - int charWidth; - int x = 0; - - //int wordStartIndex = 0; - wchar_t *wordStartIndex = _utext; - int wordLength = 0; - bool hasWord = false; - bool justStartedNewLine = true; - bool wordStartedOnNewLine = true; - - int startChar = 0; - - // clear the line breaks list - m_LineBreaks.RemoveAll(); - m_LineXIndent.RemoveAll(); - - // handle the case where this char is a new line, in that case - // we have already taken its break index into account above so skip it. - if (_utext[startChar] == '\r' || _utext[startChar] == '\n') - { - startChar++; - } - - // loop through all the characters - for (wchar_t *wsz = &_utext[startChar]; *wsz != 0; wsz++) - { - // handle stupid special characters, these should be removed - // 0x01, 0x02 and 0x03 are color escape characters and should be ignored - if ( ( wsz[0] == '&' || wsz[0] == 0x01 || wsz[0] == 0x02 || wsz[0] == 0x03 ) && wsz[1] != 0 ) - { - wsz++; - } - - wchar_t ch = wsz[0]; - - if ( m_bAllCaps ) - { - ch = towupper( ch ); - } - - // line break only on whitespace characters - if (!iswspace(ch)) - { - if ( !hasWord ) - { - // Start a new word - wordStartIndex = wsz; - hasWord = true; - wordStartedOnNewLine = justStartedNewLine; - wordLength = 0; - } - //else append to the current word - } - else - { - // whitespace/punctuation character - // end the word - hasWord = false; - } - - // get the width -#if USE_GETKERNEDCHARWIDTH - wchar_t chBefore = 0; - wchar_t chAfter = 0; - if ( wsz > _utext ) - chBefore = wsz[-1]; - chAfter = wsz[1]; - float flWide = 0.0f, flabcA = 0.0f; - surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA ); - charWidth = floor( flWide + 0.6 ); -#else - charWidth = surface()->GetCharacterWidth(font, ch); -#endif - if (!iswcntrl(ch)) - { - justStartedNewLine = false; - } - - // check to see if the word is past the end of the line [wordStartIndex, i) - if ((x + charWidth) > _drawWidth || ch == '\r' || ch == '\n') - { - justStartedNewLine = true; - hasWord = false; - - if (ch == '\r' || ch == '\n') - { - // set the break at the current character - //don't do this, paint will manually wrap on newline chars - // m_LineBreaks.AddToTail(i); - } - else if (wordStartedOnNewLine) - { - // word is longer than a line, so set the break at the current cursor - m_LineBreaks.AddToTail(wsz); - } - else - { - // set it at the last word Start - m_LineBreaks.AddToTail(wordStartIndex); - - // just back to reparse the next line of text - // ywb 8/1/07: Back off one extra char since the 'continue' will increment wsz for us by one in the for loop - wsz = wordStartIndex - 1; - } - - // reset word length - wordLength = 0; - x = 0; - continue; - } - - // add to the size - x += charWidth; - wordLength += charWidth; - } - - RecalculateCenterWrapIndents(); -} - -//----------------------------------------------------------------------------- -// Purpose: Calculates where the text should be truncated -//----------------------------------------------------------------------------- -void TextImage::RecalculateEllipsesPosition() -{ - m_bRecalculateTruncation = false; - m_pwszEllipsesPosition = NULL; - - //don't insert ellipses on wrapped strings - if ( m_bWrap || m_bWrapCenter ) - return; - - // don't truncate strings with newlines - if (wcschr(_utext, '\n') != NULL) - return; - - if ( _drawWidth == 0 ) - { - int h; - GetSize( _drawWidth, h ); - } - - for ( int check = 0; check < (m_bUseFallbackFont ? 2 : 1); ++check ) - { - HFont font = GetFont(); - if ( check == 1 && _fallbackFont != INVALID_FONT ) - { - m_pwszEllipsesPosition = NULL; - font = _fallbackFont; - m_bRenderUsingFallbackFont = true; - } - - int ellipsesWidth = 3 * surface()->GetCharacterWidth(font, '.'); - int x = 0; - - for (wchar_t *wsz = _utext; *wsz != 0; wsz++) - { - wchar_t ch = wsz[0]; - - if ( m_bAllCaps ) - { - ch = towupper( ch ); - } - - // check for special characters - if (ch == '\r') - { - // ignore, just use \n for newlines - continue; - } - else if (ch == '&') - { - // "&&" means draw a single ampersand, single one is a shortcut character - if (wsz[1] == '&') - { - // just move on and draw the second ampersand - wsz++; - } - else - { - continue; - } - } - -#if USE_GETKERNEDCHARWIDTH - wchar_t chBefore = 0; - wchar_t chAfter = 0; - if ( wsz > _utext ) - chBefore = wsz[-1]; - chAfter = wsz[1]; - float flWide = 0.0f, flabcA = 0.0f; - surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA ); - int len = floor( flWide + 0.6 ); -#else - int len = surface()->GetCharacterWidth(font, ch); -#endif - - // don't truncate the first character - if (wsz == _utext) - { - x += len; - continue; - } - - if (x + len + ellipsesWidth > _drawWidth) - { - // potential have an ellipses, see if the remaining characters will fit - int remainingLength = len; - for (const wchar_t *rwsz = wsz + 1; *rwsz != 0; rwsz++) - { -#if USE_GETKERNEDCHARWIDTH - wchar_t chBefore = 0; - wchar_t chAfter = 0; - if ( rwsz > _utext ) - chBefore = rwsz[-1]; - chAfter = rwsz[1]; - float flWide = 0.0f, flabcA = 0.0f; - surface()->GetKernedCharWidth( font, *rwsz, chBefore, chAfter, flWide, flabcA ); - int len = floor( flWide + 0.6 ); - remainingLength += floor( flWide + 0.6 ); -#else - remainingLength += surface()->GetCharacterWidth(font, *rwsz); -#endif - } - - if (x + remainingLength > _drawWidth) - { - // looks like we've got an ellipses situation - m_pwszEllipsesPosition = wsz; - break; - } - } - - x += len; - } - - // Didn't need ellipses... - if ( !m_pwszEllipsesPosition ) - break; - } -} - -void TextImage::SetWrap( bool bWrap ) -{ - m_bWrap = bWrap; -} - -void TextImage::SetCenterWrap( bool bWrap ) -{ - m_bWrapCenter = bWrap; -} - - -void TextImage::SetUseFallbackFont( bool bState, HFont hFallback ) -{ - m_bUseFallbackFont = bState; - _fallbackFont = hFallback; -} - -void TextImage::SetAllCaps( bool bAllCaps ) -{ - m_bAllCaps = bAllCaps; -} - -void TextImage::ResizeImageToContentMaxWidth( int nMaxWidth ) -{ - _drawWidth = nMaxWidth; - // Since we might have to use the "fallback" font, go ahead and recalc the ellipses state first to see if that's the case - // NOTE: I think there may be a race condition lurking here, but this seems to work. - if ( m_bRecalculateTruncation ) - { - if ( m_bWrap || m_bWrapCenter ) - { - RecalculateNewLinePositions(); - } - - RecalculateEllipsesPosition(); - } - - ResizeImageToContent(); -} - -//----------------------------------------------------------------------------- -// Purpose: For center wrapping of multi-line text images, determines the indent each line needs to be centered -//----------------------------------------------------------------------------- -void TextImage::RecalculateCenterWrapIndents() -{ - m_LineXIndent.RemoveAll(); - - if ( !m_bWrapCenter ) - return; - - if (!_utext || GetFont() == INVALID_FONT ) - return; - - HFont font = GetFont(); - int px, py; - GetPos(px, py); - - int currentLineBreak = 0; - int iCurLineW = 0; - - for (wchar_t *wsz = _utext; *wsz != 0; wsz++) - { - wchar_t ch = wsz[0]; - - if ( m_bAllCaps ) - { - ch = towupper( ch ); - } - - // check for special characters - if (ch == '\r') - { - // ignore, just use \n for newlines - continue; - } - else if (ch == '\n') - { - int iIdx = m_LineXIndent.AddToTail(); - m_LineXIndent[iIdx] = (_drawWidth - iCurLineW) * 0.5; - - iCurLineW = 0; - continue; - } - else if (ch == '&') - { - // "&&" means draw a single ampersand, single one is a shortcut character - if (wsz[1] == '&') - { - // just move on and draw the second ampersand - wsz++; - } - else - { - // draw the underline, then continue to the next character without moving forward - continue; - } - } - - // Don't need to check ellipses, they're not used when wrapping - - if (currentLineBreak != m_LineBreaks.Count()) - { - if (wsz == m_LineBreaks[currentLineBreak]) - { - int iIdx = m_LineXIndent.AddToTail(); - m_LineXIndent[iIdx] = (_drawWidth - iCurLineW) * 0.5; - - iCurLineW = 0; - currentLineBreak++; - } - } - -#if USE_GETKERNEDCHARWIDTH - wchar_t chBefore = 0; - wchar_t chAfter = 0; - if ( wsz > _utext ) - chBefore = wsz[-1]; - chAfter = wsz[1]; - float flWide = 0.0f, flabcA = 0.0f; - surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA ); - iCurLineW += floor( flWide + 0.6 ); -#else - iCurLineW += surface()->GetCharacterWidth(font, ch); -#endif - } - - // Add the final line - int iIdx = m_LineXIndent.AddToTail(); - m_LineXIndent[iIdx] = (_drawWidth - iCurLineW) * 0.5; -} - -void TextImage::AddColorChange( Color col, int iTextStreamIndex ) -{ - label_colorchange_t tmpChange; - tmpChange.color = col; - tmpChange.textStreamIndex = iTextStreamIndex; - m_ColorChangeStream.Insert( tmpChange ); -} - -void TextImage::SetColorChangeStream( CUtlSortVector *pUtlVecStream ) -{ - ClearColorChangeStream(); - - m_ColorChangeStream = *pUtlVecStream; -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Implementation of vgui::TextImage control +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tier0/dbg.h" +// memdbgon must be the last include file in a .cpp file!!! +#include + +// enable this define if you want unlocalized strings logged to files unfound.txt and unlocalized.txt +// #define LOG_UNLOCALIZED_STRINGS + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +TextImage::TextImage(const char *text) : Image() +{ + _utext = NULL; + _textBufferLen = 0; + _font = INVALID_FONT; + _fallbackFont = INVALID_FONT; + _unlocalizedTextSymbol = INVALID_LOCALIZE_STRING_INDEX; + _drawWidth = 0; + _textBufferLen = 0; + _textLen = 0; + m_bWrap = false; + m_bWrapCenter = false; + m_LineBreaks.RemoveAll(); + m_LineXIndent.RemoveAll(); + m_pwszEllipsesPosition = NULL; + m_bUseFallbackFont = false; + m_bRenderUsingFallbackFont = false; + m_bAllCaps = false; + + SetText(text); // set the text. +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +TextImage::TextImage(const wchar_t *wszText) : Image() +{ + _utext = NULL; + _textBufferLen = 0; + _font = INVALID_FONT; + _fallbackFont = INVALID_FONT; + _unlocalizedTextSymbol = INVALID_LOCALIZE_STRING_INDEX; + _drawWidth = 0; + _textBufferLen = 0; + _textLen = 0; + m_bWrap = false; + m_bWrapCenter = false; + m_LineBreaks.RemoveAll(); + m_LineXIndent.RemoveAll(); + m_bUseFallbackFont = false; + m_bRenderUsingFallbackFont = false; + m_bAllCaps = false; + + SetText(wszText); // set the text. +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +TextImage::~TextImage() +{ + delete [] _utext; +} + +//----------------------------------------------------------------------------- +// Purpose: takes the string and looks it up in the localization file to convert it to unicode +//----------------------------------------------------------------------------- +void TextImage::SetText(const char *text) +{ + if (!text) + { + text = ""; + } + + // check for localization + if (*text == '#') + { + // try lookup in localization tables + _unlocalizedTextSymbol = g_pVGuiLocalize->FindIndex(text + 1); + + if (_unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX) + { + wchar_t *unicode = g_pVGuiLocalize->GetValueByIndex(_unlocalizedTextSymbol); + SetText(unicode); + return; + } + else + { + // could not find string + // debug code for logging unlocalized strings +#if defined(LOG_UNLOCALIZED_STRINGS) + if (*text) + { + // write out error to unfound.txt log file + static bool first = true; + FILE *f; + if (first) + { + first = false; + f = fopen("unfound.txt", "wt"); + } + else + { + f = fopen("unfound.txt", "at"); + } + + if (f) + { + fprintf(f, "\"%s\"\n", text); + fclose(f); + } + } +#endif // LOG_UNLOCALIZED_STRINGS + } + } + else + { + // debug code for logging unlocalized strings +#if defined(LOG_UNLOCALIZED_STRINGS) + if (text[0]) + { + // setting a label to be ANSI text, write out error to unlocalized.txt log file + static bool first = true; + FILE *f; + if (first) + { + first = false; + f = fopen("unlocalized.txt", "wt"); + } + else + { + f = fopen("unlocalized.txt", "at"); + } + if (f) + { + fprintf(f, "\"%s\"\n", text); + fclose(f); + } + } +#endif // LOG_UNLOCALIZED_STRINGS + } + + // convert the ansi string to unicode and use that + wchar_t unicode[1024]; + g_pVGuiLocalize->ConvertANSIToUnicode(text, unicode, sizeof(unicode)); + SetText(unicode); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the width that the text can be. +//----------------------------------------------------------------------------- +void TextImage::SetDrawWidth(int width) +{ + _drawWidth = width; + m_bRecalculateTruncation = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the width that the text can be. +//----------------------------------------------------------------------------- +void TextImage::GetDrawWidth(int &width) +{ + width = _drawWidth; +} + +//----------------------------------------------------------------------------- +// Purpose: sets unicode text directly +//----------------------------------------------------------------------------- +void TextImage::SetText(const wchar_t *unicode, bool bClearUnlocalizedSymbol) +{ + if ( bClearUnlocalizedSymbol ) + { + // Clear out unlocalized text symbol so that changing dialog variables + // doesn't stomp over the custom unicode string we're being set to. + _unlocalizedTextSymbol = INVALID_LOCALIZE_STRING_INDEX; + } + + if (!unicode) + { + unicode = L""; + } + + // reallocate the buffer if necessary + _textLen = (short)wcslen(unicode); + if (_textLen >= _textBufferLen) + { + delete [] _utext; + _textBufferLen = (short)(_textLen + 1); + _utext = new wchar_t[_textBufferLen]; + } + + m_LineBreaks.RemoveAll(); + m_LineXIndent.RemoveAll(); + + // store the text as unicode + wcscpy(_utext, unicode); + + m_bRecalculateTruncation = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the text in the textImage +//----------------------------------------------------------------------------- +void TextImage::GetText(char *buffer, int bufferSize) +{ + g_pVGuiLocalize->ConvertUnicodeToANSI(_utext, buffer, bufferSize); + + if ( m_bAllCaps ) + { + // Uppercase all the letters + for ( int i = Q_strlen( buffer ); i >= 0; --i ) + { + buffer[ i ] = toupper( buffer[ i ] ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the text in the textImage +//----------------------------------------------------------------------------- +void TextImage::GetText(wchar_t *buffer, int bufLenInBytes) +{ + wcsncpy(buffer, _utext, bufLenInBytes / sizeof(wchar_t)); + + if ( m_bAllCaps ) + { + // Uppercase all the letters + for ( int i = Q_wcslen( buffer ) - 1; i >= 0; --i ) + { + buffer[ i ] = towupper( buffer[ i ] ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void TextImage::GetUnlocalizedText(char *buffer, int bufferSize) +{ + if (_unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX) + { + const char *text = g_pVGuiLocalize->GetNameByIndex(_unlocalizedTextSymbol); + buffer[0] = '#'; + Q_strncpy(buffer + 1, text, bufferSize - 1); + buffer[bufferSize-1] = 0; + } + else + { + GetText(buffer, bufferSize); + } +} + +//----------------------------------------------------------------------------- +// Purpose: unlocalized text symbol +//----------------------------------------------------------------------------- +StringIndex_t TextImage::GetUnlocalizedTextSymbol() +{ + return _unlocalizedTextSymbol; +} + +//----------------------------------------------------------------------------- +// Purpose: Changes the current font +//----------------------------------------------------------------------------- +void TextImage::SetFont(HFont font) +{ + _font = font; + m_bRecalculateTruncation = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the font of the text. +//----------------------------------------------------------------------------- +HFont TextImage::GetFont() +{ + if ( m_bRenderUsingFallbackFont ) + { + return _fallbackFont; + } + + return _font; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the size of the TextImage. This is directly tied to drawWidth +//----------------------------------------------------------------------------- +void TextImage::SetSize(int wide, int tall) +{ + Image::SetSize(wide, tall); + _drawWidth = wide; + m_bRecalculateTruncation = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Draw the Image on screen. +//----------------------------------------------------------------------------- +void TextImage::Paint() +{ + int wide, tall; + GetSize(wide, tall); + + if (!_utext || GetFont() == INVALID_FONT ) + return; + + if (m_bRecalculateTruncation) + { + if ( m_bWrap || m_bWrapCenter ) + { + RecalculateNewLinePositions(); + } + + RecalculateEllipsesPosition(); + } + + DrawSetTextColor(GetColor()); + HFont font = GetFont(); + DrawSetTextFont(font); + + int lineHeight = surface()->GetFontTall(font); + float x = 0.0f; + int y = 0; + int iIndent = 0; + int iNextColorChange = 0; + + int px, py; + GetPos(px, py); + + int currentLineBreak = 0; + + if ( m_bWrapCenter && m_LineXIndent.Count() ) + { + x = m_LineXIndent[0]; + } + + for (wchar_t *wsz = _utext; *wsz != 0; wsz++) + { + wchar_t ch = wsz[0]; + + if ( m_bAllCaps ) + { + ch = towupper( ch ); + } + + if ( m_ColorChangeStream.Count() > iNextColorChange ) + { + if ( m_ColorChangeStream[iNextColorChange].textStreamIndex == (wsz - _utext) ) + { + DrawSetTextColor( m_ColorChangeStream[iNextColorChange].color ); + iNextColorChange++; + } + } + + // check for special characters + if ( ch == '\r' || ch <= 8 ) + { + // ignore, just use \n for newlines + continue; + } + else if (ch == '\n') + { + // newline + iIndent++; + if ( m_bWrapCenter && iIndent < m_LineXIndent.Count() ) + { + x = m_LineXIndent[iIndent]; + } + else + { + x = 0; + } + y += lineHeight; + continue; + } + else if (ch == '&') + { + // "&&" means draw a single ampersand, single one is a shortcut character + if (wsz[1] == '&') + { + // just move on and draw the second ampersand + wsz++; + } + else + { + // draw the underline, then continue to the next character without moving forward +#ifdef VGUI_DRAW_HOTKEYS_ENABLED + surface()->DrawSetTextPos(x + px, y + py); + surface()->DrawUnicodeChar('_'); +#endif + continue; + } + } + + // see if we've hit the truncated portion of the string + if (wsz == m_pwszEllipsesPosition) + { + // string is truncated, draw ellipses + for (int i = 0; i < 3; i++) + { + surface()->DrawSetTextPos(x + px, y + py); + surface()->DrawUnicodeChar('.'); + x += surface()->GetCharacterWidth(font, '.'); + } + break; + } + + if (currentLineBreak != m_LineBreaks.Count()) + { + if (wsz == m_LineBreaks[currentLineBreak]) + { + // newline + iIndent++; + if ( m_bWrapCenter && iIndent < m_LineXIndent.Count() ) + { + x = m_LineXIndent[iIndent]; + } + else + { + x = 0; + } + + y += lineHeight; + currentLineBreak++; + } + } + + // Underlined text wants to draw the spaces anyway +#if USE_GETKERNEDCHARWIDTH + wchar_t chBefore = 0; + wchar_t chAfter = 0; + if ( wsz > _utext ) + chBefore = wsz[-1]; + chAfter = wsz[1]; + float flWide = 0.0f, flabcA = 0.0f; + surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA ); + if ( ch == L' ' ) + x = ceil( x ); + + surface()->DrawSetTextPos( x + px, y + py); + surface()->DrawUnicodeChar(ch); + x += floor(flWide + 0.6); +#else + surface()->DrawSetTextPos( x + px, y + py); + surface()->DrawUnicodeChar(ch); + x += surface()->GetCharacterWidth(font, ch); +#endif + } + + // Useful debugging + /* + DrawSetColor(GetColor()); + DrawOutlinedRect( 0,0, _drawWidth, tall ); + */ +} + +//----------------------------------------------------------------------------- +// Purpose: Get the size of a text string in pixels +//----------------------------------------------------------------------------- +void TextImage::GetTextSize(int &wide, int &tall) +{ + wide = 0; + tall = 0; + int maxWide = 0; + const wchar_t *text = _utext; + + HFont font = _font; + if ( font == INVALID_FONT ) + return; + + if ( m_bWrap || m_bWrapCenter ) + { + RecalculateNewLinePositions(); + } + + // For height, use the remapped font + int fontHeight = surface()->GetFontTall(GetFont()); + tall = fontHeight; + + int textLen = wcslen(text); + for (int i = 0; i < textLen; i++) + { + wchar_t ch = text[i]; + + // handle stupid special characters, these should be removed + if ( ch == '&' ) + { + continue; + } + + if ( m_bAllCaps ) + { + ch = towupper( ch ); + } + +#if USE_GETKERNEDCHARWIDTH + wchar_t chBefore = 0; + wchar_t chAfter = 0; + if ( i > 0 ) + chBefore = text[i-1]; + chAfter = text[i+1]; + float flWide = 0.0f, flabcA; + surface()->GetKernedCharWidth( font, text[i], chBefore, chAfter, flWide, flabcA ); + if ( text[i] == L' ' ) + flWide = ceil( flWide ); + wide += floor( flWide + 0.6); +#else + int a, b, c; + surface()->GetCharABCwide(font, ch, a, b, c); + wide += (a + b + c); +#endif + + + if (ch == '\n') + { + tall += fontHeight; + if(wide>maxWide) + { + maxWide=wide; + } + wide=0; // new line, wide is reset... + } + + if ( m_bWrap || m_bWrapCenter ) + { + for(int j=0; jmaxWide) + { + maxWide=wide; + } + wide=0; // new line, wide is reset... + } + } + } + + } +#ifdef OSX + wide += 2; + if ( textLen < 3 ) + wide += 3; +#endif + if (wide < maxWide) + { + // maxWide only gets set if a newline is in the label + wide = maxWide; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get the size of the text in the image +//----------------------------------------------------------------------------- +void TextImage::GetContentSize(int &wide, int &tall) +{ + GetTextSize(wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: Resize the text image to the content size +//----------------------------------------------------------------------------- +void TextImage::ResizeImageToContent() +{ + int wide, tall; + GetContentSize(wide, tall); + SetSize(wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: Recalculates line breaks +//----------------------------------------------------------------------------- +void TextImage::RecalculateNewLinePositions() +{ + HFont font = GetFont(); + + int charWidth; + int x = 0; + + //int wordStartIndex = 0; + wchar_t *wordStartIndex = _utext; + int wordLength = 0; + bool hasWord = false; + bool justStartedNewLine = true; + bool wordStartedOnNewLine = true; + + int startChar = 0; + + // clear the line breaks list + m_LineBreaks.RemoveAll(); + m_LineXIndent.RemoveAll(); + + // handle the case where this char is a new line, in that case + // we have already taken its break index into account above so skip it. + if (_utext[startChar] == '\r' || _utext[startChar] == '\n') + { + startChar++; + } + + // loop through all the characters + for (wchar_t *wsz = &_utext[startChar]; *wsz != 0; wsz++) + { + // handle stupid special characters, these should be removed + // 0x01, 0x02 and 0x03 are color escape characters and should be ignored + if ( ( wsz[0] == '&' || wsz[0] == 0x01 || wsz[0] == 0x02 || wsz[0] == 0x03 ) && wsz[1] != 0 ) + { + wsz++; + } + + wchar_t ch = wsz[0]; + + if ( m_bAllCaps ) + { + ch = towupper( ch ); + } + + // line break only on whitespace characters + if (!iswspace(ch)) + { + if ( !hasWord ) + { + // Start a new word + wordStartIndex = wsz; + hasWord = true; + wordStartedOnNewLine = justStartedNewLine; + wordLength = 0; + } + //else append to the current word + } + else + { + // whitespace/punctuation character + // end the word + hasWord = false; + } + + // get the width +#if USE_GETKERNEDCHARWIDTH + wchar_t chBefore = 0; + wchar_t chAfter = 0; + if ( wsz > _utext ) + chBefore = wsz[-1]; + chAfter = wsz[1]; + float flWide = 0.0f, flabcA = 0.0f; + surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA ); + charWidth = floor( flWide + 0.6 ); +#else + charWidth = surface()->GetCharacterWidth(font, ch); +#endif + if (!iswcntrl(ch)) + { + justStartedNewLine = false; + } + + // check to see if the word is past the end of the line [wordStartIndex, i) + if ((x + charWidth) > _drawWidth || ch == '\r' || ch == '\n') + { + justStartedNewLine = true; + hasWord = false; + + if (ch == '\r' || ch == '\n') + { + // set the break at the current character + //don't do this, paint will manually wrap on newline chars + // m_LineBreaks.AddToTail(i); + } + else if (wordStartedOnNewLine) + { + // word is longer than a line, so set the break at the current cursor + m_LineBreaks.AddToTail(wsz); + } + else + { + // set it at the last word Start + m_LineBreaks.AddToTail(wordStartIndex); + + // just back to reparse the next line of text + // ywb 8/1/07: Back off one extra char since the 'continue' will increment wsz for us by one in the for loop + wsz = wordStartIndex - 1; + } + + // reset word length + wordLength = 0; + x = 0; + continue; + } + + // add to the size + x += charWidth; + wordLength += charWidth; + } + + RecalculateCenterWrapIndents(); +} + +//----------------------------------------------------------------------------- +// Purpose: Calculates where the text should be truncated +//----------------------------------------------------------------------------- +void TextImage::RecalculateEllipsesPosition() +{ + m_bRecalculateTruncation = false; + m_pwszEllipsesPosition = NULL; + + //don't insert ellipses on wrapped strings + if ( m_bWrap || m_bWrapCenter ) + return; + + // don't truncate strings with newlines + if (wcschr(_utext, '\n') != NULL) + return; + + if ( _drawWidth == 0 ) + { + int h; + GetSize( _drawWidth, h ); + } + + for ( int check = 0; check < (m_bUseFallbackFont ? 2 : 1); ++check ) + { + HFont font = GetFont(); + if ( check == 1 && _fallbackFont != INVALID_FONT ) + { + m_pwszEllipsesPosition = NULL; + font = _fallbackFont; + m_bRenderUsingFallbackFont = true; + } + + int ellipsesWidth = 3 * surface()->GetCharacterWidth(font, '.'); + int x = 0; + + for (wchar_t *wsz = _utext; *wsz != 0; wsz++) + { + wchar_t ch = wsz[0]; + + if ( m_bAllCaps ) + { + ch = towupper( ch ); + } + + // check for special characters + if (ch == '\r') + { + // ignore, just use \n for newlines + continue; + } + else if (ch == '&') + { + // "&&" means draw a single ampersand, single one is a shortcut character + if (wsz[1] == '&') + { + // just move on and draw the second ampersand + wsz++; + } + else + { + continue; + } + } + +#if USE_GETKERNEDCHARWIDTH + wchar_t chBefore = 0; + wchar_t chAfter = 0; + if ( wsz > _utext ) + chBefore = wsz[-1]; + chAfter = wsz[1]; + float flWide = 0.0f, flabcA = 0.0f; + surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA ); + int len = floor( flWide + 0.6 ); +#else + int len = surface()->GetCharacterWidth(font, ch); +#endif + + // don't truncate the first character + if (wsz == _utext) + { + x += len; + continue; + } + + if (x + len + ellipsesWidth > _drawWidth) + { + // potential have an ellipses, see if the remaining characters will fit + int remainingLength = len; + for (const wchar_t *rwsz = wsz + 1; *rwsz != 0; rwsz++) + { +#if USE_GETKERNEDCHARWIDTH + wchar_t chBefore = 0; + wchar_t chAfter = 0; + if ( rwsz > _utext ) + chBefore = rwsz[-1]; + chAfter = rwsz[1]; + float flWide = 0.0f, flabcA = 0.0f; + surface()->GetKernedCharWidth( font, *rwsz, chBefore, chAfter, flWide, flabcA ); + int len = floor( flWide + 0.6 ); + remainingLength += floor( flWide + 0.6 ); +#else + remainingLength += surface()->GetCharacterWidth(font, *rwsz); +#endif + } + + if (x + remainingLength > _drawWidth) + { + // looks like we've got an ellipses situation + m_pwszEllipsesPosition = wsz; + break; + } + } + + x += len; + } + + // Didn't need ellipses... + if ( !m_pwszEllipsesPosition ) + break; + } +} + +void TextImage::SetWrap( bool bWrap ) +{ + m_bWrap = bWrap; +} + +void TextImage::SetCenterWrap( bool bWrap ) +{ + m_bWrapCenter = bWrap; +} + + +void TextImage::SetUseFallbackFont( bool bState, HFont hFallback ) +{ + m_bUseFallbackFont = bState; + _fallbackFont = hFallback; +} + +void TextImage::SetAllCaps( bool bAllCaps ) +{ + m_bAllCaps = bAllCaps; +} + +void TextImage::ResizeImageToContentMaxWidth( int nMaxWidth ) +{ + _drawWidth = nMaxWidth; + // Since we might have to use the "fallback" font, go ahead and recalc the ellipses state first to see if that's the case + // NOTE: I think there may be a race condition lurking here, but this seems to work. + if ( m_bRecalculateTruncation ) + { + if ( m_bWrap || m_bWrapCenter ) + { + RecalculateNewLinePositions(); + } + + RecalculateEllipsesPosition(); + } + + ResizeImageToContent(); +} + +//----------------------------------------------------------------------------- +// Purpose: For center wrapping of multi-line text images, determines the indent each line needs to be centered +//----------------------------------------------------------------------------- +void TextImage::RecalculateCenterWrapIndents() +{ + m_LineXIndent.RemoveAll(); + + if ( !m_bWrapCenter ) + return; + + if (!_utext || GetFont() == INVALID_FONT ) + return; + + HFont font = GetFont(); + int px, py; + GetPos(px, py); + + int currentLineBreak = 0; + int iCurLineW = 0; + + for (wchar_t *wsz = _utext; *wsz != 0; wsz++) + { + wchar_t ch = wsz[0]; + + if ( m_bAllCaps ) + { + ch = towupper( ch ); + } + + // check for special characters + if (ch == '\r') + { + // ignore, just use \n for newlines + continue; + } + else if (ch == '\n') + { + int iIdx = m_LineXIndent.AddToTail(); + m_LineXIndent[iIdx] = (_drawWidth - iCurLineW) * 0.5; + + iCurLineW = 0; + continue; + } + else if (ch == '&') + { + // "&&" means draw a single ampersand, single one is a shortcut character + if (wsz[1] == '&') + { + // just move on and draw the second ampersand + wsz++; + } + else + { + // draw the underline, then continue to the next character without moving forward + continue; + } + } + + // Don't need to check ellipses, they're not used when wrapping + + if (currentLineBreak != m_LineBreaks.Count()) + { + if (wsz == m_LineBreaks[currentLineBreak]) + { + int iIdx = m_LineXIndent.AddToTail(); + m_LineXIndent[iIdx] = (_drawWidth - iCurLineW) * 0.5; + + iCurLineW = 0; + currentLineBreak++; + } + } + +#if USE_GETKERNEDCHARWIDTH + wchar_t chBefore = 0; + wchar_t chAfter = 0; + if ( wsz > _utext ) + chBefore = wsz[-1]; + chAfter = wsz[1]; + float flWide = 0.0f, flabcA = 0.0f; + surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA ); + iCurLineW += floor( flWide + 0.6 ); +#else + iCurLineW += surface()->GetCharacterWidth(font, ch); +#endif + } + + // Add the final line + int iIdx = m_LineXIndent.AddToTail(); + m_LineXIndent[iIdx] = (_drawWidth - iCurLineW) * 0.5; +} + +void TextImage::AddColorChange( Color col, int iTextStreamIndex ) +{ + label_colorchange_t tmpChange; + tmpChange.color = col; + tmpChange.textStreamIndex = iTextStreamIndex; + m_ColorChangeStream.Insert( tmpChange ); +} + +void TextImage::SetColorChangeStream( CUtlSortVector *pUtlVecStream ) +{ + ClearColorChangeStream(); + + m_ColorChangeStream = *pUtlVecStream; +} diff --git a/mp/src/vgui2/vgui_controls/ToggleButton.cpp b/mp/src/vgui2/vgui_controls/ToggleButton.cpp index 292faa38..63e4dd69 100644 --- a/mp/src/vgui2/vgui_controls/ToggleButton.cpp +++ b/mp/src/vgui2/vgui_controls/ToggleButton.cpp @@ -1,102 +1,102 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#include - -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY_DEFAULT_TEXT( ToggleButton, ToggleButton ); - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -ToggleButton::ToggleButton(Panel *parent, const char *panelName, const char* text) : Button(parent, panelName, text) -{ - SetButtonActivationType(ACTIVATE_ONPRESSED); -} - -//----------------------------------------------------------------------------- -// Purpose: Turns double-click into normal click -//----------------------------------------------------------------------------- -void ToggleButton::OnMouseDoublePressed(MouseCode code) -{ - OnMousePressed(code); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Color ToggleButton::GetButtonFgColor() -{ - if (IsSelected()) - { - // highlight the text when depressed - return _selectedColor; - } - else - { - return BaseClass::GetButtonFgColor(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool ToggleButton::CanBeDefaultButton(void) -{ - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Toggles the state of the button -//----------------------------------------------------------------------------- -void ToggleButton::DoClick() -{ - if (IsSelected()) - { - ForceDepressed(false); - } - else if (!IsSelected()) - { - ForceDepressed(true); - } - - SetSelected(!IsSelected()); - FireActionSignal(); - - // post a button toggled message - KeyValues *msg = new KeyValues("ButtonToggled"); - msg->SetInt("state", (int)IsSelected()); - PostActionSignal(msg); - - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ToggleButton::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - _selectedColor = GetSchemeColor("ToggleButton.SelectedTextColor", pScheme); -} - -void ToggleButton::OnKeyCodePressed(KeyCode code) -{ - if (code != KEY_ENTER) - { - BaseClass::OnKeyCodePressed(code); - } -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include + +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY_DEFAULT_TEXT( ToggleButton, ToggleButton ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ToggleButton::ToggleButton(Panel *parent, const char *panelName, const char* text) : Button(parent, panelName, text) +{ + SetButtonActivationType(ACTIVATE_ONPRESSED); +} + +//----------------------------------------------------------------------------- +// Purpose: Turns double-click into normal click +//----------------------------------------------------------------------------- +void ToggleButton::OnMouseDoublePressed(MouseCode code) +{ + OnMousePressed(code); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Color ToggleButton::GetButtonFgColor() +{ + if (IsSelected()) + { + // highlight the text when depressed + return _selectedColor; + } + else + { + return BaseClass::GetButtonFgColor(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool ToggleButton::CanBeDefaultButton(void) +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Toggles the state of the button +//----------------------------------------------------------------------------- +void ToggleButton::DoClick() +{ + if (IsSelected()) + { + ForceDepressed(false); + } + else if (!IsSelected()) + { + ForceDepressed(true); + } + + SetSelected(!IsSelected()); + FireActionSignal(); + + // post a button toggled message + KeyValues *msg = new KeyValues("ButtonToggled"); + msg->SetInt("state", (int)IsSelected()); + PostActionSignal(msg); + + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ToggleButton::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + _selectedColor = GetSchemeColor("ToggleButton.SelectedTextColor", pScheme); +} + +void ToggleButton::OnKeyCodePressed(KeyCode code) +{ + if (code != KEY_ENTER) + { + BaseClass::OnKeyCodePressed(code); + } +} + diff --git a/mp/src/vgui2/vgui_controls/ToolWindow.cpp b/mp/src/vgui2/vgui_controls/ToolWindow.cpp index 3208f128..51109c64 100644 --- a/mp/src/vgui2/vgui_controls/ToolWindow.cpp +++ b/mp/src/vgui2/vgui_controls/ToolWindow.cpp @@ -1,478 +1,478 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include "vgui/IInput.h" -#include "vgui/MouseCode.h" -#include "vgui/ISurface.h" - -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -CUtlVector< ToolWindow * > ToolWindow::s_ToolWindows; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : int -//----------------------------------------------------------------------------- -int ToolWindow::GetToolWindowCount() -{ - return s_ToolWindows.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : index - -// Output : PropertySheet -//----------------------------------------------------------------------------- -ToolWindow *ToolWindow::GetToolWindow( int index ) -{ - return s_ToolWindows[ index ]; -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -ToolWindow::ToolWindow( - Panel *parent, - bool contextlabel, - IToolWindowFactory *factory /*= 0*/, - Panel *page /*= NULL*/, - char const *title /*= NULL */, - bool contextMenu /*=false*/, - bool inGlobalList /*= true*/ ) : BaseClass( parent, "ToolWindow" ), - m_pFactory( factory ) -{ - if ( inGlobalList ) - { - s_ToolWindows.AddToTail( this ); - } - - // create the property sheet - m_pPropertySheet = new PropertySheet(this, "ToolWindowSheet", true ); - m_pPropertySheet->ShowContextButtons( contextlabel ); - m_pPropertySheet->AddPage( page, title, 0, contextMenu ); - m_pPropertySheet->AddActionSignalTarget(this); - m_pPropertySheet->SetSmallTabs( true ); - m_pPropertySheet->SetKBNavigationEnabled( false ); - - SetSmallCaption( true ); - - SetMenuButtonResponsive(false); - SetMinimizeButtonVisible(false); - SetCloseButtonVisible(true); - SetMoveable( true ); - SetSizeable(true); - - SetClipToParent( false ); - SetVisible( true ); - - SetDeleteSelfOnClose( true ); - - SetTitle( "", false ); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -ToolWindow::~ToolWindow() -{ - // These don't actually kill the children of the property sheet - m_pPropertySheet->RemoveAllPages(); - - s_ToolWindows.FindAndRemove( this ); -} - -//----------------------------------------------------------------------------- -// Purpose: Pass through to sheet -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool ToolWindow::IsDraggableTabContainer() const -{ - return m_pPropertySheet->IsDraggableTab(); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns a pointer to the PropertySheet this dialog encapsulates -// Output : PropertySheet * -//----------------------------------------------------------------------------- -PropertySheet *ToolWindow::GetPropertySheet() -{ - return m_pPropertySheet; -} - -//----------------------------------------------------------------------------- -// Purpose: Gets a pointer to the currently active page. -// Output : Panel -//----------------------------------------------------------------------------- -Panel *ToolWindow::GetActivePage() -{ - return m_pPropertySheet->GetActivePage(); -} - -void ToolWindow::SetActivePage( Panel *page ) -{ - m_pPropertySheet->SetActivePage( page ); -} - -//----------------------------------------------------------------------------- -// Purpose: Wrapped function -//----------------------------------------------------------------------------- -void ToolWindow::AddPage(Panel *page, const char *title, bool contextMenu) -{ - m_pPropertySheet->AddPage(page, title, 0, contextMenu ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *page - -//----------------------------------------------------------------------------- -void ToolWindow::RemovePage( Panel *page ) -{ - m_pPropertySheet->RemovePage( page ); - if ( m_pPropertySheet->GetNumPages() == 0 ) - { - MarkForDeletion(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Sets up the sheet -//----------------------------------------------------------------------------- -void ToolWindow::PerformLayout() -{ - BaseClass::PerformLayout(); - - int x, y, wide, tall; - GetClientArea(x, y, wide, tall); - m_pPropertySheet->SetBounds(x, y, wide, tall); - m_pPropertySheet->InvalidateLayout(); // tell the propertysheet to redraw! - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Overrides build mode so it edits the sub panel -//----------------------------------------------------------------------------- -void ToolWindow::ActivateBuildMode() -{ - // no subpanel, no build mode - EditablePanel *panel = dynamic_cast(GetActivePage()); - if (!panel) - return; - - panel->ActivateBuildMode(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void ToolWindow::RequestFocus(int direction) -{ - m_pPropertySheet->RequestFocus(direction); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *factory - -//----------------------------------------------------------------------------- -void ToolWindow::SetToolWindowFactory( IToolWindowFactory *factory ) -{ - m_pFactory = factory; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : IToolWindowFactory -//----------------------------------------------------------------------------- -IToolWindowFactory *ToolWindow::GetToolWindowFactory() -{ - return m_pFactory; -} - -//----------------------------------------------------------------------------- -// Purpose: To fill the space left by other tool windows -// Input : edge: 0=all, 1=top, 2=right, 3=bottom, 4=left -// Output : -//----------------------------------------------------------------------------- - -void ToolWindow::Grow( int edge, int from_x, int from_y ) -{ - int status_h = 24; - int menubar_h = 27; - - int sw, sh; - surface()->GetScreenSize( sw, sh ); - - int old_x, old_y, old_w, old_h; - GetBounds( old_x, old_y, old_w, old_h ); - - int new_x, new_y, new_w, new_h; - new_x = old_x; - new_y = old_y; - new_w = old_w; - new_h = old_h; - - int c = GetToolWindowCount(); - - // grow up - if ( ( edge == 0 ) || ( edge == 1 ) ) - { - // first shrink the edge back to the grow point - if ( from_y >= 0 ) - { - old_h = old_h - ( from_y - old_y ); - old_y = from_y; - } - - // now grow the edge as far as it can go - new_h = old_h + ( old_y - menubar_h ); - new_y = menubar_h; - - for ( int i = 0 ; i < c; ++i ) - { - ToolWindow *tw = GetToolWindow( i ); - Assert( tw ); - if ( ( !tw ) || ( tw == this ) ) - continue; - - // Get panel bounds - int x, y, w, h; - tw->GetBounds( x, y, w, h ); - - // grow it - if ( ( ( ( old_x > x ) && ( old_x < x + w ) ) - || ( ( old_x + old_w > x ) && ( old_x + old_w < x + w ) ) - || ( ( old_x <= x ) && old_x + old_w >= x + w )) - && ( ( old_y >= y + h ) && ( new_y < y + h ) ) ) - { - new_h = old_h + ( old_y - ( y + h ) ); - new_y = y + h; - } - } - old_h = new_h; - old_y = new_y; - } - - // grow right - if ( ( edge == 0 ) || ( edge == 2 ) ) - { - // first shrink the edge back to the grow point - if ( from_x >= 0 ) - { - old_w = from_x - old_x; - } - - // now grow the edge as far as it can go - new_w = sw - old_x; - - for ( int i = 0 ; i < c; ++i ) - { - ToolWindow *tw = GetToolWindow( i ); - Assert( tw ); - if ( ( !tw ) || ( tw == this ) ) - continue; - - // Get panel bounds - int x, y, w, h; - tw->GetBounds( x, y, w, h ); - - // grow it - if ( ( ( ( old_y > y ) && ( old_y < y + h ) ) - || ( ( old_y + old_h > y ) && ( old_y + old_h < y + h ) ) - || ( ( old_y <= y ) && old_y + old_h >= y + h )) - && ( ( old_x + old_w <= x ) && ( new_w > x - old_x ) ) ) - { - new_w = x - old_x; - } - } - old_w = new_w; - } - - // grow down - if ( ( edge == 0 ) || ( edge == 3 ) ) - { - // first shrink the edge back to the grow point - if ( from_y >= 0 ) - { - old_h = from_y - old_y; - } - - // now grow the edge as far as it can go - new_h = sh - old_y - status_h; - - for ( int i = 0 ; i < c; ++i ) - { - ToolWindow *tw = GetToolWindow( i ); - Assert( tw ); - if ( ( !tw ) || ( tw == this ) ) - continue; - - // Get panel bounds - int x, y, w, h; - tw->GetBounds( x, y, w, h ); - - // grow it - if ( ( ( ( old_x > x ) && ( old_x < x + w ) ) - || ( ( old_x + old_w > x ) && ( old_x + old_w < x + w ) ) - || ( ( old_x <= x ) && old_x + old_w >= x + w )) - && ( ( old_y + old_h <= y ) && ( new_h > y - old_y ) ) ) - { - new_h = y - old_y; - } - } - old_h = new_h; - } - - // grow left - if ( ( edge == 0 ) || ( edge == 4 ) ) - { - // first shrink the edge back to the grow point - if ( from_x >= 0 ) - { - old_w = old_w - ( from_x - old_x ); - old_x = from_x; - } - - // now grow the edge as far as it can go - new_w = old_w + old_x; - new_x = 0; - - for ( int i = 0 ; i < c; ++i ) - { - ToolWindow *tw = GetToolWindow( i ); - Assert( tw ); - if ( ( !tw ) || ( tw == this ) ) - continue; - - // Get panel bounds - int x, y, w, h; - tw->GetBounds( x, y, w, h ); - - // grow it - if ( ( ( ( old_y > y ) && ( old_y < y + h ) ) - || ( ( old_y + old_h > y ) && ( old_y + old_h < y + h ) ) - || ( ( old_y <= y ) && old_y + old_h >= y + h )) - && ( ( old_x >= x + w ) && ( new_x < x + w ) ) ) - { - new_w = old_w + ( old_x - ( x + w ) ); - new_x = x + w; - } - } - old_w = new_w; - old_x = new_x; - } - - // Set panel bounds - SetBounds( new_x, new_y, new_w, new_h ); - -} - -//----------------------------------------------------------------------------- -// Purpose: Calls Grow based on where the mouse is. -// over titlebar: grows all edges ( from mouse pos ) -// over edge grab area: grows just that edge -// over corner grab area: grows the two adjacent edges -// Input : -// Output : -//----------------------------------------------------------------------------- - -void ToolWindow::GrowFromClick() -{ - int mx, my; - input()->GetCursorPos( mx, my ); - - int esz, csz, brsz, ch; - esz = GetDraggerSize(); - csz = GetCornerSize(); - brsz = GetBottomRightSize(); - ch = GetCaptionHeight(); - - int x, y, w, h; - GetBounds( x, y, w, h ); - - // upper right - if ( ( mx > x+w-csz-1 ) && ( my < y+csz ) ) - { - Grow(1); - Grow(2); - } - // lower right (the big one) - else if ( ( mx > x+w-brsz-1 ) && ( my > y+h-brsz-1 ) ) - { - Grow(2); - Grow(3); - } - // lower left - else if ( ( mx < x+csz ) && ( my > y+h-csz-1 ) ) - { - Grow(3); - Grow(4); - } - // upper left - else if ( ( mx < x+csz ) && ( my < y+csz ) ) - { - Grow(4); - Grow(1); - } - // top edge - else if ( my < y+esz ) - { - Grow(1); - } - // right edge - else if ( mx > x+w-esz-1 ) - { - Grow(2); - } - // bottom edge - else if ( my > y+h-esz-1 ) - { - Grow(3); - } - // left edge - else if ( mx < x+esz ) - { - Grow(4); - } - // otherwise (if over the grab bar), grow all edges (from the clicked point) - else if ( my < y + ch ) - { - Grow(0, mx, my); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : -//----------------------------------------------------------------------------- - -void ToolWindow::OnMouseDoublePressed( MouseCode code ) -{ - GrowFromClick(); -} - -void ToolWindow::OnMousePressed( MouseCode code ) -{ - switch ( code ) - { - case MOUSE_MIDDLE: - GrowFromClick(); - break; - default: - BaseClass::OnMousePressed( code ); - } -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include "vgui/IInput.h" +#include "vgui/MouseCode.h" +#include "vgui/ISurface.h" + +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +CUtlVector< ToolWindow * > ToolWindow::s_ToolWindows; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : int +//----------------------------------------------------------------------------- +int ToolWindow::GetToolWindowCount() +{ + return s_ToolWindows.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : PropertySheet +//----------------------------------------------------------------------------- +ToolWindow *ToolWindow::GetToolWindow( int index ) +{ + return s_ToolWindows[ index ]; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ToolWindow::ToolWindow( + Panel *parent, + bool contextlabel, + IToolWindowFactory *factory /*= 0*/, + Panel *page /*= NULL*/, + char const *title /*= NULL */, + bool contextMenu /*=false*/, + bool inGlobalList /*= true*/ ) : BaseClass( parent, "ToolWindow" ), + m_pFactory( factory ) +{ + if ( inGlobalList ) + { + s_ToolWindows.AddToTail( this ); + } + + // create the property sheet + m_pPropertySheet = new PropertySheet(this, "ToolWindowSheet", true ); + m_pPropertySheet->ShowContextButtons( contextlabel ); + m_pPropertySheet->AddPage( page, title, 0, contextMenu ); + m_pPropertySheet->AddActionSignalTarget(this); + m_pPropertySheet->SetSmallTabs( true ); + m_pPropertySheet->SetKBNavigationEnabled( false ); + + SetSmallCaption( true ); + + SetMenuButtonResponsive(false); + SetMinimizeButtonVisible(false); + SetCloseButtonVisible(true); + SetMoveable( true ); + SetSizeable(true); + + SetClipToParent( false ); + SetVisible( true ); + + SetDeleteSelfOnClose( true ); + + SetTitle( "", false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +ToolWindow::~ToolWindow() +{ + // These don't actually kill the children of the property sheet + m_pPropertySheet->RemoveAllPages(); + + s_ToolWindows.FindAndRemove( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Pass through to sheet +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool ToolWindow::IsDraggableTabContainer() const +{ + return m_pPropertySheet->IsDraggableTab(); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a pointer to the PropertySheet this dialog encapsulates +// Output : PropertySheet * +//----------------------------------------------------------------------------- +PropertySheet *ToolWindow::GetPropertySheet() +{ + return m_pPropertySheet; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets a pointer to the currently active page. +// Output : Panel +//----------------------------------------------------------------------------- +Panel *ToolWindow::GetActivePage() +{ + return m_pPropertySheet->GetActivePage(); +} + +void ToolWindow::SetActivePage( Panel *page ) +{ + m_pPropertySheet->SetActivePage( page ); +} + +//----------------------------------------------------------------------------- +// Purpose: Wrapped function +//----------------------------------------------------------------------------- +void ToolWindow::AddPage(Panel *page, const char *title, bool contextMenu) +{ + m_pPropertySheet->AddPage(page, title, 0, contextMenu ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *page - +//----------------------------------------------------------------------------- +void ToolWindow::RemovePage( Panel *page ) +{ + m_pPropertySheet->RemovePage( page ); + if ( m_pPropertySheet->GetNumPages() == 0 ) + { + MarkForDeletion(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets up the sheet +//----------------------------------------------------------------------------- +void ToolWindow::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y, wide, tall; + GetClientArea(x, y, wide, tall); + m_pPropertySheet->SetBounds(x, y, wide, tall); + m_pPropertySheet->InvalidateLayout(); // tell the propertysheet to redraw! + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Overrides build mode so it edits the sub panel +//----------------------------------------------------------------------------- +void ToolWindow::ActivateBuildMode() +{ + // no subpanel, no build mode + EditablePanel *panel = dynamic_cast(GetActivePage()); + if (!panel) + return; + + panel->ActivateBuildMode(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ToolWindow::RequestFocus(int direction) +{ + m_pPropertySheet->RequestFocus(direction); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *factory - +//----------------------------------------------------------------------------- +void ToolWindow::SetToolWindowFactory( IToolWindowFactory *factory ) +{ + m_pFactory = factory; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : IToolWindowFactory +//----------------------------------------------------------------------------- +IToolWindowFactory *ToolWindow::GetToolWindowFactory() +{ + return m_pFactory; +} + +//----------------------------------------------------------------------------- +// Purpose: To fill the space left by other tool windows +// Input : edge: 0=all, 1=top, 2=right, 3=bottom, 4=left +// Output : +//----------------------------------------------------------------------------- + +void ToolWindow::Grow( int edge, int from_x, int from_y ) +{ + int status_h = 24; + int menubar_h = 27; + + int sw, sh; + surface()->GetScreenSize( sw, sh ); + + int old_x, old_y, old_w, old_h; + GetBounds( old_x, old_y, old_w, old_h ); + + int new_x, new_y, new_w, new_h; + new_x = old_x; + new_y = old_y; + new_w = old_w; + new_h = old_h; + + int c = GetToolWindowCount(); + + // grow up + if ( ( edge == 0 ) || ( edge == 1 ) ) + { + // first shrink the edge back to the grow point + if ( from_y >= 0 ) + { + old_h = old_h - ( from_y - old_y ); + old_y = from_y; + } + + // now grow the edge as far as it can go + new_h = old_h + ( old_y - menubar_h ); + new_y = menubar_h; + + for ( int i = 0 ; i < c; ++i ) + { + ToolWindow *tw = GetToolWindow( i ); + Assert( tw ); + if ( ( !tw ) || ( tw == this ) ) + continue; + + // Get panel bounds + int x, y, w, h; + tw->GetBounds( x, y, w, h ); + + // grow it + if ( ( ( ( old_x > x ) && ( old_x < x + w ) ) + || ( ( old_x + old_w > x ) && ( old_x + old_w < x + w ) ) + || ( ( old_x <= x ) && old_x + old_w >= x + w )) + && ( ( old_y >= y + h ) && ( new_y < y + h ) ) ) + { + new_h = old_h + ( old_y - ( y + h ) ); + new_y = y + h; + } + } + old_h = new_h; + old_y = new_y; + } + + // grow right + if ( ( edge == 0 ) || ( edge == 2 ) ) + { + // first shrink the edge back to the grow point + if ( from_x >= 0 ) + { + old_w = from_x - old_x; + } + + // now grow the edge as far as it can go + new_w = sw - old_x; + + for ( int i = 0 ; i < c; ++i ) + { + ToolWindow *tw = GetToolWindow( i ); + Assert( tw ); + if ( ( !tw ) || ( tw == this ) ) + continue; + + // Get panel bounds + int x, y, w, h; + tw->GetBounds( x, y, w, h ); + + // grow it + if ( ( ( ( old_y > y ) && ( old_y < y + h ) ) + || ( ( old_y + old_h > y ) && ( old_y + old_h < y + h ) ) + || ( ( old_y <= y ) && old_y + old_h >= y + h )) + && ( ( old_x + old_w <= x ) && ( new_w > x - old_x ) ) ) + { + new_w = x - old_x; + } + } + old_w = new_w; + } + + // grow down + if ( ( edge == 0 ) || ( edge == 3 ) ) + { + // first shrink the edge back to the grow point + if ( from_y >= 0 ) + { + old_h = from_y - old_y; + } + + // now grow the edge as far as it can go + new_h = sh - old_y - status_h; + + for ( int i = 0 ; i < c; ++i ) + { + ToolWindow *tw = GetToolWindow( i ); + Assert( tw ); + if ( ( !tw ) || ( tw == this ) ) + continue; + + // Get panel bounds + int x, y, w, h; + tw->GetBounds( x, y, w, h ); + + // grow it + if ( ( ( ( old_x > x ) && ( old_x < x + w ) ) + || ( ( old_x + old_w > x ) && ( old_x + old_w < x + w ) ) + || ( ( old_x <= x ) && old_x + old_w >= x + w )) + && ( ( old_y + old_h <= y ) && ( new_h > y - old_y ) ) ) + { + new_h = y - old_y; + } + } + old_h = new_h; + } + + // grow left + if ( ( edge == 0 ) || ( edge == 4 ) ) + { + // first shrink the edge back to the grow point + if ( from_x >= 0 ) + { + old_w = old_w - ( from_x - old_x ); + old_x = from_x; + } + + // now grow the edge as far as it can go + new_w = old_w + old_x; + new_x = 0; + + for ( int i = 0 ; i < c; ++i ) + { + ToolWindow *tw = GetToolWindow( i ); + Assert( tw ); + if ( ( !tw ) || ( tw == this ) ) + continue; + + // Get panel bounds + int x, y, w, h; + tw->GetBounds( x, y, w, h ); + + // grow it + if ( ( ( ( old_y > y ) && ( old_y < y + h ) ) + || ( ( old_y + old_h > y ) && ( old_y + old_h < y + h ) ) + || ( ( old_y <= y ) && old_y + old_h >= y + h )) + && ( ( old_x >= x + w ) && ( new_x < x + w ) ) ) + { + new_w = old_w + ( old_x - ( x + w ) ); + new_x = x + w; + } + } + old_w = new_w; + old_x = new_x; + } + + // Set panel bounds + SetBounds( new_x, new_y, new_w, new_h ); + +} + +//----------------------------------------------------------------------------- +// Purpose: Calls Grow based on where the mouse is. +// over titlebar: grows all edges ( from mouse pos ) +// over edge grab area: grows just that edge +// over corner grab area: grows the two adjacent edges +// Input : +// Output : +//----------------------------------------------------------------------------- + +void ToolWindow::GrowFromClick() +{ + int mx, my; + input()->GetCursorPos( mx, my ); + + int esz, csz, brsz, ch; + esz = GetDraggerSize(); + csz = GetCornerSize(); + brsz = GetBottomRightSize(); + ch = GetCaptionHeight(); + + int x, y, w, h; + GetBounds( x, y, w, h ); + + // upper right + if ( ( mx > x+w-csz-1 ) && ( my < y+csz ) ) + { + Grow(1); + Grow(2); + } + // lower right (the big one) + else if ( ( mx > x+w-brsz-1 ) && ( my > y+h-brsz-1 ) ) + { + Grow(2); + Grow(3); + } + // lower left + else if ( ( mx < x+csz ) && ( my > y+h-csz-1 ) ) + { + Grow(3); + Grow(4); + } + // upper left + else if ( ( mx < x+csz ) && ( my < y+csz ) ) + { + Grow(4); + Grow(1); + } + // top edge + else if ( my < y+esz ) + { + Grow(1); + } + // right edge + else if ( mx > x+w-esz-1 ) + { + Grow(2); + } + // bottom edge + else if ( my > y+h-esz-1 ) + { + Grow(3); + } + // left edge + else if ( mx < x+esz ) + { + Grow(4); + } + // otherwise (if over the grab bar), grow all edges (from the clicked point) + else if ( my < y + ch ) + { + Grow(0, mx, my); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : +//----------------------------------------------------------------------------- + +void ToolWindow::OnMouseDoublePressed( MouseCode code ) +{ + GrowFromClick(); +} + +void ToolWindow::OnMousePressed( MouseCode code ) +{ + switch ( code ) + { + case MOUSE_MIDDLE: + GrowFromClick(); + break; + default: + BaseClass::OnMousePressed( code ); + } +} diff --git a/mp/src/vgui2/vgui_controls/Tooltip.cpp b/mp/src/vgui2/vgui_controls/Tooltip.cpp index 4debfbda..e18427eb 100644 --- a/mp/src/vgui2/vgui_controls/Tooltip.cpp +++ b/mp/src/vgui2/vgui_controls/Tooltip.cpp @@ -1,406 +1,406 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// This class is a message box that has two buttons, ok and cancel instead of -// just the ok button of a message box. We use a message box class for the ok button -// and implement another button here. -//=============================================================================// - -#include -#define PROTECTED_THINGS_DISABLE - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - - -//------------------------------------------------------------------------------------------------------ -//------------------------------------------------------------------------------------------------------ -static vgui::DHANDLE< TextEntry > s_TooltipWindow; -static int s_iTooltipWindowCount = 0; - - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -BaseTooltip::BaseTooltip(Panel *parent, const char *text) -{ - SetText(text); - - _displayOnOneLine = false; - _makeVisible = false; - _isDirty = false; - _enabled = true; - - _tooltipDelay = 500; // default delay for opening tooltips - _delay = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Reset the tooltip delay timer -//----------------------------------------------------------------------------- -void BaseTooltip::ResetDelay() -{ - _isDirty = true; - _delay = system()->GetTimeMillis() + _tooltipDelay; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the tooltip delay before a tooltip comes up. -//----------------------------------------------------------------------------- -void BaseTooltip::SetTooltipDelay( int tooltipDelay ) -{ - _tooltipDelay = tooltipDelay; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the tooltip delay before a tooltip comes up. -//----------------------------------------------------------------------------- -int BaseTooltip::GetTooltipDelay() -{ - return _tooltipDelay; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the tool tip to display on one line only -// Default is multiple lines. -//----------------------------------------------------------------------------- -void BaseTooltip::SetTooltipFormatToSingleLine() -{ - _displayOnOneLine = true; - _isDirty = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the tool tip to display on multiple lines. -//----------------------------------------------------------------------------- -void BaseTooltip::SetTooltipFormatToMultiLine() -{ - _displayOnOneLine = false; - _isDirty = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Display the tooltip -//----------------------------------------------------------------------------- -void BaseTooltip::ShowTooltip(Panel *currentPanel) -{ - _makeVisible = true; - - PerformLayout(); -} - -void BaseTooltip::SetEnabled( bool bState ) -{ - _enabled = bState; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool BaseTooltip::ShouldLayout( void ) -{ - if (!_makeVisible) - return false; - - if (_delay > system()->GetTimeMillis()) - return false; - - // We only need to layout when we first become visible - if ( !_isDirty ) - return false; - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Display the tooltip -//----------------------------------------------------------------------------- -void BaseTooltip::HideTooltip() -{ - _makeVisible = false; - _isDirty = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the tooltip text -//----------------------------------------------------------------------------- -void BaseTooltip::SetText(const char *text) -{ - _isDirty = true; - - if (!text) - { - text = ""; - } - - if (m_Text.Size() > 0) - { - m_Text.RemoveAll(); - } - - for (unsigned int i = 0; i < strlen(text); i++) - { - m_Text.AddToTail(text[i]); - } - m_Text.AddToTail('\0'); - - if (s_TooltipWindow.Get() && m_pParent == s_TooltipWindow.Get()->GetParent()) - { - s_TooltipWindow->SetText(m_Text.Base()); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Get the tooltip text -//----------------------------------------------------------------------------- -const char *BaseTooltip::GetText() -{ - return m_Text.Base(); -} - -//----------------------------------------------------------------------------- -// Purpose: Position the tool tip -//----------------------------------------------------------------------------- -void BaseTooltip::PositionWindow( Panel *pTipPanel ) -{ - int iTipW, iTipH; - pTipPanel->GetSize( iTipW, iTipH ); - - int cursorX, cursorY; - input()->GetCursorPos(cursorX, cursorY); - - int wide, tall; - surface()->GetScreenSize(wide, tall); - - if (wide - iTipW > cursorX) - { - cursorY += 20; - // menu hanging right - if (tall - iTipH > cursorY) - { - // menu hanging down - pTipPanel->SetPos(cursorX, cursorY); - } - else - { - // menu hanging up - pTipPanel->SetPos(cursorX, cursorY - iTipH - 20); - } - } - else - { - // menu hanging left - if (tall - iTipH > cursorY) - { - // menu hanging down - pTipPanel->SetPos(cursorX - iTipW, cursorY); - } - else - { - // menu hanging up - pTipPanel->SetPos(cursorX - iTipW, cursorY - iTipH - 20 ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -TextTooltip::TextTooltip(Panel *parent, const char *text) : BaseTooltip( parent, text ) -{ - if (!s_TooltipWindow.Get()) - { - s_TooltipWindow = new TextEntry(NULL, "tooltip"); - - s_TooltipWindow->InvalidateLayout(false, true); - - // this bit of hackery is necessary because tooltips don't get ApplySchemeSettings called from their parents - IScheme *pScheme = scheme()->GetIScheme( s_TooltipWindow->GetScheme() ); - s_TooltipWindow->SetBgColor(s_TooltipWindow->GetSchemeColor("Tooltip.BgColor", s_TooltipWindow->GetBgColor(), pScheme)); - s_TooltipWindow->SetFgColor(s_TooltipWindow->GetSchemeColor("Tooltip.TextColor", s_TooltipWindow->GetFgColor(), pScheme)); - s_TooltipWindow->SetBorder(pScheme->GetBorder("ToolTipBorder")); - s_TooltipWindow->SetFont( pScheme->GetFont("DefaultSmall", s_TooltipWindow->IsProportional())); - } - s_iTooltipWindowCount++; - - // this line prevents the main window from losing focus - // when a tooltip pops up - s_TooltipWindow->MakePopup(false, true); - s_TooltipWindow->SetKeyBoardInputEnabled( false ); - s_TooltipWindow->SetMouseInputEnabled( false ); - - SetText(text); - s_TooltipWindow->SetText(m_Text.Base()); - s_TooltipWindow->SetEditable(false); - s_TooltipWindow->SetMultiline(true); - s_TooltipWindow->SetVisible(false); -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -TextTooltip::~TextTooltip() -{ - if (--s_iTooltipWindowCount < 1) - { - if ( s_TooltipWindow.Get() ) - { - s_TooltipWindow->MarkForDeletion(); - } - s_TooltipWindow = NULL; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set the tooltip text -//----------------------------------------------------------------------------- -void TextTooltip::SetText(const char *text) -{ - BaseTooltip::SetText( text ); - - if (s_TooltipWindow.Get()) - { - s_TooltipWindow->SetText(m_Text.Base()); - } -} - -//----------------------------------------------------------------------------- -// Purpose: gets the font from the scheme -//----------------------------------------------------------------------------- -void TextTooltip::ApplySchemeSettings(IScheme *pScheme) -{ - if ( s_TooltipWindow ) - { - s_TooltipWindow->SetFont(pScheme->GetFont("DefaultSmall", s_TooltipWindow->IsProportional())); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Display the tooltip -//----------------------------------------------------------------------------- -void TextTooltip::ShowTooltip(Panel *currentPanel) -{ - if ( s_TooltipWindow.Get() ) - { - int nLen = s_TooltipWindow->GetTextLength(); - - if ( nLen <= 0 ) - { - // Empty tool tip, no need to show it - _makeVisible = false; - return; - } - - char *pBuf = (char*)_alloca( nLen+1 ); - s_TooltipWindow->GetText( pBuf, nLen+1 ); - Panel *pCurrentParent = s_TooltipWindow->GetParent(); - - _isDirty = _isDirty || ( pCurrentParent != currentPanel ); - s_TooltipWindow->SetText( m_Text.Base() ); - s_TooltipWindow->SetParent(currentPanel); - } - BaseTooltip::ShowTooltip( currentPanel ); -} - -//----------------------------------------------------------------------------- -// Purpose: Display the tooltip -//----------------------------------------------------------------------------- -void TextTooltip::PerformLayout() -{ - if ( !ShouldLayout() ) - return; - // we're ready, just make us visible - if ( !s_TooltipWindow.Get() ) - return; - - _isDirty = false; - - s_TooltipWindow->SetVisible(true); - s_TooltipWindow->MakePopup( false, true ); - s_TooltipWindow->SetKeyBoardInputEnabled( false ); - s_TooltipWindow->SetMouseInputEnabled( false ); - - // relayout the textwindow immediately so that we know it's size - //m_pTextEntry->InvalidateLayout(true); - - SizeTextWindow(); - PositionWindow( s_TooltipWindow ); -} - -//----------------------------------------------------------------------------- -// Purpose: Size the text window so all the text fits inside it. -//----------------------------------------------------------------------------- -void TextTooltip::SizeTextWindow() -{ - if ( !s_TooltipWindow.Get() ) - return; - - if (_displayOnOneLine) - { - // We want the tool tip to be one line - s_TooltipWindow->SetMultiline(false); - s_TooltipWindow->SetToFullWidth(); - } - else - { - // We want the tool tip to be one line - s_TooltipWindow->SetMultiline(false); - s_TooltipWindow->SetToFullWidth(); - int wide, tall; - s_TooltipWindow->GetSize( wide, tall ); - double newWide = sqrt( (2.0/1) * wide * tall ); - double newTall = (1/2) * newWide; - s_TooltipWindow->SetMultiline(true); - s_TooltipWindow->SetSize((int)newWide, (int)newTall ); - s_TooltipWindow->SetToFullHeight(); - - s_TooltipWindow->GetSize( wide, tall ); - - if (( wide < 100 ) && ( s_TooltipWindow->GetNumLines() == 2) ) - { - s_TooltipWindow->SetMultiline(false); - s_TooltipWindow->SetToFullWidth(); - } - else - { - - while ( (float)wide/(float)tall < 2 ) - { - s_TooltipWindow->SetSize( wide + 1, tall ); - s_TooltipWindow->SetToFullHeight(); - s_TooltipWindow->GetSize( wide, tall ); - } - } - s_TooltipWindow->GetSize( wide, tall ); - // ivgui()->DPrintf("End Ratio: %f\n", (float)wide/(float)tall); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Display the tooltip -//----------------------------------------------------------------------------- -void TextTooltip::HideTooltip() -{ - if ( s_TooltipWindow.Get() ) - { - s_TooltipWindow->SetVisible(false); - } - - BaseTooltip::HideTooltip(); -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// This class is a message box that has two buttons, ok and cancel instead of +// just the ok button of a message box. We use a message box class for the ok button +// and implement another button here. +//=============================================================================// + +#include +#define PROTECTED_THINGS_DISABLE + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + + +//------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------------ +static vgui::DHANDLE< TextEntry > s_TooltipWindow; +static int s_iTooltipWindowCount = 0; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +BaseTooltip::BaseTooltip(Panel *parent, const char *text) +{ + SetText(text); + + _displayOnOneLine = false; + _makeVisible = false; + _isDirty = false; + _enabled = true; + + _tooltipDelay = 500; // default delay for opening tooltips + _delay = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Reset the tooltip delay timer +//----------------------------------------------------------------------------- +void BaseTooltip::ResetDelay() +{ + _isDirty = true; + _delay = system()->GetTimeMillis() + _tooltipDelay; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the tooltip delay before a tooltip comes up. +//----------------------------------------------------------------------------- +void BaseTooltip::SetTooltipDelay( int tooltipDelay ) +{ + _tooltipDelay = tooltipDelay; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the tooltip delay before a tooltip comes up. +//----------------------------------------------------------------------------- +int BaseTooltip::GetTooltipDelay() +{ + return _tooltipDelay; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the tool tip to display on one line only +// Default is multiple lines. +//----------------------------------------------------------------------------- +void BaseTooltip::SetTooltipFormatToSingleLine() +{ + _displayOnOneLine = true; + _isDirty = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the tool tip to display on multiple lines. +//----------------------------------------------------------------------------- +void BaseTooltip::SetTooltipFormatToMultiLine() +{ + _displayOnOneLine = false; + _isDirty = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Display the tooltip +//----------------------------------------------------------------------------- +void BaseTooltip::ShowTooltip(Panel *currentPanel) +{ + _makeVisible = true; + + PerformLayout(); +} + +void BaseTooltip::SetEnabled( bool bState ) +{ + _enabled = bState; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool BaseTooltip::ShouldLayout( void ) +{ + if (!_makeVisible) + return false; + + if (_delay > system()->GetTimeMillis()) + return false; + + // We only need to layout when we first become visible + if ( !_isDirty ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Display the tooltip +//----------------------------------------------------------------------------- +void BaseTooltip::HideTooltip() +{ + _makeVisible = false; + _isDirty = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the tooltip text +//----------------------------------------------------------------------------- +void BaseTooltip::SetText(const char *text) +{ + _isDirty = true; + + if (!text) + { + text = ""; + } + + if (m_Text.Size() > 0) + { + m_Text.RemoveAll(); + } + + for (unsigned int i = 0; i < strlen(text); i++) + { + m_Text.AddToTail(text[i]); + } + m_Text.AddToTail('\0'); + + if (s_TooltipWindow.Get() && m_pParent == s_TooltipWindow.Get()->GetParent()) + { + s_TooltipWindow->SetText(m_Text.Base()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Get the tooltip text +//----------------------------------------------------------------------------- +const char *BaseTooltip::GetText() +{ + return m_Text.Base(); +} + +//----------------------------------------------------------------------------- +// Purpose: Position the tool tip +//----------------------------------------------------------------------------- +void BaseTooltip::PositionWindow( Panel *pTipPanel ) +{ + int iTipW, iTipH; + pTipPanel->GetSize( iTipW, iTipH ); + + int cursorX, cursorY; + input()->GetCursorPos(cursorX, cursorY); + + int wide, tall; + surface()->GetScreenSize(wide, tall); + + if (wide - iTipW > cursorX) + { + cursorY += 20; + // menu hanging right + if (tall - iTipH > cursorY) + { + // menu hanging down + pTipPanel->SetPos(cursorX, cursorY); + } + else + { + // menu hanging up + pTipPanel->SetPos(cursorX, cursorY - iTipH - 20); + } + } + else + { + // menu hanging left + if (tall - iTipH > cursorY) + { + // menu hanging down + pTipPanel->SetPos(cursorX - iTipW, cursorY); + } + else + { + // menu hanging up + pTipPanel->SetPos(cursorX - iTipW, cursorY - iTipH - 20 ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +TextTooltip::TextTooltip(Panel *parent, const char *text) : BaseTooltip( parent, text ) +{ + if (!s_TooltipWindow.Get()) + { + s_TooltipWindow = new TextEntry(NULL, "tooltip"); + + s_TooltipWindow->InvalidateLayout(false, true); + + // this bit of hackery is necessary because tooltips don't get ApplySchemeSettings called from their parents + IScheme *pScheme = scheme()->GetIScheme( s_TooltipWindow->GetScheme() ); + s_TooltipWindow->SetBgColor(s_TooltipWindow->GetSchemeColor("Tooltip.BgColor", s_TooltipWindow->GetBgColor(), pScheme)); + s_TooltipWindow->SetFgColor(s_TooltipWindow->GetSchemeColor("Tooltip.TextColor", s_TooltipWindow->GetFgColor(), pScheme)); + s_TooltipWindow->SetBorder(pScheme->GetBorder("ToolTipBorder")); + s_TooltipWindow->SetFont( pScheme->GetFont("DefaultSmall", s_TooltipWindow->IsProportional())); + } + s_iTooltipWindowCount++; + + // this line prevents the main window from losing focus + // when a tooltip pops up + s_TooltipWindow->MakePopup(false, true); + s_TooltipWindow->SetKeyBoardInputEnabled( false ); + s_TooltipWindow->SetMouseInputEnabled( false ); + + SetText(text); + s_TooltipWindow->SetText(m_Text.Base()); + s_TooltipWindow->SetEditable(false); + s_TooltipWindow->SetMultiline(true); + s_TooltipWindow->SetVisible(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +TextTooltip::~TextTooltip() +{ + if (--s_iTooltipWindowCount < 1) + { + if ( s_TooltipWindow.Get() ) + { + s_TooltipWindow->MarkForDeletion(); + } + s_TooltipWindow = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the tooltip text +//----------------------------------------------------------------------------- +void TextTooltip::SetText(const char *text) +{ + BaseTooltip::SetText( text ); + + if (s_TooltipWindow.Get()) + { + s_TooltipWindow->SetText(m_Text.Base()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: gets the font from the scheme +//----------------------------------------------------------------------------- +void TextTooltip::ApplySchemeSettings(IScheme *pScheme) +{ + if ( s_TooltipWindow ) + { + s_TooltipWindow->SetFont(pScheme->GetFont("DefaultSmall", s_TooltipWindow->IsProportional())); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Display the tooltip +//----------------------------------------------------------------------------- +void TextTooltip::ShowTooltip(Panel *currentPanel) +{ + if ( s_TooltipWindow.Get() ) + { + int nLen = s_TooltipWindow->GetTextLength(); + + if ( nLen <= 0 ) + { + // Empty tool tip, no need to show it + _makeVisible = false; + return; + } + + char *pBuf = (char*)_alloca( nLen+1 ); + s_TooltipWindow->GetText( pBuf, nLen+1 ); + Panel *pCurrentParent = s_TooltipWindow->GetParent(); + + _isDirty = _isDirty || ( pCurrentParent != currentPanel ); + s_TooltipWindow->SetText( m_Text.Base() ); + s_TooltipWindow->SetParent(currentPanel); + } + BaseTooltip::ShowTooltip( currentPanel ); +} + +//----------------------------------------------------------------------------- +// Purpose: Display the tooltip +//----------------------------------------------------------------------------- +void TextTooltip::PerformLayout() +{ + if ( !ShouldLayout() ) + return; + // we're ready, just make us visible + if ( !s_TooltipWindow.Get() ) + return; + + _isDirty = false; + + s_TooltipWindow->SetVisible(true); + s_TooltipWindow->MakePopup( false, true ); + s_TooltipWindow->SetKeyBoardInputEnabled( false ); + s_TooltipWindow->SetMouseInputEnabled( false ); + + // relayout the textwindow immediately so that we know it's size + //m_pTextEntry->InvalidateLayout(true); + + SizeTextWindow(); + PositionWindow( s_TooltipWindow ); +} + +//----------------------------------------------------------------------------- +// Purpose: Size the text window so all the text fits inside it. +//----------------------------------------------------------------------------- +void TextTooltip::SizeTextWindow() +{ + if ( !s_TooltipWindow.Get() ) + return; + + if (_displayOnOneLine) + { + // We want the tool tip to be one line + s_TooltipWindow->SetMultiline(false); + s_TooltipWindow->SetToFullWidth(); + } + else + { + // We want the tool tip to be one line + s_TooltipWindow->SetMultiline(false); + s_TooltipWindow->SetToFullWidth(); + int wide, tall; + s_TooltipWindow->GetSize( wide, tall ); + double newWide = sqrt( (2.0/1) * wide * tall ); + double newTall = (1/2) * newWide; + s_TooltipWindow->SetMultiline(true); + s_TooltipWindow->SetSize((int)newWide, (int)newTall ); + s_TooltipWindow->SetToFullHeight(); + + s_TooltipWindow->GetSize( wide, tall ); + + if (( wide < 100 ) && ( s_TooltipWindow->GetNumLines() == 2) ) + { + s_TooltipWindow->SetMultiline(false); + s_TooltipWindow->SetToFullWidth(); + } + else + { + + while ( (float)wide/(float)tall < 2 ) + { + s_TooltipWindow->SetSize( wide + 1, tall ); + s_TooltipWindow->SetToFullHeight(); + s_TooltipWindow->GetSize( wide, tall ); + } + } + s_TooltipWindow->GetSize( wide, tall ); + // ivgui()->DPrintf("End Ratio: %f\n", (float)wide/(float)tall); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Display the tooltip +//----------------------------------------------------------------------------- +void TextTooltip::HideTooltip() +{ + if ( s_TooltipWindow.Get() ) + { + s_TooltipWindow->SetVisible(false); + } + + BaseTooltip::HideTooltip(); +} + diff --git a/mp/src/vgui2/vgui_controls/TreeView.cpp b/mp/src/vgui2/vgui_controls/TreeView.cpp index 066ebd50..b7ad4d3b 100644 --- a/mp/src/vgui2/vgui_controls/TreeView.cpp +++ b/mp/src/vgui2/vgui_controls/TreeView.cpp @@ -1,2854 +1,2854 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#define PROTECTED_THINGS_DISABLE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tier1/utlstring.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -using namespace vgui; -enum -{ - WINDOW_BORDER_WIDTH=2 // the width of the window's border -}; - -#define TREE_INDENT_AMOUNT 20 - -namespace vgui -{ - -//----------------------------------------------------------------------------- -// Purpose: Displays an editable text field for the text control -//----------------------------------------------------------------------------- -class TreeNodeText : public TextEntry -{ - DECLARE_CLASS_SIMPLE( TreeNodeText, TextEntry ); - -public: - TreeNodeText(Panel *parent, const char *panelName, TreeView *tree) : BaseClass(parent, panelName), m_pTree( tree ) - { - m_bEditingInPlace = false; - m_bLabelEditingAllowed = false; - SetDragEnabled( false ); - SetDropEnabled( false ); - AddActionSignalTarget( this ); - m_bArmForEditing = false; - m_bWaitingForRelease = false; - m_lArmingTime = 0L; - SetAllowKeyBindingChainToParent( true ); - } - - MESSAGE_FUNC( OnTextChanged, "TextChanged" ) - { - GetParent()->InvalidateLayout(); - } - - bool IsKeyRebound( KeyCode code, int modifiers ) - { - // If in editing mode, don't try and chain keypresses - if ( m_bEditingInPlace ) - { - return false; - } - - return BaseClass::IsKeyRebound( code, modifiers ); - } - - virtual void PaintBackground() - { - BaseClass::PaintBackground(); - - if ( !m_bLabelEditingAllowed ) - return; - - if ( !m_bEditingInPlace ) - return; - - int w, h; - GetSize( w, h ); - surface()->DrawSetColor( GetFgColor() ); - surface()->DrawOutlinedRect( 0, 0, w, h ); - } - - virtual void ApplySchemeSettings(IScheme *pScheme) - { - TextEntry::ApplySchemeSettings(pScheme); - SetBorder(NULL); - SetCursor(dc_arrow); - } - - virtual void OnKeyCodeTyped(KeyCode code) - { - if ( m_bEditingInPlace ) - { - if ( code == KEY_ENTER ) - { - FinishEditingInPlace(); - } - else if ( code == KEY_ESCAPE ) - { - FinishEditingInPlace( true ); - } - else - { - BaseClass::OnKeyCodeTyped( code ); - } - return; - } - else if ( code == KEY_ENTER && IsLabelEditingAllowed() ) - { - EnterEditingInPlace(); - } - else - { - // let parent deal with it (don't chain back to TextEntry) - CallParentFunction(new KeyValues("KeyCodeTyped", "code", code)); - } - } - -#define CLICK_TO_EDIT_DELAY_MSEC 500 - - virtual void OnTick() - { - BaseClass::OnTick(); - if ( m_bArmForEditing ) - { - long msecSinceArming = system()->GetTimeMillis() - m_lArmingTime; - - if ( msecSinceArming > CLICK_TO_EDIT_DELAY_MSEC ) - { - m_bArmForEditing = false; - m_bWaitingForRelease = false; - ivgui()->RemoveTickSignal( GetVPanel() ); - EnterEditingInPlace(); - } - } - } - - virtual void OnMouseReleased( MouseCode code ) - { - if ( m_bEditingInPlace ) - { - BaseClass::OnMouseReleased( code ); - return; - } - else - { - if ( m_bWaitingForRelease && !IsBeingDragged() ) - { - m_bArmForEditing = true; - m_bWaitingForRelease = false; - m_lArmingTime = system()->GetTimeMillis(); - ivgui()->AddTickSignal( GetVPanel() ); - } - else - { - m_bWaitingForRelease = false; - } - } - - // let parent deal with it - CallParentFunction(new KeyValues("MouseReleased", "code", code)); - } - - virtual void OnCursorMoved( int x, int y ) - { - // let parent deal with it - CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); - } - - virtual void OnMousePressed(MouseCode code) - { - if ( m_bEditingInPlace ) - { - BaseClass::OnMousePressed( code ); - return; - } - else - { - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - - // make sure there is only one item selected - // before "WaitingForRelease" which leads to label editing. - CUtlVector< int > list; - m_pTree->GetSelectedItems( list ); - bool bIsOnlyOneItemSelected = ( list.Count() == 1 ); - - if ( !shift && - !ctrl && - !m_bArmForEditing && - IsLabelEditingAllowed() && - bIsOnlyOneItemSelected && - IsTextFullySelected() && - !IsBeingDragged() ) - { - m_bWaitingForRelease = true; - } - } - - // let parent deal with it - CallParentFunction(new KeyValues("MousePressed", "code", code)); - } - - void SetLabelEditingAllowed( bool state ) - { - m_bLabelEditingAllowed = state; - } - - bool IsLabelEditingAllowed() - { - return m_bLabelEditingAllowed; - } - - virtual void OnMouseDoublePressed(MouseCode code) - { - // Once we are editing, double pressing shouldn't chain up - if ( m_bEditingInPlace ) - { - BaseClass::OnMouseDoublePressed( code ); - return; - } - - if ( m_bArmForEditing ) - { - m_bArmForEditing = false; - m_bWaitingForRelease = false; - ivgui()->RemoveTickSignal( GetVPanel() ); - } - - CallParentFunction(new KeyValues("MouseDoublePressed", "code", code)); - } - - void EnterEditingInPlace() - { - if ( m_bEditingInPlace ) - return; - - m_bEditingInPlace = true; - char buf[ 1024 ]; - GetText( buf, sizeof( buf ) ); - m_OriginalText = buf; - SetCursor(dc_ibeam); - SetEditable( true ); - SelectNone(); - GotoTextEnd(); - RequestFocus(); - SelectAllText(false); - m_pTree->SetLabelBeingEdited( true ); - } - - void FinishEditingInPlace( bool revert = false ) - { - if ( !m_bEditingInPlace ) - return; - - m_pTree->SetLabelBeingEdited( false ); - SetEditable( false ); - SetCursor(dc_arrow); - m_bEditingInPlace = false; - char buf[ 1024 ]; - GetText( buf, sizeof( buf ) ); - - // Not actually changed... - if ( !Q_strcmp( buf, m_OriginalText.Get() ) ) - return; - - if ( revert ) - { - SetText( m_OriginalText.Get() ); - GetParent()->InvalidateLayout(); - } - else - { - KeyValues *kv = new KeyValues( "LabelChanged", "original", m_OriginalText.Get(), "changed", buf ); - PostActionSignal( kv ); - } - } - - virtual void OnKillFocus() - { - BaseClass::OnKillFocus(); - - FinishEditingInPlace(); - } - - virtual void OnMouseWheeled(int delta) - { - if ( m_bEditingInPlace ) - { - BaseClass::OnMouseWheeled( delta ); - return; - } - - CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); - } - // editable - cursor normal, and ability to edit text - - bool IsBeingEdited() const - { - return m_bEditingInPlace; - } - -private: - - bool m_bEditingInPlace; - CUtlString m_OriginalText; - bool m_bLabelEditingAllowed; - - bool m_bArmForEditing; - bool m_bWaitingForRelease; - long m_lArmingTime; - TreeView *m_pTree; -}; - -//----------------------------------------------------------------------------- -// Purpose: icon for the tree node (folder icon, file icon, etc.) -//----------------------------------------------------------------------------- -class TreeNodeImage : public ImagePanel -{ -public: - TreeNodeImage(Panel *parent, const char *name) : ImagePanel(parent, name) - { - SetBlockDragChaining( true ); - } - - //!! this could possibly be changed to just disallow mouse input on the image panel - virtual void OnMousePressed(MouseCode code) - { - // let parent deal with it - CallParentFunction(new KeyValues("MousePressed", "code", code)); - } - - virtual void OnMouseDoublePressed(MouseCode code) - { - // let parent deal with it - CallParentFunction(new KeyValues("MouseDoublePressed", "code", code)); - } - - virtual void OnMouseWheeled(int delta) - { - // let parent deal with it - CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); - } - - virtual void OnCursorMoved( int x, int y ) - { - // let parent deal with it - CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); - } -}; - -//----------------------------------------------------------------------------- -// Purpose: Scrollable area of the tree control, holds the tree itself only -//----------------------------------------------------------------------------- -class TreeViewSubPanel : public Panel -{ -public: - TreeViewSubPanel(Panel *parent) : Panel(parent) {} - - virtual void ApplySchemeSettings(IScheme *pScheme) - { - Panel::ApplySchemeSettings(pScheme); - - SetBorder(NULL); - } - - virtual void OnMouseWheeled(int delta) - { - // let parent deal with it - CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); - } - virtual void OnMousePressed(MouseCode code) - { - // let parent deal with it - CallParentFunction(new KeyValues("MousePressed", "code", code)); - } - virtual void OnMouseDoublePressed(MouseCode code) - { - // let parent deal with it - CallParentFunction(new KeyValues("MouseDoublePressed", "code", code)); - } - - virtual void OnCursorMoved( int x, int y ) - { - // let parent deal with it - CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); - } -}; - -//----------------------------------------------------------------------------- -// Purpose: A single entry in the tree -//----------------------------------------------------------------------------- -class TreeNode : public Panel -{ - DECLARE_CLASS_SIMPLE( TreeNode, Panel ); - -public: - TreeNode(Panel *parent, TreeView *pTreeView); - ~TreeNode(); - void SetText(const char *pszText); - void SetFont(HFont font); - void SetKeyValues(KeyValues *data); - bool IsSelected(); - // currently unused, could be re-used if necessary -// bool IsInFocus(); - virtual void PaintBackground(); - virtual void PerformLayout(); - TreeNode *GetParentNode(); - int GetChildrenCount(); - void ClearChildren(); - int ComputeInsertionPosition( TreeNode *pChild ); - int FindChild( TreeNode *pChild ); - void AddChild(TreeNode *pChild); - void SetNodeExpanded(bool bExpanded); - bool IsExpanded(); - int CountVisibleNodes(); - void CalculateVisibleMaxWidth(); - void OnChildWidthChange(); - int GetMaxChildrenWidth(); - int GetVisibleMaxWidth(); - int GetDepth(); - bool HasParent(TreeNode *pTreeNode); - bool IsBeingDisplayed(); - virtual void SetVisible(bool state); - virtual void Paint(); - virtual void ApplySchemeSettings(IScheme *pScheme); - virtual void SetBgColor( Color color ); - virtual void SetFgColor( Color color ); - virtual void OnSetFocus(); - void SelectPrevChild(TreeNode *pCurrentChild); - void SelectNextChild(TreeNode *pCurrentChild); - - int GetPrevChildItemIndex( TreeNode *pCurrentChild ); - int GetNextChildItemIndex( TreeNode *pCurrentChild ); - - virtual void ClosePreviousParents( TreeNode *pPreviousParent ); - virtual void StepInto( bool bClosePrevious=true ); - virtual void StepOut( bool bClosePrevious=true ); - virtual void StepOver( bool bClosePrevious=true ); - virtual void OnKeyCodeTyped(KeyCode code); - virtual void OnMouseWheeled(int delta); - virtual void OnMousePressed( MouseCode code); - virtual void OnMouseReleased( MouseCode code); - virtual void OnCursorMoved( int x, int y ); - virtual bool IsDragEnabled() const; - void PositionAndSetVisibleNodes(int &nStart, int &nCount, int x, int &y); - - // counts items above this item including itself - int CountVisibleIndex(); - - virtual void OnCreateDragData( KeyValues *msg ); - // For handling multiple selections... - virtual void OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles ); - virtual void OnMouseDoublePressed( MouseCode code ); - TreeNode *FindItemUnderMouse( int &nStart, int& nCount, int x, int &y, int mx, int my ); - MESSAGE_FUNC_PARAMS( OnLabelChanged, "LabelChanged", data ); - void EditLabel(); - void SetLabelEditingAllowed( bool state ); - bool IsLabelEditingAllowed() const; - - virtual bool IsDroppable( CUtlVector< KeyValues * >& msglist ); - virtual void OnPanelDropped( CUtlVector< KeyValues * >& msglist ); - virtual HCursor GetDropCursor( CUtlVector< KeyValues * >& msglist ); - virtual bool GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ); - - void FindNodesInRange( CUtlVector< TreeNode * >& list, int startIndex, int endIndex ); - - void RemoveChildren(); - - void SetSelectionTextColor( const Color& clr ); - void SetSelectionBgColor( const Color& clr ); - void SetSelectionUnfocusedBgColor( const Color& clr ); -public: - int m_ItemIndex; - int m_ParentIndex; - KeyValues *m_pData; - CUtlVector m_Children; - bool m_bExpand; - -private: - - void FindNodesInRange_R( CUtlVector< TreeNode * >& list, bool& finished, bool& foundStart, int startIndex, int endIndex ); - - int m_iNodeWidth; - int m_iMaxVisibleWidth; - - TreeNodeText *m_pText; - TextImage *m_pExpandImage; - TreeNodeImage *m_pImagePanel; - - bool m_bExpandableWithoutChildren; - - TreeView *m_pTreeView; - int m_nClickedItem; - bool m_bClickedSelected; -}; - - -TreeNode::TreeNode(Panel *parent, TreeView *pTreeView) : - BaseClass(parent, "TreeNode" ), - m_nClickedItem( 0 ), - m_bClickedSelected( false ) -{ - m_pData = NULL; - m_pTreeView = pTreeView; - m_ItemIndex = -1; - m_iNodeWidth = 0; - m_iMaxVisibleWidth = 0; - - m_pExpandImage = new TextImage("+"); - m_pExpandImage->SetPos(3, 1); - - m_pImagePanel = new TreeNodeImage(this, "TreeImage"); - m_pImagePanel->SetPos(TREE_INDENT_AMOUNT, 3); - - m_pText = new TreeNodeText(this, "TreeNodeText",pTreeView); - m_pText->SetMultiline(false); - m_pText->SetEditable(false); - m_pText->SetPos(TREE_INDENT_AMOUNT*2, 0); - m_pText->AddActionSignalTarget( this ); - - m_bExpand = false; - m_bExpandableWithoutChildren = false; -} - -TreeNode::~TreeNode() -{ - delete m_pExpandImage; - if ( m_pData ) - { - m_pData->deleteThis(); - } -} - -void TreeNode::SetText(const char *pszText) -{ - m_pText->SetText(pszText); - InvalidateLayout(); -} - -void TreeNode::SetLabelEditingAllowed( bool state ) -{ - Assert( m_pTreeView->IsLabelEditingAllowed() ); - m_pText->SetLabelEditingAllowed( state ); -} - -bool TreeNode::IsLabelEditingAllowed() const -{ - return m_pText->IsLabelEditingAllowed(); -} - -bool TreeNode::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ) -{ - return m_pTreeView->GetItemDropContextMenu( m_ItemIndex, menu, msglist ); -} - -bool TreeNode::IsDroppable( CUtlVector< KeyValues * >& msglist ) -{ - return m_pTreeView->IsItemDroppable( m_ItemIndex, msglist ); -} - -void TreeNode::OnPanelDropped( CUtlVector< KeyValues * >& msglist ) -{ - m_pTreeView->OnItemDropped( m_ItemIndex, msglist ); -} - -HCursor TreeNode::GetDropCursor( CUtlVector< KeyValues * >& msglist ) -{ - return m_pTreeView->GetItemDropCursor( m_ItemIndex, msglist ); -} - - -void TreeNode::OnCreateDragData( KeyValues *msg ) -{ - // make sure the dragged item appears selected, - // on the off chance it appears deselected by a cntl mousedown - m_pTreeView->AddSelectedItem( m_ItemIndex, false ); - - m_pTreeView->GenerateDragDataForItem( m_ItemIndex, msg ); -} - -// For handling multiple selections... -void TreeNode::OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles ) -{ - CUtlVector< int > list; - m_pTreeView->GetSelectedItems( list ); - int c = list.Count(); - // walk this in reverse order so that panels are in order of selection - // even though GetSelectedItems returns items in reverse selection order - for ( int i = c - 1; i >= 0; --i ) - { - int itemIndex = list[ i ]; - // Skip self - if ( itemIndex == m_ItemIndex ) - continue; - - dragabbles.AddToTail( ( Panel * )m_pTreeView->GetItem( itemIndex ) ); - } -} - -void TreeNode::OnLabelChanged( KeyValues *data ) -{ - char const *oldString = data->GetString( "original" ); - char const *newString = data->GetString( "changed" ); - if ( m_pTreeView->IsLabelEditingAllowed() ) - { - m_pTreeView->OnLabelChanged( m_ItemIndex, oldString, newString ); - } -} - -void TreeNode::EditLabel() -{ - if ( m_pText->IsLabelEditingAllowed() && - !m_pText->IsBeingEdited() ) - { - m_pText->EnterEditingInPlace(); - } -} - -void TreeNode::SetFont(HFont font) -{ - Assert( font ); - if ( !font ) - return; - - m_pText->SetFont(font); - m_pExpandImage->SetFont(font); - InvalidateLayout(); - int i; - for (i=0;iSetFont(font); - } -} - -void TreeNode::SetKeyValues(KeyValues *data) -{ - if ( m_pData != data ) - { - if (m_pData) - { - m_pData->deleteThis(); - } - - m_pData = data->MakeCopy(); - } - - // set text - m_pText->SetText(data->GetString("Text", "")); - m_bExpandableWithoutChildren = data->GetInt("Expand"); - InvalidateLayout(); -} - -bool TreeNode::IsSelected() -{ - return m_pTreeView->IsItemSelected( m_ItemIndex ); -} - -void TreeNode::PaintBackground() -{ - if ( !m_pText->IsBeingEdited() ) - { - // setup panel drawing - if ( IsSelected() ) - { - m_pText->SelectAllText(false); - } - else - { - m_pText->SelectNoText(); - } - } - - BaseClass::PaintBackground(); -} - - -// currently unused, could be re-used if necessary -/* -bool TreeNode::IsInFocus() -{ - // check if our parent or one of it's children has focus - VPANEL focus = input()->GetFocus(); - return (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))); -} -*/ - -void TreeNode::PerformLayout() -{ - BaseClass::PerformLayout(); - - int width = 0; - if (m_pData->GetInt("SelectedImage", 0) == 0 && - m_pData->GetInt("Image", 0) == 0) - { - width = TREE_INDENT_AMOUNT; - } - else - { - width = TREE_INDENT_AMOUNT * 2; - } - - m_pText->SetPos(width, 0); - - int contentWide, contentTall; - m_pText->SetToFullWidth(); - m_pText->GetSize(contentWide, contentTall); - contentWide += 10; - m_pText->SetSize( contentWide, m_pTreeView->GetRowHeight() ); - width += contentWide; - SetSize(width, m_pTreeView->GetRowHeight()); - - m_iNodeWidth = width; - CalculateVisibleMaxWidth(); -} - -TreeNode *TreeNode::GetParentNode() -{ - if (m_pTreeView->m_NodeList.IsValidIndex(m_ParentIndex)) - { - return m_pTreeView->m_NodeList[m_ParentIndex]; - } - return NULL; -} - -int TreeNode::GetChildrenCount() -{ - return m_Children.Count(); -} - -int TreeNode::ComputeInsertionPosition( TreeNode *pChild ) -{ - if ( !m_pTreeView->m_pSortFunc ) - { - return GetChildrenCount() - 1; - } - - int start = 0, end = GetChildrenCount() - 1; - while (start <= end) - { - int mid = (start + end) >> 1; - if ( m_pTreeView->m_pSortFunc( m_Children[mid]->m_pData, pChild->m_pData ) ) - { - start = mid + 1; - } - else if ( m_pTreeView->m_pSortFunc( pChild->m_pData, m_Children[mid]->m_pData ) ) - { - end = mid - 1; - } - else - { - return mid; - } - } - return end; -} - -int TreeNode::FindChild( TreeNode *pChild ) -{ - if ( !m_pTreeView->m_pSortFunc ) - { - AssertMsg( 0, "This code has never been tested. Is it correct?" ); - for ( int i = 0; i < GetChildrenCount(); ++i ) - { - if ( m_Children[i] == pChild ) - return i; - } - return -1; - } - - // Find the first entry <= to the child - int start = 0, end = GetChildrenCount() - 1; - while (start <= end) - { - int mid = (start + end) >> 1; - - if ( m_Children[mid] == pChild ) - return mid; - - if ( m_pTreeView->m_pSortFunc( m_Children[mid]->m_pData, pChild->m_pData ) ) - { - start = mid + 1; - } - else - { - end = mid - 1; - } - } - - int nMax = GetChildrenCount(); - while( end < nMax ) - { - // Stop when we reach a child that has a different value - if ( m_pTreeView->m_pSortFunc( pChild->m_pData, m_Children[end]->m_pData ) ) - return -1; - - if ( m_Children[end] == pChild ) - return end; - - ++end; - } - - return -1; -} - -void TreeNode::AddChild(TreeNode *pChild) -{ - int i = ComputeInsertionPosition( pChild ); - m_Children.InsertAfter( i, pChild ); -} - -void TreeNode::SetNodeExpanded(bool bExpanded) -{ - m_bExpand = bExpanded; - - if (m_bExpand) - { - // see if we have any child nodes - if (GetChildrenCount() < 1) - { - // we need to get our children from the control - m_pTreeView->GenerateChildrenOfNode(m_ItemIndex); - - // if we still don't have any children, then hide the expand button - if (GetChildrenCount() < 1) - { - m_bExpand = false; - m_bExpandableWithoutChildren = false; - m_pTreeView->InvalidateLayout(); - return; - } - } - - m_pExpandImage->SetText("-"); - } - else - { - m_pExpandImage->SetText("+"); - - if ( m_bExpandableWithoutChildren && GetChildrenCount() > 0 ) - { - m_pTreeView->RemoveChildrenOfNode( m_ItemIndex ); - } - - // check if we've closed down on one of our children, if so, we get the focus - int selectedItem = m_pTreeView->GetFirstSelectedItem(); - if (selectedItem != -1 && m_pTreeView->m_NodeList[selectedItem]->HasParent(this)) - { - m_pTreeView->AddSelectedItem( m_ItemIndex, true ); - } - } - CalculateVisibleMaxWidth(); - m_pTreeView->InvalidateLayout(); -} - -bool TreeNode::IsExpanded() -{ - return m_bExpand; -} - -int TreeNode::CountVisibleNodes() -{ - int count = 1; // count myself - if (m_bExpand) - { - int i; - for (i=0;iCountVisibleNodes(); - } - } - return count; -} - -void TreeNode::CalculateVisibleMaxWidth() -{ - int width; - if (m_bExpand) - { - int childMaxWidth = GetMaxChildrenWidth(); - childMaxWidth += TREE_INDENT_AMOUNT; - - width = max(childMaxWidth, m_iNodeWidth); - } - else - { - width = m_iNodeWidth; - } - if (width != m_iMaxVisibleWidth) - { - m_iMaxVisibleWidth = width; - if (GetParentNode()) - { - GetParentNode()->OnChildWidthChange(); - } - else - { - m_pTreeView->InvalidateLayout(); - } - } -} - -void TreeNode::OnChildWidthChange() -{ - CalculateVisibleMaxWidth(); -} - -int TreeNode::GetMaxChildrenWidth() -{ - int maxWidth = 0; - int i; - for (i=0;iGetVisibleMaxWidth(); - if (childWidth > maxWidth) - { - maxWidth = childWidth; - } - } - return maxWidth; -} - -int TreeNode::GetVisibleMaxWidth() -{ - return m_iMaxVisibleWidth; -} - -int TreeNode::GetDepth() -{ - int depth = 0; - TreeNode *pParent = GetParentNode(); - while (pParent) - { - depth++; - pParent = pParent->GetParentNode(); - } - return depth; -} - -bool TreeNode::HasParent(TreeNode *pTreeNode) -{ - TreeNode *pParent = GetParentNode(); - while (pParent) - { - if (pParent == pTreeNode) - return true; - pParent = pParent->GetParentNode(); - } - return false; -} - -bool TreeNode::IsBeingDisplayed() -{ - TreeNode *pParent = GetParentNode(); - while (pParent) - { - // our parents aren't showing us - if (!pParent->m_bExpand) - return false; - - pParent = pParent->GetParentNode(); - } - return true; -} - -void TreeNode::SetVisible(bool state) -{ - BaseClass::SetVisible(state); - - bool bChildrenVisible = state && m_bExpand; - int i; - for (i=0;iSetVisible(bChildrenVisible); - } -} - -void TreeNode::Paint() -{ - if (GetChildrenCount() > 0 || m_bExpandableWithoutChildren) - { - m_pExpandImage->Paint(); - } - - // set image - int imageIndex = 0; - if (IsSelected()) - { - imageIndex = m_pData->GetInt("SelectedImage", 0); - } - else - { - imageIndex = m_pData->GetInt("Image", 0); - } - - if (imageIndex) - { - IImage *pImage = m_pTreeView->GetImage(imageIndex); - if (pImage) - { - m_pImagePanel->SetImage(pImage); - } - m_pImagePanel->Paint(); - } - - m_pText->Paint(); -} - -void TreeNode::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetBorder( NULL ); - SetFgColor( m_pTreeView->GetFgColor() ); - SetBgColor( m_pTreeView->GetBgColor() ); - SetFont( m_pTreeView->GetFont() ); -} - -void TreeNode::SetSelectionTextColor( const Color& clr ) -{ - if ( m_pText ) - { - m_pText->SetSelectionTextColor( clr ); - } -} - -void TreeNode::SetSelectionBgColor( const Color& clr ) -{ - if ( m_pText ) - { - m_pText->SetSelectionBgColor( clr ); - } -} - -void TreeNode::SetSelectionUnfocusedBgColor( const Color& clr ) -{ - if ( m_pText ) - { - m_pText->SetSelectionUnfocusedBgColor( clr ); - } -} - -void TreeNode::SetBgColor( Color color ) -{ - BaseClass::SetBgColor( color ); - if ( m_pText ) - { - m_pText->SetBgColor( color ); - } - -} - -void TreeNode::SetFgColor( Color color ) -{ - BaseClass::SetFgColor( color ); - if ( m_pText ) - { - m_pText->SetFgColor( color ); - } -} - -void TreeNode::OnSetFocus() -{ - m_pText->RequestFocus(); -} - -int TreeNode::GetPrevChildItemIndex( TreeNode *pCurrentChild ) -{ - int i; - for (i=0;im_ItemIndex; - } - } - return -1; -} - -int TreeNode::GetNextChildItemIndex( TreeNode *pCurrentChild ) -{ - int i; - for (i=0;i= GetChildrenCount() - 1 ) - return -1; - - TreeNode *pChild = m_Children[i+1]; - return pChild->m_ItemIndex; - } - } - return -1; -} - -void TreeNode::SelectPrevChild(TreeNode *pCurrentChild) -{ - int i; - for (i=0;iAddSelectedItem( m_ItemIndex, true ); - } - else - { - // see if we need to find a grandchild of the previous sibling - TreeNode *pChild = m_Children[i-1]; - - // if this child is expanded with children, then we have to find the last child - while (pChild->m_bExpand && pChild->GetChildrenCount()>0) - { - // find the last child - pChild = pChild->m_Children[pChild->GetChildrenCount()-1]; - } - m_pTreeView->AddSelectedItem( pChild->m_ItemIndex, true ); - } -} - -void TreeNode::SelectNextChild(TreeNode *pCurrentChild) -{ - int i; - for (i=0;iSelectNextChild(this); - } - } - else - { - m_pTreeView->AddSelectedItem( m_Children[i+1]->m_ItemIndex, true ); - } -} - -void TreeNode::ClosePreviousParents( TreeNode *pPreviousParent ) -{ - // close up all the open nodes we've just stepped out of. - CUtlVector< int > selected; - m_pTreeView->GetSelectedItems( selected ); - if ( selected.Count() == 0 ) - { - Assert( 0 ); - return; - } - - // Most recently clicked item - TreeNode *selectedItem = m_pTreeView->GetItem( selected[ 0 ] ); - TreeNode *pNewParent = selectedItem->GetParentNode(); - if ( pPreviousParent && pNewParent ) - { - while ( pPreviousParent->m_ItemIndex > pNewParent->m_ItemIndex ) - { - pPreviousParent->SetNodeExpanded(false); - pPreviousParent = pPreviousParent->GetParentNode(); - } - } -} - -void TreeNode::StepInto( bool bClosePrevious ) -{ - if ( !m_bExpand ) - { - SetNodeExpanded(true); - } - - if ( ( GetChildrenCount() > 0 ) && m_bExpand ) - { - m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true ); - } - else if ( GetParentNode() ) - { - TreeNode *pParent = GetParentNode(); - pParent->SelectNextChild(this); - - if ( bClosePrevious ) - { - ClosePreviousParents( pParent ); - } - } -} - -void TreeNode::StepOut( bool bClosePrevious ) -{ - TreeNode *pParent = GetParentNode(); - if ( pParent ) - { - m_pTreeView->AddSelectedItem( pParent->m_ItemIndex, true ); - if ( pParent->GetParentNode() ) - { - pParent->GetParentNode()->SelectNextChild(pParent); - } - if ( bClosePrevious ) - { - ClosePreviousParents( pParent ); - } - else - { - pParent->SetNodeExpanded(true); - } - } -} - -void TreeNode::StepOver( bool bClosePrevious ) -{ - TreeNode *pParent = GetParentNode(); - if ( pParent ) - { - GetParentNode()->SelectNextChild(this); - if ( bClosePrevious ) - { - ClosePreviousParents( pParent ); - } - } -} - -void TreeNode::OnKeyCodeTyped(KeyCode code) -{ - switch (code) - { - case KEY_LEFT: - { - if (m_bExpand && GetChildrenCount() > 0) - { - SetNodeExpanded(false); - } - else - { - if (GetParentNode()) - { - m_pTreeView->AddSelectedItem( GetParentNode()->m_ItemIndex, true ); - } - } - break; - } - case KEY_RIGHT: - { - if (!m_bExpand) - { - SetNodeExpanded(true); - } - else if (GetChildrenCount() > 0) - { - m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true ); - } - break; - } - case KEY_UP: - { - if (GetParentNode()) - { - GetParentNode()->SelectPrevChild(this); - } - break; - } - case KEY_DOWN: - { - if (GetChildrenCount() > 0 && m_bExpand) - { - m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true ); - } - else if (GetParentNode()) - { - GetParentNode()->SelectNextChild(this); - } - break; - } - case KEY_SPACE: - { - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - if ( shift ) - { - StepOut( !ctrl ); - } - else if ( alt ) - { - StepOver( !ctrl ); - } - else - { - StepInto( !ctrl ); - } - break; - } - case KEY_I: - { - StepInto(); - break; - } - case KEY_U: - { - StepOut(); - break; - } - case KEY_O: - { - StepOver(); - break; - } - case KEY_ESCAPE: - { - if ( m_pTreeView->GetSelectedItemCount() > 0 ) - { - m_pTreeView->ClearSelection(); - } - else - { - BaseClass::OnKeyCodeTyped(code); - } - } - break; - case KEY_A: - { - bool ctrldown = input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL ); - if ( ctrldown ) - { - m_pTreeView->SelectAll(); - } - else - { - BaseClass::OnKeyCodeTyped(code); - } - } - break; - default: - BaseClass::OnKeyCodeTyped(code); - return; - } -} - -void TreeNode::OnMouseWheeled(int delta) -{ - CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); -} - -void TreeNode::OnMouseDoublePressed( MouseCode code ) -{ - int x, y; - input()->GetCursorPos(x, y); - - if (code == MOUSE_LEFT) - { - ScreenToLocal(x, y); - if (x > TREE_INDENT_AMOUNT) - { - SetNodeExpanded(!m_bExpand); - } - } -} - -bool TreeNode::IsDragEnabled() const -{ - int x, y; - input()->GetCursorPos(x, y); - ((TreeNode *)this)->ScreenToLocal(x, y); - if ( x < TREE_INDENT_AMOUNT ) - return false; - - return BaseClass::IsDragEnabled(); -} - -void TreeNode::OnMouseReleased(MouseCode code) -{ - BaseClass::OnMouseReleased( code ); - - if ( input()->GetMouseCapture() == GetVPanel() ) - { - input()->SetMouseCapture( NULL ); - return; - } - int x, y; - input()->GetCursorPos(x, y); - ScreenToLocal(x, y); - - if ( x < TREE_INDENT_AMOUNT ) - return; - - bool ctrldown = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool shiftdown = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - - if ( !ctrldown && !shiftdown && ( code == MOUSE_LEFT ) ) - { - m_pTreeView->AddSelectedItem( m_ItemIndex, true ); - } -} - -void TreeNode::OnCursorMoved( int x, int y ) -{ - if ( input()->GetMouseCapture() != GetVPanel() ) - return; - - LocalToScreen( x, y ); - m_pTreeView->ScreenToLocal( x, y ); - int newItem = m_pTreeView->FindItemUnderMouse( x, y ); - if ( newItem == -1 ) - { - // Fixme: Figure out best item - return; - } - - int startItem = m_nClickedItem; - int endItem = newItem; - if ( startItem > endItem ) - { - int temp = startItem; - startItem = endItem; - endItem = temp; - } - - CUtlVector< TreeNode * > list; - m_pTreeView->m_pRootNode->FindNodesInRange( list, startItem, endItem ); - - int c = list.Count(); - for ( int i = 0; i < c; ++i ) - { - TreeNode *item = list[ i ]; - if ( m_bClickedSelected ) - { - m_pTreeView->AddSelectedItem( item->m_ItemIndex, false ); - } - else - { - m_pTreeView->RemoveSelectedItem( item->m_ItemIndex ); - } - } -} - -void TreeNode::OnMousePressed( MouseCode code) -{ - BaseClass::OnMousePressed( code ); - - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - int x, y; - input()->GetCursorPos(x, y); - - bool bExpandTree = m_pTreeView->m_bLeftClickExpandsTree; - - if ( code == MOUSE_LEFT ) - { - ScreenToLocal(x, y); - if ( x < TREE_INDENT_AMOUNT ) - { - if ( bExpandTree ) - { - SetNodeExpanded(!m_bExpand); - } - // m_pTreeView->SetSelectedItem(m_ItemIndex); // explorer doesn't actually select item when it expands an item - // purposely commented out in case we want to change the behavior - } - else - { - m_nClickedItem = m_ItemIndex; - if ( m_pTreeView->IsMultipleItemDragEnabled() ) - { - input()->SetMouseCapture( GetVPanel() ); - } - - if ( shift ) - { - m_pTreeView->RangeSelectItems( m_ItemIndex ); - } - else - { - if ( !IsSelected() || ctrl ) - { - if ( IsSelected() && ctrl ) - { - m_pTreeView->RemoveSelectedItem( m_ItemIndex ); - } - else - { - m_pTreeView->AddSelectedItem( m_ItemIndex, !ctrl ); - } - } - else if ( IsSelected() && m_pTreeView->IsMultipleItemDragEnabled() ) - { - m_pTreeView->AddSelectedItem( m_ItemIndex, !shift ); - } - } - - m_bClickedSelected = m_pTreeView->IsItemSelected( m_ItemIndex ); - } - } - else if (code == MOUSE_RIGHT) - { - // context menu selection - // If the item was selected, leave selected items alone, otherwise make it the only selected item - if ( !m_pTreeView->IsItemSelected( m_ItemIndex ) ) - { - m_pTreeView->AddSelectedItem( m_ItemIndex, true ); - } - - // ask parent to context menu - m_pTreeView->GenerateContextMenu(m_ItemIndex, x, y); - } -} - -void TreeNode::RemoveChildren() -{ - int c = m_Children.Count(); - for ( int i = c - 1 ; i >= 0 ; --i ) - { - m_pTreeView->RemoveItem( m_Children[ i ]->m_ItemIndex, false, true ); - } - m_Children.RemoveAll(); -} - -void TreeNode::FindNodesInRange( CUtlVector< TreeNode * >& list, int startIndex, int endIndex ) -{ - list.RemoveAll(); - bool finished = false; - bool foundstart = false; - FindNodesInRange_R( list, finished, foundstart, startIndex, endIndex ); -} - -void TreeNode::FindNodesInRange_R( CUtlVector< TreeNode * >& list, bool& finished, bool& foundStart, int startIndex, int endIndex ) -{ - if ( finished ) - return; - if ( foundStart == true ) - { - list.AddToTail( this ); - - if ( m_ItemIndex == startIndex || m_ItemIndex == endIndex ) - { - finished = true; - return; - } - } - else if ( m_ItemIndex == startIndex || m_ItemIndex == endIndex ) - { - foundStart = true; - list.AddToTail( this ); - if ( startIndex == endIndex ) - { - finished = true; - return; - } - } - - if ( !m_bExpand ) - return; - - - int i; - int c = GetChildrenCount(); - for (i=0;iFindNodesInRange_R( list, finished, foundStart, startIndex, endIndex ); - } -} - -void TreeNode::PositionAndSetVisibleNodes(int &nStart, int &nCount, int x, int &y) -{ - // position ourselves - if (nStart == 0) - { - BaseClass::SetVisible(true); - SetPos(x, y); - y += m_pTreeView->GetRowHeight(); // m_nRowHeight - nCount--; - } - else // still looking for first element - { - nStart--; - BaseClass::SetVisible(false); - } - - x += TREE_INDENT_AMOUNT; - int i; - for (i=0;i 0 && m_bExpand) - { - m_Children[i]->PositionAndSetVisibleNodes(nStart, nCount, x, y); - } - else - { - m_Children[i]->SetVisible(false); // this will make all grand children hidden as well - } - } -} - -TreeNode *TreeNode::FindItemUnderMouse( int &nStart, int& nCount, int x, int &y, int mx, int my ) -{ - // position ourselves - if (nStart == 0) - { - int posx, posy; - GetPos(posx, posy); - if ( my >= posy && my < posy + m_pTreeView->GetRowHeight() ) - { - return this; - } - y += m_pTreeView->GetRowHeight(); - nCount--; - } - else // still looking for first element - { - nStart--; - } - - x += TREE_INDENT_AMOUNT; - int i; - for (i=0;i 0 && m_bExpand) - { - TreeNode *child = m_Children[i]->FindItemUnderMouse(nStart, nCount, x, y, mx, my); - if ( child != NULL ) - { - return child; - } - } - } - - return NULL; -} - -// counts items above this item including itself -int TreeNode::CountVisibleIndex() -{ - int nCount = 1; // myself - if (GetParentNode()) - { - int i; - for (i=0;iGetChildrenCount();i++) - { - if (GetParentNode()->m_Children[i] == this) - break; - - nCount += GetParentNode()->m_Children[i]->CountVisibleNodes(); - } - return nCount + GetParentNode()->CountVisibleIndex(); - } - else - return nCount; -} - - -}; // namespace vgui - -DECLARE_BUILD_FACTORY( TreeView ); - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -TreeView::TreeView(Panel *parent, const char *panelName) : Panel(parent, panelName) -{ - m_bScrollbarExternal[ 0 ] = m_bScrollbarExternal[ 1 ] = false; - m_nRowHeight = 20; - m_pRootNode = NULL; - m_pImageList = NULL; - m_pSortFunc = NULL; - m_Font = 0; - - m_pSubPanel = new TreeViewSubPanel(this); - m_pSubPanel->SetVisible(true); - m_pSubPanel->SetPos(0,0); - - m_pHorzScrollBar = new ScrollBar(this, "HorizScrollBar", false); - m_pHorzScrollBar->AddActionSignalTarget(this); - m_pHorzScrollBar->SetVisible(false); - - m_pVertScrollBar = new ScrollBar(this, "VertScrollBar", true); - m_pVertScrollBar->SetVisible(false); - m_pVertScrollBar->AddActionSignalTarget(this); - - m_bAllowLabelEditing = false; - m_bDragEnabledItems = false; - m_bDeleteImageListWhenDone = false; - m_bLabelBeingEdited = false; - m_bMultipleItemDragging = false; - m_bLeftClickExpandsTree = true; - m_bAllowMultipleSelections = false; - m_nMostRecentlySelectedItem = -1; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -TreeView::~TreeView() -{ - CleanUpImageList(); -} - - -//----------------------------------------------------------------------------- -// Clean up the image list -//----------------------------------------------------------------------------- -void TreeView::CleanUpImageList( ) -{ - if ( m_pImageList ) - { - if ( m_bDeleteImageListWhenDone ) - { - delete m_pImageList; - } - m_pImageList = NULL; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::SetSortFunc(TreeViewSortFunc_t pSortFunc) -{ - m_pSortFunc = pSortFunc; -} - -HFont TreeView::GetFont() -{ - return m_Font; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::SetFont(HFont font) -{ - Assert( font ); - if ( !font ) - return; - - m_Font = font; - m_nRowHeight = surface()->GetFontTall(font) + 2; - - if (m_pRootNode) - { - m_pRootNode->SetFont(font); - } - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int TreeView::GetRowHeight() -{ - return m_nRowHeight; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int TreeView::GetVisibleMaxWidth() -{ - if (m_pRootNode) - { - return m_pRootNode->GetVisibleMaxWidth(); - } - else - { - return 0; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int TreeView::AddItem(KeyValues *data, int parentItemIndex) -{ - Assert(parentItemIndex == -1 || m_NodeList.IsValidIndex(parentItemIndex)); - - TreeNode *pTreeNode = new TreeNode(m_pSubPanel, this); - pTreeNode->SetDragEnabled( m_bDragEnabledItems ); - pTreeNode->m_ItemIndex = m_NodeList.AddToTail(pTreeNode); - pTreeNode->SetKeyValues(data); - - if ( m_Font != 0 ) - { - pTreeNode->SetFont( m_Font ); - } - pTreeNode->SetBgColor( GetBgColor() ); - - if ( data->GetInt( "droppable", 0 ) != 0 ) - { - float flContextDelay = data->GetFloat( "drophoverdelay" ); - if ( flContextDelay ) - { - pTreeNode->SetDropEnabled( true, flContextDelay ); - } - else - { - pTreeNode->SetDropEnabled( true ); - } - } - - // there can be only one root - if (parentItemIndex == -1) - { - Assert(m_pRootNode == NULL); - m_pRootNode = pTreeNode; - pTreeNode->m_ParentIndex = -1; - } - else - { - pTreeNode->m_ParentIndex = parentItemIndex; - - // add to parent list - pTreeNode->GetParentNode()->AddChild(pTreeNode); - } - - SETUP_PANEL( pTreeNode ); - - return pTreeNode->m_ItemIndex; -} - - -int TreeView::GetRootItemIndex() -{ - if ( m_pRootNode ) - return m_pRootNode->m_ItemIndex; - else - return -1; -} - - -int TreeView::GetNumChildren( int itemIndex ) -{ - if ( itemIndex == -1 ) - return 0; - - return m_NodeList[itemIndex]->m_Children.Count(); -} - - -int TreeView::GetChild( int iParentItemIndex, int iChild ) -{ - return m_NodeList[iParentItemIndex]->m_Children[iChild]->m_ItemIndex; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : itemIndex - -// Output : TreeNode -//----------------------------------------------------------------------------- -TreeNode *TreeView::GetItem( int itemIndex ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - { - Assert( 0 ); - return NULL; - } - - return m_NodeList[ itemIndex ]; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int TreeView::GetItemCount(void) -{ - return m_NodeList.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -KeyValues* TreeView::GetItemData(int itemIndex) -{ - if (!m_NodeList.IsValidIndex(itemIndex)) - return NULL; - else - return m_NodeList[itemIndex]->m_pData; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::RemoveItem(int itemIndex, bool bPromoteChildren, bool bFullDelete ) -{ - // HACK: there's a bug with RemoveItem where panels are lingering. This gets around it temporarily. - - // FIXME: Negative item indices is a bogus interface method! - // because what if you want to recursively remove everything under node 0? - // Use the bFullDelete parameter instead. - if ( itemIndex < 0 ) - { - itemIndex = -itemIndex; - bFullDelete = true; - } - - if (!m_NodeList.IsValidIndex(itemIndex)) - return; - - TreeNode *pNode = m_NodeList[itemIndex]; - TreeNode *pParent = pNode->GetParentNode(); - - // are we promoting the children - if (bPromoteChildren && pParent) - { - int i; - for (i=0;iGetChildrenCount();i++) - { - TreeNode *pChild = pNode->m_Children[i]; - pChild->m_ParentIndex = pParent->m_ItemIndex; - } - } - else - { - // delete our children - if ( bFullDelete ) - { - while ( pNode->GetChildrenCount() ) - RemoveItem( -pNode->m_Children[0]->m_ItemIndex, false ); - } - else - { - int i; - for (i=0;iGetChildrenCount();i++) - { - TreeNode *pDeleteChild = pNode->m_Children[i]; - RemoveItem(pDeleteChild->m_ItemIndex, false); - } - } - } - - // remove from our parent's children list - if (pParent) - { - pParent->m_Children.FindAndRemove(pNode); - } - - // finally get rid of ourselves from the main list - m_NodeList.Remove(itemIndex); - - if ( bFullDelete ) - delete pNode; - else - pNode->MarkForDeletion(); - - // Make sure we don't leave ourselves with an invalid selected item. - m_SelectedItems.FindAndRemove( pNode ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::RemoveAll() -{ - int i; - for (i=0;iMarkForDeletion(); - } - m_NodeList.RemoveAll(); - m_pRootNode = NULL; - ClearSelection(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool TreeView::ModifyItem(int itemIndex, KeyValues *data) -{ - if (!m_NodeList.IsValidIndex(itemIndex)) - return false; - - TreeNode *pNode = m_NodeList[itemIndex]; - TreeNode *pParent = pNode->GetParentNode(); - bool bReSort = ( m_pSortFunc && pParent ); - int nChildIndex = -1; - if ( bReSort ) - { - nChildIndex = pParent->FindChild( pNode ); - } - - pNode->SetKeyValues(data); - - // Changing the data can cause it to re-sort - if ( bReSort ) - { - int nChildren = pParent->GetChildrenCount(); - bool bLeftBad = (nChildIndex > 0) && m_pSortFunc( pNode->m_pData, pParent->m_Children[nChildIndex-1]->m_pData ); - bool bRightBad = (nChildIndex < nChildren - 1) && m_pSortFunc( pParent->m_Children[nChildIndex+1]->m_pData, pNode->m_pData ); - if ( bLeftBad || bRightBad ) - { - pParent->m_Children.Remove( nChildIndex ); - pParent->AddChild( pNode ); - } - } - - InvalidateLayout(); - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: set the selection colors of an element in the tree view -//----------------------------------------------------------------------------- - -void TreeView::SetItemSelectionTextColor( int itemIndex, const Color& clr ) -{ - Assert( m_NodeList.IsValidIndex(itemIndex) ); - if ( !m_NodeList.IsValidIndex(itemIndex) ) - return; - - TreeNode *pNode = m_NodeList[itemIndex]; - pNode->SetSelectionTextColor( clr ); -} - -void TreeView::SetItemSelectionBgColor( int itemIndex, const Color& clr ) -{ - Assert( m_NodeList.IsValidIndex(itemIndex) ); - if ( !m_NodeList.IsValidIndex(itemIndex) ) - return; - - TreeNode *pNode = m_NodeList[itemIndex]; - pNode->SetSelectionBgColor( clr ); -} - -void TreeView::SetItemSelectionUnfocusedBgColor( int itemIndex, const Color& clr ) -{ - Assert( m_NodeList.IsValidIndex(itemIndex) ); - if ( !m_NodeList.IsValidIndex(itemIndex) ) - return; - - TreeNode *pNode = m_NodeList[itemIndex]; - pNode->SetSelectionUnfocusedBgColor( clr ); -} - -//----------------------------------------------------------------------------- -// Purpose: set the fg color of an element in the tree view -//----------------------------------------------------------------------------- -void TreeView::SetItemFgColor(int itemIndex, const Color& color) -{ - Assert( m_NodeList.IsValidIndex(itemIndex) ); - if ( !m_NodeList.IsValidIndex(itemIndex) ) - return; - - TreeNode *pNode = m_NodeList[itemIndex]; - pNode->SetFgColor( color ); -} - -//----------------------------------------------------------------------------- -// Purpose: set the bg color of an element in the tree view -//----------------------------------------------------------------------------- -void TreeView::SetItemBgColor(int itemIndex, const Color& color) -{ - Assert( m_NodeList.IsValidIndex(itemIndex) ); - if ( !m_NodeList.IsValidIndex(itemIndex) ) - return; - - TreeNode *pNode = m_NodeList[itemIndex]; - pNode->SetBgColor( color ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int TreeView::GetItemParent(int itemIndex) -{ - return m_NodeList[itemIndex]->m_ParentIndex; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::SetImageList(ImageList *imageList, bool deleteImageListWhenDone) -{ - CleanUpImageList(); - m_pImageList = imageList; - m_bDeleteImageListWhenDone = deleteImageListWhenDone; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -IImage *TreeView::GetImage(int index) -{ - return m_pImageList->GetImage(index); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::GetSelectedItems( CUtlVector< int >& list ) -{ - list.RemoveAll(); - - int c = m_SelectedItems.Count(); - for ( int i = 0 ; i < c; ++i ) - { - list.AddToTail( m_SelectedItems[ i ]->m_ItemIndex ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::GetSelectedItemData( CUtlVector< KeyValues * >& list ) -{ - list.RemoveAll(); - - int c = m_SelectedItems.Count(); - for ( int i = 0 ; i < c; ++i ) - { - list.AddToTail( m_SelectedItems[ i ]->m_pData ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool TreeView::IsItemIDValid(int itemIndex) -{ - return m_NodeList.IsValidIndex(itemIndex); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int TreeView::GetHighestItemID() -{ - return m_NodeList.MaxElementIndex(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::ExpandItem(int itemIndex, bool bExpand) -{ - if (!m_NodeList.IsValidIndex(itemIndex)) - return; - - m_NodeList[itemIndex]->SetNodeExpanded(bExpand); - InvalidateLayout(); -} - -bool TreeView::IsItemExpanded( int itemIndex ) -{ - if (!m_NodeList.IsValidIndex(itemIndex)) - return false; - - return m_NodeList[itemIndex]->IsExpanded(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Scrolls the list according to the mouse wheel movement -//----------------------------------------------------------------------------- -void TreeView::OnMouseWheeled(int delta) -{ - if ( !m_pVertScrollBar->IsVisible() ) - { - return; - } - int val = m_pVertScrollBar->GetValue(); - val -= (delta * 3); - m_pVertScrollBar->SetValue(val); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::OnSizeChanged(int wide, int tall) -{ - BaseClass::OnSizeChanged(wide, tall); - InvalidateLayout(); - Repaint(); -} - -void TreeView::GetScrollBarSize( bool vertical, int& w, int& h ) -{ - int idx = vertical ? 0 : 1; - - if ( m_bScrollbarExternal[ idx ] ) - { - w = h = 0; - return; - } - - if ( vertical ) - { - m_pVertScrollBar->GetSize( w, h ); - } - else - { - m_pHorzScrollBar->GetSize( w, h ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::PerformLayout() -{ - int wide, tall; - GetSize( wide, tall ); - - if ( !m_pRootNode ) - { - m_pSubPanel->SetSize( wide, tall ); - return; - } - - int sbhw, sbhh; - GetScrollBarSize( false, sbhw, sbhh ); - int sbvw, sbvh; - GetScrollBarSize( true, sbvw, sbvh ); - - bool vbarNeeded = false; - bool hbarNeeded = false; - - // okay we have to check if we need either scroll bars, since if we need one - // it might make it necessary to have the other one - int nodesVisible = tall / m_nRowHeight; - - // count the number of visible items - int visibleItemCount = m_pRootNode->CountVisibleNodes(); - int maxWidth = m_pRootNode->GetVisibleMaxWidth() + 10; // 10 pixel buffer - - vbarNeeded = visibleItemCount > nodesVisible; - - if (!vbarNeeded) - { - if (maxWidth > wide) - { - hbarNeeded = true; - - // recalculate if vbar is needed now - // double check that we really don't need it - nodesVisible = (tall - sbhh) / m_nRowHeight; - vbarNeeded = visibleItemCount > nodesVisible; - } - } - else - { - // we've got the vertical bar here, so shrink the width - hbarNeeded = maxWidth > (wide - (sbvw+2)); - - if (hbarNeeded) - { - nodesVisible = (tall - sbhh) / m_nRowHeight; - } - } - - int subPanelWidth = wide; - int subPanelHeight = tall; - - int vbarPos = 0; - if (vbarNeeded) - { - subPanelWidth -= (sbvw + 2); - int barSize = tall; - if (hbarNeeded) - { - barSize -= sbhh; - } - - //!! need to make it recalculate scroll positions - m_pVertScrollBar->SetVisible(true); - m_pVertScrollBar->SetEnabled(false); - m_pVertScrollBar->SetRangeWindow( nodesVisible ); - m_pVertScrollBar->SetRange( 0, visibleItemCount); - m_pVertScrollBar->SetButtonPressedScrollValue( 1 ); - - if ( !m_bScrollbarExternal[ 0 ] ) - { - m_pVertScrollBar->SetPos(wide - (sbvw + WINDOW_BORDER_WIDTH), 0); - m_pVertScrollBar->SetSize(sbvw, barSize - 2); - } - - // need to figure out - vbarPos = m_pVertScrollBar->GetValue(); - } - else - { - m_pVertScrollBar->SetVisible(false); - m_pVertScrollBar->SetValue( 0 ); - } - - int hbarPos = 0; - if (hbarNeeded) - { - subPanelHeight -= (sbhh + 2); - int barSize = wide; - if (vbarNeeded) - { - barSize -= sbvw; - } - m_pHorzScrollBar->SetVisible(true); - m_pHorzScrollBar->SetEnabled(false); - m_pHorzScrollBar->SetRangeWindow( barSize ); - m_pHorzScrollBar->SetRange( 0, maxWidth); - m_pHorzScrollBar->SetButtonPressedScrollValue( 10 ); - - if ( !m_bScrollbarExternal[ 1 ] ) - { - m_pHorzScrollBar->SetPos(0, tall - (sbhh + WINDOW_BORDER_WIDTH)); - m_pHorzScrollBar->SetSize(barSize - 2, sbhh); - } - - hbarPos = m_pHorzScrollBar->GetValue(); - } - else - { - m_pHorzScrollBar->SetVisible(false); - m_pHorzScrollBar->SetValue( 0 ); - } - - m_pSubPanel->SetSize(subPanelWidth, subPanelHeight); - - int y = 0; - m_pRootNode->PositionAndSetVisibleNodes(vbarPos, visibleItemCount, -hbarPos, y); - - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::MakeItemVisible(int itemIndex) -{ - // first make sure that all parents are expanded - TreeNode *pNode = m_NodeList[itemIndex]; - TreeNode *pParent = pNode->GetParentNode(); - while (pParent) - { - if (!pParent->m_bExpand) - { - pParent->SetNodeExpanded(true); - } - pParent = pParent->GetParentNode(); - } - - // recalculate scroll bar due to possible exapnsion - PerformLayout(); - - if (!m_pVertScrollBar->IsVisible()) - return; - - int visibleIndex = pNode->CountVisibleIndex()-1; - int range = m_pVertScrollBar->GetRangeWindow(); - int vbarPos = m_pVertScrollBar->GetValue(); - - // do we need to scroll up or down? - if (visibleIndex < vbarPos) - { - m_pVertScrollBar->SetValue(visibleIndex); - } - else if (visibleIndex+1 > vbarPos+range) - { - m_pVertScrollBar->SetValue(visibleIndex+1-range); - } - InvalidateLayout(); -} - -void TreeView::GetVBarInfo( int &top, int &nItemsVisible, bool& hbarVisible ) -{ - int wide, tall; - GetSize( wide, tall ); - nItemsVisible = tall / m_nRowHeight; - - if ( m_pVertScrollBar->IsVisible() ) - { - top = m_pVertScrollBar->GetValue(); - } - else - { - top = 0; - } - hbarVisible = m_pHorzScrollBar->IsVisible(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); - SetBgColor(GetSchemeColor("TreeView.BgColor", GetSchemeColor("WindowDisabledBgColor", pScheme), pScheme)); - SetFont( pScheme->GetFont( "Default", IsProportional() ) ); - m_pSubPanel->SetBgColor( GetBgColor() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::SetBgColor( Color color ) -{ - BaseClass::SetBgColor( color ); - m_pSubPanel->SetBgColor( color ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::OnSliderMoved( int position ) -{ - InvalidateLayout(); - Repaint(); -} - -void TreeView::GenerateDragDataForItem( int itemIndex, KeyValues *msg ) -{ - // Implemented by subclassed TreeView -} - -void TreeView::SetDragEnabledItems( bool state ) -{ - m_bDragEnabledItems = state; -} - -void TreeView::OnLabelChanged( int itemIndex, char const *oldString, char const *newString ) -{ -} - -bool TreeView::IsLabelEditingAllowed() const -{ - return m_bAllowLabelEditing; -} - -void TreeView::SetLabelBeingEdited( bool state ) -{ - m_bLabelBeingEdited = state; -} - -bool TreeView::IsLabelBeingEdited() const -{ - return m_bLabelBeingEdited; -} - -void TreeView::SetAllowLabelEditing( bool state ) -{ - m_bAllowLabelEditing = state; -} - -void TreeView::EnableExpandTreeOnLeftClick( bool bEnable ) -{ - m_bLeftClickExpandsTree = bEnable; -} - -int TreeView::FindItemUnderMouse( int mx, int my ) -{ - mx = clamp( mx, 0, GetWide() - 1 ); - my = clamp( my, 0, GetTall() - 1 ); - if ( mx >= TREE_INDENT_AMOUNT ) - { - // Find what's under this position - // need to figure out - int vbarPos = m_pVertScrollBar->IsVisible() ? m_pVertScrollBar->GetValue() : 0; - int hbarPos = m_pHorzScrollBar->IsVisible() ? m_pHorzScrollBar->GetValue() : 0; - int count = m_pRootNode->CountVisibleNodes(); - - int y = 0; - TreeNode *item = m_pRootNode->FindItemUnderMouse( vbarPos, count, -hbarPos, y, mx, my ); - if ( item ) - { - return item->m_ItemIndex; - } - } - - return -1; -} - -void TreeView::OnMousePressed( MouseCode code ) -{ - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - - // Try to map mouse position to a row - if ( code == MOUSE_LEFT && m_pRootNode ) - { - int mx, my; - input()->GetCursorPos( mx, my ); - ScreenToLocal( mx, my ); - if ( mx >= TREE_INDENT_AMOUNT ) - { - // Find what's under this position - // need to figure out - int vbarPos = m_pVertScrollBar->IsVisible() ? m_pVertScrollBar->GetValue() : 0; - int hbarPos = m_pHorzScrollBar->IsVisible() ? m_pHorzScrollBar->GetValue() : 0; - int count = m_pRootNode->CountVisibleNodes(); - - int y = 0; - TreeNode *item = m_pRootNode->FindItemUnderMouse( vbarPos, count, -hbarPos, y, mx, my ); - if ( item ) - { - if ( !item->IsSelected() ) - { - AddSelectedItem( item->m_ItemIndex, !ctrl && !shift ); - } - return; - } - else - { - ClearSelection(); - } - } - } - - BaseClass::OnMousePressed( code ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : state - -//----------------------------------------------------------------------------- -void TreeView::SetAllowMultipleSelections( bool state ) -{ - m_bAllowMultipleSelections = state; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool TreeView::IsMultipleSelectionAllowed() const -{ - return m_bAllowMultipleSelections; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : int -//----------------------------------------------------------------------------- -int TreeView::GetSelectedItemCount() const -{ - return m_SelectedItems.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -void TreeView::ClearSelection() -{ - m_SelectedItems.RemoveAll(); - m_nMostRecentlySelectedItem = -1; - PostActionSignal( new KeyValues( "TreeViewItemSelectionCleared" ) ); -} - -void TreeView::RangeSelectItems( int endItem ) -{ - int startItem = m_nMostRecentlySelectedItem; - ClearSelection(); - m_nMostRecentlySelectedItem = startItem; - - if ( !m_NodeList.IsValidIndex( startItem ) ) - { - AddSelectedItem( endItem, false ); - return; - } - - Assert( m_NodeList.IsValidIndex( endItem ) ); - - if ( !m_pRootNode ) - { - return; - } - - CUtlVector< TreeNode * > list; - m_pRootNode->FindNodesInRange( list, startItem, endItem ); - - int c = list.Count(); - for ( int i = 0; i < c; ++i ) - { - TreeNode *item = list[ i ]; - AddSelectedItem( item->m_ItemIndex, false ); - } -} - -void TreeView::FindNodesInRange( int startItem, int endItem, CUtlVector< int >& itemIndices ) -{ - CUtlVector< TreeNode * > nodes; - m_pRootNode->FindNodesInRange( nodes, startItem, endItem ); - - int c = nodes.Count(); - for ( int i = 0; i < c; ++i ) - { - TreeNode *item = nodes[ i ]; - itemIndices.AddToTail( item->m_ItemIndex ); - } -} - -void TreeView::RemoveSelectedItem( int itemIndex ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return; - - TreeNode *sel = m_NodeList[ itemIndex ]; - Assert( sel ); - int slot = m_SelectedItems.Find( sel ); - if ( slot != m_SelectedItems.InvalidIndex() ) - { - m_SelectedItems.Remove( slot ); - PostActionSignal( new KeyValues( "TreeViewItemDeselected", "itemIndex", itemIndex ) ); - - m_nMostRecentlySelectedItem = itemIndex; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::AddSelectedItem( int itemIndex, bool clearCurrentSelection, bool requestFocus /* = true */, bool bMakeItemVisible /*= true*/ ) -{ - if ( clearCurrentSelection ) - { - ClearSelection(); - } - - // Assume it's bogus - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return; - - TreeNode *sel = m_NodeList[ itemIndex ]; - Assert( sel ); - if ( requestFocus ) - { - sel->RequestFocus(); - } - - // Item 0 is most recently selected!!! - int slot = m_SelectedItems.Find( sel ); - if ( slot == m_SelectedItems.InvalidIndex() ) - { - m_SelectedItems.AddToHead( sel ); - } - else if ( slot != 0 ) - { - m_SelectedItems.Remove( slot ); - m_SelectedItems.AddToHead( sel ); - } - - if ( bMakeItemVisible ) - { - MakeItemVisible( itemIndex ); - } - - PostActionSignal( new KeyValues( "TreeViewItemSelected", "itemIndex", itemIndex ) ); - InvalidateLayout(); - - if ( clearCurrentSelection ) - { - m_nMostRecentlySelectedItem = itemIndex; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : int -//----------------------------------------------------------------------------- -int TreeView::GetFirstSelectedItem() const -{ - if ( m_SelectedItems.Count() <= 0 ) - return -1; - return m_SelectedItems[ 0 ]->m_ItemIndex; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : itemIndex - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool TreeView::IsItemSelected( int itemIndex ) -{ - // Assume it's bogus - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return false; - - TreeNode *sel = m_NodeList[ itemIndex ]; - return m_SelectedItems.Find( sel ) != m_SelectedItems.InvalidIndex(); -} - -void TreeView::SetLabelEditingAllowed( int itemIndex, bool state ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return; - - TreeNode *sel = m_NodeList[ itemIndex ]; - sel->SetLabelEditingAllowed( state ); -} - -void TreeView::StartEditingLabel( int itemIndex ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return; - - Assert( IsLabelEditingAllowed() ); - - TreeNode *sel = m_NodeList[ itemIndex ]; - Assert( sel->IsLabelEditingAllowed() ); - if ( !sel->IsLabelEditingAllowed() ) - return; - - sel->EditLabel(); -} - -int TreeView::GetPrevChildItemIndex( int itemIndex ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return -1; - TreeNode *sel = m_NodeList[ itemIndex ]; - TreeNode *parent = sel->GetParentNode(); - if ( !parent ) - return -1; - - return parent->GetPrevChildItemIndex( sel ); -} - -int TreeView::GetNextChildItemIndex( int itemIndex ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return -1; - TreeNode *sel = m_NodeList[ itemIndex ]; - TreeNode *parent = sel->GetParentNode(); - if ( !parent ) - return -1; - - return parent->GetNextChildItemIndex( sel ); -} - -bool TreeView::IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist ) -{ - // Derived classes should implement - return false; -} - -void TreeView::OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist ) -{ -} - -bool TreeView::GetItemDropContextMenu( int itemIndex, Menu *menu, CUtlVector< KeyValues * >& msglist ) -{ - return false; -} - -HCursor TreeView::GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist ) -{ - return dc_arrow; -} - -void TreeView::RemoveChildrenOfNode( int itemIndex ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return; - - TreeNode *node = m_NodeList[ itemIndex ]; - node->RemoveChildren(); -} - -ScrollBar *TreeView::SetScrollBarExternal( bool vertical, Panel *newParent ) -{ - if ( vertical ) - { - m_bScrollbarExternal[ 0 ] = true; - m_pVertScrollBar->SetParent( newParent ); - return m_pVertScrollBar; - } - m_bScrollbarExternal[ 1 ] = true; - m_pHorzScrollBar->SetParent( newParent ); - return m_pHorzScrollBar; -} - -// if this is set, then clicking on one row and dragging will select a run or items, etc. -void TreeView::SetMultipleItemDragEnabled( bool state ) -{ - m_bMultipleItemDragging = state; -} - -bool TreeView::IsMultipleItemDragEnabled() const -{ - return m_bMultipleItemDragging; -} - -void TreeView::SelectAll() -{ - m_SelectedItems.RemoveAll(); - FOR_EACH_LL( m_NodeList, i ) - { - m_SelectedItems.AddToTail( m_NodeList[ i ] ); - } - - PostActionSignal( new KeyValues( "TreeViewItemSelected", "itemIndex", GetRootItemIndex() ) ); - InvalidateLayout(); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#define PROTECTED_THINGS_DISABLE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tier1/utlstring.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +using namespace vgui; +enum +{ + WINDOW_BORDER_WIDTH=2 // the width of the window's border +}; + +#define TREE_INDENT_AMOUNT 20 + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Displays an editable text field for the text control +//----------------------------------------------------------------------------- +class TreeNodeText : public TextEntry +{ + DECLARE_CLASS_SIMPLE( TreeNodeText, TextEntry ); + +public: + TreeNodeText(Panel *parent, const char *panelName, TreeView *tree) : BaseClass(parent, panelName), m_pTree( tree ) + { + m_bEditingInPlace = false; + m_bLabelEditingAllowed = false; + SetDragEnabled( false ); + SetDropEnabled( false ); + AddActionSignalTarget( this ); + m_bArmForEditing = false; + m_bWaitingForRelease = false; + m_lArmingTime = 0L; + SetAllowKeyBindingChainToParent( true ); + } + + MESSAGE_FUNC( OnTextChanged, "TextChanged" ) + { + GetParent()->InvalidateLayout(); + } + + bool IsKeyRebound( KeyCode code, int modifiers ) + { + // If in editing mode, don't try and chain keypresses + if ( m_bEditingInPlace ) + { + return false; + } + + return BaseClass::IsKeyRebound( code, modifiers ); + } + + virtual void PaintBackground() + { + BaseClass::PaintBackground(); + + if ( !m_bLabelEditingAllowed ) + return; + + if ( !m_bEditingInPlace ) + return; + + int w, h; + GetSize( w, h ); + surface()->DrawSetColor( GetFgColor() ); + surface()->DrawOutlinedRect( 0, 0, w, h ); + } + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + TextEntry::ApplySchemeSettings(pScheme); + SetBorder(NULL); + SetCursor(dc_arrow); + } + + virtual void OnKeyCodeTyped(KeyCode code) + { + if ( m_bEditingInPlace ) + { + if ( code == KEY_ENTER ) + { + FinishEditingInPlace(); + } + else if ( code == KEY_ESCAPE ) + { + FinishEditingInPlace( true ); + } + else + { + BaseClass::OnKeyCodeTyped( code ); + } + return; + } + else if ( code == KEY_ENTER && IsLabelEditingAllowed() ) + { + EnterEditingInPlace(); + } + else + { + // let parent deal with it (don't chain back to TextEntry) + CallParentFunction(new KeyValues("KeyCodeTyped", "code", code)); + } + } + +#define CLICK_TO_EDIT_DELAY_MSEC 500 + + virtual void OnTick() + { + BaseClass::OnTick(); + if ( m_bArmForEditing ) + { + long msecSinceArming = system()->GetTimeMillis() - m_lArmingTime; + + if ( msecSinceArming > CLICK_TO_EDIT_DELAY_MSEC ) + { + m_bArmForEditing = false; + m_bWaitingForRelease = false; + ivgui()->RemoveTickSignal( GetVPanel() ); + EnterEditingInPlace(); + } + } + } + + virtual void OnMouseReleased( MouseCode code ) + { + if ( m_bEditingInPlace ) + { + BaseClass::OnMouseReleased( code ); + return; + } + else + { + if ( m_bWaitingForRelease && !IsBeingDragged() ) + { + m_bArmForEditing = true; + m_bWaitingForRelease = false; + m_lArmingTime = system()->GetTimeMillis(); + ivgui()->AddTickSignal( GetVPanel() ); + } + else + { + m_bWaitingForRelease = false; + } + } + + // let parent deal with it + CallParentFunction(new KeyValues("MouseReleased", "code", code)); + } + + virtual void OnCursorMoved( int x, int y ) + { + // let parent deal with it + CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); + } + + virtual void OnMousePressed(MouseCode code) + { + if ( m_bEditingInPlace ) + { + BaseClass::OnMousePressed( code ); + return; + } + else + { + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + + // make sure there is only one item selected + // before "WaitingForRelease" which leads to label editing. + CUtlVector< int > list; + m_pTree->GetSelectedItems( list ); + bool bIsOnlyOneItemSelected = ( list.Count() == 1 ); + + if ( !shift && + !ctrl && + !m_bArmForEditing && + IsLabelEditingAllowed() && + bIsOnlyOneItemSelected && + IsTextFullySelected() && + !IsBeingDragged() ) + { + m_bWaitingForRelease = true; + } + } + + // let parent deal with it + CallParentFunction(new KeyValues("MousePressed", "code", code)); + } + + void SetLabelEditingAllowed( bool state ) + { + m_bLabelEditingAllowed = state; + } + + bool IsLabelEditingAllowed() + { + return m_bLabelEditingAllowed; + } + + virtual void OnMouseDoublePressed(MouseCode code) + { + // Once we are editing, double pressing shouldn't chain up + if ( m_bEditingInPlace ) + { + BaseClass::OnMouseDoublePressed( code ); + return; + } + + if ( m_bArmForEditing ) + { + m_bArmForEditing = false; + m_bWaitingForRelease = false; + ivgui()->RemoveTickSignal( GetVPanel() ); + } + + CallParentFunction(new KeyValues("MouseDoublePressed", "code", code)); + } + + void EnterEditingInPlace() + { + if ( m_bEditingInPlace ) + return; + + m_bEditingInPlace = true; + char buf[ 1024 ]; + GetText( buf, sizeof( buf ) ); + m_OriginalText = buf; + SetCursor(dc_ibeam); + SetEditable( true ); + SelectNone(); + GotoTextEnd(); + RequestFocus(); + SelectAllText(false); + m_pTree->SetLabelBeingEdited( true ); + } + + void FinishEditingInPlace( bool revert = false ) + { + if ( !m_bEditingInPlace ) + return; + + m_pTree->SetLabelBeingEdited( false ); + SetEditable( false ); + SetCursor(dc_arrow); + m_bEditingInPlace = false; + char buf[ 1024 ]; + GetText( buf, sizeof( buf ) ); + + // Not actually changed... + if ( !Q_strcmp( buf, m_OriginalText.Get() ) ) + return; + + if ( revert ) + { + SetText( m_OriginalText.Get() ); + GetParent()->InvalidateLayout(); + } + else + { + KeyValues *kv = new KeyValues( "LabelChanged", "original", m_OriginalText.Get(), "changed", buf ); + PostActionSignal( kv ); + } + } + + virtual void OnKillFocus() + { + BaseClass::OnKillFocus(); + + FinishEditingInPlace(); + } + + virtual void OnMouseWheeled(int delta) + { + if ( m_bEditingInPlace ) + { + BaseClass::OnMouseWheeled( delta ); + return; + } + + CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); + } + // editable - cursor normal, and ability to edit text + + bool IsBeingEdited() const + { + return m_bEditingInPlace; + } + +private: + + bool m_bEditingInPlace; + CUtlString m_OriginalText; + bool m_bLabelEditingAllowed; + + bool m_bArmForEditing; + bool m_bWaitingForRelease; + long m_lArmingTime; + TreeView *m_pTree; +}; + +//----------------------------------------------------------------------------- +// Purpose: icon for the tree node (folder icon, file icon, etc.) +//----------------------------------------------------------------------------- +class TreeNodeImage : public ImagePanel +{ +public: + TreeNodeImage(Panel *parent, const char *name) : ImagePanel(parent, name) + { + SetBlockDragChaining( true ); + } + + //!! this could possibly be changed to just disallow mouse input on the image panel + virtual void OnMousePressed(MouseCode code) + { + // let parent deal with it + CallParentFunction(new KeyValues("MousePressed", "code", code)); + } + + virtual void OnMouseDoublePressed(MouseCode code) + { + // let parent deal with it + CallParentFunction(new KeyValues("MouseDoublePressed", "code", code)); + } + + virtual void OnMouseWheeled(int delta) + { + // let parent deal with it + CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); + } + + virtual void OnCursorMoved( int x, int y ) + { + // let parent deal with it + CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: Scrollable area of the tree control, holds the tree itself only +//----------------------------------------------------------------------------- +class TreeViewSubPanel : public Panel +{ +public: + TreeViewSubPanel(Panel *parent) : Panel(parent) {} + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + Panel::ApplySchemeSettings(pScheme); + + SetBorder(NULL); + } + + virtual void OnMouseWheeled(int delta) + { + // let parent deal with it + CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); + } + virtual void OnMousePressed(MouseCode code) + { + // let parent deal with it + CallParentFunction(new KeyValues("MousePressed", "code", code)); + } + virtual void OnMouseDoublePressed(MouseCode code) + { + // let parent deal with it + CallParentFunction(new KeyValues("MouseDoublePressed", "code", code)); + } + + virtual void OnCursorMoved( int x, int y ) + { + // let parent deal with it + CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: A single entry in the tree +//----------------------------------------------------------------------------- +class TreeNode : public Panel +{ + DECLARE_CLASS_SIMPLE( TreeNode, Panel ); + +public: + TreeNode(Panel *parent, TreeView *pTreeView); + ~TreeNode(); + void SetText(const char *pszText); + void SetFont(HFont font); + void SetKeyValues(KeyValues *data); + bool IsSelected(); + // currently unused, could be re-used if necessary +// bool IsInFocus(); + virtual void PaintBackground(); + virtual void PerformLayout(); + TreeNode *GetParentNode(); + int GetChildrenCount(); + void ClearChildren(); + int ComputeInsertionPosition( TreeNode *pChild ); + int FindChild( TreeNode *pChild ); + void AddChild(TreeNode *pChild); + void SetNodeExpanded(bool bExpanded); + bool IsExpanded(); + int CountVisibleNodes(); + void CalculateVisibleMaxWidth(); + void OnChildWidthChange(); + int GetMaxChildrenWidth(); + int GetVisibleMaxWidth(); + int GetDepth(); + bool HasParent(TreeNode *pTreeNode); + bool IsBeingDisplayed(); + virtual void SetVisible(bool state); + virtual void Paint(); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void SetBgColor( Color color ); + virtual void SetFgColor( Color color ); + virtual void OnSetFocus(); + void SelectPrevChild(TreeNode *pCurrentChild); + void SelectNextChild(TreeNode *pCurrentChild); + + int GetPrevChildItemIndex( TreeNode *pCurrentChild ); + int GetNextChildItemIndex( TreeNode *pCurrentChild ); + + virtual void ClosePreviousParents( TreeNode *pPreviousParent ); + virtual void StepInto( bool bClosePrevious=true ); + virtual void StepOut( bool bClosePrevious=true ); + virtual void StepOver( bool bClosePrevious=true ); + virtual void OnKeyCodeTyped(KeyCode code); + virtual void OnMouseWheeled(int delta); + virtual void OnMousePressed( MouseCode code); + virtual void OnMouseReleased( MouseCode code); + virtual void OnCursorMoved( int x, int y ); + virtual bool IsDragEnabled() const; + void PositionAndSetVisibleNodes(int &nStart, int &nCount, int x, int &y); + + // counts items above this item including itself + int CountVisibleIndex(); + + virtual void OnCreateDragData( KeyValues *msg ); + // For handling multiple selections... + virtual void OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles ); + virtual void OnMouseDoublePressed( MouseCode code ); + TreeNode *FindItemUnderMouse( int &nStart, int& nCount, int x, int &y, int mx, int my ); + MESSAGE_FUNC_PARAMS( OnLabelChanged, "LabelChanged", data ); + void EditLabel(); + void SetLabelEditingAllowed( bool state ); + bool IsLabelEditingAllowed() const; + + virtual bool IsDroppable( CUtlVector< KeyValues * >& msglist ); + virtual void OnPanelDropped( CUtlVector< KeyValues * >& msglist ); + virtual HCursor GetDropCursor( CUtlVector< KeyValues * >& msglist ); + virtual bool GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ); + + void FindNodesInRange( CUtlVector< TreeNode * >& list, int startIndex, int endIndex ); + + void RemoveChildren(); + + void SetSelectionTextColor( const Color& clr ); + void SetSelectionBgColor( const Color& clr ); + void SetSelectionUnfocusedBgColor( const Color& clr ); +public: + int m_ItemIndex; + int m_ParentIndex; + KeyValues *m_pData; + CUtlVector m_Children; + bool m_bExpand; + +private: + + void FindNodesInRange_R( CUtlVector< TreeNode * >& list, bool& finished, bool& foundStart, int startIndex, int endIndex ); + + int m_iNodeWidth; + int m_iMaxVisibleWidth; + + TreeNodeText *m_pText; + TextImage *m_pExpandImage; + TreeNodeImage *m_pImagePanel; + + bool m_bExpandableWithoutChildren; + + TreeView *m_pTreeView; + int m_nClickedItem; + bool m_bClickedSelected; +}; + + +TreeNode::TreeNode(Panel *parent, TreeView *pTreeView) : + BaseClass(parent, "TreeNode" ), + m_nClickedItem( 0 ), + m_bClickedSelected( false ) +{ + m_pData = NULL; + m_pTreeView = pTreeView; + m_ItemIndex = -1; + m_iNodeWidth = 0; + m_iMaxVisibleWidth = 0; + + m_pExpandImage = new TextImage("+"); + m_pExpandImage->SetPos(3, 1); + + m_pImagePanel = new TreeNodeImage(this, "TreeImage"); + m_pImagePanel->SetPos(TREE_INDENT_AMOUNT, 3); + + m_pText = new TreeNodeText(this, "TreeNodeText",pTreeView); + m_pText->SetMultiline(false); + m_pText->SetEditable(false); + m_pText->SetPos(TREE_INDENT_AMOUNT*2, 0); + m_pText->AddActionSignalTarget( this ); + + m_bExpand = false; + m_bExpandableWithoutChildren = false; +} + +TreeNode::~TreeNode() +{ + delete m_pExpandImage; + if ( m_pData ) + { + m_pData->deleteThis(); + } +} + +void TreeNode::SetText(const char *pszText) +{ + m_pText->SetText(pszText); + InvalidateLayout(); +} + +void TreeNode::SetLabelEditingAllowed( bool state ) +{ + Assert( m_pTreeView->IsLabelEditingAllowed() ); + m_pText->SetLabelEditingAllowed( state ); +} + +bool TreeNode::IsLabelEditingAllowed() const +{ + return m_pText->IsLabelEditingAllowed(); +} + +bool TreeNode::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ) +{ + return m_pTreeView->GetItemDropContextMenu( m_ItemIndex, menu, msglist ); +} + +bool TreeNode::IsDroppable( CUtlVector< KeyValues * >& msglist ) +{ + return m_pTreeView->IsItemDroppable( m_ItemIndex, msglist ); +} + +void TreeNode::OnPanelDropped( CUtlVector< KeyValues * >& msglist ) +{ + m_pTreeView->OnItemDropped( m_ItemIndex, msglist ); +} + +HCursor TreeNode::GetDropCursor( CUtlVector< KeyValues * >& msglist ) +{ + return m_pTreeView->GetItemDropCursor( m_ItemIndex, msglist ); +} + + +void TreeNode::OnCreateDragData( KeyValues *msg ) +{ + // make sure the dragged item appears selected, + // on the off chance it appears deselected by a cntl mousedown + m_pTreeView->AddSelectedItem( m_ItemIndex, false ); + + m_pTreeView->GenerateDragDataForItem( m_ItemIndex, msg ); +} + +// For handling multiple selections... +void TreeNode::OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles ) +{ + CUtlVector< int > list; + m_pTreeView->GetSelectedItems( list ); + int c = list.Count(); + // walk this in reverse order so that panels are in order of selection + // even though GetSelectedItems returns items in reverse selection order + for ( int i = c - 1; i >= 0; --i ) + { + int itemIndex = list[ i ]; + // Skip self + if ( itemIndex == m_ItemIndex ) + continue; + + dragabbles.AddToTail( ( Panel * )m_pTreeView->GetItem( itemIndex ) ); + } +} + +void TreeNode::OnLabelChanged( KeyValues *data ) +{ + char const *oldString = data->GetString( "original" ); + char const *newString = data->GetString( "changed" ); + if ( m_pTreeView->IsLabelEditingAllowed() ) + { + m_pTreeView->OnLabelChanged( m_ItemIndex, oldString, newString ); + } +} + +void TreeNode::EditLabel() +{ + if ( m_pText->IsLabelEditingAllowed() && + !m_pText->IsBeingEdited() ) + { + m_pText->EnterEditingInPlace(); + } +} + +void TreeNode::SetFont(HFont font) +{ + Assert( font ); + if ( !font ) + return; + + m_pText->SetFont(font); + m_pExpandImage->SetFont(font); + InvalidateLayout(); + int i; + for (i=0;iSetFont(font); + } +} + +void TreeNode::SetKeyValues(KeyValues *data) +{ + if ( m_pData != data ) + { + if (m_pData) + { + m_pData->deleteThis(); + } + + m_pData = data->MakeCopy(); + } + + // set text + m_pText->SetText(data->GetString("Text", "")); + m_bExpandableWithoutChildren = data->GetInt("Expand"); + InvalidateLayout(); +} + +bool TreeNode::IsSelected() +{ + return m_pTreeView->IsItemSelected( m_ItemIndex ); +} + +void TreeNode::PaintBackground() +{ + if ( !m_pText->IsBeingEdited() ) + { + // setup panel drawing + if ( IsSelected() ) + { + m_pText->SelectAllText(false); + } + else + { + m_pText->SelectNoText(); + } + } + + BaseClass::PaintBackground(); +} + + +// currently unused, could be re-used if necessary +/* +bool TreeNode::IsInFocus() +{ + // check if our parent or one of it's children has focus + VPANEL focus = input()->GetFocus(); + return (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))); +} +*/ + +void TreeNode::PerformLayout() +{ + BaseClass::PerformLayout(); + + int width = 0; + if (m_pData->GetInt("SelectedImage", 0) == 0 && + m_pData->GetInt("Image", 0) == 0) + { + width = TREE_INDENT_AMOUNT; + } + else + { + width = TREE_INDENT_AMOUNT * 2; + } + + m_pText->SetPos(width, 0); + + int contentWide, contentTall; + m_pText->SetToFullWidth(); + m_pText->GetSize(contentWide, contentTall); + contentWide += 10; + m_pText->SetSize( contentWide, m_pTreeView->GetRowHeight() ); + width += contentWide; + SetSize(width, m_pTreeView->GetRowHeight()); + + m_iNodeWidth = width; + CalculateVisibleMaxWidth(); +} + +TreeNode *TreeNode::GetParentNode() +{ + if (m_pTreeView->m_NodeList.IsValidIndex(m_ParentIndex)) + { + return m_pTreeView->m_NodeList[m_ParentIndex]; + } + return NULL; +} + +int TreeNode::GetChildrenCount() +{ + return m_Children.Count(); +} + +int TreeNode::ComputeInsertionPosition( TreeNode *pChild ) +{ + if ( !m_pTreeView->m_pSortFunc ) + { + return GetChildrenCount() - 1; + } + + int start = 0, end = GetChildrenCount() - 1; + while (start <= end) + { + int mid = (start + end) >> 1; + if ( m_pTreeView->m_pSortFunc( m_Children[mid]->m_pData, pChild->m_pData ) ) + { + start = mid + 1; + } + else if ( m_pTreeView->m_pSortFunc( pChild->m_pData, m_Children[mid]->m_pData ) ) + { + end = mid - 1; + } + else + { + return mid; + } + } + return end; +} + +int TreeNode::FindChild( TreeNode *pChild ) +{ + if ( !m_pTreeView->m_pSortFunc ) + { + AssertMsg( 0, "This code has never been tested. Is it correct?" ); + for ( int i = 0; i < GetChildrenCount(); ++i ) + { + if ( m_Children[i] == pChild ) + return i; + } + return -1; + } + + // Find the first entry <= to the child + int start = 0, end = GetChildrenCount() - 1; + while (start <= end) + { + int mid = (start + end) >> 1; + + if ( m_Children[mid] == pChild ) + return mid; + + if ( m_pTreeView->m_pSortFunc( m_Children[mid]->m_pData, pChild->m_pData ) ) + { + start = mid + 1; + } + else + { + end = mid - 1; + } + } + + int nMax = GetChildrenCount(); + while( end < nMax ) + { + // Stop when we reach a child that has a different value + if ( m_pTreeView->m_pSortFunc( pChild->m_pData, m_Children[end]->m_pData ) ) + return -1; + + if ( m_Children[end] == pChild ) + return end; + + ++end; + } + + return -1; +} + +void TreeNode::AddChild(TreeNode *pChild) +{ + int i = ComputeInsertionPosition( pChild ); + m_Children.InsertAfter( i, pChild ); +} + +void TreeNode::SetNodeExpanded(bool bExpanded) +{ + m_bExpand = bExpanded; + + if (m_bExpand) + { + // see if we have any child nodes + if (GetChildrenCount() < 1) + { + // we need to get our children from the control + m_pTreeView->GenerateChildrenOfNode(m_ItemIndex); + + // if we still don't have any children, then hide the expand button + if (GetChildrenCount() < 1) + { + m_bExpand = false; + m_bExpandableWithoutChildren = false; + m_pTreeView->InvalidateLayout(); + return; + } + } + + m_pExpandImage->SetText("-"); + } + else + { + m_pExpandImage->SetText("+"); + + if ( m_bExpandableWithoutChildren && GetChildrenCount() > 0 ) + { + m_pTreeView->RemoveChildrenOfNode( m_ItemIndex ); + } + + // check if we've closed down on one of our children, if so, we get the focus + int selectedItem = m_pTreeView->GetFirstSelectedItem(); + if (selectedItem != -1 && m_pTreeView->m_NodeList[selectedItem]->HasParent(this)) + { + m_pTreeView->AddSelectedItem( m_ItemIndex, true ); + } + } + CalculateVisibleMaxWidth(); + m_pTreeView->InvalidateLayout(); +} + +bool TreeNode::IsExpanded() +{ + return m_bExpand; +} + +int TreeNode::CountVisibleNodes() +{ + int count = 1; // count myself + if (m_bExpand) + { + int i; + for (i=0;iCountVisibleNodes(); + } + } + return count; +} + +void TreeNode::CalculateVisibleMaxWidth() +{ + int width; + if (m_bExpand) + { + int childMaxWidth = GetMaxChildrenWidth(); + childMaxWidth += TREE_INDENT_AMOUNT; + + width = max(childMaxWidth, m_iNodeWidth); + } + else + { + width = m_iNodeWidth; + } + if (width != m_iMaxVisibleWidth) + { + m_iMaxVisibleWidth = width; + if (GetParentNode()) + { + GetParentNode()->OnChildWidthChange(); + } + else + { + m_pTreeView->InvalidateLayout(); + } + } +} + +void TreeNode::OnChildWidthChange() +{ + CalculateVisibleMaxWidth(); +} + +int TreeNode::GetMaxChildrenWidth() +{ + int maxWidth = 0; + int i; + for (i=0;iGetVisibleMaxWidth(); + if (childWidth > maxWidth) + { + maxWidth = childWidth; + } + } + return maxWidth; +} + +int TreeNode::GetVisibleMaxWidth() +{ + return m_iMaxVisibleWidth; +} + +int TreeNode::GetDepth() +{ + int depth = 0; + TreeNode *pParent = GetParentNode(); + while (pParent) + { + depth++; + pParent = pParent->GetParentNode(); + } + return depth; +} + +bool TreeNode::HasParent(TreeNode *pTreeNode) +{ + TreeNode *pParent = GetParentNode(); + while (pParent) + { + if (pParent == pTreeNode) + return true; + pParent = pParent->GetParentNode(); + } + return false; +} + +bool TreeNode::IsBeingDisplayed() +{ + TreeNode *pParent = GetParentNode(); + while (pParent) + { + // our parents aren't showing us + if (!pParent->m_bExpand) + return false; + + pParent = pParent->GetParentNode(); + } + return true; +} + +void TreeNode::SetVisible(bool state) +{ + BaseClass::SetVisible(state); + + bool bChildrenVisible = state && m_bExpand; + int i; + for (i=0;iSetVisible(bChildrenVisible); + } +} + +void TreeNode::Paint() +{ + if (GetChildrenCount() > 0 || m_bExpandableWithoutChildren) + { + m_pExpandImage->Paint(); + } + + // set image + int imageIndex = 0; + if (IsSelected()) + { + imageIndex = m_pData->GetInt("SelectedImage", 0); + } + else + { + imageIndex = m_pData->GetInt("Image", 0); + } + + if (imageIndex) + { + IImage *pImage = m_pTreeView->GetImage(imageIndex); + if (pImage) + { + m_pImagePanel->SetImage(pImage); + } + m_pImagePanel->Paint(); + } + + m_pText->Paint(); +} + +void TreeNode::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetBorder( NULL ); + SetFgColor( m_pTreeView->GetFgColor() ); + SetBgColor( m_pTreeView->GetBgColor() ); + SetFont( m_pTreeView->GetFont() ); +} + +void TreeNode::SetSelectionTextColor( const Color& clr ) +{ + if ( m_pText ) + { + m_pText->SetSelectionTextColor( clr ); + } +} + +void TreeNode::SetSelectionBgColor( const Color& clr ) +{ + if ( m_pText ) + { + m_pText->SetSelectionBgColor( clr ); + } +} + +void TreeNode::SetSelectionUnfocusedBgColor( const Color& clr ) +{ + if ( m_pText ) + { + m_pText->SetSelectionUnfocusedBgColor( clr ); + } +} + +void TreeNode::SetBgColor( Color color ) +{ + BaseClass::SetBgColor( color ); + if ( m_pText ) + { + m_pText->SetBgColor( color ); + } + +} + +void TreeNode::SetFgColor( Color color ) +{ + BaseClass::SetFgColor( color ); + if ( m_pText ) + { + m_pText->SetFgColor( color ); + } +} + +void TreeNode::OnSetFocus() +{ + m_pText->RequestFocus(); +} + +int TreeNode::GetPrevChildItemIndex( TreeNode *pCurrentChild ) +{ + int i; + for (i=0;im_ItemIndex; + } + } + return -1; +} + +int TreeNode::GetNextChildItemIndex( TreeNode *pCurrentChild ) +{ + int i; + for (i=0;i= GetChildrenCount() - 1 ) + return -1; + + TreeNode *pChild = m_Children[i+1]; + return pChild->m_ItemIndex; + } + } + return -1; +} + +void TreeNode::SelectPrevChild(TreeNode *pCurrentChild) +{ + int i; + for (i=0;iAddSelectedItem( m_ItemIndex, true ); + } + else + { + // see if we need to find a grandchild of the previous sibling + TreeNode *pChild = m_Children[i-1]; + + // if this child is expanded with children, then we have to find the last child + while (pChild->m_bExpand && pChild->GetChildrenCount()>0) + { + // find the last child + pChild = pChild->m_Children[pChild->GetChildrenCount()-1]; + } + m_pTreeView->AddSelectedItem( pChild->m_ItemIndex, true ); + } +} + +void TreeNode::SelectNextChild(TreeNode *pCurrentChild) +{ + int i; + for (i=0;iSelectNextChild(this); + } + } + else + { + m_pTreeView->AddSelectedItem( m_Children[i+1]->m_ItemIndex, true ); + } +} + +void TreeNode::ClosePreviousParents( TreeNode *pPreviousParent ) +{ + // close up all the open nodes we've just stepped out of. + CUtlVector< int > selected; + m_pTreeView->GetSelectedItems( selected ); + if ( selected.Count() == 0 ) + { + Assert( 0 ); + return; + } + + // Most recently clicked item + TreeNode *selectedItem = m_pTreeView->GetItem( selected[ 0 ] ); + TreeNode *pNewParent = selectedItem->GetParentNode(); + if ( pPreviousParent && pNewParent ) + { + while ( pPreviousParent->m_ItemIndex > pNewParent->m_ItemIndex ) + { + pPreviousParent->SetNodeExpanded(false); + pPreviousParent = pPreviousParent->GetParentNode(); + } + } +} + +void TreeNode::StepInto( bool bClosePrevious ) +{ + if ( !m_bExpand ) + { + SetNodeExpanded(true); + } + + if ( ( GetChildrenCount() > 0 ) && m_bExpand ) + { + m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true ); + } + else if ( GetParentNode() ) + { + TreeNode *pParent = GetParentNode(); + pParent->SelectNextChild(this); + + if ( bClosePrevious ) + { + ClosePreviousParents( pParent ); + } + } +} + +void TreeNode::StepOut( bool bClosePrevious ) +{ + TreeNode *pParent = GetParentNode(); + if ( pParent ) + { + m_pTreeView->AddSelectedItem( pParent->m_ItemIndex, true ); + if ( pParent->GetParentNode() ) + { + pParent->GetParentNode()->SelectNextChild(pParent); + } + if ( bClosePrevious ) + { + ClosePreviousParents( pParent ); + } + else + { + pParent->SetNodeExpanded(true); + } + } +} + +void TreeNode::StepOver( bool bClosePrevious ) +{ + TreeNode *pParent = GetParentNode(); + if ( pParent ) + { + GetParentNode()->SelectNextChild(this); + if ( bClosePrevious ) + { + ClosePreviousParents( pParent ); + } + } +} + +void TreeNode::OnKeyCodeTyped(KeyCode code) +{ + switch (code) + { + case KEY_LEFT: + { + if (m_bExpand && GetChildrenCount() > 0) + { + SetNodeExpanded(false); + } + else + { + if (GetParentNode()) + { + m_pTreeView->AddSelectedItem( GetParentNode()->m_ItemIndex, true ); + } + } + break; + } + case KEY_RIGHT: + { + if (!m_bExpand) + { + SetNodeExpanded(true); + } + else if (GetChildrenCount() > 0) + { + m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true ); + } + break; + } + case KEY_UP: + { + if (GetParentNode()) + { + GetParentNode()->SelectPrevChild(this); + } + break; + } + case KEY_DOWN: + { + if (GetChildrenCount() > 0 && m_bExpand) + { + m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true ); + } + else if (GetParentNode()) + { + GetParentNode()->SelectNextChild(this); + } + break; + } + case KEY_SPACE: + { + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + if ( shift ) + { + StepOut( !ctrl ); + } + else if ( alt ) + { + StepOver( !ctrl ); + } + else + { + StepInto( !ctrl ); + } + break; + } + case KEY_I: + { + StepInto(); + break; + } + case KEY_U: + { + StepOut(); + break; + } + case KEY_O: + { + StepOver(); + break; + } + case KEY_ESCAPE: + { + if ( m_pTreeView->GetSelectedItemCount() > 0 ) + { + m_pTreeView->ClearSelection(); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } + } + break; + case KEY_A: + { + bool ctrldown = input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL ); + if ( ctrldown ) + { + m_pTreeView->SelectAll(); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } + } + break; + default: + BaseClass::OnKeyCodeTyped(code); + return; + } +} + +void TreeNode::OnMouseWheeled(int delta) +{ + CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); +} + +void TreeNode::OnMouseDoublePressed( MouseCode code ) +{ + int x, y; + input()->GetCursorPos(x, y); + + if (code == MOUSE_LEFT) + { + ScreenToLocal(x, y); + if (x > TREE_INDENT_AMOUNT) + { + SetNodeExpanded(!m_bExpand); + } + } +} + +bool TreeNode::IsDragEnabled() const +{ + int x, y; + input()->GetCursorPos(x, y); + ((TreeNode *)this)->ScreenToLocal(x, y); + if ( x < TREE_INDENT_AMOUNT ) + return false; + + return BaseClass::IsDragEnabled(); +} + +void TreeNode::OnMouseReleased(MouseCode code) +{ + BaseClass::OnMouseReleased( code ); + + if ( input()->GetMouseCapture() == GetVPanel() ) + { + input()->SetMouseCapture( NULL ); + return; + } + int x, y; + input()->GetCursorPos(x, y); + ScreenToLocal(x, y); + + if ( x < TREE_INDENT_AMOUNT ) + return; + + bool ctrldown = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool shiftdown = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + + if ( !ctrldown && !shiftdown && ( code == MOUSE_LEFT ) ) + { + m_pTreeView->AddSelectedItem( m_ItemIndex, true ); + } +} + +void TreeNode::OnCursorMoved( int x, int y ) +{ + if ( input()->GetMouseCapture() != GetVPanel() ) + return; + + LocalToScreen( x, y ); + m_pTreeView->ScreenToLocal( x, y ); + int newItem = m_pTreeView->FindItemUnderMouse( x, y ); + if ( newItem == -1 ) + { + // Fixme: Figure out best item + return; + } + + int startItem = m_nClickedItem; + int endItem = newItem; + if ( startItem > endItem ) + { + int temp = startItem; + startItem = endItem; + endItem = temp; + } + + CUtlVector< TreeNode * > list; + m_pTreeView->m_pRootNode->FindNodesInRange( list, startItem, endItem ); + + int c = list.Count(); + for ( int i = 0; i < c; ++i ) + { + TreeNode *item = list[ i ]; + if ( m_bClickedSelected ) + { + m_pTreeView->AddSelectedItem( item->m_ItemIndex, false ); + } + else + { + m_pTreeView->RemoveSelectedItem( item->m_ItemIndex ); + } + } +} + +void TreeNode::OnMousePressed( MouseCode code) +{ + BaseClass::OnMousePressed( code ); + + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + int x, y; + input()->GetCursorPos(x, y); + + bool bExpandTree = m_pTreeView->m_bLeftClickExpandsTree; + + if ( code == MOUSE_LEFT ) + { + ScreenToLocal(x, y); + if ( x < TREE_INDENT_AMOUNT ) + { + if ( bExpandTree ) + { + SetNodeExpanded(!m_bExpand); + } + // m_pTreeView->SetSelectedItem(m_ItemIndex); // explorer doesn't actually select item when it expands an item + // purposely commented out in case we want to change the behavior + } + else + { + m_nClickedItem = m_ItemIndex; + if ( m_pTreeView->IsMultipleItemDragEnabled() ) + { + input()->SetMouseCapture( GetVPanel() ); + } + + if ( shift ) + { + m_pTreeView->RangeSelectItems( m_ItemIndex ); + } + else + { + if ( !IsSelected() || ctrl ) + { + if ( IsSelected() && ctrl ) + { + m_pTreeView->RemoveSelectedItem( m_ItemIndex ); + } + else + { + m_pTreeView->AddSelectedItem( m_ItemIndex, !ctrl ); + } + } + else if ( IsSelected() && m_pTreeView->IsMultipleItemDragEnabled() ) + { + m_pTreeView->AddSelectedItem( m_ItemIndex, !shift ); + } + } + + m_bClickedSelected = m_pTreeView->IsItemSelected( m_ItemIndex ); + } + } + else if (code == MOUSE_RIGHT) + { + // context menu selection + // If the item was selected, leave selected items alone, otherwise make it the only selected item + if ( !m_pTreeView->IsItemSelected( m_ItemIndex ) ) + { + m_pTreeView->AddSelectedItem( m_ItemIndex, true ); + } + + // ask parent to context menu + m_pTreeView->GenerateContextMenu(m_ItemIndex, x, y); + } +} + +void TreeNode::RemoveChildren() +{ + int c = m_Children.Count(); + for ( int i = c - 1 ; i >= 0 ; --i ) + { + m_pTreeView->RemoveItem( m_Children[ i ]->m_ItemIndex, false, true ); + } + m_Children.RemoveAll(); +} + +void TreeNode::FindNodesInRange( CUtlVector< TreeNode * >& list, int startIndex, int endIndex ) +{ + list.RemoveAll(); + bool finished = false; + bool foundstart = false; + FindNodesInRange_R( list, finished, foundstart, startIndex, endIndex ); +} + +void TreeNode::FindNodesInRange_R( CUtlVector< TreeNode * >& list, bool& finished, bool& foundStart, int startIndex, int endIndex ) +{ + if ( finished ) + return; + if ( foundStart == true ) + { + list.AddToTail( this ); + + if ( m_ItemIndex == startIndex || m_ItemIndex == endIndex ) + { + finished = true; + return; + } + } + else if ( m_ItemIndex == startIndex || m_ItemIndex == endIndex ) + { + foundStart = true; + list.AddToTail( this ); + if ( startIndex == endIndex ) + { + finished = true; + return; + } + } + + if ( !m_bExpand ) + return; + + + int i; + int c = GetChildrenCount(); + for (i=0;iFindNodesInRange_R( list, finished, foundStart, startIndex, endIndex ); + } +} + +void TreeNode::PositionAndSetVisibleNodes(int &nStart, int &nCount, int x, int &y) +{ + // position ourselves + if (nStart == 0) + { + BaseClass::SetVisible(true); + SetPos(x, y); + y += m_pTreeView->GetRowHeight(); // m_nRowHeight + nCount--; + } + else // still looking for first element + { + nStart--; + BaseClass::SetVisible(false); + } + + x += TREE_INDENT_AMOUNT; + int i; + for (i=0;i 0 && m_bExpand) + { + m_Children[i]->PositionAndSetVisibleNodes(nStart, nCount, x, y); + } + else + { + m_Children[i]->SetVisible(false); // this will make all grand children hidden as well + } + } +} + +TreeNode *TreeNode::FindItemUnderMouse( int &nStart, int& nCount, int x, int &y, int mx, int my ) +{ + // position ourselves + if (nStart == 0) + { + int posx, posy; + GetPos(posx, posy); + if ( my >= posy && my < posy + m_pTreeView->GetRowHeight() ) + { + return this; + } + y += m_pTreeView->GetRowHeight(); + nCount--; + } + else // still looking for first element + { + nStart--; + } + + x += TREE_INDENT_AMOUNT; + int i; + for (i=0;i 0 && m_bExpand) + { + TreeNode *child = m_Children[i]->FindItemUnderMouse(nStart, nCount, x, y, mx, my); + if ( child != NULL ) + { + return child; + } + } + } + + return NULL; +} + +// counts items above this item including itself +int TreeNode::CountVisibleIndex() +{ + int nCount = 1; // myself + if (GetParentNode()) + { + int i; + for (i=0;iGetChildrenCount();i++) + { + if (GetParentNode()->m_Children[i] == this) + break; + + nCount += GetParentNode()->m_Children[i]->CountVisibleNodes(); + } + return nCount + GetParentNode()->CountVisibleIndex(); + } + else + return nCount; +} + + +}; // namespace vgui + +DECLARE_BUILD_FACTORY( TreeView ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +TreeView::TreeView(Panel *parent, const char *panelName) : Panel(parent, panelName) +{ + m_bScrollbarExternal[ 0 ] = m_bScrollbarExternal[ 1 ] = false; + m_nRowHeight = 20; + m_pRootNode = NULL; + m_pImageList = NULL; + m_pSortFunc = NULL; + m_Font = 0; + + m_pSubPanel = new TreeViewSubPanel(this); + m_pSubPanel->SetVisible(true); + m_pSubPanel->SetPos(0,0); + + m_pHorzScrollBar = new ScrollBar(this, "HorizScrollBar", false); + m_pHorzScrollBar->AddActionSignalTarget(this); + m_pHorzScrollBar->SetVisible(false); + + m_pVertScrollBar = new ScrollBar(this, "VertScrollBar", true); + m_pVertScrollBar->SetVisible(false); + m_pVertScrollBar->AddActionSignalTarget(this); + + m_bAllowLabelEditing = false; + m_bDragEnabledItems = false; + m_bDeleteImageListWhenDone = false; + m_bLabelBeingEdited = false; + m_bMultipleItemDragging = false; + m_bLeftClickExpandsTree = true; + m_bAllowMultipleSelections = false; + m_nMostRecentlySelectedItem = -1; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +TreeView::~TreeView() +{ + CleanUpImageList(); +} + + +//----------------------------------------------------------------------------- +// Clean up the image list +//----------------------------------------------------------------------------- +void TreeView::CleanUpImageList( ) +{ + if ( m_pImageList ) + { + if ( m_bDeleteImageListWhenDone ) + { + delete m_pImageList; + } + m_pImageList = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::SetSortFunc(TreeViewSortFunc_t pSortFunc) +{ + m_pSortFunc = pSortFunc; +} + +HFont TreeView::GetFont() +{ + return m_Font; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::SetFont(HFont font) +{ + Assert( font ); + if ( !font ) + return; + + m_Font = font; + m_nRowHeight = surface()->GetFontTall(font) + 2; + + if (m_pRootNode) + { + m_pRootNode->SetFont(font); + } + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TreeView::GetRowHeight() +{ + return m_nRowHeight; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TreeView::GetVisibleMaxWidth() +{ + if (m_pRootNode) + { + return m_pRootNode->GetVisibleMaxWidth(); + } + else + { + return 0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TreeView::AddItem(KeyValues *data, int parentItemIndex) +{ + Assert(parentItemIndex == -1 || m_NodeList.IsValidIndex(parentItemIndex)); + + TreeNode *pTreeNode = new TreeNode(m_pSubPanel, this); + pTreeNode->SetDragEnabled( m_bDragEnabledItems ); + pTreeNode->m_ItemIndex = m_NodeList.AddToTail(pTreeNode); + pTreeNode->SetKeyValues(data); + + if ( m_Font != 0 ) + { + pTreeNode->SetFont( m_Font ); + } + pTreeNode->SetBgColor( GetBgColor() ); + + if ( data->GetInt( "droppable", 0 ) != 0 ) + { + float flContextDelay = data->GetFloat( "drophoverdelay" ); + if ( flContextDelay ) + { + pTreeNode->SetDropEnabled( true, flContextDelay ); + } + else + { + pTreeNode->SetDropEnabled( true ); + } + } + + // there can be only one root + if (parentItemIndex == -1) + { + Assert(m_pRootNode == NULL); + m_pRootNode = pTreeNode; + pTreeNode->m_ParentIndex = -1; + } + else + { + pTreeNode->m_ParentIndex = parentItemIndex; + + // add to parent list + pTreeNode->GetParentNode()->AddChild(pTreeNode); + } + + SETUP_PANEL( pTreeNode ); + + return pTreeNode->m_ItemIndex; +} + + +int TreeView::GetRootItemIndex() +{ + if ( m_pRootNode ) + return m_pRootNode->m_ItemIndex; + else + return -1; +} + + +int TreeView::GetNumChildren( int itemIndex ) +{ + if ( itemIndex == -1 ) + return 0; + + return m_NodeList[itemIndex]->m_Children.Count(); +} + + +int TreeView::GetChild( int iParentItemIndex, int iChild ) +{ + return m_NodeList[iParentItemIndex]->m_Children[iChild]->m_ItemIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : itemIndex - +// Output : TreeNode +//----------------------------------------------------------------------------- +TreeNode *TreeView::GetItem( int itemIndex ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + { + Assert( 0 ); + return NULL; + } + + return m_NodeList[ itemIndex ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TreeView::GetItemCount(void) +{ + return m_NodeList.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +KeyValues* TreeView::GetItemData(int itemIndex) +{ + if (!m_NodeList.IsValidIndex(itemIndex)) + return NULL; + else + return m_NodeList[itemIndex]->m_pData; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::RemoveItem(int itemIndex, bool bPromoteChildren, bool bFullDelete ) +{ + // HACK: there's a bug with RemoveItem where panels are lingering. This gets around it temporarily. + + // FIXME: Negative item indices is a bogus interface method! + // because what if you want to recursively remove everything under node 0? + // Use the bFullDelete parameter instead. + if ( itemIndex < 0 ) + { + itemIndex = -itemIndex; + bFullDelete = true; + } + + if (!m_NodeList.IsValidIndex(itemIndex)) + return; + + TreeNode *pNode = m_NodeList[itemIndex]; + TreeNode *pParent = pNode->GetParentNode(); + + // are we promoting the children + if (bPromoteChildren && pParent) + { + int i; + for (i=0;iGetChildrenCount();i++) + { + TreeNode *pChild = pNode->m_Children[i]; + pChild->m_ParentIndex = pParent->m_ItemIndex; + } + } + else + { + // delete our children + if ( bFullDelete ) + { + while ( pNode->GetChildrenCount() ) + RemoveItem( -pNode->m_Children[0]->m_ItemIndex, false ); + } + else + { + int i; + for (i=0;iGetChildrenCount();i++) + { + TreeNode *pDeleteChild = pNode->m_Children[i]; + RemoveItem(pDeleteChild->m_ItemIndex, false); + } + } + } + + // remove from our parent's children list + if (pParent) + { + pParent->m_Children.FindAndRemove(pNode); + } + + // finally get rid of ourselves from the main list + m_NodeList.Remove(itemIndex); + + if ( bFullDelete ) + delete pNode; + else + pNode->MarkForDeletion(); + + // Make sure we don't leave ourselves with an invalid selected item. + m_SelectedItems.FindAndRemove( pNode ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::RemoveAll() +{ + int i; + for (i=0;iMarkForDeletion(); + } + m_NodeList.RemoveAll(); + m_pRootNode = NULL; + ClearSelection(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool TreeView::ModifyItem(int itemIndex, KeyValues *data) +{ + if (!m_NodeList.IsValidIndex(itemIndex)) + return false; + + TreeNode *pNode = m_NodeList[itemIndex]; + TreeNode *pParent = pNode->GetParentNode(); + bool bReSort = ( m_pSortFunc && pParent ); + int nChildIndex = -1; + if ( bReSort ) + { + nChildIndex = pParent->FindChild( pNode ); + } + + pNode->SetKeyValues(data); + + // Changing the data can cause it to re-sort + if ( bReSort ) + { + int nChildren = pParent->GetChildrenCount(); + bool bLeftBad = (nChildIndex > 0) && m_pSortFunc( pNode->m_pData, pParent->m_Children[nChildIndex-1]->m_pData ); + bool bRightBad = (nChildIndex < nChildren - 1) && m_pSortFunc( pParent->m_Children[nChildIndex+1]->m_pData, pNode->m_pData ); + if ( bLeftBad || bRightBad ) + { + pParent->m_Children.Remove( nChildIndex ); + pParent->AddChild( pNode ); + } + } + + InvalidateLayout(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: set the selection colors of an element in the tree view +//----------------------------------------------------------------------------- + +void TreeView::SetItemSelectionTextColor( int itemIndex, const Color& clr ) +{ + Assert( m_NodeList.IsValidIndex(itemIndex) ); + if ( !m_NodeList.IsValidIndex(itemIndex) ) + return; + + TreeNode *pNode = m_NodeList[itemIndex]; + pNode->SetSelectionTextColor( clr ); +} + +void TreeView::SetItemSelectionBgColor( int itemIndex, const Color& clr ) +{ + Assert( m_NodeList.IsValidIndex(itemIndex) ); + if ( !m_NodeList.IsValidIndex(itemIndex) ) + return; + + TreeNode *pNode = m_NodeList[itemIndex]; + pNode->SetSelectionBgColor( clr ); +} + +void TreeView::SetItemSelectionUnfocusedBgColor( int itemIndex, const Color& clr ) +{ + Assert( m_NodeList.IsValidIndex(itemIndex) ); + if ( !m_NodeList.IsValidIndex(itemIndex) ) + return; + + TreeNode *pNode = m_NodeList[itemIndex]; + pNode->SetSelectionUnfocusedBgColor( clr ); +} + +//----------------------------------------------------------------------------- +// Purpose: set the fg color of an element in the tree view +//----------------------------------------------------------------------------- +void TreeView::SetItemFgColor(int itemIndex, const Color& color) +{ + Assert( m_NodeList.IsValidIndex(itemIndex) ); + if ( !m_NodeList.IsValidIndex(itemIndex) ) + return; + + TreeNode *pNode = m_NodeList[itemIndex]; + pNode->SetFgColor( color ); +} + +//----------------------------------------------------------------------------- +// Purpose: set the bg color of an element in the tree view +//----------------------------------------------------------------------------- +void TreeView::SetItemBgColor(int itemIndex, const Color& color) +{ + Assert( m_NodeList.IsValidIndex(itemIndex) ); + if ( !m_NodeList.IsValidIndex(itemIndex) ) + return; + + TreeNode *pNode = m_NodeList[itemIndex]; + pNode->SetBgColor( color ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TreeView::GetItemParent(int itemIndex) +{ + return m_NodeList[itemIndex]->m_ParentIndex; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::SetImageList(ImageList *imageList, bool deleteImageListWhenDone) +{ + CleanUpImageList(); + m_pImageList = imageList; + m_bDeleteImageListWhenDone = deleteImageListWhenDone; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IImage *TreeView::GetImage(int index) +{ + return m_pImageList->GetImage(index); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::GetSelectedItems( CUtlVector< int >& list ) +{ + list.RemoveAll(); + + int c = m_SelectedItems.Count(); + for ( int i = 0 ; i < c; ++i ) + { + list.AddToTail( m_SelectedItems[ i ]->m_ItemIndex ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::GetSelectedItemData( CUtlVector< KeyValues * >& list ) +{ + list.RemoveAll(); + + int c = m_SelectedItems.Count(); + for ( int i = 0 ; i < c; ++i ) + { + list.AddToTail( m_SelectedItems[ i ]->m_pData ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool TreeView::IsItemIDValid(int itemIndex) +{ + return m_NodeList.IsValidIndex(itemIndex); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TreeView::GetHighestItemID() +{ + return m_NodeList.MaxElementIndex(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::ExpandItem(int itemIndex, bool bExpand) +{ + if (!m_NodeList.IsValidIndex(itemIndex)) + return; + + m_NodeList[itemIndex]->SetNodeExpanded(bExpand); + InvalidateLayout(); +} + +bool TreeView::IsItemExpanded( int itemIndex ) +{ + if (!m_NodeList.IsValidIndex(itemIndex)) + return false; + + return m_NodeList[itemIndex]->IsExpanded(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Scrolls the list according to the mouse wheel movement +//----------------------------------------------------------------------------- +void TreeView::OnMouseWheeled(int delta) +{ + if ( !m_pVertScrollBar->IsVisible() ) + { + return; + } + int val = m_pVertScrollBar->GetValue(); + val -= (delta * 3); + m_pVertScrollBar->SetValue(val); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::OnSizeChanged(int wide, int tall) +{ + BaseClass::OnSizeChanged(wide, tall); + InvalidateLayout(); + Repaint(); +} + +void TreeView::GetScrollBarSize( bool vertical, int& w, int& h ) +{ + int idx = vertical ? 0 : 1; + + if ( m_bScrollbarExternal[ idx ] ) + { + w = h = 0; + return; + } + + if ( vertical ) + { + m_pVertScrollBar->GetSize( w, h ); + } + else + { + m_pHorzScrollBar->GetSize( w, h ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::PerformLayout() +{ + int wide, tall; + GetSize( wide, tall ); + + if ( !m_pRootNode ) + { + m_pSubPanel->SetSize( wide, tall ); + return; + } + + int sbhw, sbhh; + GetScrollBarSize( false, sbhw, sbhh ); + int sbvw, sbvh; + GetScrollBarSize( true, sbvw, sbvh ); + + bool vbarNeeded = false; + bool hbarNeeded = false; + + // okay we have to check if we need either scroll bars, since if we need one + // it might make it necessary to have the other one + int nodesVisible = tall / m_nRowHeight; + + // count the number of visible items + int visibleItemCount = m_pRootNode->CountVisibleNodes(); + int maxWidth = m_pRootNode->GetVisibleMaxWidth() + 10; // 10 pixel buffer + + vbarNeeded = visibleItemCount > nodesVisible; + + if (!vbarNeeded) + { + if (maxWidth > wide) + { + hbarNeeded = true; + + // recalculate if vbar is needed now + // double check that we really don't need it + nodesVisible = (tall - sbhh) / m_nRowHeight; + vbarNeeded = visibleItemCount > nodesVisible; + } + } + else + { + // we've got the vertical bar here, so shrink the width + hbarNeeded = maxWidth > (wide - (sbvw+2)); + + if (hbarNeeded) + { + nodesVisible = (tall - sbhh) / m_nRowHeight; + } + } + + int subPanelWidth = wide; + int subPanelHeight = tall; + + int vbarPos = 0; + if (vbarNeeded) + { + subPanelWidth -= (sbvw + 2); + int barSize = tall; + if (hbarNeeded) + { + barSize -= sbhh; + } + + //!! need to make it recalculate scroll positions + m_pVertScrollBar->SetVisible(true); + m_pVertScrollBar->SetEnabled(false); + m_pVertScrollBar->SetRangeWindow( nodesVisible ); + m_pVertScrollBar->SetRange( 0, visibleItemCount); + m_pVertScrollBar->SetButtonPressedScrollValue( 1 ); + + if ( !m_bScrollbarExternal[ 0 ] ) + { + m_pVertScrollBar->SetPos(wide - (sbvw + WINDOW_BORDER_WIDTH), 0); + m_pVertScrollBar->SetSize(sbvw, barSize - 2); + } + + // need to figure out + vbarPos = m_pVertScrollBar->GetValue(); + } + else + { + m_pVertScrollBar->SetVisible(false); + m_pVertScrollBar->SetValue( 0 ); + } + + int hbarPos = 0; + if (hbarNeeded) + { + subPanelHeight -= (sbhh + 2); + int barSize = wide; + if (vbarNeeded) + { + barSize -= sbvw; + } + m_pHorzScrollBar->SetVisible(true); + m_pHorzScrollBar->SetEnabled(false); + m_pHorzScrollBar->SetRangeWindow( barSize ); + m_pHorzScrollBar->SetRange( 0, maxWidth); + m_pHorzScrollBar->SetButtonPressedScrollValue( 10 ); + + if ( !m_bScrollbarExternal[ 1 ] ) + { + m_pHorzScrollBar->SetPos(0, tall - (sbhh + WINDOW_BORDER_WIDTH)); + m_pHorzScrollBar->SetSize(barSize - 2, sbhh); + } + + hbarPos = m_pHorzScrollBar->GetValue(); + } + else + { + m_pHorzScrollBar->SetVisible(false); + m_pHorzScrollBar->SetValue( 0 ); + } + + m_pSubPanel->SetSize(subPanelWidth, subPanelHeight); + + int y = 0; + m_pRootNode->PositionAndSetVisibleNodes(vbarPos, visibleItemCount, -hbarPos, y); + + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::MakeItemVisible(int itemIndex) +{ + // first make sure that all parents are expanded + TreeNode *pNode = m_NodeList[itemIndex]; + TreeNode *pParent = pNode->GetParentNode(); + while (pParent) + { + if (!pParent->m_bExpand) + { + pParent->SetNodeExpanded(true); + } + pParent = pParent->GetParentNode(); + } + + // recalculate scroll bar due to possible exapnsion + PerformLayout(); + + if (!m_pVertScrollBar->IsVisible()) + return; + + int visibleIndex = pNode->CountVisibleIndex()-1; + int range = m_pVertScrollBar->GetRangeWindow(); + int vbarPos = m_pVertScrollBar->GetValue(); + + // do we need to scroll up or down? + if (visibleIndex < vbarPos) + { + m_pVertScrollBar->SetValue(visibleIndex); + } + else if (visibleIndex+1 > vbarPos+range) + { + m_pVertScrollBar->SetValue(visibleIndex+1-range); + } + InvalidateLayout(); +} + +void TreeView::GetVBarInfo( int &top, int &nItemsVisible, bool& hbarVisible ) +{ + int wide, tall; + GetSize( wide, tall ); + nItemsVisible = tall / m_nRowHeight; + + if ( m_pVertScrollBar->IsVisible() ) + { + top = m_pVertScrollBar->GetValue(); + } + else + { + top = 0; + } + hbarVisible = m_pHorzScrollBar->IsVisible(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); + SetBgColor(GetSchemeColor("TreeView.BgColor", GetSchemeColor("WindowDisabledBgColor", pScheme), pScheme)); + SetFont( pScheme->GetFont( "Default", IsProportional() ) ); + m_pSubPanel->SetBgColor( GetBgColor() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::SetBgColor( Color color ) +{ + BaseClass::SetBgColor( color ); + m_pSubPanel->SetBgColor( color ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::OnSliderMoved( int position ) +{ + InvalidateLayout(); + Repaint(); +} + +void TreeView::GenerateDragDataForItem( int itemIndex, KeyValues *msg ) +{ + // Implemented by subclassed TreeView +} + +void TreeView::SetDragEnabledItems( bool state ) +{ + m_bDragEnabledItems = state; +} + +void TreeView::OnLabelChanged( int itemIndex, char const *oldString, char const *newString ) +{ +} + +bool TreeView::IsLabelEditingAllowed() const +{ + return m_bAllowLabelEditing; +} + +void TreeView::SetLabelBeingEdited( bool state ) +{ + m_bLabelBeingEdited = state; +} + +bool TreeView::IsLabelBeingEdited() const +{ + return m_bLabelBeingEdited; +} + +void TreeView::SetAllowLabelEditing( bool state ) +{ + m_bAllowLabelEditing = state; +} + +void TreeView::EnableExpandTreeOnLeftClick( bool bEnable ) +{ + m_bLeftClickExpandsTree = bEnable; +} + +int TreeView::FindItemUnderMouse( int mx, int my ) +{ + mx = clamp( mx, 0, GetWide() - 1 ); + my = clamp( my, 0, GetTall() - 1 ); + if ( mx >= TREE_INDENT_AMOUNT ) + { + // Find what's under this position + // need to figure out + int vbarPos = m_pVertScrollBar->IsVisible() ? m_pVertScrollBar->GetValue() : 0; + int hbarPos = m_pHorzScrollBar->IsVisible() ? m_pHorzScrollBar->GetValue() : 0; + int count = m_pRootNode->CountVisibleNodes(); + + int y = 0; + TreeNode *item = m_pRootNode->FindItemUnderMouse( vbarPos, count, -hbarPos, y, mx, my ); + if ( item ) + { + return item->m_ItemIndex; + } + } + + return -1; +} + +void TreeView::OnMousePressed( MouseCode code ) +{ + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + + // Try to map mouse position to a row + if ( code == MOUSE_LEFT && m_pRootNode ) + { + int mx, my; + input()->GetCursorPos( mx, my ); + ScreenToLocal( mx, my ); + if ( mx >= TREE_INDENT_AMOUNT ) + { + // Find what's under this position + // need to figure out + int vbarPos = m_pVertScrollBar->IsVisible() ? m_pVertScrollBar->GetValue() : 0; + int hbarPos = m_pHorzScrollBar->IsVisible() ? m_pHorzScrollBar->GetValue() : 0; + int count = m_pRootNode->CountVisibleNodes(); + + int y = 0; + TreeNode *item = m_pRootNode->FindItemUnderMouse( vbarPos, count, -hbarPos, y, mx, my ); + if ( item ) + { + if ( !item->IsSelected() ) + { + AddSelectedItem( item->m_ItemIndex, !ctrl && !shift ); + } + return; + } + else + { + ClearSelection(); + } + } + } + + BaseClass::OnMousePressed( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : state - +//----------------------------------------------------------------------------- +void TreeView::SetAllowMultipleSelections( bool state ) +{ + m_bAllowMultipleSelections = state; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool TreeView::IsMultipleSelectionAllowed() const +{ + return m_bAllowMultipleSelections; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : int +//----------------------------------------------------------------------------- +int TreeView::GetSelectedItemCount() const +{ + return m_SelectedItems.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void TreeView::ClearSelection() +{ + m_SelectedItems.RemoveAll(); + m_nMostRecentlySelectedItem = -1; + PostActionSignal( new KeyValues( "TreeViewItemSelectionCleared" ) ); +} + +void TreeView::RangeSelectItems( int endItem ) +{ + int startItem = m_nMostRecentlySelectedItem; + ClearSelection(); + m_nMostRecentlySelectedItem = startItem; + + if ( !m_NodeList.IsValidIndex( startItem ) ) + { + AddSelectedItem( endItem, false ); + return; + } + + Assert( m_NodeList.IsValidIndex( endItem ) ); + + if ( !m_pRootNode ) + { + return; + } + + CUtlVector< TreeNode * > list; + m_pRootNode->FindNodesInRange( list, startItem, endItem ); + + int c = list.Count(); + for ( int i = 0; i < c; ++i ) + { + TreeNode *item = list[ i ]; + AddSelectedItem( item->m_ItemIndex, false ); + } +} + +void TreeView::FindNodesInRange( int startItem, int endItem, CUtlVector< int >& itemIndices ) +{ + CUtlVector< TreeNode * > nodes; + m_pRootNode->FindNodesInRange( nodes, startItem, endItem ); + + int c = nodes.Count(); + for ( int i = 0; i < c; ++i ) + { + TreeNode *item = nodes[ i ]; + itemIndices.AddToTail( item->m_ItemIndex ); + } +} + +void TreeView::RemoveSelectedItem( int itemIndex ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return; + + TreeNode *sel = m_NodeList[ itemIndex ]; + Assert( sel ); + int slot = m_SelectedItems.Find( sel ); + if ( slot != m_SelectedItems.InvalidIndex() ) + { + m_SelectedItems.Remove( slot ); + PostActionSignal( new KeyValues( "TreeViewItemDeselected", "itemIndex", itemIndex ) ); + + m_nMostRecentlySelectedItem = itemIndex; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::AddSelectedItem( int itemIndex, bool clearCurrentSelection, bool requestFocus /* = true */, bool bMakeItemVisible /*= true*/ ) +{ + if ( clearCurrentSelection ) + { + ClearSelection(); + } + + // Assume it's bogus + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return; + + TreeNode *sel = m_NodeList[ itemIndex ]; + Assert( sel ); + if ( requestFocus ) + { + sel->RequestFocus(); + } + + // Item 0 is most recently selected!!! + int slot = m_SelectedItems.Find( sel ); + if ( slot == m_SelectedItems.InvalidIndex() ) + { + m_SelectedItems.AddToHead( sel ); + } + else if ( slot != 0 ) + { + m_SelectedItems.Remove( slot ); + m_SelectedItems.AddToHead( sel ); + } + + if ( bMakeItemVisible ) + { + MakeItemVisible( itemIndex ); + } + + PostActionSignal( new KeyValues( "TreeViewItemSelected", "itemIndex", itemIndex ) ); + InvalidateLayout(); + + if ( clearCurrentSelection ) + { + m_nMostRecentlySelectedItem = itemIndex; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : int +//----------------------------------------------------------------------------- +int TreeView::GetFirstSelectedItem() const +{ + if ( m_SelectedItems.Count() <= 0 ) + return -1; + return m_SelectedItems[ 0 ]->m_ItemIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : itemIndex - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool TreeView::IsItemSelected( int itemIndex ) +{ + // Assume it's bogus + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return false; + + TreeNode *sel = m_NodeList[ itemIndex ]; + return m_SelectedItems.Find( sel ) != m_SelectedItems.InvalidIndex(); +} + +void TreeView::SetLabelEditingAllowed( int itemIndex, bool state ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return; + + TreeNode *sel = m_NodeList[ itemIndex ]; + sel->SetLabelEditingAllowed( state ); +} + +void TreeView::StartEditingLabel( int itemIndex ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return; + + Assert( IsLabelEditingAllowed() ); + + TreeNode *sel = m_NodeList[ itemIndex ]; + Assert( sel->IsLabelEditingAllowed() ); + if ( !sel->IsLabelEditingAllowed() ) + return; + + sel->EditLabel(); +} + +int TreeView::GetPrevChildItemIndex( int itemIndex ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return -1; + TreeNode *sel = m_NodeList[ itemIndex ]; + TreeNode *parent = sel->GetParentNode(); + if ( !parent ) + return -1; + + return parent->GetPrevChildItemIndex( sel ); +} + +int TreeView::GetNextChildItemIndex( int itemIndex ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return -1; + TreeNode *sel = m_NodeList[ itemIndex ]; + TreeNode *parent = sel->GetParentNode(); + if ( !parent ) + return -1; + + return parent->GetNextChildItemIndex( sel ); +} + +bool TreeView::IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + // Derived classes should implement + return false; +} + +void TreeView::OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ +} + +bool TreeView::GetItemDropContextMenu( int itemIndex, Menu *menu, CUtlVector< KeyValues * >& msglist ) +{ + return false; +} + +HCursor TreeView::GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + return dc_arrow; +} + +void TreeView::RemoveChildrenOfNode( int itemIndex ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return; + + TreeNode *node = m_NodeList[ itemIndex ]; + node->RemoveChildren(); +} + +ScrollBar *TreeView::SetScrollBarExternal( bool vertical, Panel *newParent ) +{ + if ( vertical ) + { + m_bScrollbarExternal[ 0 ] = true; + m_pVertScrollBar->SetParent( newParent ); + return m_pVertScrollBar; + } + m_bScrollbarExternal[ 1 ] = true; + m_pHorzScrollBar->SetParent( newParent ); + return m_pHorzScrollBar; +} + +// if this is set, then clicking on one row and dragging will select a run or items, etc. +void TreeView::SetMultipleItemDragEnabled( bool state ) +{ + m_bMultipleItemDragging = state; +} + +bool TreeView::IsMultipleItemDragEnabled() const +{ + return m_bMultipleItemDragging; +} + +void TreeView::SelectAll() +{ + m_SelectedItems.RemoveAll(); + FOR_EACH_LL( m_NodeList, i ) + { + m_SelectedItems.AddToTail( m_NodeList[ i ] ); + } + + PostActionSignal( new KeyValues( "TreeViewItemSelected", "itemIndex", GetRootItemIndex() ) ); + InvalidateLayout(); +} diff --git a/mp/src/vgui2/vgui_controls/TreeViewListControl.cpp b/mp/src/vgui2/vgui_controls/TreeViewListControl.cpp index 81575496..30cb3b75 100644 --- a/mp/src/vgui2/vgui_controls/TreeViewListControl.cpp +++ b/mp/src/vgui2/vgui_controls/TreeViewListControl.cpp @@ -1,315 +1,315 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#include - -#define PROTECTED_THINGS_DISABLE - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -DECLARE_BUILD_FACTORY( CTreeViewListControl ); - -CTreeViewListControl::CTreeViewListControl( vgui::Panel *pParent, const char *pName ) : - BaseClass( pParent, pName ) -{ - m_pTree = NULL; - m_BorderColor.SetColor( 255, 255, 255, 255 ); - m_TitleBarFont = NULL; - m_TitleBarHeight = 20; - SetPostChildPaintEnabled( true ); -} - -void CTreeViewListControl::SetTreeView( vgui::TreeView *pTree ) -{ - m_pTree = pTree; - if ( m_pTree ) - { - m_pTree->SetParent( this ); - m_pTree->SetPaintBackgroundEnabled( false ); - } - - InvalidateLayout(); -} - -vgui::TreeView *CTreeViewListControl::GetTree() -{ - return m_pTree; -} - -int CTreeViewListControl::GetTitleBarHeight() -{ - return m_TitleBarHeight; -} - -void CTreeViewListControl::SetTitleBarInfo( vgui::HFont hFont, int titleBarHeight ) -{ - m_TitleBarFont = hFont; - m_TitleBarHeight = titleBarHeight; - - InvalidateLayout(); -} - -void CTreeViewListControl::SetBorderColor( Color clr ) -{ - m_BorderColor = clr; -} - -void CTreeViewListControl::SetNumColumns( int nColumns ) -{ - m_Columns.Purge(); - m_Columns.SetSize( nColumns ); - InvalidateLayout(); -} - -int CTreeViewListControl::GetNumColumns() const -{ - return m_Columns.Count(); -} - -void CTreeViewListControl::SetColumnInfo( int iColumn, const char *pTitle, int width, int ciFlags ) -{ - if ( iColumn < 0 || iColumn >= m_Columns.Count() ) - { - Assert( false ); - return; - } - CColumnInfo *pInfo = &m_Columns[iColumn]; - pInfo->m_Title = pTitle; - pInfo->m_Width = width; - pInfo->m_ciFlags = ciFlags; - - InvalidateLayout(); -} - -int CTreeViewListControl::GetNumRows() -{ - return m_Rows.Count(); -} - -int CTreeViewListControl::GetTreeItemAtRow( int iRow ) -{ - if ( iRow < 0 || iRow >= m_Rows.Count() ) - return -1; - else - return m_Rows[iRow]; -} - -void CTreeViewListControl::GetGridElementBounds( int iColumn, int iRow, int &left, int &top, int &right, int &bottom ) -{ - left = m_Columns[iColumn].m_Left; - right = m_Columns[iColumn].m_Right; - - // vgui doesn't seem to be drawing things exactly right. Like it you draw a line at (0,0) to (100,0), - // then a rectangle from (1,1) to (100,100), it'll overwrite the line at the top. - int treeTopBorder = 0; - IBorder *treeBorder = m_pTree->GetBorder(); - if ( treeBorder ) - { - int l, t, r, b; - treeBorder->GetInset( l, t, r, b ); - treeTopBorder = t; - } - if ( iRow == -1 ) - { - top = 1; - bottom = m_TitleBarHeight - 2; - } - else if ( m_pTree ) - { - int x, y; - m_pTree->GetPos( x, y ); - - top = treeTopBorder + m_TitleBarHeight + ( iRow * m_pTree->GetRowHeight() ); - bottom = top + m_pTree->GetRowHeight(); - } - else - { - left = top = right = bottom = 0; - } -} - -void CTreeViewListControl::PerformLayout() -{ - RecalculateRows(); - RecalculateColumns(); - - // Reposition the tree view. - if ( m_pTree && m_Columns.Count() > 0 ) - { - int left, top, right, bottom; - GetGridElementBounds( 0, -1, left, top, right, bottom ); - - top = m_TitleBarHeight; - - m_pTree->SetBounds( left, top, right - left, GetTall() - top ); - } - - BaseClass::PerformLayout(); -} - - -void CTreeViewListControl::RecalculateRows() -{ - m_Rows.Purge(); - - if ( !m_pTree || m_pTree->GetRootItemIndex() == -1 ) - return; - - int iRoot = m_pTree->GetRootItemIndex(); - RecalculateRows_R( iRoot ); -} - - -void CTreeViewListControl::RecalculateRows_R( int index ) -{ - m_Rows.AddToTail( index ); - if ( !m_pTree->IsItemExpanded( index ) ) - return; - - int nChildren = m_pTree->GetNumChildren( index ); - for ( int i=0; i < nChildren; i++ ) - { - int iChild = m_pTree->GetChild( index, i ); - RecalculateRows_R( iChild ); - } -} - -int CTreeViewListControl::GetScrollBarSize() -{ - return 0; -} - -void CTreeViewListControl::RecalculateColumns() -{ - int rightEdge = GetWide()-1 - GetScrollBarSize(); - - int x = 0; - int c = m_Columns.Count(); - for ( int i=0; i < c; i++ ) - { - m_Columns[i].m_Left = x + 1; - int cw = m_Columns[i].m_Width; - if ( i == c - 1 ) - { - cw = rightEdge - x - 2; - } - m_Columns[i].m_Right = x + cw - 2; - x += cw; - } -} - -void CTreeViewListControl::PostChildPaint() -{ - BaseClass::PostChildPaint(); - - // Draw the grid lines. - vgui::surface()->DrawSetColor( m_BorderColor ); - - if ( m_Columns.Count() <= 0 ) - return; - - // Draw the horizontal lines. - int endX = 0; - endX = m_Columns[m_Columns.Count()-1].m_Right + 1; - - int bottomY = 0; - for ( int i=0; i < m_Rows.Count(); i++ ) - { - int left, top, right, bottom; - GetGridElementBounds( 0, i, left, top, right, bottom ); - - bottomY = bottom; - vgui::surface()->DrawLine( 0, bottomY, endX, bottomY ); - } - - // Draw the vertical lines. - int curX = 0; - for ( int i=0; i < m_Columns.Count(); i++ ) - { - vgui::surface()->DrawLine( curX, 0, curX, bottomY ); - curX += m_Columns[i].m_Width; - } - vgui::surface()->DrawLine( curX, 0, curX, bottomY ); -} - -void CTreeViewListControl::Paint() -{ - BaseClass::Paint(); - - // Draw the title bars. - DrawTitleBars(); -} - -void CTreeViewListControl::DrawTitleBars() -{ - int rightEdge = GetWide(); - - for ( int i=0; i < m_Columns.Count(); i++ ) - { - int left, top, right, bottom; - GetGridElementBounds( i, -1, left, top, right, bottom ); - - if ( left >= rightEdge ) - continue; - - vgui::surface()->DrawSetColor( 0, 0, 0, 255 ); - vgui::surface()->DrawFilledRect( left, top, right, bottom ); - - vgui::surface()->DrawSetTextColor( 255, 255, 255, 255 ); - - const char *pTitleString = m_Columns[i].m_Title.String(); - - wchar_t unicodeString[1024]; - g_pVGuiLocalize->ConvertANSIToUnicode( pTitleString, unicodeString, sizeof(unicodeString) ); - - int wide, tall; - surface()->GetTextSize( m_TitleBarFont, unicodeString, wide, tall ); - - surface()->DrawSetTextFont( m_TitleBarFont ); - - if ( m_Columns[i].m_ciFlags & CTreeViewListControl::CI_HEADER_LEFTALIGN ) - { - int midy = (top+bottom)/2; - surface()->DrawSetTextPos( left, midy ); - } - else - { - int textRight = min( right, rightEdge ); - - int midx = (left+textRight)/2; - int midy = (top+bottom)/2; - - surface()->DrawSetTextPos( midx - wide/2, midy - tall/2 ); - } - - surface()->DrawPrintText( unicodeString, strlen( pTitleString ) ); - } -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include + +#define PROTECTED_THINGS_DISABLE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +DECLARE_BUILD_FACTORY( CTreeViewListControl ); + +CTreeViewListControl::CTreeViewListControl( vgui::Panel *pParent, const char *pName ) : + BaseClass( pParent, pName ) +{ + m_pTree = NULL; + m_BorderColor.SetColor( 255, 255, 255, 255 ); + m_TitleBarFont = NULL; + m_TitleBarHeight = 20; + SetPostChildPaintEnabled( true ); +} + +void CTreeViewListControl::SetTreeView( vgui::TreeView *pTree ) +{ + m_pTree = pTree; + if ( m_pTree ) + { + m_pTree->SetParent( this ); + m_pTree->SetPaintBackgroundEnabled( false ); + } + + InvalidateLayout(); +} + +vgui::TreeView *CTreeViewListControl::GetTree() +{ + return m_pTree; +} + +int CTreeViewListControl::GetTitleBarHeight() +{ + return m_TitleBarHeight; +} + +void CTreeViewListControl::SetTitleBarInfo( vgui::HFont hFont, int titleBarHeight ) +{ + m_TitleBarFont = hFont; + m_TitleBarHeight = titleBarHeight; + + InvalidateLayout(); +} + +void CTreeViewListControl::SetBorderColor( Color clr ) +{ + m_BorderColor = clr; +} + +void CTreeViewListControl::SetNumColumns( int nColumns ) +{ + m_Columns.Purge(); + m_Columns.SetSize( nColumns ); + InvalidateLayout(); +} + +int CTreeViewListControl::GetNumColumns() const +{ + return m_Columns.Count(); +} + +void CTreeViewListControl::SetColumnInfo( int iColumn, const char *pTitle, int width, int ciFlags ) +{ + if ( iColumn < 0 || iColumn >= m_Columns.Count() ) + { + Assert( false ); + return; + } + CColumnInfo *pInfo = &m_Columns[iColumn]; + pInfo->m_Title = pTitle; + pInfo->m_Width = width; + pInfo->m_ciFlags = ciFlags; + + InvalidateLayout(); +} + +int CTreeViewListControl::GetNumRows() +{ + return m_Rows.Count(); +} + +int CTreeViewListControl::GetTreeItemAtRow( int iRow ) +{ + if ( iRow < 0 || iRow >= m_Rows.Count() ) + return -1; + else + return m_Rows[iRow]; +} + +void CTreeViewListControl::GetGridElementBounds( int iColumn, int iRow, int &left, int &top, int &right, int &bottom ) +{ + left = m_Columns[iColumn].m_Left; + right = m_Columns[iColumn].m_Right; + + // vgui doesn't seem to be drawing things exactly right. Like it you draw a line at (0,0) to (100,0), + // then a rectangle from (1,1) to (100,100), it'll overwrite the line at the top. + int treeTopBorder = 0; + IBorder *treeBorder = m_pTree->GetBorder(); + if ( treeBorder ) + { + int l, t, r, b; + treeBorder->GetInset( l, t, r, b ); + treeTopBorder = t; + } + if ( iRow == -1 ) + { + top = 1; + bottom = m_TitleBarHeight - 2; + } + else if ( m_pTree ) + { + int x, y; + m_pTree->GetPos( x, y ); + + top = treeTopBorder + m_TitleBarHeight + ( iRow * m_pTree->GetRowHeight() ); + bottom = top + m_pTree->GetRowHeight(); + } + else + { + left = top = right = bottom = 0; + } +} + +void CTreeViewListControl::PerformLayout() +{ + RecalculateRows(); + RecalculateColumns(); + + // Reposition the tree view. + if ( m_pTree && m_Columns.Count() > 0 ) + { + int left, top, right, bottom; + GetGridElementBounds( 0, -1, left, top, right, bottom ); + + top = m_TitleBarHeight; + + m_pTree->SetBounds( left, top, right - left, GetTall() - top ); + } + + BaseClass::PerformLayout(); +} + + +void CTreeViewListControl::RecalculateRows() +{ + m_Rows.Purge(); + + if ( !m_pTree || m_pTree->GetRootItemIndex() == -1 ) + return; + + int iRoot = m_pTree->GetRootItemIndex(); + RecalculateRows_R( iRoot ); +} + + +void CTreeViewListControl::RecalculateRows_R( int index ) +{ + m_Rows.AddToTail( index ); + if ( !m_pTree->IsItemExpanded( index ) ) + return; + + int nChildren = m_pTree->GetNumChildren( index ); + for ( int i=0; i < nChildren; i++ ) + { + int iChild = m_pTree->GetChild( index, i ); + RecalculateRows_R( iChild ); + } +} + +int CTreeViewListControl::GetScrollBarSize() +{ + return 0; +} + +void CTreeViewListControl::RecalculateColumns() +{ + int rightEdge = GetWide()-1 - GetScrollBarSize(); + + int x = 0; + int c = m_Columns.Count(); + for ( int i=0; i < c; i++ ) + { + m_Columns[i].m_Left = x + 1; + int cw = m_Columns[i].m_Width; + if ( i == c - 1 ) + { + cw = rightEdge - x - 2; + } + m_Columns[i].m_Right = x + cw - 2; + x += cw; + } +} + +void CTreeViewListControl::PostChildPaint() +{ + BaseClass::PostChildPaint(); + + // Draw the grid lines. + vgui::surface()->DrawSetColor( m_BorderColor ); + + if ( m_Columns.Count() <= 0 ) + return; + + // Draw the horizontal lines. + int endX = 0; + endX = m_Columns[m_Columns.Count()-1].m_Right + 1; + + int bottomY = 0; + for ( int i=0; i < m_Rows.Count(); i++ ) + { + int left, top, right, bottom; + GetGridElementBounds( 0, i, left, top, right, bottom ); + + bottomY = bottom; + vgui::surface()->DrawLine( 0, bottomY, endX, bottomY ); + } + + // Draw the vertical lines. + int curX = 0; + for ( int i=0; i < m_Columns.Count(); i++ ) + { + vgui::surface()->DrawLine( curX, 0, curX, bottomY ); + curX += m_Columns[i].m_Width; + } + vgui::surface()->DrawLine( curX, 0, curX, bottomY ); +} + +void CTreeViewListControl::Paint() +{ + BaseClass::Paint(); + + // Draw the title bars. + DrawTitleBars(); +} + +void CTreeViewListControl::DrawTitleBars() +{ + int rightEdge = GetWide(); + + for ( int i=0; i < m_Columns.Count(); i++ ) + { + int left, top, right, bottom; + GetGridElementBounds( i, -1, left, top, right, bottom ); + + if ( left >= rightEdge ) + continue; + + vgui::surface()->DrawSetColor( 0, 0, 0, 255 ); + vgui::surface()->DrawFilledRect( left, top, right, bottom ); + + vgui::surface()->DrawSetTextColor( 255, 255, 255, 255 ); + + const char *pTitleString = m_Columns[i].m_Title.String(); + + wchar_t unicodeString[1024]; + g_pVGuiLocalize->ConvertANSIToUnicode( pTitleString, unicodeString, sizeof(unicodeString) ); + + int wide, tall; + surface()->GetTextSize( m_TitleBarFont, unicodeString, wide, tall ); + + surface()->DrawSetTextFont( m_TitleBarFont ); + + if ( m_Columns[i].m_ciFlags & CTreeViewListControl::CI_HEADER_LEFTALIGN ) + { + int midy = (top+bottom)/2; + surface()->DrawSetTextPos( left, midy ); + } + else + { + int textRight = min( right, rightEdge ); + + int midx = (left+textRight)/2; + int midy = (top+bottom)/2; + + surface()->DrawSetTextPos( midx - wide/2, midy - tall/2 ); + } + + surface()->DrawPrintText( unicodeString, strlen( pTitleString ) ); + } +} + diff --git a/mp/src/vgui2/vgui_controls/URLLabel.cpp b/mp/src/vgui2/vgui_controls/URLLabel.cpp index 349b2e71..456f0dbe 100644 --- a/mp/src/vgui2/vgui_controls/URLLabel.cpp +++ b/mp/src/vgui2/vgui_controls/URLLabel.cpp @@ -1,158 +1,158 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#include "vgui/ISurface.h" -#include "vgui/ISystem.h" -#include "vgui/MouseCode.h" -#include "vgui/Cursor.h" -#include "KeyValues.h" - -#include "vgui_controls/URLLabel.h" -#include "vgui_controls/TextImage.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -using namespace vgui; - -vgui::Panel *URLLabel_Factory() -{ - return new URLLabel(NULL, NULL, "URLLabel", NULL); -} - -DECLARE_BUILD_FACTORY_CUSTOM( URLLabel, URLLabel_Factory ); -//----------------------------------------------------------------------------- -// Purpose: constructor -//----------------------------------------------------------------------------- -URLLabel::URLLabel(Panel *parent, const char *panelName, const char *text, const char *pszURL) : Label(parent, panelName, text) -{ - m_pszURL = NULL; - m_bUnderline = false; - m_iURLSize = 0; - if (pszURL && strlen(pszURL) > 0) - { - SetURL(pszURL); - } -} - -//----------------------------------------------------------------------------- -// Purpose: unicode constructor -//----------------------------------------------------------------------------- -URLLabel::URLLabel(Panel *parent, const char *panelName, const wchar_t *wszText, const char *pszURL) : Label(parent, panelName, wszText) -{ - m_pszURL = NULL; - m_bUnderline = false; - m_iURLSize = 0; - if (pszURL && strlen(pszURL) > 0) - { - SetURL(pszURL); - } -} - -//----------------------------------------------------------------------------- -// Purpose: destructor -//----------------------------------------------------------------------------- -URLLabel::~URLLabel() -{ - if (m_pszURL) - delete [] m_pszURL; -} - -//----------------------------------------------------------------------------- -// Purpose: sets the URL -//----------------------------------------------------------------------------- -void URLLabel::SetURL(const char *pszURL) -{ - int iNewURLSize = strlen(pszURL); - if (iNewURLSize > m_iURLSize || !m_pszURL) - { - delete [] m_pszURL; - m_pszURL = new char [iNewURLSize + 1]; - } - strcpy(m_pszURL, pszURL); - m_iURLSize = iNewURLSize; -} - -//----------------------------------------------------------------------------- -// Purpose: If we were left clicked on, launch the URL -//----------------------------------------------------------------------------- -void URLLabel::OnMousePressed(MouseCode code) -{ - if (code == MOUSE_LEFT) - { - if (m_pszURL) - { - system()->ShellExecute("open", m_pszURL); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Applies resouce settings -//----------------------------------------------------------------------------- -void URLLabel::ApplySettings(KeyValues *inResourceData) -{ - BaseClass::ApplySettings(inResourceData); - - const char *pszURL = inResourceData->GetString("URLText", NULL); - if (pszURL) - { - if (pszURL[0] == '#') - { - // it's a localized url, look it up - const wchar_t *ws = g_pVGuiLocalize->Find(pszURL + 1); - if (ws) - { - char localizedUrl[512]; - g_pVGuiLocalize->ConvertUnicodeToANSI(ws, localizedUrl, sizeof(localizedUrl)); - SetURL(localizedUrl); - } - } - else - { - SetURL(pszURL); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: saves them to disk -//----------------------------------------------------------------------------- -void URLLabel::GetSettings( KeyValues *outResourceData ) -{ - BaseClass::GetSettings(outResourceData); - - if (m_pszURL) - { - outResourceData->SetString("URLText", m_pszURL); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Returns a description of the label string -//----------------------------------------------------------------------------- -const char *URLLabel::GetDescription( void ) -{ - static char buf[1024]; - _snprintf(buf, sizeof(buf), "%s, string URLText", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: scheme settings -//----------------------------------------------------------------------------- -void URLLabel::ApplySchemeSettings(IScheme *pScheme) -{ - // set our font to be underlined by default - // the Label::ApplySchemeSettings() will override it if override set in scheme file - SetFont( pScheme->GetFont( "DefaultUnderline", IsProportional() ) ); - BaseClass::ApplySchemeSettings(pScheme); - SetCursor(dc_hand); -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include "vgui/ISurface.h" +#include "vgui/ISystem.h" +#include "vgui/MouseCode.h" +#include "vgui/Cursor.h" +#include "KeyValues.h" + +#include "vgui_controls/URLLabel.h" +#include "vgui_controls/TextImage.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +vgui::Panel *URLLabel_Factory() +{ + return new URLLabel(NULL, NULL, "URLLabel", NULL); +} + +DECLARE_BUILD_FACTORY_CUSTOM( URLLabel, URLLabel_Factory ); +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +URLLabel::URLLabel(Panel *parent, const char *panelName, const char *text, const char *pszURL) : Label(parent, panelName, text) +{ + m_pszURL = NULL; + m_bUnderline = false; + m_iURLSize = 0; + if (pszURL && strlen(pszURL) > 0) + { + SetURL(pszURL); + } +} + +//----------------------------------------------------------------------------- +// Purpose: unicode constructor +//----------------------------------------------------------------------------- +URLLabel::URLLabel(Panel *parent, const char *panelName, const wchar_t *wszText, const char *pszURL) : Label(parent, panelName, wszText) +{ + m_pszURL = NULL; + m_bUnderline = false; + m_iURLSize = 0; + if (pszURL && strlen(pszURL) > 0) + { + SetURL(pszURL); + } +} + +//----------------------------------------------------------------------------- +// Purpose: destructor +//----------------------------------------------------------------------------- +URLLabel::~URLLabel() +{ + if (m_pszURL) + delete [] m_pszURL; +} + +//----------------------------------------------------------------------------- +// Purpose: sets the URL +//----------------------------------------------------------------------------- +void URLLabel::SetURL(const char *pszURL) +{ + int iNewURLSize = strlen(pszURL); + if (iNewURLSize > m_iURLSize || !m_pszURL) + { + delete [] m_pszURL; + m_pszURL = new char [iNewURLSize + 1]; + } + strcpy(m_pszURL, pszURL); + m_iURLSize = iNewURLSize; +} + +//----------------------------------------------------------------------------- +// Purpose: If we were left clicked on, launch the URL +//----------------------------------------------------------------------------- +void URLLabel::OnMousePressed(MouseCode code) +{ + if (code == MOUSE_LEFT) + { + if (m_pszURL) + { + system()->ShellExecute("open", m_pszURL); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Applies resouce settings +//----------------------------------------------------------------------------- +void URLLabel::ApplySettings(KeyValues *inResourceData) +{ + BaseClass::ApplySettings(inResourceData); + + const char *pszURL = inResourceData->GetString("URLText", NULL); + if (pszURL) + { + if (pszURL[0] == '#') + { + // it's a localized url, look it up + const wchar_t *ws = g_pVGuiLocalize->Find(pszURL + 1); + if (ws) + { + char localizedUrl[512]; + g_pVGuiLocalize->ConvertUnicodeToANSI(ws, localizedUrl, sizeof(localizedUrl)); + SetURL(localizedUrl); + } + } + else + { + SetURL(pszURL); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: saves them to disk +//----------------------------------------------------------------------------- +void URLLabel::GetSettings( KeyValues *outResourceData ) +{ + BaseClass::GetSettings(outResourceData); + + if (m_pszURL) + { + outResourceData->SetString("URLText", m_pszURL); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a description of the label string +//----------------------------------------------------------------------------- +const char *URLLabel::GetDescription( void ) +{ + static char buf[1024]; + _snprintf(buf, sizeof(buf), "%s, string URLText", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: scheme settings +//----------------------------------------------------------------------------- +void URLLabel::ApplySchemeSettings(IScheme *pScheme) +{ + // set our font to be underlined by default + // the Label::ApplySchemeSettings() will override it if override set in scheme file + SetFont( pScheme->GetFont( "DefaultUnderline", IsProportional() ) ); + BaseClass::ApplySchemeSettings(pScheme); + SetCursor(dc_hand); +} + diff --git a/mp/src/vgui2/vgui_controls/WizardPanel.cpp b/mp/src/vgui2/vgui_controls/WizardPanel.cpp index 9ce5f802..87b78a59 100644 --- a/mp/src/vgui2/vgui_controls/WizardPanel.cpp +++ b/mp/src/vgui2/vgui_controls/WizardPanel.cpp @@ -1,720 +1,720 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include - -#include -#include -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -WizardPanel::WizardPanel(Panel *parent, const char *panelName) : Frame(parent, panelName) -{ - _currentSubPanel = NULL; - _currentData = new KeyValues("WizardData"); - _showButtons = true; - - - SetSizeable(false); - - CreateButtons(); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -WizardPanel::~WizardPanel() -{ - if (_currentData) - { - _currentData->deleteThis(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::PerformLayout() -{ - BaseClass::PerformLayout(); - - // resize the sub panel to fit in the Client area - int x, y, wide, tall; - GetClientArea(x, y, wide, tall); - - if (_currentSubPanel && _currentSubPanel->isNonWizardPanel()) - { - // just have the subpanel cover the full size - _currentSubPanel->SetBounds(x, y, wide, tall); - _cancelButton->SetVisible(false); - _prevButton->SetVisible(false); - _nextButton->SetVisible(false); - _finishButton->SetVisible(false); - } - else - { - // make room for the buttons at bottom - if (_currentSubPanel) - { - if( _showButtons ) - { - _currentSubPanel->SetBounds(x, y, wide, tall - 35); - } - else - { - _currentSubPanel->SetBounds(x, y, wide, tall); - } - } - - // align the buttons to the right hand side - GetSize(wide, tall); - - - int bwide, btall; - _cancelButton->GetSize(bwide, btall); - - x = wide - (20 + bwide); - y = tall - (12 + btall); - - _cancelButton->SetPos(x, y); - x -= (20 + bwide); - - // only display one of the next or finish buttons (and only if both are visible) - if ( _showButtons ) - { - if (_finishButton->IsEnabled() ) - { - _nextButton->SetVisible(false); - _finishButton->SetVisible(true); - _finishButton->SetPos(x, y); - } - else - { - _nextButton->SetVisible(true); - _finishButton->SetVisible(false); - _nextButton->SetPos(x, y); - } - } - - x -= (1 + bwide); - _prevButton->SetPos(x, y); - - ResetDefaultButton(); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: if we don't show buttons then let the sub panel occupy the whole screen -//----------------------------------------------------------------------------- -void WizardPanel::GetClientArea(int &x, int &y, int &wide, int &tall) -{ - if( _showButtons ) - { - BaseClass::GetClientArea( x, y, wide, tall ); - } - else - { - x = 0; - y = 0; - GetSize( wide, tall ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::Run(WizardSubPanel *startPanel) -{ - // skip over sub panels if they don't want to be displayed - startPanel = FindNextValidSubPanel(startPanel); - - // show it - ActivateNextSubPanel(startPanel); - - // make sure we're set up and Run the first panel - Activate(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::ActivateBuildMode() -{ - // no subpanel, no build mode - if (!_currentSubPanel) - return; - - _currentSubPanel->ActivateBuildMode(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::ResetDefaultButton() -{ - // work out which is the default button - if (_nextButton->IsEnabled()) - { - _nextButton->SetAsDefaultButton(true); - } - else if (_finishButton->IsEnabled()) - { - _finishButton->SetAsDefaultButton(true); - } - else if (_prevButton->IsEnabled()) - { - _prevButton->SetAsDefaultButton(true); - } - /* Don't ever set the cancel button as the default, as it is too easy for users to quit the wizard without realizing - else if (_cancelButton->IsEnabled()) - { - _cancelButton->SetAsDefaultButton(true); - } - */ - - // reset them all (this may not be necessary) - _nextButton->InvalidateLayout(); - _prevButton->InvalidateLayout(); - _cancelButton->InvalidateLayout(); - _finishButton->InvalidateLayout(); - - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::ResetKeyFocus() -{ - // set the focus on the default - FocusNavGroup &navGroup = GetFocusNavGroup(); - Panel *def = navGroup.GetDefaultPanel(); - if (def) - { - if (def->IsEnabled() && def->IsVisible()) - { - def->RequestFocus(); - } - else - { - def->RequestFocusNext(); - } - } - - ResetDefaultButton(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::CreateButtons() -{ - _prevButton = new Button(this, "PrevButton", ""); - _nextButton = new Button(this, "NextButton", ""); - _cancelButton = new Button(this, "CancelButton", ""); - _finishButton = new Button(this, "FinishButton", ""); - - _prevButton->SetCommand(new KeyValues("PrevButton")); - _nextButton->SetCommand(new KeyValues("NextButton")); - _cancelButton->SetCommand(new KeyValues("CancelButton")); - _finishButton->SetCommand(new KeyValues("FinishButton")); - - SetNextButtonText(NULL); - SetPrevButtonText(NULL); - SetFinishButtonText(NULL); - SetCancelButtonText(NULL); - - _prevButton->SetSize(82, 24); - _nextButton->SetSize(82, 24); - _cancelButton->SetSize(82, 24); - _finishButton->SetSize(82, 24); -} - -//----------------------------------------------------------------------------- -// Purpose: clears all previous history -//----------------------------------------------------------------------------- -void WizardPanel::ResetHistory() -{ - _subPanelStack.RemoveAll(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::ActivateNextSubPanel(WizardSubPanel *subPanel) -{ - // get rid of previous panel - WizardSubPanel *prevPanel = _currentSubPanel; - if (prevPanel && prevPanel->ShouldDisplayPanel()) - { - // hide - prevPanel->SetVisible(false); - - // push onto history stack - _subPanelStack.AddElement(_currentSubPanel); - } - - // reenable all buttons, returning them to their default state - _prevButton->SetEnabled(true); - _nextButton->SetEnabled(true); - _cancelButton->SetEnabled(true); - _finishButton->SetEnabled(true); - if ( _showButtons ) - { - _prevButton->SetVisible(true); - _cancelButton->SetVisible(true); - } - - // set up new subpanel - _currentSubPanel = subPanel; - _currentSubPanel->SetParent(this); - _currentSubPanel->SetVisible(true); - - _currentSubPanel->SetWizardPanel(this); - _currentSubPanel->OnDisplayAsNext(); - _currentSubPanel->OnDisplay(); - _currentSubPanel->InvalidateLayout(false); - - SETUP_PANEL( _currentSubPanel ); - int wide, tall; - if ( _currentSubPanel->GetDesiredSize(wide, tall) ) - { - SetSize(wide, tall); - } - - if (!prevPanel) - { - // no previous panel, so disable the back button - _prevButton->SetEnabled(false); - } - - _currentSubPanel->RequestFocus(); - - RecalculateTabOrdering(); - InvalidateLayout(false); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Pops the last panel off the stack and runs it -//----------------------------------------------------------------------------- -void WizardPanel::ActivatePrevSubPanel() -{ - _currentSubPanel->SetVisible(false); - - WizardSubPanel *prevPanel = NULL; - if (_subPanelStack.GetCount()) - { - // check to see if we need to jump back to a previous sub panel - WizardSubPanel *searchPanel = _currentSubPanel->GetPrevSubPanel(); - if (searchPanel && _subPanelStack.HasElement(searchPanel)) - { - // keep poping the stack till we find it - while (_subPanelStack.GetCount() && prevPanel != searchPanel) - { - prevPanel = _subPanelStack[_subPanelStack.GetCount() - 1]; - _subPanelStack.RemoveElementAt(_subPanelStack.GetCount() - 1); - } - } - else - { - // just get the last one - prevPanel = _subPanelStack[_subPanelStack.GetCount() - 1]; - _subPanelStack.RemoveElementAt(_subPanelStack.GetCount() - 1); - } - } - - if (!prevPanel) - { - ivgui()->DPrintf2("Error: WizardPanel::ActivatePrevSubPanel(): no previous panel to go back to\n"); - return; - } - - // hide old panel - _currentSubPanel->SetVisible(false); - - // reenable all buttons, returning them to their default state - _prevButton->SetEnabled(true); - _nextButton->SetEnabled(true); - _cancelButton->SetEnabled(true); - _finishButton->SetEnabled(true); - - // Activate new panel - _currentSubPanel = prevPanel; - _currentSubPanel->RequestFocus(); - _currentSubPanel->SetWizardPanel(this); - _currentSubPanel->OnDisplayAsPrev(); - _currentSubPanel->OnDisplay(); - _currentSubPanel->InvalidateLayout(false); - - SETUP_PANEL( _currentSubPanel ); - int wide, tall; - if ( _currentSubPanel->GetDesiredSize(wide, tall) ) - { - SetSize(wide, tall); - } - - // show the previous panel, but don't Activate it (since it should show just what it was previously) - _currentSubPanel->SetVisible(true); - - if (!_subPanelStack.GetCount()) - { - // no previous panel, so disable the back button - _prevButton->SetEnabled(false); - } - - RecalculateTabOrdering(); - InvalidateLayout(false); - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets up the new tab ordering -//----------------------------------------------------------------------------- -void WizardPanel::RecalculateTabOrdering() -{ - if (_currentSubPanel) - { - _currentSubPanel->SetTabPosition(1); - } - _prevButton->SetTabPosition(2); - _nextButton->SetTabPosition(3); - _finishButton->SetTabPosition(4); - _cancelButton->SetTabPosition(5); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::SetNextButtonEnabled(bool state) -{ - if (_nextButton->IsEnabled() != state) - { - _nextButton->SetEnabled(state); - InvalidateLayout(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::SetPrevButtonEnabled(bool state) -{ - if (_prevButton->IsEnabled() != state) - { - _prevButton->SetEnabled(state); - InvalidateLayout(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::SetFinishButtonEnabled(bool state) -{ - if (_finishButton->IsEnabled() != state) - { - _finishButton->SetEnabled(state); - InvalidateLayout(false); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::SetCancelButtonEnabled(bool state) -{ - if (_cancelButton->IsEnabled() != state) - { - _cancelButton->SetEnabled(state); - InvalidateLayout(false); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::SetNextButtonVisible(bool state) -{ - _nextButton->SetVisible(state); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::SetPrevButtonVisible(bool state) -{ - _prevButton->SetVisible(state); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::SetFinishButtonVisible(bool state) -{ - _finishButton->SetVisible(state); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::SetCancelButtonVisible(bool state) -{ - _cancelButton->SetVisible(state); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::SetNextButtonText(const char *text) -{ - if (text) - { - _nextButton->SetText(text); - } - else - { - _nextButton->SetText("#WizardPanel_Next"); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::SetPrevButtonText(const char *text) -{ - if (text) - { - _prevButton->SetText(text); - } - else - { - _prevButton->SetText("#WizardPanel_Back"); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::SetFinishButtonText(const char *text) -{ - if (text) - { - _finishButton->SetText(text); - } - else - { - _finishButton->SetText("#WizardPanel_Finish"); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::SetCancelButtonText(const char *text) -{ - if (text) - { - _cancelButton->SetText(text); - } - else - { - _cancelButton->SetText("#WizardPanel_Cancel"); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Finds the next panel that wants to be shown -//----------------------------------------------------------------------------- -WizardSubPanel *WizardPanel::FindNextValidSubPanel(WizardSubPanel *currentPanel) -{ - // skip over sub panels if they don't want to be displayed - while (currentPanel) - { - currentPanel->SetWizardPanel(this); - if (currentPanel->ShouldDisplayPanel()) - break; - - // ok the panel wants to be skipped, so skip ahead - currentPanel = currentPanel->GetNextSubPanel(); - } - - return currentPanel; -} - -//----------------------------------------------------------------------------- -// Purpose: Advances to the next panel -//----------------------------------------------------------------------------- -void WizardPanel::OnNextButton() -{ - if (_currentSubPanel) - { - bool shouldAdvance = _currentSubPanel->OnNextButton(); - if (shouldAdvance) - { - WizardSubPanel *nextPanel = FindNextValidSubPanel(_currentSubPanel->GetNextSubPanel()); - - if (nextPanel) - { - KeyValues *kv = new KeyValues("ActivateNextSubPanel"); - kv->SetPtr("panel", nextPanel); - ivgui()->PostMessage(GetVPanel(), kv, GetVPanel()); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Retreats to the previous panel -//----------------------------------------------------------------------------- -void WizardPanel::OnPrevButton() -{ - bool shouldRetreat = true; - if (_currentSubPanel) - { - shouldRetreat = _currentSubPanel->OnPrevButton(); - } - - if (shouldRetreat) - { - ActivatePrevSubPanel(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::OnFinishButton() -{ - if (_currentSubPanel && _currentSubPanel->OnFinishButton()) - { - // hide ourselves away - BaseClass::OnClose(); - - // automatically delete ourselves if marked to do so - if (IsAutoDeleteSet()) - { - MarkForDeletion(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardPanel::OnCancelButton() -{ - if (_currentSubPanel && _currentSubPanel->OnCancelButton()) - { - // hide ourselves away - BaseClass::OnClose(); - if (IsAutoDeleteSet()) - { - MarkForDeletion(); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: command handler for catching escape key presses -//----------------------------------------------------------------------------- -void WizardPanel::OnCommand(const char *command) -{ - if (!stricmp(command, "Cancel")) - { - if (_cancelButton->IsEnabled()) - { - _cancelButton->DoClick(); - } - } - else - { - BaseClass::OnCommand(command); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Maps close button to cancel button -//----------------------------------------------------------------------------- -void WizardPanel::OnClose() -{ - if (_cancelButton->IsEnabled()) - { - _cancelButton->DoClick(); - } - else if (_finishButton->IsEnabled()) - { - _finishButton->DoClick(); - } - - // don't chain back -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -KeyValues *WizardPanel::GetWizardData() -{ - return _currentData; -} - - -//----------------------------------------------------------------------------- -// Purpose: whether to show the next,prev,finish and cancel buttons -//----------------------------------------------------------------------------- -void WizardPanel::ShowButtons(bool state) -{ - _showButtons = state; // hide the wizard panel buttons - SetNextButtonVisible( state ); - SetPrevButtonVisible( state ); - SetFinishButtonVisible( state ); - SetCancelButtonVisible( state ); -} - -//----------------------------------------------------------------------------- -// Purpose: filters close buttons -//----------------------------------------------------------------------------- -void WizardPanel::OnCloseFrameButtonPressed() -{ - // only allow close if the cancel button is enabled - if (_cancelButton->IsEnabled()) - { - BaseClass::OnCloseFrameButtonPressed(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: returns a page by name -//----------------------------------------------------------------------------- -WizardSubPanel *WizardPanel::GetSubPanelByName(const char *pageName) -{ - return dynamic_cast(FindChildByName(pageName)); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include + +#include +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +WizardPanel::WizardPanel(Panel *parent, const char *panelName) : Frame(parent, panelName) +{ + _currentSubPanel = NULL; + _currentData = new KeyValues("WizardData"); + _showButtons = true; + + + SetSizeable(false); + + CreateButtons(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +WizardPanel::~WizardPanel() +{ + if (_currentData) + { + _currentData->deleteThis(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + // resize the sub panel to fit in the Client area + int x, y, wide, tall; + GetClientArea(x, y, wide, tall); + + if (_currentSubPanel && _currentSubPanel->isNonWizardPanel()) + { + // just have the subpanel cover the full size + _currentSubPanel->SetBounds(x, y, wide, tall); + _cancelButton->SetVisible(false); + _prevButton->SetVisible(false); + _nextButton->SetVisible(false); + _finishButton->SetVisible(false); + } + else + { + // make room for the buttons at bottom + if (_currentSubPanel) + { + if( _showButtons ) + { + _currentSubPanel->SetBounds(x, y, wide, tall - 35); + } + else + { + _currentSubPanel->SetBounds(x, y, wide, tall); + } + } + + // align the buttons to the right hand side + GetSize(wide, tall); + + + int bwide, btall; + _cancelButton->GetSize(bwide, btall); + + x = wide - (20 + bwide); + y = tall - (12 + btall); + + _cancelButton->SetPos(x, y); + x -= (20 + bwide); + + // only display one of the next or finish buttons (and only if both are visible) + if ( _showButtons ) + { + if (_finishButton->IsEnabled() ) + { + _nextButton->SetVisible(false); + _finishButton->SetVisible(true); + _finishButton->SetPos(x, y); + } + else + { + _nextButton->SetVisible(true); + _finishButton->SetVisible(false); + _nextButton->SetPos(x, y); + } + } + + x -= (1 + bwide); + _prevButton->SetPos(x, y); + + ResetDefaultButton(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: if we don't show buttons then let the sub panel occupy the whole screen +//----------------------------------------------------------------------------- +void WizardPanel::GetClientArea(int &x, int &y, int &wide, int &tall) +{ + if( _showButtons ) + { + BaseClass::GetClientArea( x, y, wide, tall ); + } + else + { + x = 0; + y = 0; + GetSize( wide, tall ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::Run(WizardSubPanel *startPanel) +{ + // skip over sub panels if they don't want to be displayed + startPanel = FindNextValidSubPanel(startPanel); + + // show it + ActivateNextSubPanel(startPanel); + + // make sure we're set up and Run the first panel + Activate(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::ActivateBuildMode() +{ + // no subpanel, no build mode + if (!_currentSubPanel) + return; + + _currentSubPanel->ActivateBuildMode(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::ResetDefaultButton() +{ + // work out which is the default button + if (_nextButton->IsEnabled()) + { + _nextButton->SetAsDefaultButton(true); + } + else if (_finishButton->IsEnabled()) + { + _finishButton->SetAsDefaultButton(true); + } + else if (_prevButton->IsEnabled()) + { + _prevButton->SetAsDefaultButton(true); + } + /* Don't ever set the cancel button as the default, as it is too easy for users to quit the wizard without realizing + else if (_cancelButton->IsEnabled()) + { + _cancelButton->SetAsDefaultButton(true); + } + */ + + // reset them all (this may not be necessary) + _nextButton->InvalidateLayout(); + _prevButton->InvalidateLayout(); + _cancelButton->InvalidateLayout(); + _finishButton->InvalidateLayout(); + + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::ResetKeyFocus() +{ + // set the focus on the default + FocusNavGroup &navGroup = GetFocusNavGroup(); + Panel *def = navGroup.GetDefaultPanel(); + if (def) + { + if (def->IsEnabled() && def->IsVisible()) + { + def->RequestFocus(); + } + else + { + def->RequestFocusNext(); + } + } + + ResetDefaultButton(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::CreateButtons() +{ + _prevButton = new Button(this, "PrevButton", ""); + _nextButton = new Button(this, "NextButton", ""); + _cancelButton = new Button(this, "CancelButton", ""); + _finishButton = new Button(this, "FinishButton", ""); + + _prevButton->SetCommand(new KeyValues("PrevButton")); + _nextButton->SetCommand(new KeyValues("NextButton")); + _cancelButton->SetCommand(new KeyValues("CancelButton")); + _finishButton->SetCommand(new KeyValues("FinishButton")); + + SetNextButtonText(NULL); + SetPrevButtonText(NULL); + SetFinishButtonText(NULL); + SetCancelButtonText(NULL); + + _prevButton->SetSize(82, 24); + _nextButton->SetSize(82, 24); + _cancelButton->SetSize(82, 24); + _finishButton->SetSize(82, 24); +} + +//----------------------------------------------------------------------------- +// Purpose: clears all previous history +//----------------------------------------------------------------------------- +void WizardPanel::ResetHistory() +{ + _subPanelStack.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::ActivateNextSubPanel(WizardSubPanel *subPanel) +{ + // get rid of previous panel + WizardSubPanel *prevPanel = _currentSubPanel; + if (prevPanel && prevPanel->ShouldDisplayPanel()) + { + // hide + prevPanel->SetVisible(false); + + // push onto history stack + _subPanelStack.AddElement(_currentSubPanel); + } + + // reenable all buttons, returning them to their default state + _prevButton->SetEnabled(true); + _nextButton->SetEnabled(true); + _cancelButton->SetEnabled(true); + _finishButton->SetEnabled(true); + if ( _showButtons ) + { + _prevButton->SetVisible(true); + _cancelButton->SetVisible(true); + } + + // set up new subpanel + _currentSubPanel = subPanel; + _currentSubPanel->SetParent(this); + _currentSubPanel->SetVisible(true); + + _currentSubPanel->SetWizardPanel(this); + _currentSubPanel->OnDisplayAsNext(); + _currentSubPanel->OnDisplay(); + _currentSubPanel->InvalidateLayout(false); + + SETUP_PANEL( _currentSubPanel ); + int wide, tall; + if ( _currentSubPanel->GetDesiredSize(wide, tall) ) + { + SetSize(wide, tall); + } + + if (!prevPanel) + { + // no previous panel, so disable the back button + _prevButton->SetEnabled(false); + } + + _currentSubPanel->RequestFocus(); + + RecalculateTabOrdering(); + InvalidateLayout(false); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Pops the last panel off the stack and runs it +//----------------------------------------------------------------------------- +void WizardPanel::ActivatePrevSubPanel() +{ + _currentSubPanel->SetVisible(false); + + WizardSubPanel *prevPanel = NULL; + if (_subPanelStack.GetCount()) + { + // check to see if we need to jump back to a previous sub panel + WizardSubPanel *searchPanel = _currentSubPanel->GetPrevSubPanel(); + if (searchPanel && _subPanelStack.HasElement(searchPanel)) + { + // keep poping the stack till we find it + while (_subPanelStack.GetCount() && prevPanel != searchPanel) + { + prevPanel = _subPanelStack[_subPanelStack.GetCount() - 1]; + _subPanelStack.RemoveElementAt(_subPanelStack.GetCount() - 1); + } + } + else + { + // just get the last one + prevPanel = _subPanelStack[_subPanelStack.GetCount() - 1]; + _subPanelStack.RemoveElementAt(_subPanelStack.GetCount() - 1); + } + } + + if (!prevPanel) + { + ivgui()->DPrintf2("Error: WizardPanel::ActivatePrevSubPanel(): no previous panel to go back to\n"); + return; + } + + // hide old panel + _currentSubPanel->SetVisible(false); + + // reenable all buttons, returning them to their default state + _prevButton->SetEnabled(true); + _nextButton->SetEnabled(true); + _cancelButton->SetEnabled(true); + _finishButton->SetEnabled(true); + + // Activate new panel + _currentSubPanel = prevPanel; + _currentSubPanel->RequestFocus(); + _currentSubPanel->SetWizardPanel(this); + _currentSubPanel->OnDisplayAsPrev(); + _currentSubPanel->OnDisplay(); + _currentSubPanel->InvalidateLayout(false); + + SETUP_PANEL( _currentSubPanel ); + int wide, tall; + if ( _currentSubPanel->GetDesiredSize(wide, tall) ) + { + SetSize(wide, tall); + } + + // show the previous panel, but don't Activate it (since it should show just what it was previously) + _currentSubPanel->SetVisible(true); + + if (!_subPanelStack.GetCount()) + { + // no previous panel, so disable the back button + _prevButton->SetEnabled(false); + } + + RecalculateTabOrdering(); + InvalidateLayout(false); + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets up the new tab ordering +//----------------------------------------------------------------------------- +void WizardPanel::RecalculateTabOrdering() +{ + if (_currentSubPanel) + { + _currentSubPanel->SetTabPosition(1); + } + _prevButton->SetTabPosition(2); + _nextButton->SetTabPosition(3); + _finishButton->SetTabPosition(4); + _cancelButton->SetTabPosition(5); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::SetNextButtonEnabled(bool state) +{ + if (_nextButton->IsEnabled() != state) + { + _nextButton->SetEnabled(state); + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::SetPrevButtonEnabled(bool state) +{ + if (_prevButton->IsEnabled() != state) + { + _prevButton->SetEnabled(state); + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::SetFinishButtonEnabled(bool state) +{ + if (_finishButton->IsEnabled() != state) + { + _finishButton->SetEnabled(state); + InvalidateLayout(false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::SetCancelButtonEnabled(bool state) +{ + if (_cancelButton->IsEnabled() != state) + { + _cancelButton->SetEnabled(state); + InvalidateLayout(false); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::SetNextButtonVisible(bool state) +{ + _nextButton->SetVisible(state); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::SetPrevButtonVisible(bool state) +{ + _prevButton->SetVisible(state); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::SetFinishButtonVisible(bool state) +{ + _finishButton->SetVisible(state); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::SetCancelButtonVisible(bool state) +{ + _cancelButton->SetVisible(state); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::SetNextButtonText(const char *text) +{ + if (text) + { + _nextButton->SetText(text); + } + else + { + _nextButton->SetText("#WizardPanel_Next"); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::SetPrevButtonText(const char *text) +{ + if (text) + { + _prevButton->SetText(text); + } + else + { + _prevButton->SetText("#WizardPanel_Back"); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::SetFinishButtonText(const char *text) +{ + if (text) + { + _finishButton->SetText(text); + } + else + { + _finishButton->SetText("#WizardPanel_Finish"); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::SetCancelButtonText(const char *text) +{ + if (text) + { + _cancelButton->SetText(text); + } + else + { + _cancelButton->SetText("#WizardPanel_Cancel"); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Finds the next panel that wants to be shown +//----------------------------------------------------------------------------- +WizardSubPanel *WizardPanel::FindNextValidSubPanel(WizardSubPanel *currentPanel) +{ + // skip over sub panels if they don't want to be displayed + while (currentPanel) + { + currentPanel->SetWizardPanel(this); + if (currentPanel->ShouldDisplayPanel()) + break; + + // ok the panel wants to be skipped, so skip ahead + currentPanel = currentPanel->GetNextSubPanel(); + } + + return currentPanel; +} + +//----------------------------------------------------------------------------- +// Purpose: Advances to the next panel +//----------------------------------------------------------------------------- +void WizardPanel::OnNextButton() +{ + if (_currentSubPanel) + { + bool shouldAdvance = _currentSubPanel->OnNextButton(); + if (shouldAdvance) + { + WizardSubPanel *nextPanel = FindNextValidSubPanel(_currentSubPanel->GetNextSubPanel()); + + if (nextPanel) + { + KeyValues *kv = new KeyValues("ActivateNextSubPanel"); + kv->SetPtr("panel", nextPanel); + ivgui()->PostMessage(GetVPanel(), kv, GetVPanel()); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Retreats to the previous panel +//----------------------------------------------------------------------------- +void WizardPanel::OnPrevButton() +{ + bool shouldRetreat = true; + if (_currentSubPanel) + { + shouldRetreat = _currentSubPanel->OnPrevButton(); + } + + if (shouldRetreat) + { + ActivatePrevSubPanel(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::OnFinishButton() +{ + if (_currentSubPanel && _currentSubPanel->OnFinishButton()) + { + // hide ourselves away + BaseClass::OnClose(); + + // automatically delete ourselves if marked to do so + if (IsAutoDeleteSet()) + { + MarkForDeletion(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardPanel::OnCancelButton() +{ + if (_currentSubPanel && _currentSubPanel->OnCancelButton()) + { + // hide ourselves away + BaseClass::OnClose(); + if (IsAutoDeleteSet()) + { + MarkForDeletion(); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: command handler for catching escape key presses +//----------------------------------------------------------------------------- +void WizardPanel::OnCommand(const char *command) +{ + if (!stricmp(command, "Cancel")) + { + if (_cancelButton->IsEnabled()) + { + _cancelButton->DoClick(); + } + } + else + { + BaseClass::OnCommand(command); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Maps close button to cancel button +//----------------------------------------------------------------------------- +void WizardPanel::OnClose() +{ + if (_cancelButton->IsEnabled()) + { + _cancelButton->DoClick(); + } + else if (_finishButton->IsEnabled()) + { + _finishButton->DoClick(); + } + + // don't chain back +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +KeyValues *WizardPanel::GetWizardData() +{ + return _currentData; +} + + +//----------------------------------------------------------------------------- +// Purpose: whether to show the next,prev,finish and cancel buttons +//----------------------------------------------------------------------------- +void WizardPanel::ShowButtons(bool state) +{ + _showButtons = state; // hide the wizard panel buttons + SetNextButtonVisible( state ); + SetPrevButtonVisible( state ); + SetFinishButtonVisible( state ); + SetCancelButtonVisible( state ); +} + +//----------------------------------------------------------------------------- +// Purpose: filters close buttons +//----------------------------------------------------------------------------- +void WizardPanel::OnCloseFrameButtonPressed() +{ + // only allow close if the cancel button is enabled + if (_cancelButton->IsEnabled()) + { + BaseClass::OnCloseFrameButtonPressed(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns a page by name +//----------------------------------------------------------------------------- +WizardSubPanel *WizardPanel::GetSubPanelByName(const char *pageName) +{ + return dynamic_cast(FindChildByName(pageName)); +} diff --git a/mp/src/vgui2/vgui_controls/WizardSubPanel.cpp b/mp/src/vgui2/vgui_controls/WizardSubPanel.cpp index 52562a26..ae682cce 100644 --- a/mp/src/vgui2/vgui_controls/WizardSubPanel.cpp +++ b/mp/src/vgui2/vgui_controls/WizardSubPanel.cpp @@ -1,114 +1,114 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "vgui_controls/WizardPanel.h" -#include "vgui_controls/WizardSubPanel.h" -#include "vgui_controls/BuildGroup.h" - -#include "KeyValues.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - -#include -using namespace vgui; - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -WizardSubPanel::WizardSubPanel(Panel *parent, const char *panelName) : EditablePanel(parent, panelName), _wizardPanel(NULL) -{ - SetVisible(false); - m_iDesiredWide = 0; - m_iDesiredTall = 0; - SetBuildGroup(GetBuildGroup()); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -WizardSubPanel::~WizardSubPanel() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardSubPanel::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - SetBgColor(GetSchemeColor("WizardSubPanel.BgColor",pScheme)); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardSubPanel::GetSettings( KeyValues *outResourceData ) -{ - BaseClass::GetSettings(outResourceData); - - outResourceData->SetInt("WizardWide", m_iDesiredWide); - outResourceData->SetInt("WizardTall", m_iDesiredTall); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void WizardSubPanel::ApplySettings(KeyValues *inResourceData) -{ - // don't adjust visiblity during settings application (since it's our parent who really controls it) - bool bVisible = IsVisible(); - - BaseClass::ApplySettings(inResourceData); - - m_iDesiredWide = inResourceData->GetInt("WizardWide", 0); - m_iDesiredTall = inResourceData->GetInt("WizardTall", 0); - - if (GetWizardPanel() && m_iDesiredWide && m_iDesiredTall) - { - GetWizardPanel()->SetSize(m_iDesiredWide, m_iDesiredTall); - } - - SetVisible(bVisible); -} - -//----------------------------------------------------------------------------- -// Purpose: build mode description -//----------------------------------------------------------------------------- -const char *WizardSubPanel::GetDescription() -{ - static char buf[1024]; - _snprintf(buf, sizeof(buf), "%s, int WizardWide, int WizardTall", BaseClass::GetDescription()); - return buf; -} - -//----------------------------------------------------------------------------- -// Purpose: gets the size this subpanel would like the wizard to be -//----------------------------------------------------------------------------- -bool WizardSubPanel::GetDesiredSize(int &wide, int &tall) -{ - wide = m_iDesiredWide; - tall = m_iDesiredTall; - - return (m_iDesiredWide && m_iDesiredTall); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -KeyValues *WizardSubPanel::GetWizardData() -{ - return GetWizardPanel()->GetWizardData(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -WizardSubPanel *WizardSubPanel::GetSiblingSubPanelByName(const char *pageName) -{ - return GetWizardPanel()->GetSubPanelByName(pageName); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "vgui_controls/WizardPanel.h" +#include "vgui_controls/WizardSubPanel.h" +#include "vgui_controls/BuildGroup.h" + +#include "KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +#include +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +WizardSubPanel::WizardSubPanel(Panel *parent, const char *panelName) : EditablePanel(parent, panelName), _wizardPanel(NULL) +{ + SetVisible(false); + m_iDesiredWide = 0; + m_iDesiredTall = 0; + SetBuildGroup(GetBuildGroup()); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +WizardSubPanel::~WizardSubPanel() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardSubPanel::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + SetBgColor(GetSchemeColor("WizardSubPanel.BgColor",pScheme)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardSubPanel::GetSettings( KeyValues *outResourceData ) +{ + BaseClass::GetSettings(outResourceData); + + outResourceData->SetInt("WizardWide", m_iDesiredWide); + outResourceData->SetInt("WizardTall", m_iDesiredTall); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void WizardSubPanel::ApplySettings(KeyValues *inResourceData) +{ + // don't adjust visiblity during settings application (since it's our parent who really controls it) + bool bVisible = IsVisible(); + + BaseClass::ApplySettings(inResourceData); + + m_iDesiredWide = inResourceData->GetInt("WizardWide", 0); + m_iDesiredTall = inResourceData->GetInt("WizardTall", 0); + + if (GetWizardPanel() && m_iDesiredWide && m_iDesiredTall) + { + GetWizardPanel()->SetSize(m_iDesiredWide, m_iDesiredTall); + } + + SetVisible(bVisible); +} + +//----------------------------------------------------------------------------- +// Purpose: build mode description +//----------------------------------------------------------------------------- +const char *WizardSubPanel::GetDescription() +{ + static char buf[1024]; + _snprintf(buf, sizeof(buf), "%s, int WizardWide, int WizardTall", BaseClass::GetDescription()); + return buf; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the size this subpanel would like the wizard to be +//----------------------------------------------------------------------------- +bool WizardSubPanel::GetDesiredSize(int &wide, int &tall) +{ + wide = m_iDesiredWide; + tall = m_iDesiredTall; + + return (m_iDesiredWide && m_iDesiredTall); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +KeyValues *WizardSubPanel::GetWizardData() +{ + return GetWizardPanel()->GetWizardData(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +WizardSubPanel *WizardSubPanel::GetSiblingSubPanelByName(const char *pageName) +{ + return GetWizardPanel()->GetSubPanelByName(pageName); +} diff --git a/mp/src/vgui2/vgui_controls/consoledialog.cpp b/mp/src/vgui2/vgui_controls/consoledialog.cpp index dba72dd8..d788abc6 100644 --- a/mp/src/vgui2/vgui_controls/consoledialog.cpp +++ b/mp/src/vgui2/vgui_controls/consoledialog.cpp @@ -1,1256 +1,1256 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//===========================================================================// - -#include "vgui_controls/consoledialog.h" - -#include "vgui/IInput.h" -#include "vgui/IScheme.h" -#include "vgui/IVGui.h" -#include "vgui/ISurface.h" -#include "vgui/ILocalize.h" -#include "KeyValues.h" - -#include "vgui_controls/Button.h" -#include "vgui/KeyCode.h" -#include "vgui_controls/Menu.h" -#include "vgui_controls/TextEntry.h" -#include "vgui_controls/RichText.h" -#include "tier1/convar.h" -#include "tier1/convar_serverbounded.h" -#include "icvar.h" -#include "filesystem.h" - -#include - -#if defined( _X360 ) -#include "xbox/xbox_win32stubs.h" -#endif - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -using namespace vgui; - - -//----------------------------------------------------------------------------- -// Used by the autocompletion system -//----------------------------------------------------------------------------- -class CNonFocusableMenu : public Menu -{ - DECLARE_CLASS_SIMPLE( CNonFocusableMenu, Menu ); - -public: - CNonFocusableMenu( Panel *parent, const char *panelName ) - : BaseClass( parent, panelName ), - m_pFocus( 0 ) - { - } - - void SetFocusPanel( Panel *panel ) - { - m_pFocus = panel; - } - - VPANEL GetCurrentKeyFocus() - { - if ( !m_pFocus ) - return GetVPanel(); - - return m_pFocus->GetVPanel(); - } - -private: - Panel *m_pFocus; -}; - - -//----------------------------------------------------------------------------- -// Purpose: forwards tab key presses up from the text entry so we can do autocomplete -//----------------------------------------------------------------------------- -class TabCatchingTextEntry : public TextEntry -{ -public: - TabCatchingTextEntry(Panel *parent, const char *name, VPANEL comp) : TextEntry(parent, name), m_pCompletionList( comp ) - { - SetAllowNonAsciiCharacters( true ); - SetDragEnabled( true ); - } - - virtual void OnKeyCodeTyped(KeyCode code) - { - if (code == KEY_TAB) - { - GetParent()->OnKeyCodeTyped(code); - } - else if ( code == KEY_ENTER ) - { - // submit is the default button whose click event will have been called already - } - else - { - TextEntry::OnKeyCodeTyped(code); - } - } - - virtual void OnKillFocus() - { - if ( input()->GetFocus() != m_pCompletionList ) // if its not the completion window trying to steal our focus - { - PostMessage(GetParent(), new KeyValues("CloseCompletionList")); - } - } - -private: - VPANEL m_pCompletionList; -}; - - - -// Things the user typed in and hit submit/return with -CHistoryItem::CHistoryItem( void ) -{ - m_text = NULL; - m_extraText = NULL; - m_bHasExtra = false; -} - -CHistoryItem::CHistoryItem( const char *text, const char *extra ) -{ - Assert( text ); - m_text = NULL; - m_extraText = NULL; - m_bHasExtra = false; - SetText( text , extra ); -} - -CHistoryItem::CHistoryItem( const CHistoryItem& src ) -{ - m_text = NULL; - m_extraText = NULL; - m_bHasExtra = false; - SetText( src.GetText(), src.GetExtra() ); -} - -CHistoryItem::~CHistoryItem( void ) -{ - delete[] m_text; - delete[] m_extraText; - m_text = NULL; -} - -const char *CHistoryItem::GetText() const -{ - if ( m_text ) - { - return m_text; - } - else - { - return ""; - } -} - -const char *CHistoryItem::GetExtra() const -{ - if ( m_extraText ) - { - return m_extraText; - } - else - { - return NULL; - } -} - -void CHistoryItem::SetText( const char *text, const char *extra ) -{ - delete[] m_text; - int len = strlen( text ) + 1; - - m_text = new char[ len ]; - Q_memset( m_text, 0x0, len ); - Q_strncpy( m_text, text, len ); - - if ( extra ) - { - m_bHasExtra = true; - delete[] m_extraText; - int elen = strlen( extra ) + 1; - m_extraText = new char[ elen ]; - Q_memset( m_extraText, 0x0, elen); - Q_strncpy( m_extraText, extra, elen ); - } - else - { - m_bHasExtra = false; - } -} - - -//----------------------------------------------------------------------------- -// -// Console page completion item starts here -// -//----------------------------------------------------------------------------- -CConsolePanel::CompletionItem::CompletionItem( void ) -{ - m_bIsCommand = true; - m_pCommand = NULL; - m_pText = NULL; -} - -CConsolePanel::CompletionItem::CompletionItem( const CompletionItem& src ) -{ - m_bIsCommand = src.m_bIsCommand; - m_pCommand = src.m_pCommand; - if ( src.m_pText ) - { - m_pText = new CHistoryItem( (const CHistoryItem& )src.m_pText ); - } - else - { - m_pText = NULL; - } -} - -CConsolePanel::CompletionItem& CConsolePanel::CompletionItem::operator =( const CompletionItem& src ) -{ - if ( this == &src ) - return *this; - - m_bIsCommand = src.m_bIsCommand; - m_pCommand = src.m_pCommand; - if ( src.m_pText ) - { - m_pText = new CHistoryItem( (const CHistoryItem& )*src.m_pText ); - } - else - { - m_pText = NULL; - } - - return *this; -} - -CConsolePanel::CompletionItem::~CompletionItem( void ) -{ - if ( m_pText ) - { - delete m_pText; - m_pText = NULL; - } -} - -const char *CConsolePanel::CompletionItem::GetName() const -{ - if ( m_bIsCommand ) - return m_pCommand->GetName(); - return m_pCommand ? m_pCommand->GetName() : GetCommand(); -} - -const char *CConsolePanel::CompletionItem::GetItemText( void ) -{ - static char text[256]; - text[0] = 0; - if ( m_pText ) - { - if ( m_pText->HasExtra() ) - { - Q_snprintf( text, sizeof( text ), "%s %s", m_pText->GetText(), m_pText->GetExtra() ); - } - else - { - Q_strncpy( text, m_pText->GetText(), sizeof( text ) ); - } - } - return text; -} - -const char *CConsolePanel::CompletionItem::GetCommand( void ) const -{ - static char text[256]; - text[0] = 0; - if ( m_pText ) - { - Q_strncpy( text, m_pText->GetText(), sizeof( text ) ); - } - return text; -} - - -//----------------------------------------------------------------------------- -// -// Console page starts here -// -//----------------------------------------------------------------------------- - - -//----------------------------------------------------------------------------- -// Purpose: Constructor, destuctor -//----------------------------------------------------------------------------- -CConsolePanel::CConsolePanel( vgui::Panel *pParent, const char *pName, bool bStatusVersion ) : - BaseClass( pParent, pName ), m_bStatusVersion( bStatusVersion ) -{ - SetKeyBoardInputEnabled( true ); - - if ( !m_bStatusVersion ) - { - SetMinimumSize(100,100); - } - - // create controls - m_pHistory = new RichText(this, "ConsoleHistory"); - m_pHistory->SetAllowKeyBindingChainToParent( false ); - SETUP_PANEL( m_pHistory ); - m_pHistory->SetVerticalScrollbar( !m_bStatusVersion ); - if ( m_bStatusVersion ) - { - m_pHistory->SetDrawOffsets( 3, 3 ); - } - m_pHistory->GotoTextEnd(); - - m_pSubmit = new Button(this, "ConsoleSubmit", "#Console_Submit"); - m_pSubmit->SetCommand("submit"); - m_pSubmit->SetVisible( !m_bStatusVersion ); - - CNonFocusableMenu *pCompletionList = new CNonFocusableMenu( this, "CompletionList" ); - m_pCompletionList = pCompletionList; - m_pCompletionList->SetVisible(false); - - m_pEntry = new TabCatchingTextEntry(this, "ConsoleEntry", m_pCompletionList->GetVPanel() ); - m_pEntry->AddActionSignalTarget(this); - m_pEntry->SendNewLine(true); - pCompletionList->SetFocusPanel( m_pEntry ); - - // need to set up default colors, since ApplySchemeSettings won't be called until later - m_PrintColor = Color(216, 222, 211, 255); - m_DPrintColor = Color(196, 181, 80, 255); - - m_pEntry->SetTabPosition(1); - - m_bAutoCompleteMode = false; - m_szPartialText[0] = 0; - m_szPreviousPartialText[0]=0; - - // Add to global console list - g_pCVar->InstallConsoleDisplayFunc( this ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -CConsolePanel::~CConsolePanel() -{ - ClearCompletionList(); - m_CommandHistory.Purge(); - g_pCVar->RemoveConsoleDisplayFunc( this ); -} - - -//----------------------------------------------------------------------------- -// Updates the completion list -//----------------------------------------------------------------------------- -void CConsolePanel::OnThink() -{ - BaseClass::OnThink(); - - if ( !IsVisible() ) - return; - - if ( !m_pCompletionList->IsVisible() ) - return; - - UpdateCompletionListPosition(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Clears the console -//----------------------------------------------------------------------------- -void CConsolePanel::Clear() -{ - m_pHistory->SetText(""); - m_pHistory->GotoTextEnd(); -} - - -//----------------------------------------------------------------------------- -// Purpose: color text print -//----------------------------------------------------------------------------- -void CConsolePanel::ColorPrint( const Color& clr, const char *msg ) -{ - if ( m_bStatusVersion ) - { - Clear(); - } - - m_pHistory->InsertColorChange( clr ); - m_pHistory->InsertString( msg ); -} - - -//----------------------------------------------------------------------------- -// Purpose: normal text print -//----------------------------------------------------------------------------- -void CConsolePanel::Print(const char *msg) -{ - ColorPrint( m_PrintColor, msg ); -} - - -//----------------------------------------------------------------------------- -// Purpose: debug text print -//----------------------------------------------------------------------------- -void CConsolePanel::DPrint( const char *msg ) -{ - ColorPrint( m_DPrintColor, msg ); -} - - -void CConsolePanel::ClearCompletionList() -{ - int c = m_CompletionList.Count(); - int i; - for ( i = c - 1; i >= 0; i-- ) - { - delete m_CompletionList[ i ]; - } - m_CompletionList.Purge(); -} - - -static ConCommand *FindAutoCompleteCommmandFromPartial( const char *partial ) -{ - char command[ 256 ]; - Q_strncpy( command, partial, sizeof( command ) ); - - char *space = Q_strstr( command, " " ); - if ( space ) - { - *space = 0; - } - - ConCommand *cmd = g_pCVar->FindCommand( command ); - if ( !cmd ) - return NULL; - - if ( !cmd->CanAutoComplete() ) - return NULL; - - return cmd; -} - - -//----------------------------------------------------------------------------- -// Purpose: rebuilds the list of possible completions from the current entered text -//----------------------------------------------------------------------------- -void CConsolePanel::RebuildCompletionList(const char *text) -{ - ClearCompletionList(); - - // we need the length of the text for the partial string compares - int len = Q_strlen(text); - if ( len < 1 ) - { - // Fill the completion list with history instead - for ( int i = 0 ; i < m_CommandHistory.Count(); i++ ) - { - CHistoryItem *item = &m_CommandHistory[ i ]; - CompletionItem *comp = new CompletionItem(); - m_CompletionList.AddToTail( comp ); - comp->m_bIsCommand = false; - comp->m_pCommand = NULL; - comp->m_pText = new CHistoryItem( *item ); - } - return; - } - - bool bNormalBuild = true; - - // if there is a space in the text, and the command isn't of the type to know how to autocomplet, then command completion is over - const char *space = strstr( text, " " ); - if ( space ) - { - ConCommand *pCommand = FindAutoCompleteCommmandFromPartial( text ); - if ( !pCommand ) - return; - - bNormalBuild = false; - - CUtlVector< CUtlString > commands; - int count = pCommand->AutoCompleteSuggest( text, commands ); - Assert( count <= COMMAND_COMPLETION_MAXITEMS ); - int i; - - for ( i = 0; i < count; i++ ) - { - // match found, add to list - CompletionItem *item = new CompletionItem(); - m_CompletionList.AddToTail( item ); - item->m_bIsCommand = false; - item->m_pCommand = NULL; - item->m_pText = new CHistoryItem( commands[ i ].String() ); - } - } - - if ( bNormalBuild ) - { - // look through the command list for all matches - ConCommandBase const *cmd = (ConCommandBase const *)cvar->GetCommands(); - while (cmd) - { - if ( cmd->IsFlagSet( FCVAR_DEVELOPMENTONLY ) || cmd->IsFlagSet( FCVAR_HIDDEN ) ) - { - cmd = cmd->GetNext(); - continue; - } - - if ( !strnicmp(text, cmd->GetName(), len)) - { - // match found, add to list - CompletionItem *item = new CompletionItem(); - m_CompletionList.AddToTail( item ); - item->m_pCommand = (ConCommandBase *)cmd; - const char *tst = cmd->GetName(); - if ( !cmd->IsCommand() ) - { - item->m_bIsCommand = false; - ConVar *var = ( ConVar * )cmd; - ConVar_ServerBounded *pBounded = dynamic_cast( var ); - if ( pBounded || var->IsFlagSet( FCVAR_NEVER_AS_STRING ) ) - { - char strValue[512]; - - int intVal = pBounded ? pBounded->GetInt() : var->GetInt(); - float floatVal = pBounded ? pBounded->GetFloat() : var->GetFloat(); - - if ( floatVal == intVal ) - Q_snprintf( strValue, sizeof( strValue ), "%d", intVal ); - else - Q_snprintf( strValue, sizeof( strValue ), "%f", floatVal ); - - item->m_pText = new CHistoryItem( var->GetName(), strValue ); - } - else - { - item->m_pText = new CHistoryItem( var->GetName(), var->GetString() ); - } - } - else - { - item->m_bIsCommand = true; - item->m_pText = new CHistoryItem( tst ); - } - } - - cmd = cmd->GetNext(); - } - - // Now sort the list by command name - if ( m_CompletionList.Count() >= 2 ) - { - for ( int i = 0 ; i < m_CompletionList.Count(); i++ ) - { - for ( int j = i + 1; j < m_CompletionList.Count(); j++ ) - { - const CompletionItem *i1, *i2; - i1 = m_CompletionList[ i ]; - i2 = m_CompletionList[ j ]; - - if ( Q_stricmp( i1->GetName(), i2->GetName() ) > 0 ) - { - CompletionItem *temp = m_CompletionList[ i ]; - m_CompletionList[ i ] = m_CompletionList[ j ]; - m_CompletionList[ j ] = temp; - } - } - } - } - } - -} - -//----------------------------------------------------------------------------- -// Purpose: auto completes current text -//----------------------------------------------------------------------------- -void CConsolePanel::OnAutoComplete(bool reverse) -{ - if (!m_bAutoCompleteMode) - { - // we're not in auto-complete mode, Start - m_iNextCompletion = 0; - m_bAutoCompleteMode = true; - } - - // if we're in reverse, move back to before the current - if (reverse) - { - m_iNextCompletion -= 2; - if (m_iNextCompletion < 0) - { - // loop around in reverse - m_iNextCompletion = m_CompletionList.Size() - 1; - } - } - - // get the next completion - if (!m_CompletionList.IsValidIndex(m_iNextCompletion)) - { - // loop completion list - m_iNextCompletion = 0; - } - - // make sure everything is still valid - if (!m_CompletionList.IsValidIndex(m_iNextCompletion)) - return; - - // match found, set text - char completedText[256]; - CompletionItem *item = m_CompletionList[m_iNextCompletion]; - Assert( item ); - - if ( !item->m_bIsCommand && item->m_pCommand ) - { - Q_strncpy(completedText, item->GetCommand(), sizeof(completedText) - 2 ); - } - else - { - Q_strncpy(completedText, item->GetItemText(), sizeof(completedText) - 2 ); - } - - if ( !Q_strstr( completedText, " " ) ) - { - Q_strncat(completedText, " ", sizeof(completedText), COPY_ALL_CHARACTERS ); - } - - m_pEntry->SetText(completedText); - m_pEntry->GotoTextEnd(); - m_pEntry->SelectNone(); - - m_iNextCompletion++; -} - - -//----------------------------------------------------------------------------- -// Purpose: Called whenever the user types text -//----------------------------------------------------------------------------- -void CConsolePanel::OnTextChanged(Panel *panel) -{ - if (panel != m_pEntry) - return; - - Q_strncpy( m_szPreviousPartialText, m_szPartialText, sizeof( m_szPreviousPartialText ) ); - - // get the partial text the user type - m_pEntry->GetText(m_szPartialText, sizeof(m_szPartialText)); - - // see if they've hit the tilde key (which opens & closes the console) - int len = Q_strlen(m_szPartialText); - - bool hitTilde = ( m_szPartialText[len - 1] == '~' || m_szPartialText[len - 1] == '`' ) ? true : false; - - bool altKeyDown = ( vgui::input()->IsKeyDown( KEY_LALT ) || vgui::input()->IsKeyDown( KEY_RALT ) ) ? true : false; - bool ctrlKeyDown = ( vgui::input()->IsKeyDown( KEY_LCONTROL ) || vgui::input()->IsKeyDown( KEY_RCONTROL ) ) ? true : false; - - // Alt-Tilde toggles Japanese IME on/off!!! - if ( ( len > 0 ) && hitTilde ) - { - // Strip the last character (tilde) - m_szPartialText[ len - 1 ] = L'\0'; - - if( !altKeyDown && !ctrlKeyDown ) - { - m_pEntry->SetText( "" ); - - // close the console - PostMessage( this, new KeyValues( "Close" ) ); - PostActionSignal( new KeyValues( "ClosedByHittingTilde" ) ); - } - else - { - m_pEntry->SetText( m_szPartialText ); - } - return; - } - - // clear auto-complete state since the user has typed - m_bAutoCompleteMode = false; - - RebuildCompletionList(m_szPartialText); - - // build the menu - if ( m_CompletionList.Count() < 1 ) - { - m_pCompletionList->SetVisible(false); - } - else - { - m_pCompletionList->SetVisible(true); - m_pCompletionList->DeleteAllItems(); - const int MAX_MENU_ITEMS = 10; - - // add the first ten items to the list - for (int i = 0; i < m_CompletionList.Count() && i < MAX_MENU_ITEMS; i++) - { - char text[256]; - text[0] = 0; - if (i == MAX_MENU_ITEMS - 1) - { - Q_strncpy(text, "...", sizeof( text ) ); - } - else - { - Assert( m_CompletionList[i] ); - Q_strncpy(text, m_CompletionList[i]->GetItemText(), sizeof( text ) ); - } - KeyValues *kv = new KeyValues("CompletionCommand"); - kv->SetString("command",text); - m_pCompletionList->AddMenuItem(text, kv, this); - } - - UpdateCompletionListPosition(); - } - - RequestFocus(); - m_pEntry->RequestFocus(); - -} - -//----------------------------------------------------------------------------- -// Purpose: generic vgui command handler -//----------------------------------------------------------------------------- -void CConsolePanel::OnCommand(const char *command) -{ - if ( !Q_stricmp( command, "Submit" ) ) - { - // submit the entry as a console commmand - char szCommand[256]; - m_pEntry->GetText(szCommand, sizeof(szCommand)); - PostActionSignal( new KeyValues( "CommandSubmitted", "command", szCommand ) ); - - // add to the history - Print("] "); - Print(szCommand); - Print("\n"); - - // clear the field - m_pEntry->SetText(""); - - // clear the completion state - OnTextChanged(m_pEntry); - - // always go the end of the buffer when the user has typed something - m_pHistory->GotoTextEnd(); - - // Add the command to the history - char *extra = strchr(szCommand, ' '); - if ( extra ) - { - *extra = '\0'; - extra++; - } - - if ( Q_strlen( szCommand ) > 0 ) - { - AddToHistory( szCommand, extra ); - } - m_pCompletionList->SetVisible(false); - } - else - { - BaseClass::OnCommand(command); - } -} - - -//----------------------------------------------------------------------------- -// Focus related methods -//----------------------------------------------------------------------------- -bool CConsolePanel::TextEntryHasFocus() const -{ - return ( input()->GetFocus() == m_pEntry->GetVPanel() ); -} - -void CConsolePanel::TextEntryRequestFocus() -{ - m_pEntry->RequestFocus(); -} - - -//----------------------------------------------------------------------------- -// Purpose: swallows tab key pressed -//----------------------------------------------------------------------------- -void CConsolePanel::OnKeyCodeTyped(KeyCode code) -{ - BaseClass::OnKeyCodeTyped(code); - - // check for processing - if ( TextEntryHasFocus() ) - { - if (code == KEY_TAB) - { - bool reverse = false; - if (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)) - { - reverse = true; - } - - // attempt auto-completion - OnAutoComplete(reverse); - m_pEntry->RequestFocus(); - } - else if (code == KEY_DOWN) - { - OnAutoComplete(false); - // UpdateCompletionListPosition(); - // m_pCompletionList->SetVisible(true); - - m_pEntry->RequestFocus(); - } - else if (code == KEY_UP) - { - OnAutoComplete(true); - m_pEntry->RequestFocus(); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: lays out controls -//----------------------------------------------------------------------------- -void CConsolePanel::PerformLayout() -{ - BaseClass::PerformLayout(); - - // setup tab ordering - GetFocusNavGroup().SetDefaultButton(m_pSubmit); - - IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); - m_pEntry->SetBorder(pScheme->GetBorder("DepressedButtonBorder")); - m_pHistory->SetBorder(pScheme->GetBorder("DepressedButtonBorder")); - - // layout controls - int wide, tall; - GetSize(wide, tall); - - if ( !m_bStatusVersion ) - { - const int inset = 8; - const int entryHeight = 24; - const int topHeight = 4; - const int entryInset = 4; - const int submitWide = 64; - const int submitInset = 7; // x inset to pull the submit button away from the frame grab - - m_pHistory->SetPos(inset, inset + topHeight); - m_pHistory->SetSize(wide - (inset * 2), tall - (entryInset * 2 + inset * 2 + topHeight + entryHeight)); - m_pHistory->InvalidateLayout(); - - int nSubmitXPos = wide - ( inset + submitWide + submitInset ); - m_pSubmit->SetPos( nSubmitXPos, tall - (entryInset * 2 + entryHeight)); - m_pSubmit->SetSize( submitWide, entryHeight); - - m_pEntry->SetPos( inset, tall - (entryInset * 2 + entryHeight) ); - m_pEntry->SetSize( nSubmitXPos - entryInset - 2 * inset, entryHeight); - } - else - { - const int inset = 2; - - int entryWidth = wide / 2; - if ( wide > 400 ) - { - entryWidth = 200; - } - - m_pEntry->SetBounds( inset, inset, entryWidth, tall - 2 * inset ); - - m_pHistory->SetBounds( inset + entryWidth + inset, inset, ( wide - entryWidth ) - inset, tall - 2 * inset ); - } - - UpdateCompletionListPosition(); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the position of the completion list popup -//----------------------------------------------------------------------------- -void CConsolePanel::UpdateCompletionListPosition() -{ - int ex, ey; - m_pEntry->GetPos(ex, ey); - - if ( !m_bStatusVersion ) - { - // Position below text entry - ey += m_pEntry->GetTall(); - } - else - { - // Position above text entry - int menuwide, menutall; - m_pCompletionList->GetSize( menuwide, menutall ); - ey -= ( menutall + 4 ); - } - - LocalToScreen( ex, ey ); - m_pCompletionList->SetPos( ex, ey ); - - if ( m_pCompletionList->IsVisible() ) - { - m_pEntry->RequestFocus(); - MoveToFront(); - m_pCompletionList->MoveToFront(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Closes the completion list -//----------------------------------------------------------------------------- -void CConsolePanel::CloseCompletionList() -{ - m_pCompletionList->SetVisible(false); -} - -//----------------------------------------------------------------------------- -// Purpose: sets up colors -//----------------------------------------------------------------------------- -void CConsolePanel::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - m_PrintColor = GetSchemeColor("Console.TextColor", pScheme); - m_DPrintColor = GetSchemeColor("Console.DevTextColor", pScheme); - m_pHistory->SetFont( pScheme->GetFont( "ConsoleText", IsProportional() ) ); - m_pCompletionList->SetFont( pScheme->GetFont( "DefaultSmall", IsProportional() ) ); - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: Handles autocompletion menu input -//----------------------------------------------------------------------------- -void CConsolePanel::OnMenuItemSelected(const char *command) -{ - if ( strstr( command, "..." ) ) // stop the menu going away if you click on ... - { - m_pCompletionList->SetVisible( true ); - } - else - { - m_pEntry->SetText(command); - m_pEntry->GotoTextEnd(); - m_pEntry->InsertChar(' '); - m_pEntry->GotoTextEnd(); - } -} - -void CConsolePanel::Hide() -{ - OnClose(); - m_iNextCompletion = 0; - RebuildCompletionList(""); -} - -void CConsolePanel::AddToHistory( const char *commandText, const char *extraText ) -{ - // Newest at end, oldest at head - while ( m_CommandHistory.Count() >= MAX_HISTORY_ITEMS ) - { - // Remove from head until size is reasonable - m_CommandHistory.Remove( 0 ); - } - - // strip the space off the end of the command before adding it to the history - // If this code gets cleaned up then we should remove the redundant calls to strlen, - // the check for whether _alloca succeeded, and should use V_strncpy instead of the - // error prone memset/strncpy sequence. - char *command = static_cast( _alloca( (strlen( commandText ) + 1 ) * sizeof( char ) )); - if ( command ) - { - memset( command, 0x0, strlen( commandText ) + 1 ); - strncpy( command, commandText, strlen( commandText )); - // There is no actual bug here, just some sloppy/odd code. - // src\vgui2\vgui_controls\consoledialog.cpp(974): warning C6053: The prior call to 'strncpy' might not zero-terminate string 'command' - ANALYZE_SUPPRESS( 6053 ) - if ( command[ strlen( command ) -1 ] == ' ' ) - { - command[ strlen( command ) -1 ] = '\0'; - } - } - - // strip the quotes off the extra text - char *extra = NULL; - - if ( extraText ) - { - extra = static_cast( malloc( (strlen( extraText ) + 1 ) * sizeof( char ) )); - if ( extra ) - { - memset( extra, 0x0, strlen( extraText ) + 1 ); - strncpy( extra, extraText, strlen( extraText )); // +1 to dodge the starting quote - - // Strip trailing spaces - int i = strlen( extra ) - 1; - while ( i >= 0 && // Check I before referencing i == -1 into the extra array! - extra[ i ] == ' ' ) - { - extra[ i ] = '\0'; - i--; - } - } - } - - // If it's already there, then remove since we'll add it to the end instead - CHistoryItem *item = NULL; - for ( int i = m_CommandHistory.Count() - 1; i >= 0; i-- ) - { - item = &m_CommandHistory[ i ]; - if ( !item ) - continue; - - if ( stricmp( item->GetText(), command ) ) - continue; - - if ( extra || item->GetExtra() ) - { - if ( !extra || !item->GetExtra() ) - continue; - - // stricmp so two commands with the same starting text get added - if ( stricmp( item->GetExtra(), extra ) ) - continue; - } - m_CommandHistory.Remove( i ); - } - - item = &m_CommandHistory[ m_CommandHistory.AddToTail() ]; - Assert( item ); - item->SetText( command, extra ); - - m_iNextCompletion = 0; - RebuildCompletionList( m_szPartialText ); - - free( extra ); -} - -void CConsolePanel::GetConsoleText( char *pchText, size_t bufSize ) const -{ - wchar_t *temp = new wchar_t[ bufSize ]; - m_pHistory->GetText( 0, temp, bufSize * sizeof( wchar_t ) ); - g_pVGuiLocalize->ConvertUnicodeToANSI( temp, pchText, bufSize ); - delete[] temp; -} - -//----------------------------------------------------------------------------- -// Purpose: writes out console to disk -//----------------------------------------------------------------------------- -void CConsolePanel::DumpConsoleTextToFile() -{ - const int CONDUMP_FILES_MAX_NUM = 1000; - - FileHandle_t handle; - bool found = false; - char szfile[ 512 ]; - - // we don't want to overwrite other condump.txt files - for ( int i = 0 ; i < CONDUMP_FILES_MAX_NUM ; ++i ) - { - _snprintf( szfile, sizeof(szfile), "condump%03d.txt", i ); - if ( !g_pFullFileSystem->FileExists(szfile) ) - { - found = true; - break; - } - } - - if ( !found ) - { - Print( "Can't condump! Too many existing condump output files in the gamedir!\n" ); - return; - } - - handle = g_pFullFileSystem->Open( szfile, "wb" ); - if ( handle != FILESYSTEM_INVALID_HANDLE ) - { - int pos = 0; - while (1) - { - wchar_t buf[512]; - m_pHistory->GetText(pos, buf, sizeof(buf)); - pos += sizeof(buf) / sizeof(wchar_t); - - // don't continue if none left - if (buf[0] == 0) - break; - - // convert to ansi - char ansi[512]; - g_pVGuiLocalize->ConvertUnicodeToANSI(buf, ansi, sizeof(ansi)); - - // write to disk - int len = strlen(ansi); - for (int i = 0; i < len; i++) - { - // preceed newlines with a return - if (ansi[i] == '\n') - { - char ret = '\r'; - g_pFullFileSystem->Write( &ret, 1, handle ); - } - - g_pFullFileSystem->Write( ansi + i, 1, handle ); - } - } - - g_pFullFileSystem->Close( handle ); - - Print( "console dumped to " ); - Print( szfile ); - Print( "\n" ); - } - else - { - Print( "Unable to condump to " ); - Print( szfile ); - Print( "\n" ); - } -} - - -//----------------------------------------------------------------------------- -// -// Console dialog starts here -// -//----------------------------------------------------------------------------- -CConsoleDialog::CConsoleDialog( vgui::Panel *pParent, const char *pName, bool bStatusVersion ) : - BaseClass( pParent, pName ) -{ - // initialize dialog - SetVisible( false ); - SetTitle( "#Console_Title", true ); - m_pConsolePanel = new CConsolePanel( this, "ConsolePage", bStatusVersion ); - m_pConsolePanel->AddActionSignalTarget( this ); -} - -void CConsoleDialog::OnScreenSizeChanged( int iOldWide, int iOldTall ) -{ - BaseClass::OnScreenSizeChanged( iOldWide, iOldTall ); - - int sx, sy; - surface()->GetScreenSize( sx, sy ); - - int w, h; - GetSize( w, h ); - if ( w > sx || h > sy ) - { - if ( w > sx ) - { - w = sx; - } - if ( h > sy ) - { - h = sy; - } - - // Try and lower the size to match the screen bounds - SetSize( w, h ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: brings dialog to the fore -//----------------------------------------------------------------------------- -void CConsoleDialog::PerformLayout() -{ - BaseClass::PerformLayout(); - - int x, y, w, h; - GetClientArea( x, y, w, h ); - m_pConsolePanel->SetBounds( x, y, w, h ); -} - - -//----------------------------------------------------------------------------- -// Purpose: brings dialog to the fore -//----------------------------------------------------------------------------- -void CConsoleDialog::Activate() -{ - BaseClass::Activate(); - m_pConsolePanel->m_pEntry->RequestFocus(); -} - - -//----------------------------------------------------------------------------- -// Hides the dialog -//----------------------------------------------------------------------------- -void CConsoleDialog::Hide() -{ - OnClose(); - m_pConsolePanel->Hide(); -} - - -//----------------------------------------------------------------------------- -// Close just hides the dialog -//----------------------------------------------------------------------------- -void CConsoleDialog::Close() -{ - Hide(); -} - - -//----------------------------------------------------------------------------- -// Submits commands -//----------------------------------------------------------------------------- -void CConsoleDialog::OnCommandSubmitted( const char *pCommand ) -{ - PostActionSignal( new KeyValues( "CommandSubmitted", "command", pCommand ) ); -} - - -//----------------------------------------------------------------------------- -// Chain to the page -//----------------------------------------------------------------------------- -void CConsoleDialog::Print( const char *pMessage ) -{ - m_pConsolePanel->Print( pMessage ); -} - -void CConsoleDialog::DPrint( const char *pMessage ) -{ - m_pConsolePanel->DPrint( pMessage ); -} - -void CConsoleDialog::ColorPrint( const Color& clr, const char *msg ) -{ - m_pConsolePanel->ColorPrint( clr, msg ); -} - -void CConsoleDialog::Clear() -{ - m_pConsolePanel->Clear( ); -} - -void CConsoleDialog::DumpConsoleTextToFile() -{ - m_pConsolePanel->DumpConsoleTextToFile( ); -} - - -void CConsoleDialog::OnKeyCodePressed( vgui::KeyCode code ) -{ - if ( code == KEY_XBUTTON_B ) - { - Hide(); - } - else - { - BaseClass::OnKeyCodePressed(code); - } +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "vgui_controls/consoledialog.h" + +#include "vgui/IInput.h" +#include "vgui/IScheme.h" +#include "vgui/IVGui.h" +#include "vgui/ISurface.h" +#include "vgui/ILocalize.h" +#include "KeyValues.h" + +#include "vgui_controls/Button.h" +#include "vgui/KeyCode.h" +#include "vgui_controls/Menu.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/RichText.h" +#include "tier1/convar.h" +#include "tier1/convar_serverbounded.h" +#include "icvar.h" +#include "filesystem.h" + +#include + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Used by the autocompletion system +//----------------------------------------------------------------------------- +class CNonFocusableMenu : public Menu +{ + DECLARE_CLASS_SIMPLE( CNonFocusableMenu, Menu ); + +public: + CNonFocusableMenu( Panel *parent, const char *panelName ) + : BaseClass( parent, panelName ), + m_pFocus( 0 ) + { + } + + void SetFocusPanel( Panel *panel ) + { + m_pFocus = panel; + } + + VPANEL GetCurrentKeyFocus() + { + if ( !m_pFocus ) + return GetVPanel(); + + return m_pFocus->GetVPanel(); + } + +private: + Panel *m_pFocus; +}; + + +//----------------------------------------------------------------------------- +// Purpose: forwards tab key presses up from the text entry so we can do autocomplete +//----------------------------------------------------------------------------- +class TabCatchingTextEntry : public TextEntry +{ +public: + TabCatchingTextEntry(Panel *parent, const char *name, VPANEL comp) : TextEntry(parent, name), m_pCompletionList( comp ) + { + SetAllowNonAsciiCharacters( true ); + SetDragEnabled( true ); + } + + virtual void OnKeyCodeTyped(KeyCode code) + { + if (code == KEY_TAB) + { + GetParent()->OnKeyCodeTyped(code); + } + else if ( code == KEY_ENTER ) + { + // submit is the default button whose click event will have been called already + } + else + { + TextEntry::OnKeyCodeTyped(code); + } + } + + virtual void OnKillFocus() + { + if ( input()->GetFocus() != m_pCompletionList ) // if its not the completion window trying to steal our focus + { + PostMessage(GetParent(), new KeyValues("CloseCompletionList")); + } + } + +private: + VPANEL m_pCompletionList; +}; + + + +// Things the user typed in and hit submit/return with +CHistoryItem::CHistoryItem( void ) +{ + m_text = NULL; + m_extraText = NULL; + m_bHasExtra = false; +} + +CHistoryItem::CHistoryItem( const char *text, const char *extra ) +{ + Assert( text ); + m_text = NULL; + m_extraText = NULL; + m_bHasExtra = false; + SetText( text , extra ); +} + +CHistoryItem::CHistoryItem( const CHistoryItem& src ) +{ + m_text = NULL; + m_extraText = NULL; + m_bHasExtra = false; + SetText( src.GetText(), src.GetExtra() ); +} + +CHistoryItem::~CHistoryItem( void ) +{ + delete[] m_text; + delete[] m_extraText; + m_text = NULL; +} + +const char *CHistoryItem::GetText() const +{ + if ( m_text ) + { + return m_text; + } + else + { + return ""; + } +} + +const char *CHistoryItem::GetExtra() const +{ + if ( m_extraText ) + { + return m_extraText; + } + else + { + return NULL; + } +} + +void CHistoryItem::SetText( const char *text, const char *extra ) +{ + delete[] m_text; + int len = strlen( text ) + 1; + + m_text = new char[ len ]; + Q_memset( m_text, 0x0, len ); + Q_strncpy( m_text, text, len ); + + if ( extra ) + { + m_bHasExtra = true; + delete[] m_extraText; + int elen = strlen( extra ) + 1; + m_extraText = new char[ elen ]; + Q_memset( m_extraText, 0x0, elen); + Q_strncpy( m_extraText, extra, elen ); + } + else + { + m_bHasExtra = false; + } +} + + +//----------------------------------------------------------------------------- +// +// Console page completion item starts here +// +//----------------------------------------------------------------------------- +CConsolePanel::CompletionItem::CompletionItem( void ) +{ + m_bIsCommand = true; + m_pCommand = NULL; + m_pText = NULL; +} + +CConsolePanel::CompletionItem::CompletionItem( const CompletionItem& src ) +{ + m_bIsCommand = src.m_bIsCommand; + m_pCommand = src.m_pCommand; + if ( src.m_pText ) + { + m_pText = new CHistoryItem( (const CHistoryItem& )src.m_pText ); + } + else + { + m_pText = NULL; + } +} + +CConsolePanel::CompletionItem& CConsolePanel::CompletionItem::operator =( const CompletionItem& src ) +{ + if ( this == &src ) + return *this; + + m_bIsCommand = src.m_bIsCommand; + m_pCommand = src.m_pCommand; + if ( src.m_pText ) + { + m_pText = new CHistoryItem( (const CHistoryItem& )*src.m_pText ); + } + else + { + m_pText = NULL; + } + + return *this; +} + +CConsolePanel::CompletionItem::~CompletionItem( void ) +{ + if ( m_pText ) + { + delete m_pText; + m_pText = NULL; + } +} + +const char *CConsolePanel::CompletionItem::GetName() const +{ + if ( m_bIsCommand ) + return m_pCommand->GetName(); + return m_pCommand ? m_pCommand->GetName() : GetCommand(); +} + +const char *CConsolePanel::CompletionItem::GetItemText( void ) +{ + static char text[256]; + text[0] = 0; + if ( m_pText ) + { + if ( m_pText->HasExtra() ) + { + Q_snprintf( text, sizeof( text ), "%s %s", m_pText->GetText(), m_pText->GetExtra() ); + } + else + { + Q_strncpy( text, m_pText->GetText(), sizeof( text ) ); + } + } + return text; +} + +const char *CConsolePanel::CompletionItem::GetCommand( void ) const +{ + static char text[256]; + text[0] = 0; + if ( m_pText ) + { + Q_strncpy( text, m_pText->GetText(), sizeof( text ) ); + } + return text; +} + + +//----------------------------------------------------------------------------- +// +// Console page starts here +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Purpose: Constructor, destuctor +//----------------------------------------------------------------------------- +CConsolePanel::CConsolePanel( vgui::Panel *pParent, const char *pName, bool bStatusVersion ) : + BaseClass( pParent, pName ), m_bStatusVersion( bStatusVersion ) +{ + SetKeyBoardInputEnabled( true ); + + if ( !m_bStatusVersion ) + { + SetMinimumSize(100,100); + } + + // create controls + m_pHistory = new RichText(this, "ConsoleHistory"); + m_pHistory->SetAllowKeyBindingChainToParent( false ); + SETUP_PANEL( m_pHistory ); + m_pHistory->SetVerticalScrollbar( !m_bStatusVersion ); + if ( m_bStatusVersion ) + { + m_pHistory->SetDrawOffsets( 3, 3 ); + } + m_pHistory->GotoTextEnd(); + + m_pSubmit = new Button(this, "ConsoleSubmit", "#Console_Submit"); + m_pSubmit->SetCommand("submit"); + m_pSubmit->SetVisible( !m_bStatusVersion ); + + CNonFocusableMenu *pCompletionList = new CNonFocusableMenu( this, "CompletionList" ); + m_pCompletionList = pCompletionList; + m_pCompletionList->SetVisible(false); + + m_pEntry = new TabCatchingTextEntry(this, "ConsoleEntry", m_pCompletionList->GetVPanel() ); + m_pEntry->AddActionSignalTarget(this); + m_pEntry->SendNewLine(true); + pCompletionList->SetFocusPanel( m_pEntry ); + + // need to set up default colors, since ApplySchemeSettings won't be called until later + m_PrintColor = Color(216, 222, 211, 255); + m_DPrintColor = Color(196, 181, 80, 255); + + m_pEntry->SetTabPosition(1); + + m_bAutoCompleteMode = false; + m_szPartialText[0] = 0; + m_szPreviousPartialText[0]=0; + + // Add to global console list + g_pCVar->InstallConsoleDisplayFunc( this ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CConsolePanel::~CConsolePanel() +{ + ClearCompletionList(); + m_CommandHistory.Purge(); + g_pCVar->RemoveConsoleDisplayFunc( this ); +} + + +//----------------------------------------------------------------------------- +// Updates the completion list +//----------------------------------------------------------------------------- +void CConsolePanel::OnThink() +{ + BaseClass::OnThink(); + + if ( !IsVisible() ) + return; + + if ( !m_pCompletionList->IsVisible() ) + return; + + UpdateCompletionListPosition(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Clears the console +//----------------------------------------------------------------------------- +void CConsolePanel::Clear() +{ + m_pHistory->SetText(""); + m_pHistory->GotoTextEnd(); +} + + +//----------------------------------------------------------------------------- +// Purpose: color text print +//----------------------------------------------------------------------------- +void CConsolePanel::ColorPrint( const Color& clr, const char *msg ) +{ + if ( m_bStatusVersion ) + { + Clear(); + } + + m_pHistory->InsertColorChange( clr ); + m_pHistory->InsertString( msg ); +} + + +//----------------------------------------------------------------------------- +// Purpose: normal text print +//----------------------------------------------------------------------------- +void CConsolePanel::Print(const char *msg) +{ + ColorPrint( m_PrintColor, msg ); +} + + +//----------------------------------------------------------------------------- +// Purpose: debug text print +//----------------------------------------------------------------------------- +void CConsolePanel::DPrint( const char *msg ) +{ + ColorPrint( m_DPrintColor, msg ); +} + + +void CConsolePanel::ClearCompletionList() +{ + int c = m_CompletionList.Count(); + int i; + for ( i = c - 1; i >= 0; i-- ) + { + delete m_CompletionList[ i ]; + } + m_CompletionList.Purge(); +} + + +static ConCommand *FindAutoCompleteCommmandFromPartial( const char *partial ) +{ + char command[ 256 ]; + Q_strncpy( command, partial, sizeof( command ) ); + + char *space = Q_strstr( command, " " ); + if ( space ) + { + *space = 0; + } + + ConCommand *cmd = g_pCVar->FindCommand( command ); + if ( !cmd ) + return NULL; + + if ( !cmd->CanAutoComplete() ) + return NULL; + + return cmd; +} + + +//----------------------------------------------------------------------------- +// Purpose: rebuilds the list of possible completions from the current entered text +//----------------------------------------------------------------------------- +void CConsolePanel::RebuildCompletionList(const char *text) +{ + ClearCompletionList(); + + // we need the length of the text for the partial string compares + int len = Q_strlen(text); + if ( len < 1 ) + { + // Fill the completion list with history instead + for ( int i = 0 ; i < m_CommandHistory.Count(); i++ ) + { + CHistoryItem *item = &m_CommandHistory[ i ]; + CompletionItem *comp = new CompletionItem(); + m_CompletionList.AddToTail( comp ); + comp->m_bIsCommand = false; + comp->m_pCommand = NULL; + comp->m_pText = new CHistoryItem( *item ); + } + return; + } + + bool bNormalBuild = true; + + // if there is a space in the text, and the command isn't of the type to know how to autocomplet, then command completion is over + const char *space = strstr( text, " " ); + if ( space ) + { + ConCommand *pCommand = FindAutoCompleteCommmandFromPartial( text ); + if ( !pCommand ) + return; + + bNormalBuild = false; + + CUtlVector< CUtlString > commands; + int count = pCommand->AutoCompleteSuggest( text, commands ); + Assert( count <= COMMAND_COMPLETION_MAXITEMS ); + int i; + + for ( i = 0; i < count; i++ ) + { + // match found, add to list + CompletionItem *item = new CompletionItem(); + m_CompletionList.AddToTail( item ); + item->m_bIsCommand = false; + item->m_pCommand = NULL; + item->m_pText = new CHistoryItem( commands[ i ].String() ); + } + } + + if ( bNormalBuild ) + { + // look through the command list for all matches + ConCommandBase const *cmd = (ConCommandBase const *)cvar->GetCommands(); + while (cmd) + { + if ( cmd->IsFlagSet( FCVAR_DEVELOPMENTONLY ) || cmd->IsFlagSet( FCVAR_HIDDEN ) ) + { + cmd = cmd->GetNext(); + continue; + } + + if ( !strnicmp(text, cmd->GetName(), len)) + { + // match found, add to list + CompletionItem *item = new CompletionItem(); + m_CompletionList.AddToTail( item ); + item->m_pCommand = (ConCommandBase *)cmd; + const char *tst = cmd->GetName(); + if ( !cmd->IsCommand() ) + { + item->m_bIsCommand = false; + ConVar *var = ( ConVar * )cmd; + ConVar_ServerBounded *pBounded = dynamic_cast( var ); + if ( pBounded || var->IsFlagSet( FCVAR_NEVER_AS_STRING ) ) + { + char strValue[512]; + + int intVal = pBounded ? pBounded->GetInt() : var->GetInt(); + float floatVal = pBounded ? pBounded->GetFloat() : var->GetFloat(); + + if ( floatVal == intVal ) + Q_snprintf( strValue, sizeof( strValue ), "%d", intVal ); + else + Q_snprintf( strValue, sizeof( strValue ), "%f", floatVal ); + + item->m_pText = new CHistoryItem( var->GetName(), strValue ); + } + else + { + item->m_pText = new CHistoryItem( var->GetName(), var->GetString() ); + } + } + else + { + item->m_bIsCommand = true; + item->m_pText = new CHistoryItem( tst ); + } + } + + cmd = cmd->GetNext(); + } + + // Now sort the list by command name + if ( m_CompletionList.Count() >= 2 ) + { + for ( int i = 0 ; i < m_CompletionList.Count(); i++ ) + { + for ( int j = i + 1; j < m_CompletionList.Count(); j++ ) + { + const CompletionItem *i1, *i2; + i1 = m_CompletionList[ i ]; + i2 = m_CompletionList[ j ]; + + if ( Q_stricmp( i1->GetName(), i2->GetName() ) > 0 ) + { + CompletionItem *temp = m_CompletionList[ i ]; + m_CompletionList[ i ] = m_CompletionList[ j ]; + m_CompletionList[ j ] = temp; + } + } + } + } + } + +} + +//----------------------------------------------------------------------------- +// Purpose: auto completes current text +//----------------------------------------------------------------------------- +void CConsolePanel::OnAutoComplete(bool reverse) +{ + if (!m_bAutoCompleteMode) + { + // we're not in auto-complete mode, Start + m_iNextCompletion = 0; + m_bAutoCompleteMode = true; + } + + // if we're in reverse, move back to before the current + if (reverse) + { + m_iNextCompletion -= 2; + if (m_iNextCompletion < 0) + { + // loop around in reverse + m_iNextCompletion = m_CompletionList.Size() - 1; + } + } + + // get the next completion + if (!m_CompletionList.IsValidIndex(m_iNextCompletion)) + { + // loop completion list + m_iNextCompletion = 0; + } + + // make sure everything is still valid + if (!m_CompletionList.IsValidIndex(m_iNextCompletion)) + return; + + // match found, set text + char completedText[256]; + CompletionItem *item = m_CompletionList[m_iNextCompletion]; + Assert( item ); + + if ( !item->m_bIsCommand && item->m_pCommand ) + { + Q_strncpy(completedText, item->GetCommand(), sizeof(completedText) - 2 ); + } + else + { + Q_strncpy(completedText, item->GetItemText(), sizeof(completedText) - 2 ); + } + + if ( !Q_strstr( completedText, " " ) ) + { + Q_strncat(completedText, " ", sizeof(completedText), COPY_ALL_CHARACTERS ); + } + + m_pEntry->SetText(completedText); + m_pEntry->GotoTextEnd(); + m_pEntry->SelectNone(); + + m_iNextCompletion++; +} + + +//----------------------------------------------------------------------------- +// Purpose: Called whenever the user types text +//----------------------------------------------------------------------------- +void CConsolePanel::OnTextChanged(Panel *panel) +{ + if (panel != m_pEntry) + return; + + Q_strncpy( m_szPreviousPartialText, m_szPartialText, sizeof( m_szPreviousPartialText ) ); + + // get the partial text the user type + m_pEntry->GetText(m_szPartialText, sizeof(m_szPartialText)); + + // see if they've hit the tilde key (which opens & closes the console) + int len = Q_strlen(m_szPartialText); + + bool hitTilde = ( m_szPartialText[len - 1] == '~' || m_szPartialText[len - 1] == '`' ) ? true : false; + + bool altKeyDown = ( vgui::input()->IsKeyDown( KEY_LALT ) || vgui::input()->IsKeyDown( KEY_RALT ) ) ? true : false; + bool ctrlKeyDown = ( vgui::input()->IsKeyDown( KEY_LCONTROL ) || vgui::input()->IsKeyDown( KEY_RCONTROL ) ) ? true : false; + + // Alt-Tilde toggles Japanese IME on/off!!! + if ( ( len > 0 ) && hitTilde ) + { + // Strip the last character (tilde) + m_szPartialText[ len - 1 ] = L'\0'; + + if( !altKeyDown && !ctrlKeyDown ) + { + m_pEntry->SetText( "" ); + + // close the console + PostMessage( this, new KeyValues( "Close" ) ); + PostActionSignal( new KeyValues( "ClosedByHittingTilde" ) ); + } + else + { + m_pEntry->SetText( m_szPartialText ); + } + return; + } + + // clear auto-complete state since the user has typed + m_bAutoCompleteMode = false; + + RebuildCompletionList(m_szPartialText); + + // build the menu + if ( m_CompletionList.Count() < 1 ) + { + m_pCompletionList->SetVisible(false); + } + else + { + m_pCompletionList->SetVisible(true); + m_pCompletionList->DeleteAllItems(); + const int MAX_MENU_ITEMS = 10; + + // add the first ten items to the list + for (int i = 0; i < m_CompletionList.Count() && i < MAX_MENU_ITEMS; i++) + { + char text[256]; + text[0] = 0; + if (i == MAX_MENU_ITEMS - 1) + { + Q_strncpy(text, "...", sizeof( text ) ); + } + else + { + Assert( m_CompletionList[i] ); + Q_strncpy(text, m_CompletionList[i]->GetItemText(), sizeof( text ) ); + } + KeyValues *kv = new KeyValues("CompletionCommand"); + kv->SetString("command",text); + m_pCompletionList->AddMenuItem(text, kv, this); + } + + UpdateCompletionListPosition(); + } + + RequestFocus(); + m_pEntry->RequestFocus(); + +} + +//----------------------------------------------------------------------------- +// Purpose: generic vgui command handler +//----------------------------------------------------------------------------- +void CConsolePanel::OnCommand(const char *command) +{ + if ( !Q_stricmp( command, "Submit" ) ) + { + // submit the entry as a console commmand + char szCommand[256]; + m_pEntry->GetText(szCommand, sizeof(szCommand)); + PostActionSignal( new KeyValues( "CommandSubmitted", "command", szCommand ) ); + + // add to the history + Print("] "); + Print(szCommand); + Print("\n"); + + // clear the field + m_pEntry->SetText(""); + + // clear the completion state + OnTextChanged(m_pEntry); + + // always go the end of the buffer when the user has typed something + m_pHistory->GotoTextEnd(); + + // Add the command to the history + char *extra = strchr(szCommand, ' '); + if ( extra ) + { + *extra = '\0'; + extra++; + } + + if ( Q_strlen( szCommand ) > 0 ) + { + AddToHistory( szCommand, extra ); + } + m_pCompletionList->SetVisible(false); + } + else + { + BaseClass::OnCommand(command); + } +} + + +//----------------------------------------------------------------------------- +// Focus related methods +//----------------------------------------------------------------------------- +bool CConsolePanel::TextEntryHasFocus() const +{ + return ( input()->GetFocus() == m_pEntry->GetVPanel() ); +} + +void CConsolePanel::TextEntryRequestFocus() +{ + m_pEntry->RequestFocus(); +} + + +//----------------------------------------------------------------------------- +// Purpose: swallows tab key pressed +//----------------------------------------------------------------------------- +void CConsolePanel::OnKeyCodeTyped(KeyCode code) +{ + BaseClass::OnKeyCodeTyped(code); + + // check for processing + if ( TextEntryHasFocus() ) + { + if (code == KEY_TAB) + { + bool reverse = false; + if (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)) + { + reverse = true; + } + + // attempt auto-completion + OnAutoComplete(reverse); + m_pEntry->RequestFocus(); + } + else if (code == KEY_DOWN) + { + OnAutoComplete(false); + // UpdateCompletionListPosition(); + // m_pCompletionList->SetVisible(true); + + m_pEntry->RequestFocus(); + } + else if (code == KEY_UP) + { + OnAutoComplete(true); + m_pEntry->RequestFocus(); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: lays out controls +//----------------------------------------------------------------------------- +void CConsolePanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + // setup tab ordering + GetFocusNavGroup().SetDefaultButton(m_pSubmit); + + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + m_pEntry->SetBorder(pScheme->GetBorder("DepressedButtonBorder")); + m_pHistory->SetBorder(pScheme->GetBorder("DepressedButtonBorder")); + + // layout controls + int wide, tall; + GetSize(wide, tall); + + if ( !m_bStatusVersion ) + { + const int inset = 8; + const int entryHeight = 24; + const int topHeight = 4; + const int entryInset = 4; + const int submitWide = 64; + const int submitInset = 7; // x inset to pull the submit button away from the frame grab + + m_pHistory->SetPos(inset, inset + topHeight); + m_pHistory->SetSize(wide - (inset * 2), tall - (entryInset * 2 + inset * 2 + topHeight + entryHeight)); + m_pHistory->InvalidateLayout(); + + int nSubmitXPos = wide - ( inset + submitWide + submitInset ); + m_pSubmit->SetPos( nSubmitXPos, tall - (entryInset * 2 + entryHeight)); + m_pSubmit->SetSize( submitWide, entryHeight); + + m_pEntry->SetPos( inset, tall - (entryInset * 2 + entryHeight) ); + m_pEntry->SetSize( nSubmitXPos - entryInset - 2 * inset, entryHeight); + } + else + { + const int inset = 2; + + int entryWidth = wide / 2; + if ( wide > 400 ) + { + entryWidth = 200; + } + + m_pEntry->SetBounds( inset, inset, entryWidth, tall - 2 * inset ); + + m_pHistory->SetBounds( inset + entryWidth + inset, inset, ( wide - entryWidth ) - inset, tall - 2 * inset ); + } + + UpdateCompletionListPosition(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the position of the completion list popup +//----------------------------------------------------------------------------- +void CConsolePanel::UpdateCompletionListPosition() +{ + int ex, ey; + m_pEntry->GetPos(ex, ey); + + if ( !m_bStatusVersion ) + { + // Position below text entry + ey += m_pEntry->GetTall(); + } + else + { + // Position above text entry + int menuwide, menutall; + m_pCompletionList->GetSize( menuwide, menutall ); + ey -= ( menutall + 4 ); + } + + LocalToScreen( ex, ey ); + m_pCompletionList->SetPos( ex, ey ); + + if ( m_pCompletionList->IsVisible() ) + { + m_pEntry->RequestFocus(); + MoveToFront(); + m_pCompletionList->MoveToFront(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Closes the completion list +//----------------------------------------------------------------------------- +void CConsolePanel::CloseCompletionList() +{ + m_pCompletionList->SetVisible(false); +} + +//----------------------------------------------------------------------------- +// Purpose: sets up colors +//----------------------------------------------------------------------------- +void CConsolePanel::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + m_PrintColor = GetSchemeColor("Console.TextColor", pScheme); + m_DPrintColor = GetSchemeColor("Console.DevTextColor", pScheme); + m_pHistory->SetFont( pScheme->GetFont( "ConsoleText", IsProportional() ) ); + m_pCompletionList->SetFont( pScheme->GetFont( "DefaultSmall", IsProportional() ) ); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Handles autocompletion menu input +//----------------------------------------------------------------------------- +void CConsolePanel::OnMenuItemSelected(const char *command) +{ + if ( strstr( command, "..." ) ) // stop the menu going away if you click on ... + { + m_pCompletionList->SetVisible( true ); + } + else + { + m_pEntry->SetText(command); + m_pEntry->GotoTextEnd(); + m_pEntry->InsertChar(' '); + m_pEntry->GotoTextEnd(); + } +} + +void CConsolePanel::Hide() +{ + OnClose(); + m_iNextCompletion = 0; + RebuildCompletionList(""); +} + +void CConsolePanel::AddToHistory( const char *commandText, const char *extraText ) +{ + // Newest at end, oldest at head + while ( m_CommandHistory.Count() >= MAX_HISTORY_ITEMS ) + { + // Remove from head until size is reasonable + m_CommandHistory.Remove( 0 ); + } + + // strip the space off the end of the command before adding it to the history + // If this code gets cleaned up then we should remove the redundant calls to strlen, + // the check for whether _alloca succeeded, and should use V_strncpy instead of the + // error prone memset/strncpy sequence. + char *command = static_cast( _alloca( (strlen( commandText ) + 1 ) * sizeof( char ) )); + if ( command ) + { + memset( command, 0x0, strlen( commandText ) + 1 ); + strncpy( command, commandText, strlen( commandText )); + // There is no actual bug here, just some sloppy/odd code. + // src\vgui2\vgui_controls\consoledialog.cpp(974): warning C6053: The prior call to 'strncpy' might not zero-terminate string 'command' + ANALYZE_SUPPRESS( 6053 ) + if ( command[ strlen( command ) -1 ] == ' ' ) + { + command[ strlen( command ) -1 ] = '\0'; + } + } + + // strip the quotes off the extra text + char *extra = NULL; + + if ( extraText ) + { + extra = static_cast( malloc( (strlen( extraText ) + 1 ) * sizeof( char ) )); + if ( extra ) + { + memset( extra, 0x0, strlen( extraText ) + 1 ); + strncpy( extra, extraText, strlen( extraText )); // +1 to dodge the starting quote + + // Strip trailing spaces + int i = strlen( extra ) - 1; + while ( i >= 0 && // Check I before referencing i == -1 into the extra array! + extra[ i ] == ' ' ) + { + extra[ i ] = '\0'; + i--; + } + } + } + + // If it's already there, then remove since we'll add it to the end instead + CHistoryItem *item = NULL; + for ( int i = m_CommandHistory.Count() - 1; i >= 0; i-- ) + { + item = &m_CommandHistory[ i ]; + if ( !item ) + continue; + + if ( stricmp( item->GetText(), command ) ) + continue; + + if ( extra || item->GetExtra() ) + { + if ( !extra || !item->GetExtra() ) + continue; + + // stricmp so two commands with the same starting text get added + if ( stricmp( item->GetExtra(), extra ) ) + continue; + } + m_CommandHistory.Remove( i ); + } + + item = &m_CommandHistory[ m_CommandHistory.AddToTail() ]; + Assert( item ); + item->SetText( command, extra ); + + m_iNextCompletion = 0; + RebuildCompletionList( m_szPartialText ); + + free( extra ); +} + +void CConsolePanel::GetConsoleText( char *pchText, size_t bufSize ) const +{ + wchar_t *temp = new wchar_t[ bufSize ]; + m_pHistory->GetText( 0, temp, bufSize * sizeof( wchar_t ) ); + g_pVGuiLocalize->ConvertUnicodeToANSI( temp, pchText, bufSize ); + delete[] temp; +} + +//----------------------------------------------------------------------------- +// Purpose: writes out console to disk +//----------------------------------------------------------------------------- +void CConsolePanel::DumpConsoleTextToFile() +{ + const int CONDUMP_FILES_MAX_NUM = 1000; + + FileHandle_t handle; + bool found = false; + char szfile[ 512 ]; + + // we don't want to overwrite other condump.txt files + for ( int i = 0 ; i < CONDUMP_FILES_MAX_NUM ; ++i ) + { + _snprintf( szfile, sizeof(szfile), "condump%03d.txt", i ); + if ( !g_pFullFileSystem->FileExists(szfile) ) + { + found = true; + break; + } + } + + if ( !found ) + { + Print( "Can't condump! Too many existing condump output files in the gamedir!\n" ); + return; + } + + handle = g_pFullFileSystem->Open( szfile, "wb" ); + if ( handle != FILESYSTEM_INVALID_HANDLE ) + { + int pos = 0; + while (1) + { + wchar_t buf[512]; + m_pHistory->GetText(pos, buf, sizeof(buf)); + pos += sizeof(buf) / sizeof(wchar_t); + + // don't continue if none left + if (buf[0] == 0) + break; + + // convert to ansi + char ansi[512]; + g_pVGuiLocalize->ConvertUnicodeToANSI(buf, ansi, sizeof(ansi)); + + // write to disk + int len = strlen(ansi); + for (int i = 0; i < len; i++) + { + // preceed newlines with a return + if (ansi[i] == '\n') + { + char ret = '\r'; + g_pFullFileSystem->Write( &ret, 1, handle ); + } + + g_pFullFileSystem->Write( ansi + i, 1, handle ); + } + } + + g_pFullFileSystem->Close( handle ); + + Print( "console dumped to " ); + Print( szfile ); + Print( "\n" ); + } + else + { + Print( "Unable to condump to " ); + Print( szfile ); + Print( "\n" ); + } +} + + +//----------------------------------------------------------------------------- +// +// Console dialog starts here +// +//----------------------------------------------------------------------------- +CConsoleDialog::CConsoleDialog( vgui::Panel *pParent, const char *pName, bool bStatusVersion ) : + BaseClass( pParent, pName ) +{ + // initialize dialog + SetVisible( false ); + SetTitle( "#Console_Title", true ); + m_pConsolePanel = new CConsolePanel( this, "ConsolePage", bStatusVersion ); + m_pConsolePanel->AddActionSignalTarget( this ); +} + +void CConsoleDialog::OnScreenSizeChanged( int iOldWide, int iOldTall ) +{ + BaseClass::OnScreenSizeChanged( iOldWide, iOldTall ); + + int sx, sy; + surface()->GetScreenSize( sx, sy ); + + int w, h; + GetSize( w, h ); + if ( w > sx || h > sy ) + { + if ( w > sx ) + { + w = sx; + } + if ( h > sy ) + { + h = sy; + } + + // Try and lower the size to match the screen bounds + SetSize( w, h ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: brings dialog to the fore +//----------------------------------------------------------------------------- +void CConsoleDialog::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y, w, h; + GetClientArea( x, y, w, h ); + m_pConsolePanel->SetBounds( x, y, w, h ); +} + + +//----------------------------------------------------------------------------- +// Purpose: brings dialog to the fore +//----------------------------------------------------------------------------- +void CConsoleDialog::Activate() +{ + BaseClass::Activate(); + m_pConsolePanel->m_pEntry->RequestFocus(); +} + + +//----------------------------------------------------------------------------- +// Hides the dialog +//----------------------------------------------------------------------------- +void CConsoleDialog::Hide() +{ + OnClose(); + m_pConsolePanel->Hide(); +} + + +//----------------------------------------------------------------------------- +// Close just hides the dialog +//----------------------------------------------------------------------------- +void CConsoleDialog::Close() +{ + Hide(); +} + + +//----------------------------------------------------------------------------- +// Submits commands +//----------------------------------------------------------------------------- +void CConsoleDialog::OnCommandSubmitted( const char *pCommand ) +{ + PostActionSignal( new KeyValues( "CommandSubmitted", "command", pCommand ) ); +} + + +//----------------------------------------------------------------------------- +// Chain to the page +//----------------------------------------------------------------------------- +void CConsoleDialog::Print( const char *pMessage ) +{ + m_pConsolePanel->Print( pMessage ); +} + +void CConsoleDialog::DPrint( const char *pMessage ) +{ + m_pConsolePanel->DPrint( pMessage ); +} + +void CConsoleDialog::ColorPrint( const Color& clr, const char *msg ) +{ + m_pConsolePanel->ColorPrint( clr, msg ); +} + +void CConsoleDialog::Clear() +{ + m_pConsolePanel->Clear( ); +} + +void CConsoleDialog::DumpConsoleTextToFile() +{ + m_pConsolePanel->DumpConsoleTextToFile( ); +} + + +void CConsoleDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + if ( code == KEY_XBUTTON_B ) + { + Hide(); + } + else + { + BaseClass::OnKeyCodePressed(code); + } } \ No newline at end of file diff --git a/mp/src/vgui2/vgui_controls/controls.cpp b/mp/src/vgui2/vgui_controls/controls.cpp index 2da48c2b..edc89e49 100644 --- a/mp/src/vgui2/vgui_controls/controls.cpp +++ b/mp/src/vgui2/vgui_controls/controls.cpp @@ -1,72 +1,72 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//===========================================================================// - -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern int g_nYou_Must_Add_Public_Vgui_Controls_Vgui_ControlsCpp_To_Your_Project; - -namespace vgui -{ - -static char g_szControlsModuleName[256]; - -//----------------------------------------------------------------------------- -// Purpose: Initializes the controls -//----------------------------------------------------------------------------- -extern "C" { extern int _heapmin(); } - -bool VGui_InitInterfacesList( const char *moduleName, CreateInterfaceFn *factoryList, int numFactories ) -{ - g_nYou_Must_Add_Public_Vgui_Controls_Vgui_ControlsCpp_To_Your_Project = 1; - - // If you hit this error, then you need to include memoverride.cpp in the project somewhere or else - // you'll get crashes later when vgui_controls allocates KeyValues and vgui tries to delete them. -#if !defined(NO_MALLOC_OVERRIDE) && defined( WIN32 ) - if ( _heapmin() != 1 ) - { - Assert( false ); - Error( "Must include memoverride.cpp in your project." ); - } -#endif - // keep a record of this module name - strncpy(g_szControlsModuleName, moduleName, sizeof(g_szControlsModuleName)); - g_szControlsModuleName[sizeof(g_szControlsModuleName) - 1] = 0; - - // initialize our locale (must be done for every vgui dll/exe) - // "" makes it use the default locale, required to make iswprint() work correctly in different languages - setlocale(LC_CTYPE, ""); - setlocale(LC_TIME, ""); - setlocale(LC_COLLATE, ""); - setlocale(LC_MONETARY, ""); - - // NOTE: Vgui expects to use these interfaces which are defined in tier3.lib - if ( !g_pVGui || !g_pVGuiInput || !g_pVGuiPanel || - !g_pVGuiSurface || !g_pVGuiSchemeManager || !g_pVGuiSystem ) - { - Warning( "vgui_controls is missing a required interface!\n" ); - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: returns the name of the module this has been compiled into -//----------------------------------------------------------------------------- -const char *GetControlsModuleName() -{ - return g_szControlsModuleName; -} - -} // namespace vgui - - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern int g_nYou_Must_Add_Public_Vgui_Controls_Vgui_ControlsCpp_To_Your_Project; + +namespace vgui +{ + +static char g_szControlsModuleName[256]; + +//----------------------------------------------------------------------------- +// Purpose: Initializes the controls +//----------------------------------------------------------------------------- +extern "C" { extern int _heapmin(); } + +bool VGui_InitInterfacesList( const char *moduleName, CreateInterfaceFn *factoryList, int numFactories ) +{ + g_nYou_Must_Add_Public_Vgui_Controls_Vgui_ControlsCpp_To_Your_Project = 1; + + // If you hit this error, then you need to include memoverride.cpp in the project somewhere or else + // you'll get crashes later when vgui_controls allocates KeyValues and vgui tries to delete them. +#if !defined(NO_MALLOC_OVERRIDE) && defined( WIN32 ) + if ( _heapmin() != 1 ) + { + Assert( false ); + Error( "Must include memoverride.cpp in your project." ); + } +#endif + // keep a record of this module name + strncpy(g_szControlsModuleName, moduleName, sizeof(g_szControlsModuleName)); + g_szControlsModuleName[sizeof(g_szControlsModuleName) - 1] = 0; + + // initialize our locale (must be done for every vgui dll/exe) + // "" makes it use the default locale, required to make iswprint() work correctly in different languages + setlocale(LC_CTYPE, ""); + setlocale(LC_TIME, ""); + setlocale(LC_COLLATE, ""); + setlocale(LC_MONETARY, ""); + + // NOTE: Vgui expects to use these interfaces which are defined in tier3.lib + if ( !g_pVGui || !g_pVGuiInput || !g_pVGuiPanel || + !g_pVGuiSurface || !g_pVGuiSchemeManager || !g_pVGuiSystem ) + { + Warning( "vgui_controls is missing a required interface!\n" ); + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the name of the module this has been compiled into +//----------------------------------------------------------------------------- +const char *GetControlsModuleName() +{ + return g_szControlsModuleName; +} + +} // namespace vgui + + + diff --git a/mp/src/vgui2/vgui_controls/cvartogglecheckbutton.cpp b/mp/src/vgui2/vgui_controls/cvartogglecheckbutton.cpp index eb8a900b..0db6deba 100644 --- a/mp/src/vgui2/vgui_controls/cvartogglecheckbutton.cpp +++ b/mp/src/vgui2/vgui_controls/cvartogglecheckbutton.cpp @@ -1,25 +1,25 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "tier1/KeyValues.h" - -#include -#include -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -vgui::Panel *Create_CvarToggleCheckButton() -{ - return new CvarToggleCheckButton< ConVarRef >( NULL, NULL ); -} - -DECLARE_BUILD_FACTORY_CUSTOM_ALIAS( CvarToggleCheckButton, CvarToggleCheckButton, Create_CvarToggleCheckButton ); - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "tier1/KeyValues.h" + +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +vgui::Panel *Create_CvarToggleCheckButton() +{ + return new CvarToggleCheckButton< ConVarRef >( NULL, NULL ); +} + +DECLARE_BUILD_FACTORY_CUSTOM_ALIAS( CvarToggleCheckButton, CvarToggleCheckButton, Create_CvarToggleCheckButton ); + diff --git a/mp/src/vgui2/vgui_controls/perforcefilelistframe.cpp b/mp/src/vgui2/vgui_controls/perforcefilelistframe.cpp index e761ec5c..b6abbf6f 100644 --- a/mp/src/vgui2/vgui_controls/perforcefilelistframe.cpp +++ b/mp/src/vgui2/vgui_controls/perforcefilelistframe.cpp @@ -1,647 +1,647 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// List of perforce files and operations -// -//============================================================================= - -#include "vgui_controls/perforcefilelistframe.h" -#include "tier1/KeyValues.h" -#include "vgui_controls/Button.h" -#include "vgui_controls/ListPanel.h" -#include "vgui_controls/Splitter.h" -#include "vgui_controls/TextEntry.h" -#include "vgui_controls/MessageBox.h" -#include "tier2/tier2.h" -#include "p4lib/ip4.h" -#include "filesystem.h" -#include "vgui/IVGui.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - - -//----------------------------------------------------------------------------- -// Sort by asset name -//----------------------------------------------------------------------------- -static int __cdecl OperationSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) -{ - const char *string1 = item1.kv->GetString("operation"); - const char *string2 = item2.kv->GetString("operation"); - int nRetVal = Q_stricmp( string1, string2 ); - if ( nRetVal != 0 ) - return nRetVal; - - string1 = item1.kv->GetString("filename"); - string2 = item2.kv->GetString("filename"); - return Q_stricmp( string1, string2 ); -} - -static int __cdecl FileBrowserSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) -{ - const char *string1 = item1.kv->GetString("filename"); - const char *string2 = item2.kv->GetString("filename"); - return Q_stricmp( string1, string2 ); -} - - -//----------------------------------------------------------------------------- -// Constructor -//----------------------------------------------------------------------------- -COperationFileListFrame::COperationFileListFrame( vgui::Panel *pParent, const char *pTitle, const char *pColumnHeader, bool bShowDescription, bool bShowOkOnly, int nDialogID ) : - BaseClass( pParent, "PerforceFileList" ) -{ - m_pText = NULL; - vgui::Panel *pBrowserParent = this; - - m_pDescription = NULL; - m_pSplitter = NULL; - if ( bShowDescription ) - { - m_pSplitter = new vgui::Splitter( this, "Splitter", vgui::SPLITTER_MODE_HORIZONTAL, 1 ); - - pBrowserParent = m_pSplitter->GetChild( 0 ); - vgui::Panel *pDescParent = m_pSplitter->GetChild( 1 ); - - m_pDescription = new vgui::TextEntry( pDescParent, "Description" ); - m_pDescription->SetMultiline( true ); - m_pDescription->SetCatchEnterKey( true ); - m_pDescription->SetText( "" ); - } - - // FIXME: Might be nice to have checkboxes per row - m_pFileBrowser = new vgui::ListPanel( pBrowserParent, "Browser" ); - m_pFileBrowser->AddColumnHeader( 0, "operation", "Operation", 52, 0 ); - m_pFileBrowser->AddColumnHeader( 1, "filename", pColumnHeader, 128, vgui::ListPanel::COLUMN_RESIZEWITHWINDOW ); - m_pFileBrowser->SetSelectIndividualCells( false ); - m_pFileBrowser->SetMultiselectEnabled( false ); - m_pFileBrowser->SetEmptyListText( "No Perforce Operations" ); - m_pFileBrowser->SetDragEnabled( true ); - m_pFileBrowser->AddActionSignalTarget( this ); - m_pFileBrowser->SetSortFunc( 0, OperationSortFunc ); - m_pFileBrowser->SetSortFunc( 1, FileBrowserSortFunc ); - m_pFileBrowser->SetSortColumn( 0 ); - - m_pYesButton = new vgui::Button( this, "YesButton", "Yes", this, "Yes" ); - m_pNoButton = new vgui::Button( this, "NoButton", "No", this, "No" ); - - SetBlockDragChaining( true ); - SetDeleteSelfOnClose( true ); - - if ( bShowDescription ) - { - LoadControlSettingsAndUserConfig( "resource/perforcefilelistdescription.res", nDialogID ); - } - else - { - LoadControlSettingsAndUserConfig( "resource/perforcefilelist.res", nDialogID ); - } - - if ( bShowOkOnly ) - { - m_pYesButton->SetText( "#MessageBox_OK" ); - m_pNoButton->SetVisible( false ); - } - - m_pContextKeyValues = NULL; - - SetTitle( pTitle, false ); -} - -COperationFileListFrame::~COperationFileListFrame() -{ - SaveUserConfig(); - CleanUpMessage(); - if ( m_pText ) - { - delete[] m_pText; - } -} - - -//----------------------------------------------------------------------------- -// Deletes the message -//----------------------------------------------------------------------------- -void COperationFileListFrame::CleanUpMessage() -{ - if ( m_pContextKeyValues ) - { - m_pContextKeyValues->deleteThis(); - m_pContextKeyValues = NULL; - } -} - - -//----------------------------------------------------------------------------- -// Performs layout -//----------------------------------------------------------------------------- -void COperationFileListFrame::PerformLayout() -{ - BaseClass::PerformLayout(); - - if ( m_pSplitter ) - { - int x, y, w, h; - GetClientArea( x, y, w, h ); - y += 6; - h -= 36; - m_pSplitter->SetBounds( x, y, w, h ); - } -} - - -//----------------------------------------------------------------------------- -// Adds files to the frame -//----------------------------------------------------------------------------- -void COperationFileListFrame::ClearAllOperations() -{ - m_pFileBrowser->RemoveAll(); -} - - -//----------------------------------------------------------------------------- -// Adds the strings to the list panel -//----------------------------------------------------------------------------- -void COperationFileListFrame::AddOperation( const char *pOperation, const char *pFileName ) -{ - KeyValues *kv = new KeyValues( "node", "filename", pFileName ); - kv->SetString( "operation", pOperation ); - m_pFileBrowser->AddItem( kv, 0, false, false ); -} - -void COperationFileListFrame::AddOperation( const char *pOperation, const char *pFileName, const Color& clr ) -{ - KeyValues *kv = new KeyValues( "node", "filename", pFileName ); - kv->SetString( "operation", pOperation ); - kv->SetColor( "cellcolor", clr ); - m_pFileBrowser->AddItem( kv, 0, false, false ); -} - - -//----------------------------------------------------------------------------- -// Resizes the operation column to fit the operation text -//----------------------------------------------------------------------------- -void COperationFileListFrame::ResizeOperationColumnToContents() -{ - m_pFileBrowser->ResizeColumnToContents( 0 ); -} - - -//----------------------------------------------------------------------------- -// Sets the column header for the 'operation' column -//----------------------------------------------------------------------------- -void COperationFileListFrame::SetOperationColumnHeaderText( const char *pText ) -{ - m_pFileBrowser->SetColumnHeaderText( 0, pText ); -} - - -//----------------------------------------------------------------------------- -// Adds the strings to the list panel -//----------------------------------------------------------------------------- -void COperationFileListFrame::DoModal( KeyValues *pContextKeyValues, const char *pMessage ) -{ - m_MessageName = pMessage ? pMessage : "OperationConfirmed"; - CleanUpMessage(); - m_pContextKeyValues = pContextKeyValues; - m_pFileBrowser->SortList(); - if ( m_pNoButton->IsVisible() ) - { - m_pYesButton->SetEnabled( m_pFileBrowser->GetItemCount() != 0 ); - } - BaseClass::DoModal(); -} - - -//----------------------------------------------------------------------------- -// Retrieves the number of files, the file names, and operations -//----------------------------------------------------------------------------- -int COperationFileListFrame::GetOperationCount() -{ - return m_pFileBrowser->GetItemCount(); -} - -const char *COperationFileListFrame::GetFileName( int i ) -{ - int nItemId = m_pFileBrowser->GetItemIDFromRow( i ); - KeyValues *pKeyValues = m_pFileBrowser->GetItem( nItemId ); - return pKeyValues->GetString( "filename" ); -} - -const char *COperationFileListFrame::GetOperation( int i ) -{ - int nItemId = m_pFileBrowser->GetItemIDFromRow( i ); - KeyValues *pKeyValues = m_pFileBrowser->GetItem( nItemId ); - return pKeyValues->GetString( "operation" ); -} - - -//----------------------------------------------------------------------------- -// Retreives the description (only if it was shown) -//----------------------------------------------------------------------------- -const char *COperationFileListFrame::GetDescription() -{ - return m_pText; -} - - -//----------------------------------------------------------------------------- -// Returns the message name -//----------------------------------------------------------------------------- -const char *COperationFileListFrame::CompletionMessage() -{ - return m_MessageName; -} - - -//----------------------------------------------------------------------------- -// On command -//----------------------------------------------------------------------------- -void COperationFileListFrame::OnCommand( const char *pCommand ) -{ - if ( !Q_stricmp( pCommand, "Yes" ) ) - { - if ( m_pDescription ) - { - int nLen = m_pDescription->GetTextLength() + 1; - m_pText = new char[ nLen ]; - m_pDescription->GetText( m_pText, nLen ); - } - - KeyValues *pActionKeys; - if ( PerformOperation() ) - { - pActionKeys = new KeyValues( CompletionMessage(), "operationPerformed", 1 ); - } - else - { - pActionKeys = new KeyValues( CompletionMessage(), "operationPerformed", 0 ); - } - - if ( m_pContextKeyValues ) - { - pActionKeys->AddSubKey( m_pContextKeyValues ); - m_pContextKeyValues = NULL; - } - CloseModal(); - PostActionSignal( pActionKeys ); - return; - } - - if ( !Q_stricmp( pCommand, "No" ) ) - { - KeyValues *pActionKeys = new KeyValues( CompletionMessage(), "operationPerformed", 0 ); - if ( m_pContextKeyValues ) - { - pActionKeys->AddSubKey( m_pContextKeyValues ); - m_pContextKeyValues = NULL; - } - CloseModal(); - PostActionSignal( pActionKeys ); - return; - } - - BaseClass::OnCommand( pCommand ); -} - - -//----------------------------------------------------------------------------- -// -// Version that does the work of perforce actions -// -//----------------------------------------------------------------------------- -CPerforceFileListFrame::CPerforceFileListFrame( vgui::Panel *pParent, const char *pTitle, const char *pColumnHeader, PerforceAction_t action ) : - BaseClass( pParent, pTitle, pColumnHeader, (action == PERFORCE_ACTION_FILE_SUBMIT), false, OPERATION_DIALOG_ID_PERFORCE ) -{ - m_Action = action; -} - -CPerforceFileListFrame::~CPerforceFileListFrame() -{ -} - - -//----------------------------------------------------------------------------- -// Activates the modal dialog -//----------------------------------------------------------------------------- -void CPerforceFileListFrame::DoModal( KeyValues *pContextKeys, const char *pMessage ) -{ - BaseClass::DoModal( pContextKeys, pMessage ? pMessage : "PerforceActionConfirmed" ); -} - - -//----------------------------------------------------------------------------- -// Adds a file for open -//----------------------------------------------------------------------------- -void CPerforceFileListFrame::AddFileForOpen( const char *pFullPath ) -{ - if ( !p4 ) - return; - - bool bIsInPerforce = p4->IsFileInPerforce( pFullPath ); - bool bIsOpened = ( p4->GetFileState( pFullPath ) != P4FILE_UNOPENED ); - switch( m_Action ) - { - case PERFORCE_ACTION_FILE_ADD: - if ( !bIsInPerforce && !bIsOpened ) - { - AddOperation( "Add", pFullPath ); - } - break; - - case PERFORCE_ACTION_FILE_EDIT: - if ( bIsInPerforce && !bIsOpened ) - { - AddOperation( "Edit", pFullPath ); - } - break; - - case PERFORCE_ACTION_FILE_DELETE: - if ( bIsInPerforce && !bIsOpened ) - { - AddOperation( "Delete", pFullPath ); - } - break; - } -} - - -//----------------------------------------------------------------------------- -// Add files to dialog for submit/revert dialogs -//----------------------------------------------------------------------------- -void CPerforceFileListFrame::AddFileForSubmit( const char *pFullPath, P4FileState_t state ) -{ - if ( state == P4FILE_UNOPENED ) - return; - - char pBuf[128]; - const char *pPrefix = (m_Action == PERFORCE_ACTION_FILE_REVERT) ? "Revert" : "Submit"; - switch( state ) - { - case P4FILE_OPENED_FOR_ADD: - Q_snprintf( pBuf, sizeof(pBuf), "%s Add", pPrefix ); - AddOperation( pBuf, pFullPath ); - break; - - case P4FILE_OPENED_FOR_EDIT: - Q_snprintf( pBuf, sizeof(pBuf), "%s Edit", pPrefix ); - AddOperation( pBuf, pFullPath ); - break; - - case P4FILE_OPENED_FOR_DELETE: - Q_snprintf( pBuf, sizeof(pBuf), "%s Delete", pPrefix ); - AddOperation( pBuf, pFullPath ); - break; - - case P4FILE_OPENED_FOR_INTEGRATE: - Q_snprintf( pBuf, sizeof(pBuf), "%s Integrate", pPrefix ); - AddOperation( pBuf, pFullPath ); - break; - } -} - - -//----------------------------------------------------------------------------- -// Version of AddFile that accepts full paths -//----------------------------------------------------------------------------- -void CPerforceFileListFrame::AddFile( const char *pFullPath ) -{ - if ( !p4 ) - return; - - if ( m_Action < PERFORCE_ACTION_FILE_REVERT ) - { - // If the file wasn't found on the disk, then abort - if ( g_pFullFileSystem->FileExists( pFullPath, NULL ) ) - { - AddFileForOpen( pFullPath ); - } - return; - } - - // Deal with submit, revert - bool bFileExists = g_pFullFileSystem->FileExists( pFullPath, NULL ); - P4FileState_t state = p4->GetFileState( pFullPath ); - if ( bFileExists || (state == P4FILE_OPENED_FOR_DELETE) ) - { - AddFileForSubmit( pFullPath, state ); - } -} - - -//----------------------------------------------------------------------------- -// Version of AddFile that accepts relative paths + search path ids -//----------------------------------------------------------------------------- -void CPerforceFileListFrame::AddFile( const char *pRelativePath, const char *pPathId ) -{ - if ( !p4 ) - return; - - // Deal with add, open, edit - if ( m_Action < PERFORCE_ACTION_FILE_REVERT ) - { - // If the file wasn't found on the disk, then abort - if ( g_pFullFileSystem->FileExists( pRelativePath, pPathId ) ) - { - char pFullPath[MAX_PATH]; - g_pFullFileSystem->RelativePathToFullPath( pRelativePath, pPathId, pFullPath, sizeof( pFullPath ) ); - AddFileForOpen( pFullPath ); - } - return; - } - - // Deal with submit, revert - - // First, handle the case where the file exists on the drive - char pFullPath[MAX_PATH]; - if ( g_pFullFileSystem->FileExists( pRelativePath, pPathId ) ) - { - g_pFullFileSystem->RelativePathToFullPath( pRelativePath, pPathId, pFullPath, sizeof( pFullPath ) ); - P4FileState_t state = p4->GetFileState( pFullPath ); - AddFileForSubmit( pFullPath, state ); - return; - } - - // Get the list of opened files, cache it off so we aren't continually reasking - if ( Q_stricmp( pPathId, m_LastOpenedFilePathId ) ) - { - p4->GetOpenedFileListInPath( pPathId, m_OpenedFiles ); - m_LastOpenedFilePathId = pPathId; - } - - // If the file doesn't exist, it was opened for delete. - // Using the client spec of the path, we need to piece together - // the full path; the full path unfortunately is usually ambiguous: - // you can never exactly know which mod it came from. - char pTemp[MAX_PATH]; - char pSearchString[MAX_PATH]; - Q_strncpy( pSearchString, pRelativePath, sizeof(pSearchString) ); - Q_FixSlashes( pSearchString ); - - int k; - int nOpenedFileCount = m_OpenedFiles.Count(); - for ( k = 0; k < nOpenedFileCount; ++k ) - { - if ( m_OpenedFiles[k].m_eOpenState != P4FILE_OPENED_FOR_DELETE ) - continue; - - // Check to see if the end of the local file matches the file - const char *pLocalFile = p4->String( m_OpenedFiles[k].m_sLocalFile ); - - // This ensures the full path lies under the search path - if ( !g_pFullFileSystem->FullPathToRelativePathEx( pLocalFile, pPathId, pTemp, sizeof(pTemp) ) ) - continue; - - // The relative paths had better be the same - if ( Q_stricmp( pTemp, pSearchString ) ) - continue; - - AddFileForSubmit( pLocalFile, m_OpenedFiles[k].m_eOpenState ); - break; - } -} - - -//----------------------------------------------------------------------------- -// Does the perforce operation -//----------------------------------------------------------------------------- -bool CPerforceFileListFrame::PerformOperation( ) -{ - if ( !p4 ) - return false; - - int nFileCount = GetOperationCount(); - const char **ppFileNames = (const char**)_alloca( nFileCount * sizeof(char*) ); - for ( int i = 0; i < nFileCount; ++i ) - { - ppFileNames[i] = GetFileName( i ); - } - - bool bSuccess = false; - - switch ( m_Action ) - { - case PERFORCE_ACTION_FILE_ADD: - bSuccess = p4->OpenFilesForAdd( nFileCount, ppFileNames ); - break; - - case PERFORCE_ACTION_FILE_EDIT: - bSuccess = p4->OpenFilesForEdit( nFileCount, ppFileNames ); - break; - - case PERFORCE_ACTION_FILE_DELETE: - bSuccess = p4->OpenFilesForDelete( nFileCount, ppFileNames ); - break; - - case PERFORCE_ACTION_FILE_REVERT: - bSuccess = p4->RevertFiles( nFileCount, ppFileNames ); - break; - - case PERFORCE_ACTION_FILE_SUBMIT: - { - // Ensure a description was added - const char *pDescription = GetDescription(); - if ( !pDescription[0] || !Q_stricmp( pDescription, "" ) ) - { - vgui::MessageBox *pError = new vgui::MessageBox( "Submission Error!", "Description required for submission.", GetParent() ); - pError->SetSmallCaption( true ); - pError->DoModal(); - return false; - } - else - { - bSuccess = p4->SubmitFiles( nFileCount, ppFileNames, pDescription ); - } - } - break; - } - - const char *pErrorString = p4->GetLastError(); - if ( !bSuccess ) - { - vgui::MessageBox *pError = new vgui::MessageBox( "Perforce Error!", pErrorString, GetParent() ); - pError->SetSmallCaption( true ); - pError->DoModal(); - } -#if 0 - if ( *pErrorString ) - { - if ( V_strstr( pErrorString, "opened for add" ) ) - return bSuccess; - if ( V_strstr( pErrorString, "opened for edit" ) ) - return bSuccess; - // TODO - figure out the rest of these... - - const char *pPrefix = "Perforce has generated the following message which may or may not be an error.\n" - "Please email joe with the text of the message, whether you think it was an error, and what perforce operation you where performing.\n" - "To copy the message, hit ~ to enter the console, where you will find the message reprinted.\n" - "Select the lines of text in the message, right click, select Copy, and then paste into an email message.\n\n"; - static int nPrefixLen = V_strlen( pPrefix ); - int nErrorStringLength = V_strlen( pErrorString ); - char *pMsg = (char*)_alloca( nPrefixLen + nErrorStringLength + 1 ); - V_strcpy( pMsg, pPrefix ); - V_strcpy( pMsg + nPrefixLen, pErrorString ); - - vgui::MessageBox *pError = new vgui::MessageBox( "Dubious Perforce Message", pMsg, GetParent() ); - pError->SetSmallCaption( true ); - pError->DoModal(); - } -#endif - return bSuccess; -} - - -//----------------------------------------------------------------------------- -// Show the perforce query dialog -//----------------------------------------------------------------------------- -void ShowPerforceQuery( vgui::Panel *pParent, const char *pFileName, vgui::Panel *pActionSignalTarget, KeyValues *pKeyValues, PerforceAction_t actionFilter ) -{ - if ( !p4 ) - { - KeyValues *pSpoofKeys = new KeyValues( "PerforceQueryCompleted", "operationPerformed", 1 ); - if ( pKeyValues ) - { - pSpoofKeys->AddSubKey( pKeyValues ); - } - vgui::ivgui()->PostMessage( pActionSignalTarget->GetVPanel(), pSpoofKeys, 0 ); - - return; - } - - // Refresh the current perforce settings - p4->RefreshActiveClient(); - - PerforceAction_t action = PERFORCE_ACTION_NONE; - const char *pTitle = NULL; - if ( !p4->IsFileInPerforce( pFileName ) ) - { - // If the file isn't in perforce, ask to add it - action = PERFORCE_ACTION_FILE_ADD; - pTitle = "Add File to Perforce?"; - } - else if ( p4->GetFileState( pFileName ) == P4FILE_UNOPENED ) - { - // If the file isn't checked out yet, ask to check it out - action = PERFORCE_ACTION_FILE_EDIT; - pTitle = "Check Out File from Perforce?"; - } - - if ( ( action == PERFORCE_ACTION_NONE ) || ( ( actionFilter != PERFORCE_ACTION_NONE ) && ( actionFilter != action ) ) ) - { - // Spoof a completion event - KeyValues *pSpoofKeys = new KeyValues( "PerforceQueryCompleted", "operationPerformed", 1 ); - if ( pKeyValues ) - { - pSpoofKeys->AddSubKey( pKeyValues ); - } - vgui::ivgui()->PostMessage( pActionSignalTarget->GetVPanel(), pSpoofKeys, 0 ); - return; - } - - CPerforceFileListFrame *pQuery = new CPerforceFileListFrame( pParent, pTitle, "File", action ); - pQuery->AddFile( pFileName ); - if ( pActionSignalTarget ) - { - pQuery->AddActionSignalTarget( pActionSignalTarget ); - } - pQuery->DoModal( pKeyValues, "PerforceQueryCompleted" ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// List of perforce files and operations +// +//============================================================================= + +#include "vgui_controls/perforcefilelistframe.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/Splitter.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/MessageBox.h" +#include "tier2/tier2.h" +#include "p4lib/ip4.h" +#include "filesystem.h" +#include "vgui/IVGui.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Sort by asset name +//----------------------------------------------------------------------------- +static int __cdecl OperationSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString("operation"); + const char *string2 = item2.kv->GetString("operation"); + int nRetVal = Q_stricmp( string1, string2 ); + if ( nRetVal != 0 ) + return nRetVal; + + string1 = item1.kv->GetString("filename"); + string2 = item2.kv->GetString("filename"); + return Q_stricmp( string1, string2 ); +} + +static int __cdecl FileBrowserSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString("filename"); + const char *string2 = item2.kv->GetString("filename"); + return Q_stricmp( string1, string2 ); +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +COperationFileListFrame::COperationFileListFrame( vgui::Panel *pParent, const char *pTitle, const char *pColumnHeader, bool bShowDescription, bool bShowOkOnly, int nDialogID ) : + BaseClass( pParent, "PerforceFileList" ) +{ + m_pText = NULL; + vgui::Panel *pBrowserParent = this; + + m_pDescription = NULL; + m_pSplitter = NULL; + if ( bShowDescription ) + { + m_pSplitter = new vgui::Splitter( this, "Splitter", vgui::SPLITTER_MODE_HORIZONTAL, 1 ); + + pBrowserParent = m_pSplitter->GetChild( 0 ); + vgui::Panel *pDescParent = m_pSplitter->GetChild( 1 ); + + m_pDescription = new vgui::TextEntry( pDescParent, "Description" ); + m_pDescription->SetMultiline( true ); + m_pDescription->SetCatchEnterKey( true ); + m_pDescription->SetText( "" ); + } + + // FIXME: Might be nice to have checkboxes per row + m_pFileBrowser = new vgui::ListPanel( pBrowserParent, "Browser" ); + m_pFileBrowser->AddColumnHeader( 0, "operation", "Operation", 52, 0 ); + m_pFileBrowser->AddColumnHeader( 1, "filename", pColumnHeader, 128, vgui::ListPanel::COLUMN_RESIZEWITHWINDOW ); + m_pFileBrowser->SetSelectIndividualCells( false ); + m_pFileBrowser->SetMultiselectEnabled( false ); + m_pFileBrowser->SetEmptyListText( "No Perforce Operations" ); + m_pFileBrowser->SetDragEnabled( true ); + m_pFileBrowser->AddActionSignalTarget( this ); + m_pFileBrowser->SetSortFunc( 0, OperationSortFunc ); + m_pFileBrowser->SetSortFunc( 1, FileBrowserSortFunc ); + m_pFileBrowser->SetSortColumn( 0 ); + + m_pYesButton = new vgui::Button( this, "YesButton", "Yes", this, "Yes" ); + m_pNoButton = new vgui::Button( this, "NoButton", "No", this, "No" ); + + SetBlockDragChaining( true ); + SetDeleteSelfOnClose( true ); + + if ( bShowDescription ) + { + LoadControlSettingsAndUserConfig( "resource/perforcefilelistdescription.res", nDialogID ); + } + else + { + LoadControlSettingsAndUserConfig( "resource/perforcefilelist.res", nDialogID ); + } + + if ( bShowOkOnly ) + { + m_pYesButton->SetText( "#MessageBox_OK" ); + m_pNoButton->SetVisible( false ); + } + + m_pContextKeyValues = NULL; + + SetTitle( pTitle, false ); +} + +COperationFileListFrame::~COperationFileListFrame() +{ + SaveUserConfig(); + CleanUpMessage(); + if ( m_pText ) + { + delete[] m_pText; + } +} + + +//----------------------------------------------------------------------------- +// Deletes the message +//----------------------------------------------------------------------------- +void COperationFileListFrame::CleanUpMessage() +{ + if ( m_pContextKeyValues ) + { + m_pContextKeyValues->deleteThis(); + m_pContextKeyValues = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Performs layout +//----------------------------------------------------------------------------- +void COperationFileListFrame::PerformLayout() +{ + BaseClass::PerformLayout(); + + if ( m_pSplitter ) + { + int x, y, w, h; + GetClientArea( x, y, w, h ); + y += 6; + h -= 36; + m_pSplitter->SetBounds( x, y, w, h ); + } +} + + +//----------------------------------------------------------------------------- +// Adds files to the frame +//----------------------------------------------------------------------------- +void COperationFileListFrame::ClearAllOperations() +{ + m_pFileBrowser->RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// Adds the strings to the list panel +//----------------------------------------------------------------------------- +void COperationFileListFrame::AddOperation( const char *pOperation, const char *pFileName ) +{ + KeyValues *kv = new KeyValues( "node", "filename", pFileName ); + kv->SetString( "operation", pOperation ); + m_pFileBrowser->AddItem( kv, 0, false, false ); +} + +void COperationFileListFrame::AddOperation( const char *pOperation, const char *pFileName, const Color& clr ) +{ + KeyValues *kv = new KeyValues( "node", "filename", pFileName ); + kv->SetString( "operation", pOperation ); + kv->SetColor( "cellcolor", clr ); + m_pFileBrowser->AddItem( kv, 0, false, false ); +} + + +//----------------------------------------------------------------------------- +// Resizes the operation column to fit the operation text +//----------------------------------------------------------------------------- +void COperationFileListFrame::ResizeOperationColumnToContents() +{ + m_pFileBrowser->ResizeColumnToContents( 0 ); +} + + +//----------------------------------------------------------------------------- +// Sets the column header for the 'operation' column +//----------------------------------------------------------------------------- +void COperationFileListFrame::SetOperationColumnHeaderText( const char *pText ) +{ + m_pFileBrowser->SetColumnHeaderText( 0, pText ); +} + + +//----------------------------------------------------------------------------- +// Adds the strings to the list panel +//----------------------------------------------------------------------------- +void COperationFileListFrame::DoModal( KeyValues *pContextKeyValues, const char *pMessage ) +{ + m_MessageName = pMessage ? pMessage : "OperationConfirmed"; + CleanUpMessage(); + m_pContextKeyValues = pContextKeyValues; + m_pFileBrowser->SortList(); + if ( m_pNoButton->IsVisible() ) + { + m_pYesButton->SetEnabled( m_pFileBrowser->GetItemCount() != 0 ); + } + BaseClass::DoModal(); +} + + +//----------------------------------------------------------------------------- +// Retrieves the number of files, the file names, and operations +//----------------------------------------------------------------------------- +int COperationFileListFrame::GetOperationCount() +{ + return m_pFileBrowser->GetItemCount(); +} + +const char *COperationFileListFrame::GetFileName( int i ) +{ + int nItemId = m_pFileBrowser->GetItemIDFromRow( i ); + KeyValues *pKeyValues = m_pFileBrowser->GetItem( nItemId ); + return pKeyValues->GetString( "filename" ); +} + +const char *COperationFileListFrame::GetOperation( int i ) +{ + int nItemId = m_pFileBrowser->GetItemIDFromRow( i ); + KeyValues *pKeyValues = m_pFileBrowser->GetItem( nItemId ); + return pKeyValues->GetString( "operation" ); +} + + +//----------------------------------------------------------------------------- +// Retreives the description (only if it was shown) +//----------------------------------------------------------------------------- +const char *COperationFileListFrame::GetDescription() +{ + return m_pText; +} + + +//----------------------------------------------------------------------------- +// Returns the message name +//----------------------------------------------------------------------------- +const char *COperationFileListFrame::CompletionMessage() +{ + return m_MessageName; +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void COperationFileListFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Yes" ) ) + { + if ( m_pDescription ) + { + int nLen = m_pDescription->GetTextLength() + 1; + m_pText = new char[ nLen ]; + m_pDescription->GetText( m_pText, nLen ); + } + + KeyValues *pActionKeys; + if ( PerformOperation() ) + { + pActionKeys = new KeyValues( CompletionMessage(), "operationPerformed", 1 ); + } + else + { + pActionKeys = new KeyValues( CompletionMessage(), "operationPerformed", 0 ); + } + + if ( m_pContextKeyValues ) + { + pActionKeys->AddSubKey( m_pContextKeyValues ); + m_pContextKeyValues = NULL; + } + CloseModal(); + PostActionSignal( pActionKeys ); + return; + } + + if ( !Q_stricmp( pCommand, "No" ) ) + { + KeyValues *pActionKeys = new KeyValues( CompletionMessage(), "operationPerformed", 0 ); + if ( m_pContextKeyValues ) + { + pActionKeys->AddSubKey( m_pContextKeyValues ); + m_pContextKeyValues = NULL; + } + CloseModal(); + PostActionSignal( pActionKeys ); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + +//----------------------------------------------------------------------------- +// +// Version that does the work of perforce actions +// +//----------------------------------------------------------------------------- +CPerforceFileListFrame::CPerforceFileListFrame( vgui::Panel *pParent, const char *pTitle, const char *pColumnHeader, PerforceAction_t action ) : + BaseClass( pParent, pTitle, pColumnHeader, (action == PERFORCE_ACTION_FILE_SUBMIT), false, OPERATION_DIALOG_ID_PERFORCE ) +{ + m_Action = action; +} + +CPerforceFileListFrame::~CPerforceFileListFrame() +{ +} + + +//----------------------------------------------------------------------------- +// Activates the modal dialog +//----------------------------------------------------------------------------- +void CPerforceFileListFrame::DoModal( KeyValues *pContextKeys, const char *pMessage ) +{ + BaseClass::DoModal( pContextKeys, pMessage ? pMessage : "PerforceActionConfirmed" ); +} + + +//----------------------------------------------------------------------------- +// Adds a file for open +//----------------------------------------------------------------------------- +void CPerforceFileListFrame::AddFileForOpen( const char *pFullPath ) +{ + if ( !p4 ) + return; + + bool bIsInPerforce = p4->IsFileInPerforce( pFullPath ); + bool bIsOpened = ( p4->GetFileState( pFullPath ) != P4FILE_UNOPENED ); + switch( m_Action ) + { + case PERFORCE_ACTION_FILE_ADD: + if ( !bIsInPerforce && !bIsOpened ) + { + AddOperation( "Add", pFullPath ); + } + break; + + case PERFORCE_ACTION_FILE_EDIT: + if ( bIsInPerforce && !bIsOpened ) + { + AddOperation( "Edit", pFullPath ); + } + break; + + case PERFORCE_ACTION_FILE_DELETE: + if ( bIsInPerforce && !bIsOpened ) + { + AddOperation( "Delete", pFullPath ); + } + break; + } +} + + +//----------------------------------------------------------------------------- +// Add files to dialog for submit/revert dialogs +//----------------------------------------------------------------------------- +void CPerforceFileListFrame::AddFileForSubmit( const char *pFullPath, P4FileState_t state ) +{ + if ( state == P4FILE_UNOPENED ) + return; + + char pBuf[128]; + const char *pPrefix = (m_Action == PERFORCE_ACTION_FILE_REVERT) ? "Revert" : "Submit"; + switch( state ) + { + case P4FILE_OPENED_FOR_ADD: + Q_snprintf( pBuf, sizeof(pBuf), "%s Add", pPrefix ); + AddOperation( pBuf, pFullPath ); + break; + + case P4FILE_OPENED_FOR_EDIT: + Q_snprintf( pBuf, sizeof(pBuf), "%s Edit", pPrefix ); + AddOperation( pBuf, pFullPath ); + break; + + case P4FILE_OPENED_FOR_DELETE: + Q_snprintf( pBuf, sizeof(pBuf), "%s Delete", pPrefix ); + AddOperation( pBuf, pFullPath ); + break; + + case P4FILE_OPENED_FOR_INTEGRATE: + Q_snprintf( pBuf, sizeof(pBuf), "%s Integrate", pPrefix ); + AddOperation( pBuf, pFullPath ); + break; + } +} + + +//----------------------------------------------------------------------------- +// Version of AddFile that accepts full paths +//----------------------------------------------------------------------------- +void CPerforceFileListFrame::AddFile( const char *pFullPath ) +{ + if ( !p4 ) + return; + + if ( m_Action < PERFORCE_ACTION_FILE_REVERT ) + { + // If the file wasn't found on the disk, then abort + if ( g_pFullFileSystem->FileExists( pFullPath, NULL ) ) + { + AddFileForOpen( pFullPath ); + } + return; + } + + // Deal with submit, revert + bool bFileExists = g_pFullFileSystem->FileExists( pFullPath, NULL ); + P4FileState_t state = p4->GetFileState( pFullPath ); + if ( bFileExists || (state == P4FILE_OPENED_FOR_DELETE) ) + { + AddFileForSubmit( pFullPath, state ); + } +} + + +//----------------------------------------------------------------------------- +// Version of AddFile that accepts relative paths + search path ids +//----------------------------------------------------------------------------- +void CPerforceFileListFrame::AddFile( const char *pRelativePath, const char *pPathId ) +{ + if ( !p4 ) + return; + + // Deal with add, open, edit + if ( m_Action < PERFORCE_ACTION_FILE_REVERT ) + { + // If the file wasn't found on the disk, then abort + if ( g_pFullFileSystem->FileExists( pRelativePath, pPathId ) ) + { + char pFullPath[MAX_PATH]; + g_pFullFileSystem->RelativePathToFullPath( pRelativePath, pPathId, pFullPath, sizeof( pFullPath ) ); + AddFileForOpen( pFullPath ); + } + return; + } + + // Deal with submit, revert + + // First, handle the case where the file exists on the drive + char pFullPath[MAX_PATH]; + if ( g_pFullFileSystem->FileExists( pRelativePath, pPathId ) ) + { + g_pFullFileSystem->RelativePathToFullPath( pRelativePath, pPathId, pFullPath, sizeof( pFullPath ) ); + P4FileState_t state = p4->GetFileState( pFullPath ); + AddFileForSubmit( pFullPath, state ); + return; + } + + // Get the list of opened files, cache it off so we aren't continually reasking + if ( Q_stricmp( pPathId, m_LastOpenedFilePathId ) ) + { + p4->GetOpenedFileListInPath( pPathId, m_OpenedFiles ); + m_LastOpenedFilePathId = pPathId; + } + + // If the file doesn't exist, it was opened for delete. + // Using the client spec of the path, we need to piece together + // the full path; the full path unfortunately is usually ambiguous: + // you can never exactly know which mod it came from. + char pTemp[MAX_PATH]; + char pSearchString[MAX_PATH]; + Q_strncpy( pSearchString, pRelativePath, sizeof(pSearchString) ); + Q_FixSlashes( pSearchString ); + + int k; + int nOpenedFileCount = m_OpenedFiles.Count(); + for ( k = 0; k < nOpenedFileCount; ++k ) + { + if ( m_OpenedFiles[k].m_eOpenState != P4FILE_OPENED_FOR_DELETE ) + continue; + + // Check to see if the end of the local file matches the file + const char *pLocalFile = p4->String( m_OpenedFiles[k].m_sLocalFile ); + + // This ensures the full path lies under the search path + if ( !g_pFullFileSystem->FullPathToRelativePathEx( pLocalFile, pPathId, pTemp, sizeof(pTemp) ) ) + continue; + + // The relative paths had better be the same + if ( Q_stricmp( pTemp, pSearchString ) ) + continue; + + AddFileForSubmit( pLocalFile, m_OpenedFiles[k].m_eOpenState ); + break; + } +} + + +//----------------------------------------------------------------------------- +// Does the perforce operation +//----------------------------------------------------------------------------- +bool CPerforceFileListFrame::PerformOperation( ) +{ + if ( !p4 ) + return false; + + int nFileCount = GetOperationCount(); + const char **ppFileNames = (const char**)_alloca( nFileCount * sizeof(char*) ); + for ( int i = 0; i < nFileCount; ++i ) + { + ppFileNames[i] = GetFileName( i ); + } + + bool bSuccess = false; + + switch ( m_Action ) + { + case PERFORCE_ACTION_FILE_ADD: + bSuccess = p4->OpenFilesForAdd( nFileCount, ppFileNames ); + break; + + case PERFORCE_ACTION_FILE_EDIT: + bSuccess = p4->OpenFilesForEdit( nFileCount, ppFileNames ); + break; + + case PERFORCE_ACTION_FILE_DELETE: + bSuccess = p4->OpenFilesForDelete( nFileCount, ppFileNames ); + break; + + case PERFORCE_ACTION_FILE_REVERT: + bSuccess = p4->RevertFiles( nFileCount, ppFileNames ); + break; + + case PERFORCE_ACTION_FILE_SUBMIT: + { + // Ensure a description was added + const char *pDescription = GetDescription(); + if ( !pDescription[0] || !Q_stricmp( pDescription, "" ) ) + { + vgui::MessageBox *pError = new vgui::MessageBox( "Submission Error!", "Description required for submission.", GetParent() ); + pError->SetSmallCaption( true ); + pError->DoModal(); + return false; + } + else + { + bSuccess = p4->SubmitFiles( nFileCount, ppFileNames, pDescription ); + } + } + break; + } + + const char *pErrorString = p4->GetLastError(); + if ( !bSuccess ) + { + vgui::MessageBox *pError = new vgui::MessageBox( "Perforce Error!", pErrorString, GetParent() ); + pError->SetSmallCaption( true ); + pError->DoModal(); + } +#if 0 + if ( *pErrorString ) + { + if ( V_strstr( pErrorString, "opened for add" ) ) + return bSuccess; + if ( V_strstr( pErrorString, "opened for edit" ) ) + return bSuccess; + // TODO - figure out the rest of these... + + const char *pPrefix = "Perforce has generated the following message which may or may not be an error.\n" + "Please email joe with the text of the message, whether you think it was an error, and what perforce operation you where performing.\n" + "To copy the message, hit ~ to enter the console, where you will find the message reprinted.\n" + "Select the lines of text in the message, right click, select Copy, and then paste into an email message.\n\n"; + static int nPrefixLen = V_strlen( pPrefix ); + int nErrorStringLength = V_strlen( pErrorString ); + char *pMsg = (char*)_alloca( nPrefixLen + nErrorStringLength + 1 ); + V_strcpy( pMsg, pPrefix ); + V_strcpy( pMsg + nPrefixLen, pErrorString ); + + vgui::MessageBox *pError = new vgui::MessageBox( "Dubious Perforce Message", pMsg, GetParent() ); + pError->SetSmallCaption( true ); + pError->DoModal(); + } +#endif + return bSuccess; +} + + +//----------------------------------------------------------------------------- +// Show the perforce query dialog +//----------------------------------------------------------------------------- +void ShowPerforceQuery( vgui::Panel *pParent, const char *pFileName, vgui::Panel *pActionSignalTarget, KeyValues *pKeyValues, PerforceAction_t actionFilter ) +{ + if ( !p4 ) + { + KeyValues *pSpoofKeys = new KeyValues( "PerforceQueryCompleted", "operationPerformed", 1 ); + if ( pKeyValues ) + { + pSpoofKeys->AddSubKey( pKeyValues ); + } + vgui::ivgui()->PostMessage( pActionSignalTarget->GetVPanel(), pSpoofKeys, 0 ); + + return; + } + + // Refresh the current perforce settings + p4->RefreshActiveClient(); + + PerforceAction_t action = PERFORCE_ACTION_NONE; + const char *pTitle = NULL; + if ( !p4->IsFileInPerforce( pFileName ) ) + { + // If the file isn't in perforce, ask to add it + action = PERFORCE_ACTION_FILE_ADD; + pTitle = "Add File to Perforce?"; + } + else if ( p4->GetFileState( pFileName ) == P4FILE_UNOPENED ) + { + // If the file isn't checked out yet, ask to check it out + action = PERFORCE_ACTION_FILE_EDIT; + pTitle = "Check Out File from Perforce?"; + } + + if ( ( action == PERFORCE_ACTION_NONE ) || ( ( actionFilter != PERFORCE_ACTION_NONE ) && ( actionFilter != action ) ) ) + { + // Spoof a completion event + KeyValues *pSpoofKeys = new KeyValues( "PerforceQueryCompleted", "operationPerformed", 1 ); + if ( pKeyValues ) + { + pSpoofKeys->AddSubKey( pKeyValues ); + } + vgui::ivgui()->PostMessage( pActionSignalTarget->GetVPanel(), pSpoofKeys, 0 ); + return; + } + + CPerforceFileListFrame *pQuery = new CPerforceFileListFrame( pParent, pTitle, "File", action ); + pQuery->AddFile( pFileName ); + if ( pActionSignalTarget ) + { + pQuery->AddActionSignalTarget( pActionSignalTarget ); + } + pQuery->DoModal( pKeyValues, "PerforceQueryCompleted" ); +} diff --git a/mp/src/vgui2/vgui_controls/savedocumentquery.cpp b/mp/src/vgui2/vgui_controls/savedocumentquery.cpp index 4efef551..05ec95b1 100644 --- a/mp/src/vgui2/vgui_controls/savedocumentquery.cpp +++ b/mp/src/vgui2/vgui_controls/savedocumentquery.cpp @@ -1,195 +1,195 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: Core Movie Maker UI API -// -//============================================================================= - -#include "vgui_controls/savedocumentquery.h" -#include "vgui_controls/Button.h" -#include "vgui_controls/Label.h" -#include "vgui_controls/Frame.h" -#include "vgui/ISurface.h" -#include "vgui/IVGui.h" -#include "tier1/KeyValues.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - - -using namespace vgui; - - -//----------------------------------------------------------------------------- -// This dialog asks if you want to save your work -//----------------------------------------------------------------------------- -class CSaveDocumentQuery : public vgui::Frame -{ - DECLARE_CLASS_SIMPLE( CSaveDocumentQuery, vgui::Frame ); - -public: - CSaveDocumentQuery( vgui::Panel *pParent, const char *filename, const char *pFileType, int nContext, - vgui::Panel *pActionSignalTarget = 0, KeyValues *pKeyValues = 0 ); - ~CSaveDocumentQuery(); - - // Inherited from vgui::Frame - virtual void OnCommand( char const *cmd ); - virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); - - // Put the message box into a modal state - void DoModal(); - -private: - // Posts commands to the action signal target - void PostCommand( const char *pCommand ); - - vgui::Label *m_pMessageLabel; - vgui::Button *m_pYesButton; - vgui::Button *m_pNoButton; - vgui::Button *m_pCancelButton; - vgui::Panel *m_pActionSignalTarget; - - char m_szFileName[ 256 ]; - char m_szFileType[ 256 ]; - int m_nContext; - KeyValues* m_pPostSaveKeyValues; -}; - - -//----------------------------------------------------------------------------- -// Show the save document query dialog -//----------------------------------------------------------------------------- -void ShowSaveDocumentQuery( vgui::Panel *pParent, const char *pFileName, const char *pFileType, int nContext, vgui::Panel *pActionSignalTarget, KeyValues *pPostSaveCommand ) -{ - CSaveDocumentQuery *query = new CSaveDocumentQuery( pParent, pFileName, pFileType, nContext, pActionSignalTarget, pPostSaveCommand ); - query->SetSmallCaption( true ); - query->DoModal(); -} - - -//----------------------------------------------------------------------------- -// Constructor -//----------------------------------------------------------------------------- -CSaveDocumentQuery::CSaveDocumentQuery( vgui::Panel *pParent, char const *pFileName, const char *pFileType, int nContext, vgui::Panel *pActionSignalTarget, KeyValues *pPostSaveCommand ) : - BaseClass( pParent, "SaveDocumentQuery" ), - m_nContext( nContext ), - m_pActionSignalTarget( pActionSignalTarget ) -{ - if ( !pFileName || !pFileName[0] ) - { - pFileName = ""; - } - Q_strncpy( m_szFileName, pFileName, sizeof( m_szFileName ) ); - Q_strncpy( m_szFileType, pFileType, sizeof( m_szFileType ) ); - m_pPostSaveKeyValues = pPostSaveCommand; - - SetDeleteSelfOnClose(true); - - SetMenuButtonResponsive(false); - SetMinimizeButtonVisible(false); - SetCloseButtonVisible(false); - SetSizeable(false); - - SetTitle( "Save Changes", true ); - - m_pMessageLabel = new Label( this, "FileNameLabel", "" ); - - m_pYesButton = new Button( this, "Yes", "Yes", this, "yes" ); - m_pNoButton = new Button( this, "No", "No", this, "no" ); - m_pCancelButton = new Button( this, "Cancel", "Cancel", this, "cancel" ); - - LoadControlSettings( "resource/ToolSaveDocumentQuery.res" ); - - m_pMessageLabel->SetText( m_szFileName ); -} - -CSaveDocumentQuery::~CSaveDocumentQuery() -{ - if ( m_pPostSaveKeyValues ) - { - m_pPostSaveKeyValues->deleteThis(); - m_pPostSaveKeyValues = NULL; - } -} - - -//----------------------------------------------------------------------------- -// Posts commands to the action signal target -//----------------------------------------------------------------------------- -void CSaveDocumentQuery::PostCommand( const char *pCommand ) -{ - KeyValues *kv = new KeyValues( pCommand ); - vgui::ivgui()->PostMessage( m_pActionSignalTarget->GetVPanel(), kv, 0 ); -} - - -//----------------------------------------------------------------------------- -// Process commands -//----------------------------------------------------------------------------- -void CSaveDocumentQuery::OnCommand( char const *cmd ) -{ - if ( !Q_stricmp( cmd, "yes" ) ) - { - KeyValues *kv = new KeyValues( "OnSaveFile" ); - kv->SetString( "filename", m_szFileName ); - kv->SetString( "filetype", m_szFileType ); - kv->SetInt( "context", m_nContext ); - kv->SetPtr( "actionTarget", m_pActionSignalTarget ); - if ( m_pPostSaveKeyValues ) - { - kv->AddSubKey( m_pPostSaveKeyValues->MakeCopy() ); - } - vgui::ivgui()->PostMessage( m_pActionSignalTarget->GetVPanel(), kv, 0 ); - MarkForDeletion(); - } - else if ( !Q_stricmp( cmd, "no" ) ) - { - PostCommand( "OnMarkNotDirty" ); - if ( m_pPostSaveKeyValues ) - { - vgui::ivgui()->PostMessage( m_pActionSignalTarget->GetVPanel(), m_pPostSaveKeyValues->MakeCopy(), 0 ); - } - MarkForDeletion(); - } - else if ( !Q_stricmp( cmd, "cancel" ) ) - { - PostCommand( "OnCancelSaveDocument" ); - MarkForDeletion(); - } - else - { - BaseClass::OnCommand( cmd ); - } -} - - -//----------------------------------------------------------------------------- -// Deal with scheme -//----------------------------------------------------------------------------- -void CSaveDocumentQuery::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - int wide, tall; - GetSize( wide, tall ); - - int swide, stall; - surface()->GetScreenSize(swide, stall); - - // put the dialog in the middle of the screen - SetPos((swide - wide) / 2, (stall - tall) / 2); -} - - -//----------------------------------------------------------------------------- -// Put the message box into a modal state -//----------------------------------------------------------------------------- -void CSaveDocumentQuery::DoModal() -{ - SetVisible( true ); - SetEnabled( true ); - MoveToFront(); - - RequestFocus(); - - InvalidateLayout(); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Core Movie Maker UI API +// +//============================================================================= + +#include "vgui_controls/savedocumentquery.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/Frame.h" +#include "vgui/ISurface.h" +#include "vgui/IVGui.h" +#include "tier1/KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// This dialog asks if you want to save your work +//----------------------------------------------------------------------------- +class CSaveDocumentQuery : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CSaveDocumentQuery, vgui::Frame ); + +public: + CSaveDocumentQuery( vgui::Panel *pParent, const char *filename, const char *pFileType, int nContext, + vgui::Panel *pActionSignalTarget = 0, KeyValues *pKeyValues = 0 ); + ~CSaveDocumentQuery(); + + // Inherited from vgui::Frame + virtual void OnCommand( char const *cmd ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + // Put the message box into a modal state + void DoModal(); + +private: + // Posts commands to the action signal target + void PostCommand( const char *pCommand ); + + vgui::Label *m_pMessageLabel; + vgui::Button *m_pYesButton; + vgui::Button *m_pNoButton; + vgui::Button *m_pCancelButton; + vgui::Panel *m_pActionSignalTarget; + + char m_szFileName[ 256 ]; + char m_szFileType[ 256 ]; + int m_nContext; + KeyValues* m_pPostSaveKeyValues; +}; + + +//----------------------------------------------------------------------------- +// Show the save document query dialog +//----------------------------------------------------------------------------- +void ShowSaveDocumentQuery( vgui::Panel *pParent, const char *pFileName, const char *pFileType, int nContext, vgui::Panel *pActionSignalTarget, KeyValues *pPostSaveCommand ) +{ + CSaveDocumentQuery *query = new CSaveDocumentQuery( pParent, pFileName, pFileType, nContext, pActionSignalTarget, pPostSaveCommand ); + query->SetSmallCaption( true ); + query->DoModal(); +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CSaveDocumentQuery::CSaveDocumentQuery( vgui::Panel *pParent, char const *pFileName, const char *pFileType, int nContext, vgui::Panel *pActionSignalTarget, KeyValues *pPostSaveCommand ) : + BaseClass( pParent, "SaveDocumentQuery" ), + m_nContext( nContext ), + m_pActionSignalTarget( pActionSignalTarget ) +{ + if ( !pFileName || !pFileName[0] ) + { + pFileName = ""; + } + Q_strncpy( m_szFileName, pFileName, sizeof( m_szFileName ) ); + Q_strncpy( m_szFileType, pFileType, sizeof( m_szFileType ) ); + m_pPostSaveKeyValues = pPostSaveCommand; + + SetDeleteSelfOnClose(true); + + SetMenuButtonResponsive(false); + SetMinimizeButtonVisible(false); + SetCloseButtonVisible(false); + SetSizeable(false); + + SetTitle( "Save Changes", true ); + + m_pMessageLabel = new Label( this, "FileNameLabel", "" ); + + m_pYesButton = new Button( this, "Yes", "Yes", this, "yes" ); + m_pNoButton = new Button( this, "No", "No", this, "no" ); + m_pCancelButton = new Button( this, "Cancel", "Cancel", this, "cancel" ); + + LoadControlSettings( "resource/ToolSaveDocumentQuery.res" ); + + m_pMessageLabel->SetText( m_szFileName ); +} + +CSaveDocumentQuery::~CSaveDocumentQuery() +{ + if ( m_pPostSaveKeyValues ) + { + m_pPostSaveKeyValues->deleteThis(); + m_pPostSaveKeyValues = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Posts commands to the action signal target +//----------------------------------------------------------------------------- +void CSaveDocumentQuery::PostCommand( const char *pCommand ) +{ + KeyValues *kv = new KeyValues( pCommand ); + vgui::ivgui()->PostMessage( m_pActionSignalTarget->GetVPanel(), kv, 0 ); +} + + +//----------------------------------------------------------------------------- +// Process commands +//----------------------------------------------------------------------------- +void CSaveDocumentQuery::OnCommand( char const *cmd ) +{ + if ( !Q_stricmp( cmd, "yes" ) ) + { + KeyValues *kv = new KeyValues( "OnSaveFile" ); + kv->SetString( "filename", m_szFileName ); + kv->SetString( "filetype", m_szFileType ); + kv->SetInt( "context", m_nContext ); + kv->SetPtr( "actionTarget", m_pActionSignalTarget ); + if ( m_pPostSaveKeyValues ) + { + kv->AddSubKey( m_pPostSaveKeyValues->MakeCopy() ); + } + vgui::ivgui()->PostMessage( m_pActionSignalTarget->GetVPanel(), kv, 0 ); + MarkForDeletion(); + } + else if ( !Q_stricmp( cmd, "no" ) ) + { + PostCommand( "OnMarkNotDirty" ); + if ( m_pPostSaveKeyValues ) + { + vgui::ivgui()->PostMessage( m_pActionSignalTarget->GetVPanel(), m_pPostSaveKeyValues->MakeCopy(), 0 ); + } + MarkForDeletion(); + } + else if ( !Q_stricmp( cmd, "cancel" ) ) + { + PostCommand( "OnCancelSaveDocument" ); + MarkForDeletion(); + } + else + { + BaseClass::OnCommand( cmd ); + } +} + + +//----------------------------------------------------------------------------- +// Deal with scheme +//----------------------------------------------------------------------------- +void CSaveDocumentQuery::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + int wide, tall; + GetSize( wide, tall ); + + int swide, stall; + surface()->GetScreenSize(swide, stall); + + // put the dialog in the middle of the screen + SetPos((swide - wide) / 2, (stall - tall) / 2); +} + + +//----------------------------------------------------------------------------- +// Put the message box into a modal state +//----------------------------------------------------------------------------- +void CSaveDocumentQuery::DoModal() +{ + SetVisible( true ); + SetEnabled( true ); + MoveToFront(); + + RequestFocus(); + + InvalidateLayout(); +} diff --git a/mp/src/vgui2/vgui_controls/subrectimage.cpp b/mp/src/vgui2/vgui_controls/subrectimage.cpp index e7405049..12939995 100644 --- a/mp/src/vgui2/vgui_controls/subrectimage.cpp +++ b/mp/src/vgui2/vgui_controls/subrectimage.cpp @@ -1,212 +1,212 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//============================================================================= - -#include "vgui_controls/subrectimage.h" -#include "tier0/dbg.h" -#include "vgui/ISurface.h" -#include "vgui_controls/Controls.h" - -// NOTE: This has to be the last file included! -#include "tier0/memdbgon.h" - - -using namespace vgui; - -// Officially the invalid texture ID is zero, but -1 is used in many -// places, and changing it carries some risk. Adding a named constant -// for this file avoids warnings and makes future changes easier. -const HTexture SUBRECT_INVALID_TEXTURE = (HTexture)-1; - -//----------------------------------------------------------------------------- -// Constructor, destructor -//----------------------------------------------------------------------------- -CSubRectImage::CSubRectImage( const char *filename, bool hardwareFiltered, int subx, int suby, int subw, int subh ) -{ - SetSize( subw, subh ); - sub[ 0 ] = subx; - sub[ 1 ] = suby; - sub[ 2 ] = subw; - sub[ 3 ] = subh; - - _filtered = hardwareFiltered; - // HACKHACK - force VGUI materials to be in the vgui/ directory - // This needs to be revisited once GoldSRC is grandfathered off. - //!! need to make this work with goldsrc - int size = strlen(filename) + 1 + strlen("vgui/"); - _filename = (char *)malloc( size ); - Assert( _filename ); - - Q_snprintf( _filename, size, "vgui/%s", filename ); - - _id = SUBRECT_INVALID_TEXTURE; - _uploaded = false; - _color = Color(255, 255, 255, 255); - _pos[0] = _pos[1] = 0; - _valid = true; - _wide = subw; - _tall = subh; - ForceUpload(); -} - -CSubRectImage::~CSubRectImage() -{ - if ( vgui::surface() && _id != SUBRECT_INVALID_TEXTURE ) - { - vgui::surface()->DestroyTextureID( _id ); - _id = SUBRECT_INVALID_TEXTURE; - } - - if ( _filename ) - { - free( _filename ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void CSubRectImage::GetSize(int &wide, int &tall) -{ - wide = _wide; - tall = _tall; -} - -//----------------------------------------------------------------------------- -// Purpose: size of the bitmap -//----------------------------------------------------------------------------- -void CSubRectImage::GetContentSize(int &wide, int &tall) -{ - wide = 0; - tall = 0; - - if (!_valid) - return; - - if ( _id != SUBRECT_INVALID_TEXTURE ) - { - surface()->DrawGetTextureSize(_id, wide, tall); - } -} - -//----------------------------------------------------------------------------- -// Purpose: ignored -//----------------------------------------------------------------------------- -void CSubRectImage::SetSize(int x, int y) -{ - _wide = x; - _tall = y; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void CSubRectImage::SetPos(int x, int y) -{ - _pos[0] = x; - _pos[1] = y; -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -void CSubRectImage::SetColor(Color col) -{ - _color = col; -} - -//----------------------------------------------------------------------------- -// Purpose: returns the file name of the bitmap -//----------------------------------------------------------------------------- -const char *CSubRectImage::GetName() -{ - return _filename; -} - -//----------------------------------------------------------------------------- -// Purpose: Renders the loaded image, uploading it if necessary -// Assumes a valid image is always returned from uploading -//----------------------------------------------------------------------------- -void CSubRectImage::Paint() -{ - if ( !_valid ) - return; - - // if we don't have an _id then lets make one - if ( _id == SUBRECT_INVALID_TEXTURE ) - { - _id = surface()->CreateNewTextureID(); - } - - // if we have not uploaded yet, lets go ahead and do so - if ( !_uploaded ) - { - ForceUpload(); - } - - // set the texture current, set the color, and draw the biatch - surface()->DrawSetColor( _color[0], _color[1], _color[2], _color[3] ); - surface()->DrawSetTexture( _id ); - - if ( _wide == 0 || _tall == 0 ) - return; - - int cw, ch; - GetContentSize( cw, ch ); - if ( cw == 0 || ch == 0 ) - return; - - float s[ 2 ]; - float t[ 2 ]; - - s[ 0 ] = (float)sub[ 0 ] / (float)cw; - s[ 1 ] = (float)(sub[ 0 ]+sub[ 2 ]) / (float)cw; - t[ 0 ] = (float)sub[ 1 ] / (float)ch; - t[ 1 ] = (float)(sub[ 1 ]+sub[ 3 ]) / (float)ch; - surface()->DrawTexturedSubRect( - _pos[0], - _pos[1], - _pos[0] + _wide, - _pos[1] + _tall, - s[ 0 ], - t[ 0 ], - s[ 1 ], - t[ 1 ] ); -} - -//----------------------------------------------------------------------------- -// Purpose: ensures the bitmap has been uploaded -//----------------------------------------------------------------------------- -void CSubRectImage::ForceUpload() -{ - if ( !_valid || _uploaded ) - return; - - if ( _id == SUBRECT_INVALID_TEXTURE ) - { - _id = surface()->CreateNewTextureID( false ); - } - - surface()->DrawSetTextureFile( _id, _filename, _filtered, false ); - - _uploaded = true; - - _valid = surface()->IsTextureIDValid( _id ); -} - - -//----------------------------------------------------------------------------- -// Purpose: data accessor -//----------------------------------------------------------------------------- -HTexture CSubRectImage::GetID() -{ - return _id; -} - -bool CSubRectImage::IsValid() -{ - return _valid; -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "vgui_controls/subrectimage.h" +#include "tier0/dbg.h" +#include "vgui/ISurface.h" +#include "vgui_controls/Controls.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +using namespace vgui; + +// Officially the invalid texture ID is zero, but -1 is used in many +// places, and changing it carries some risk. Adding a named constant +// for this file avoids warnings and makes future changes easier. +const HTexture SUBRECT_INVALID_TEXTURE = (HTexture)-1; + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CSubRectImage::CSubRectImage( const char *filename, bool hardwareFiltered, int subx, int suby, int subw, int subh ) +{ + SetSize( subw, subh ); + sub[ 0 ] = subx; + sub[ 1 ] = suby; + sub[ 2 ] = subw; + sub[ 3 ] = subh; + + _filtered = hardwareFiltered; + // HACKHACK - force VGUI materials to be in the vgui/ directory + // This needs to be revisited once GoldSRC is grandfathered off. + //!! need to make this work with goldsrc + int size = strlen(filename) + 1 + strlen("vgui/"); + _filename = (char *)malloc( size ); + Assert( _filename ); + + Q_snprintf( _filename, size, "vgui/%s", filename ); + + _id = SUBRECT_INVALID_TEXTURE; + _uploaded = false; + _color = Color(255, 255, 255, 255); + _pos[0] = _pos[1] = 0; + _valid = true; + _wide = subw; + _tall = subh; + ForceUpload(); +} + +CSubRectImage::~CSubRectImage() +{ + if ( vgui::surface() && _id != SUBRECT_INVALID_TEXTURE ) + { + vgui::surface()->DestroyTextureID( _id ); + _id = SUBRECT_INVALID_TEXTURE; + } + + if ( _filename ) + { + free( _filename ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void CSubRectImage::GetSize(int &wide, int &tall) +{ + wide = _wide; + tall = _tall; +} + +//----------------------------------------------------------------------------- +// Purpose: size of the bitmap +//----------------------------------------------------------------------------- +void CSubRectImage::GetContentSize(int &wide, int &tall) +{ + wide = 0; + tall = 0; + + if (!_valid) + return; + + if ( _id != SUBRECT_INVALID_TEXTURE ) + { + surface()->DrawGetTextureSize(_id, wide, tall); + } +} + +//----------------------------------------------------------------------------- +// Purpose: ignored +//----------------------------------------------------------------------------- +void CSubRectImage::SetSize(int x, int y) +{ + _wide = x; + _tall = y; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void CSubRectImage::SetPos(int x, int y) +{ + _pos[0] = x; + _pos[1] = y; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void CSubRectImage::SetColor(Color col) +{ + _color = col; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the file name of the bitmap +//----------------------------------------------------------------------------- +const char *CSubRectImage::GetName() +{ + return _filename; +} + +//----------------------------------------------------------------------------- +// Purpose: Renders the loaded image, uploading it if necessary +// Assumes a valid image is always returned from uploading +//----------------------------------------------------------------------------- +void CSubRectImage::Paint() +{ + if ( !_valid ) + return; + + // if we don't have an _id then lets make one + if ( _id == SUBRECT_INVALID_TEXTURE ) + { + _id = surface()->CreateNewTextureID(); + } + + // if we have not uploaded yet, lets go ahead and do so + if ( !_uploaded ) + { + ForceUpload(); + } + + // set the texture current, set the color, and draw the biatch + surface()->DrawSetColor( _color[0], _color[1], _color[2], _color[3] ); + surface()->DrawSetTexture( _id ); + + if ( _wide == 0 || _tall == 0 ) + return; + + int cw, ch; + GetContentSize( cw, ch ); + if ( cw == 0 || ch == 0 ) + return; + + float s[ 2 ]; + float t[ 2 ]; + + s[ 0 ] = (float)sub[ 0 ] / (float)cw; + s[ 1 ] = (float)(sub[ 0 ]+sub[ 2 ]) / (float)cw; + t[ 0 ] = (float)sub[ 1 ] / (float)ch; + t[ 1 ] = (float)(sub[ 1 ]+sub[ 3 ]) / (float)ch; + surface()->DrawTexturedSubRect( + _pos[0], + _pos[1], + _pos[0] + _wide, + _pos[1] + _tall, + s[ 0 ], + t[ 0 ], + s[ 1 ], + t[ 1 ] ); +} + +//----------------------------------------------------------------------------- +// Purpose: ensures the bitmap has been uploaded +//----------------------------------------------------------------------------- +void CSubRectImage::ForceUpload() +{ + if ( !_valid || _uploaded ) + return; + + if ( _id == SUBRECT_INVALID_TEXTURE ) + { + _id = surface()->CreateNewTextureID( false ); + } + + surface()->DrawSetTextureFile( _id, _filename, _filtered, false ); + + _uploaded = true; + + _valid = surface()->IsTextureIDValid( _id ); +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +HTexture CSubRectImage::GetID() +{ + return _id; +} + +bool CSubRectImage::IsValid() +{ + return _valid; +} + diff --git a/mp/src/vgui2/vgui_controls/vgui_controls.vpc b/mp/src/vgui2/vgui_controls/vgui_controls.vpc index 2b90d073..81c5978e 100644 --- a/mp/src/vgui2/vgui_controls/vgui_controls.vpc +++ b/mp/src/vgui2/vgui_controls/vgui_controls.vpc @@ -1,216 +1,216 @@ -//----------------------------------------------------------------------------- -// VGUI_CONTROLS.VPC -// -// Project Script -//----------------------------------------------------------------------------- - -$macro SRCDIR "..\.." -$Macro GENERATED_PROTO_DIR "generated_proto" -$macro PROTOBUF_LITE 0 - -$include "$SRCDIR\vpc_scripts\source_lib_base.vpc" -$include "$SRCDIR\vpc_scripts\protobuf_builder.vpc" - -$Configuration -{ - $Compiler - { - $AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty;$SRCDIR\thirdparty\cef;$GENERATED_PROTO_DIR" - } -} - -$Project "vgui_controls" -{ - $Folder "Source Files" - { - $File "AnalogBar.cpp" - $File "AnimatingImagePanel.cpp" - $File "AnimationController.cpp" - $File "BitmapImagePanel.cpp" - $File "BuildFactoryHelper.cpp" - $File "BuildGroup.cpp" - $File "BuildModeDialog.cpp" - $File "Button.cpp" - $File "CheckButton.cpp" - $File "CheckButtonList.cpp" - $File "CircularProgressBar.cpp" - $File "ComboBox.cpp" - $File "consoledialog.cpp" - $File "ControllerMap.cpp" - $File "controls.cpp" - $File "CvarToggleCheckButton.cpp" - $File "DirectorySelectDialog.cpp" - $File "Divider.cpp" - $File "EditablePanel.cpp" - $File "ExpandButton.cpp" - $File "FileOpenDialog.cpp" - $File "FileOpenStateMachine.cpp" - $File "$SRCDIR\public\filesystem_helpers.cpp" - $File "FocusNavGroup.cpp" - $File "Frame.cpp" - $File "GraphPanel.cpp" - $File "HTML.cpp" - $File "Image.cpp" - $File "ImageList.cpp" - $File "ImagePanel.cpp" - $File "InputDialog.cpp" - $File "KeyBindingHelpDialog.cpp" - $File "KeyBoardEditorDialog.cpp" - $File "KeyRepeat.cpp" - $File "Label.cpp" - $File "ListPanel.cpp" - $File "ListViewPanel.cpp" - $File "Menu.cpp" - $File "MenuBar.cpp" - $File "MenuButton.cpp" - $File "MenuItem.cpp" - $File "MessageBox.cpp" - $File "MessageDialog.cpp" - $File "Panel.cpp" - $File "PanelListPanel.cpp" - $File "PerforceFileExplorer.cpp" - $File "PerforceFileList.cpp" - $File "perforcefilelistframe.cpp" - $File "ProgressBar.cpp" - $File "ProgressBox.cpp" - $File "PropertyDialog.cpp" - $File "PropertyPage.cpp" - $File "PropertySheet.cpp" - $File "QueryBox.cpp" - $File "RadioButton.cpp" - $File "RichText.cpp" - $File "RotatingProgressBar.cpp" - $File "savedocumentquery.cpp" - $File "ScalableImagePanel.cpp" - $File "ScrollableEditablePanel.cpp" - $File "ScrollBar.cpp" - $File "ScrollBarSlider.cpp" - $File "SectionedListPanel.cpp" - $File "Slider.cpp" - $File "Splitter.cpp" - $File "subrectimage.cpp" - $File "TextEntry.cpp" - $File "TextImage.cpp" - $File "ToggleButton.cpp" - $File "Tooltip.cpp" - $File "ToolWindow.cpp" - $File "TreeView.cpp" - $File "TreeViewListControl.cpp" - $File "URLLabel.cpp" - $File "WizardPanel.cpp" - $File "WizardSubPanel.cpp" - $File "$SRCDIR\public\html\htmlprotobuf.cpp" - $File "$SRCDIR/vgui2/src/vgui_key_translation.cpp" - } - - $Folder "Public Header Files" - { - $File "$SRCDIR\public\vgui_controls\AnalogBar.h" - $File "$SRCDIR\public\vgui_controls\AnimatingImagePanel.h" - $File "$SRCDIR\public\vgui_controls\AnimationController.h" - $File "$SRCDIR\public\vgui_controls\BitmapImagePanel.h" - $File "$SRCDIR\public\vgui_controls\BuildGroup.h" - $File "$SRCDIR\public\vgui_controls\BuildModeDialog.h" - $File "$SRCDIR\public\vgui_controls\Button.h" - $File "$SRCDIR\public\vgui_controls\CheckButton.h" - $File "$SRCDIR\public\vgui_controls\CheckButtonList.h" - $File "$SRCDIR\public\vgui_controls\CircularProgressBar.h" - $File "$SRCDIR\public\Color.h" - $File "$SRCDIR\public\vgui_controls\ComboBox.h" - $File "$SRCDIR\public\vgui_controls\consoledialog.h" - $File "$SRCDIR\public\vgui_controls\ControllerMap.h" - $File "$SRCDIR\public\vgui_controls\Controls.h" - $File "$SRCDIR\public\vgui_controls\CvarToggleCheckButton.h" - $File "$SRCDIR\public\vgui_controls\DialogManager.h" - $File "$SRCDIR\public\vgui_controls\DirectorySelectDialog.h" - $File "$SRCDIR\public\vgui_controls\Divider.h" - $File "$SRCDIR\public\vgui_controls\EditablePanel.h" - $File "$SRCDIR\public\vgui_controls\ExpandButton.h" - $File "$SRCDIR\public\vgui_controls\FileOpenDialog.h" - $File "$SRCDIR\public\vgui_controls\FileOpenStateMachine.h" - $File "$SRCDIR\public\filesystem.h" - $File "$SRCDIR\public\filesystem_helpers.h" - $File "$SRCDIR\public\vgui_controls\FocusNavGroup.h" - $File "$SRCDIR\public\vgui_controls\Frame.h" - $File "$SRCDIR\public\vgui_controls\GraphPanel.h" - $File "$SRCDIR\public\vgui_controls\HTML.h" - $File "$SRCDIR\public\vgui_controls\Image.h" - $File "$SRCDIR\public\vgui_controls\ImageList.h" - $File "$SRCDIR\public\vgui_controls\ImagePanel.h" - $File "$SRCDIR\public\vgui_controls\InputDialog.h" - $File "$SRCDIR\public\tier1\interface.h" - $File "$SRCDIR\public\vgui_controls\KeyBindingHelpDialog.h" - $File "$SRCDIR\public\vgui_controls\KeyBindingMap.h" - $File "$SRCDIR\public\vgui_controls\KeyBoardEditorDialog.h" - $File "$SRCDIR\public\vgui_controls\KeyRepeat.h" - $File "$SRCDIR\public\tier1\KeyValues.h" - $File "$SRCDIR\public\vgui_controls\Label.h" - $File "$SRCDIR\public\vgui_controls\ListPanel.h" - $File "$SRCDIR\public\vgui_controls\ListViewPanel.h" - $File "$SRCDIR\public\tier0\memdbgoff.h" - $File "$SRCDIR\public\tier0\memdbgon.h" - $File "$SRCDIR\public\tier1\mempool.h" - $File "$SRCDIR\public\vgui_controls\Menu.h" - $File "$SRCDIR\public\vgui_controls\MenuBar.h" - $File "$SRCDIR\public\vgui_controls\MenuButton.h" - $File "$SRCDIR\public\vgui_controls\MenuItem.h" - $File "$SRCDIR\public\vgui_controls\MessageBox.h" - $File "$SRCDIR\public\vgui_controls\MessageDialog.h" - $File "$SRCDIR\public\vgui_controls\MessageMap.h" - $File "$SRCDIR\public\vgui_controls\Panel.h" - $File "$SRCDIR\public\vgui_controls\PanelAnimationVar.h" - $File "$SRCDIR\public\vgui_controls\PanelListPanel.h" - $File "$SRCDIR\public\vgui_controls\PerforceFileExplorer.h" - $File "$SRCDIR\public\vgui_controls\PerforceFileList.h" - $File "$SRCDIR\public\vgui_controls\perforcefilelistframe.h" - $File "$SRCDIR\public\vgui_controls\PHandle.h" - $File "$SRCDIR\public\vgui_controls\ProgressBar.h" - $File "$SRCDIR\public\vgui_controls\ProgressBox.h" - $File "$SRCDIR\public\vgui_controls\PropertyDialog.h" - $File "$SRCDIR\public\vgui_controls\PropertyPage.h" - $File "$SRCDIR\public\vgui_controls\PropertySheet.h" - $File "$SRCDIR\public\vgui_controls\QueryBox.h" - $File "$SRCDIR\public\vgui_controls\RadioButton.h" - $File "$SRCDIR\public\vgui_controls\RichText.h" - $File "$SRCDIR\public\vgui_controls\RotatingProgressBar.h" - $File "$SRCDIR\public\vgui_controls\savedocumentquery.h" - $File "$SRCDIR\public\vgui_controls\ScalableImagePanel.h" - $File "$SRCDIR\public\vgui_controls\ScrollableEditablePanel.h" - $File "$SRCDIR\public\vgui_controls\ScrollBar.h" - $File "$SRCDIR\public\vgui_controls\ScrollBarSlider.h" - $File "$SRCDIR\public\vgui_controls\SectionedListPanel.h" - $File "$SRCDIR\public\vgui_controls\Slider.h" - $File "$SRCDIR\public\vgui_controls\Splitter.h" - $File "$SRCDIR\public\vgui_controls\subrectimage.h" - $File "$SRCDIR\public\vgui_controls\TextEntry.h" - $File "$SRCDIR\public\vgui_controls\TextImage.h" - $File "$SRCDIR\public\vgui_controls\ToggleButton.h" - $File "$SRCDIR\public\vgui_controls\Tooltip.h" - $File "$SRCDIR\public\vgui_controls\ToolWindow.h" - $File "$SRCDIR\public\vgui_controls\TreeView.h" - $File "$SRCDIR\public\vgui_controls\TreeViewListControl.h" - $File "$SRCDIR\public\vgui_controls\URLLabel.h" - $File "$SRCDIR\public\tier1\utlmemory.h" - $File "$SRCDIR\public\tier1\utlrbtree.h" - $File "$SRCDIR\public\tier1\utlvector.h" - $File "$SRCDIR\public\vgui_controls\WizardPanel.h" - $File "$SRCDIR\public\vgui_controls\WizardSubPanel.h" - } - - $Folder "Protobuf Files" - { - $File "$SRCDIR\vgui2\chromehtml\htmlmessages.proto" - $DynamicFile "$GENERATED_PROTO_DIR\htmlmessages.pb.h" - $DynamicFile "$GENERATED_PROTO_DIR\htmlmessages.pb.cc" - { - $Configuration - { - $Compiler - { - $Create/UsePrecompiledHeader "Not Using Precompiled Headers" [$WINDOWS] - } - } - } - } - -} +//----------------------------------------------------------------------------- +// VGUI_CONTROLS.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$macro SRCDIR "..\.." +$Macro GENERATED_PROTO_DIR "generated_proto" +$macro PROTOBUF_LITE 0 + +$include "$SRCDIR\vpc_scripts\source_lib_base.vpc" +$include "$SRCDIR\vpc_scripts\protobuf_builder.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty;$SRCDIR\thirdparty\cef;$GENERATED_PROTO_DIR" + } +} + +$Project "vgui_controls" +{ + $Folder "Source Files" + { + $File "AnalogBar.cpp" + $File "AnimatingImagePanel.cpp" + $File "AnimationController.cpp" + $File "BitmapImagePanel.cpp" + $File "BuildFactoryHelper.cpp" + $File "BuildGroup.cpp" + $File "BuildModeDialog.cpp" + $File "Button.cpp" + $File "CheckButton.cpp" + $File "CheckButtonList.cpp" + $File "CircularProgressBar.cpp" + $File "ComboBox.cpp" + $File "consoledialog.cpp" + $File "ControllerMap.cpp" + $File "controls.cpp" + $File "CvarToggleCheckButton.cpp" + $File "DirectorySelectDialog.cpp" + $File "Divider.cpp" + $File "EditablePanel.cpp" + $File "ExpandButton.cpp" + $File "FileOpenDialog.cpp" + $File "FileOpenStateMachine.cpp" + $File "$SRCDIR\public\filesystem_helpers.cpp" + $File "FocusNavGroup.cpp" + $File "Frame.cpp" + $File "GraphPanel.cpp" + $File "HTML.cpp" + $File "Image.cpp" + $File "ImageList.cpp" + $File "ImagePanel.cpp" + $File "InputDialog.cpp" + $File "KeyBindingHelpDialog.cpp" + $File "KeyBoardEditorDialog.cpp" + $File "KeyRepeat.cpp" + $File "Label.cpp" + $File "ListPanel.cpp" + $File "ListViewPanel.cpp" + $File "Menu.cpp" + $File "MenuBar.cpp" + $File "MenuButton.cpp" + $File "MenuItem.cpp" + $File "MessageBox.cpp" + $File "MessageDialog.cpp" + $File "Panel.cpp" + $File "PanelListPanel.cpp" + $File "PerforceFileExplorer.cpp" + $File "PerforceFileList.cpp" + $File "perforcefilelistframe.cpp" + $File "ProgressBar.cpp" + $File "ProgressBox.cpp" + $File "PropertyDialog.cpp" + $File "PropertyPage.cpp" + $File "PropertySheet.cpp" + $File "QueryBox.cpp" + $File "RadioButton.cpp" + $File "RichText.cpp" + $File "RotatingProgressBar.cpp" + $File "savedocumentquery.cpp" + $File "ScalableImagePanel.cpp" + $File "ScrollableEditablePanel.cpp" + $File "ScrollBar.cpp" + $File "ScrollBarSlider.cpp" + $File "SectionedListPanel.cpp" + $File "Slider.cpp" + $File "Splitter.cpp" + $File "subrectimage.cpp" + $File "TextEntry.cpp" + $File "TextImage.cpp" + $File "ToggleButton.cpp" + $File "Tooltip.cpp" + $File "ToolWindow.cpp" + $File "TreeView.cpp" + $File "TreeViewListControl.cpp" + $File "URLLabel.cpp" + $File "WizardPanel.cpp" + $File "WizardSubPanel.cpp" + $File "$SRCDIR\public\html\htmlprotobuf.cpp" + $File "$SRCDIR/vgui2/src/vgui_key_translation.cpp" + } + + $Folder "Public Header Files" + { + $File "$SRCDIR\public\vgui_controls\AnalogBar.h" + $File "$SRCDIR\public\vgui_controls\AnimatingImagePanel.h" + $File "$SRCDIR\public\vgui_controls\AnimationController.h" + $File "$SRCDIR\public\vgui_controls\BitmapImagePanel.h" + $File "$SRCDIR\public\vgui_controls\BuildGroup.h" + $File "$SRCDIR\public\vgui_controls\BuildModeDialog.h" + $File "$SRCDIR\public\vgui_controls\Button.h" + $File "$SRCDIR\public\vgui_controls\CheckButton.h" + $File "$SRCDIR\public\vgui_controls\CheckButtonList.h" + $File "$SRCDIR\public\vgui_controls\CircularProgressBar.h" + $File "$SRCDIR\public\Color.h" + $File "$SRCDIR\public\vgui_controls\ComboBox.h" + $File "$SRCDIR\public\vgui_controls\consoledialog.h" + $File "$SRCDIR\public\vgui_controls\ControllerMap.h" + $File "$SRCDIR\public\vgui_controls\Controls.h" + $File "$SRCDIR\public\vgui_controls\CvarToggleCheckButton.h" + $File "$SRCDIR\public\vgui_controls\DialogManager.h" + $File "$SRCDIR\public\vgui_controls\DirectorySelectDialog.h" + $File "$SRCDIR\public\vgui_controls\Divider.h" + $File "$SRCDIR\public\vgui_controls\EditablePanel.h" + $File "$SRCDIR\public\vgui_controls\ExpandButton.h" + $File "$SRCDIR\public\vgui_controls\FileOpenDialog.h" + $File "$SRCDIR\public\vgui_controls\FileOpenStateMachine.h" + $File "$SRCDIR\public\filesystem.h" + $File "$SRCDIR\public\filesystem_helpers.h" + $File "$SRCDIR\public\vgui_controls\FocusNavGroup.h" + $File "$SRCDIR\public\vgui_controls\Frame.h" + $File "$SRCDIR\public\vgui_controls\GraphPanel.h" + $File "$SRCDIR\public\vgui_controls\HTML.h" + $File "$SRCDIR\public\vgui_controls\Image.h" + $File "$SRCDIR\public\vgui_controls\ImageList.h" + $File "$SRCDIR\public\vgui_controls\ImagePanel.h" + $File "$SRCDIR\public\vgui_controls\InputDialog.h" + $File "$SRCDIR\public\tier1\interface.h" + $File "$SRCDIR\public\vgui_controls\KeyBindingHelpDialog.h" + $File "$SRCDIR\public\vgui_controls\KeyBindingMap.h" + $File "$SRCDIR\public\vgui_controls\KeyBoardEditorDialog.h" + $File "$SRCDIR\public\vgui_controls\KeyRepeat.h" + $File "$SRCDIR\public\tier1\KeyValues.h" + $File "$SRCDIR\public\vgui_controls\Label.h" + $File "$SRCDIR\public\vgui_controls\ListPanel.h" + $File "$SRCDIR\public\vgui_controls\ListViewPanel.h" + $File "$SRCDIR\public\tier0\memdbgoff.h" + $File "$SRCDIR\public\tier0\memdbgon.h" + $File "$SRCDIR\public\tier1\mempool.h" + $File "$SRCDIR\public\vgui_controls\Menu.h" + $File "$SRCDIR\public\vgui_controls\MenuBar.h" + $File "$SRCDIR\public\vgui_controls\MenuButton.h" + $File "$SRCDIR\public\vgui_controls\MenuItem.h" + $File "$SRCDIR\public\vgui_controls\MessageBox.h" + $File "$SRCDIR\public\vgui_controls\MessageDialog.h" + $File "$SRCDIR\public\vgui_controls\MessageMap.h" + $File "$SRCDIR\public\vgui_controls\Panel.h" + $File "$SRCDIR\public\vgui_controls\PanelAnimationVar.h" + $File "$SRCDIR\public\vgui_controls\PanelListPanel.h" + $File "$SRCDIR\public\vgui_controls\PerforceFileExplorer.h" + $File "$SRCDIR\public\vgui_controls\PerforceFileList.h" + $File "$SRCDIR\public\vgui_controls\perforcefilelistframe.h" + $File "$SRCDIR\public\vgui_controls\PHandle.h" + $File "$SRCDIR\public\vgui_controls\ProgressBar.h" + $File "$SRCDIR\public\vgui_controls\ProgressBox.h" + $File "$SRCDIR\public\vgui_controls\PropertyDialog.h" + $File "$SRCDIR\public\vgui_controls\PropertyPage.h" + $File "$SRCDIR\public\vgui_controls\PropertySheet.h" + $File "$SRCDIR\public\vgui_controls\QueryBox.h" + $File "$SRCDIR\public\vgui_controls\RadioButton.h" + $File "$SRCDIR\public\vgui_controls\RichText.h" + $File "$SRCDIR\public\vgui_controls\RotatingProgressBar.h" + $File "$SRCDIR\public\vgui_controls\savedocumentquery.h" + $File "$SRCDIR\public\vgui_controls\ScalableImagePanel.h" + $File "$SRCDIR\public\vgui_controls\ScrollableEditablePanel.h" + $File "$SRCDIR\public\vgui_controls\ScrollBar.h" + $File "$SRCDIR\public\vgui_controls\ScrollBarSlider.h" + $File "$SRCDIR\public\vgui_controls\SectionedListPanel.h" + $File "$SRCDIR\public\vgui_controls\Slider.h" + $File "$SRCDIR\public\vgui_controls\Splitter.h" + $File "$SRCDIR\public\vgui_controls\subrectimage.h" + $File "$SRCDIR\public\vgui_controls\TextEntry.h" + $File "$SRCDIR\public\vgui_controls\TextImage.h" + $File "$SRCDIR\public\vgui_controls\ToggleButton.h" + $File "$SRCDIR\public\vgui_controls\Tooltip.h" + $File "$SRCDIR\public\vgui_controls\ToolWindow.h" + $File "$SRCDIR\public\vgui_controls\TreeView.h" + $File "$SRCDIR\public\vgui_controls\TreeViewListControl.h" + $File "$SRCDIR\public\vgui_controls\URLLabel.h" + $File "$SRCDIR\public\tier1\utlmemory.h" + $File "$SRCDIR\public\tier1\utlrbtree.h" + $File "$SRCDIR\public\tier1\utlvector.h" + $File "$SRCDIR\public\vgui_controls\WizardPanel.h" + $File "$SRCDIR\public\vgui_controls\WizardSubPanel.h" + } + + $Folder "Protobuf Files" + { + $File "$SRCDIR\vgui2\chromehtml\htmlmessages.proto" + $DynamicFile "$GENERATED_PROTO_DIR\htmlmessages.pb.h" + $DynamicFile "$GENERATED_PROTO_DIR\htmlmessages.pb.cc" + { + $Configuration + { + $Compiler + { + $Create/UsePrecompiledHeader "Not Using Precompiled Headers" [$WINDOWS] + } + } + } + } + +} -- cgit v1.2.3