diff options
Diffstat (limited to 'vgui2/vgui_controls/PropertySheet.cpp')
| -rw-r--r-- | vgui2/vgui_controls/PropertySheet.cpp | 1675 |
1 files changed, 1675 insertions, 0 deletions
diff --git a/vgui2/vgui_controls/PropertySheet.cpp b/vgui2/vgui_controls/PropertySheet.cpp new file mode 100644 index 0000000..06ff890 --- /dev/null +++ b/vgui2/vgui_controls/PropertySheet.cpp @@ -0,0 +1,1675 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include <vgui/IBorder.h> +#include <vgui/IInput.h> +#include <vgui/IPanel.h> +#include <vgui/IScheme.h> +#include <vgui/ISystem.h> +#include <vgui/IVGui.h> +#include <vgui/KeyCode.h> +#include <KeyValues.h> +#include <vgui/MouseCode.h> +#include <vgui/ISurface.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/Controls.h> +#include <vgui_controls/Label.h> +#include <vgui_controls/PropertySheet.h> +#include <vgui_controls/ComboBox.h> +#include <vgui_controls/Panel.h> +#include <vgui_controls/ToolWindow.h> +#include <vgui_controls/TextImage.h> +#include <vgui_controls/ImagePanel.h> +#include <vgui_controls/PropertyPage.h> +#include "vgui_controls/AnimationController.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +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" ) ); + 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); + + m_PageTabs[i]->GetSize(wide, tall); + + if ( m_bTabFitText ) + { + m_PageTabs[i]->SizeToContents(); + wide = m_PageTabs[i]->GetWide(); + + int iXInset, iYInset; + m_PageTabs[i]->GetTextInset( &iXInset, &iYInset ); + wide += (iXInset * 2); + } + + if (m_PageTabs[i] == _activeTab) + { + // active tab is taller + _activeTab->SetBounds(xtab, 2, wide, tabHeight); + } + else + { + m_PageTabs[i]->SetBounds(xtab, 4, wide, tabHeight - 2); + } + m_PageTabs[i]->SetVisible(true); + xtab += (wide + 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: + case STEAMCONTROLLER_DPAD_RIGHT: + { + ChangeActiveTab(_activeTabIndex+1); + break; + } + case KEY_LEFT: + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case KEY_XSTICK2_LEFT: + case STEAMCONTROLLER_DPAD_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; +} |