Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

718 lines
18 KiB

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <vgui/KeyCode.h>
#include <keyvalues.h>
#include "vgui/IInput.h"
#include "vgui/MouseCode.h"
#include "vgui/ISurface.h"
#include <vgui_controls/ToolWindow.h>
#include <vgui_controls/PropertySheet.h>
#include "tier1/tokenset.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
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_bStickyEdges( true ),
m_pFactory( NULL )
{
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<EditablePanel *>(GetActivePage());
if (!panel)
return;
panel->ActivateBuildMode();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ToolWindow::RequestFocus(int direction)
{
m_pPropertySheet->RequestFocus(direction);
}
void ToolWindow::OnSetFocus()
{
m_pPropertySheet->RequestFocus();
}
//-----------------------------------------------------------------------------
// 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 );
}
}
void ToolWindow::OnPageChanged( )
{
//char szTitle[ 256 ];
//m_pPropertySheet->GetActiveTabTitle( szTitle, sizeof( szTitle ) );
//SetTitle( szTitle, false );
}
static void GetParentSpaceExtents( Panel *p, int bounds[ 4 ] )
{
// Get parent space bounds
p->GetBounds( bounds[ 0 ], bounds[ 1 ], bounds[ 2 ], bounds[ 3 ] );
// x1, y1
bounds[ 2 ] += bounds[ 0 ];
bounds[ 3 ] += bounds[ 1 ];
}
static ESharedEdge GetOppositeEdge( ESharedEdge eEdge )
{
switch ( eEdge )
{
default:
break;
case TOOLWINDOW_LEFT:
return TOOLWINDOW_RIGHT;
case TOOLWINDOW_TOP:
return TOOLWINDOW_BOTTOM;
case TOOLWINDOW_RIGHT:
return TOOLWINDOW_LEFT;
case TOOLWINDOW_BOTTOM:
return TOOLWINDOW_TOP;
}
Assert( 0 );
return TOOLWINDOW_NONE;
}
static void GetParentSpaceEdge( Panel *p, int bounds[ 4 ], ESharedEdge eEdge )
{
GetParentSpaceExtents( p, bounds );
// Collapse down to single edge
bounds[ GetOppositeEdge( eEdge ) ] = bounds[ eEdge ];
}
static const tokenset_t< ESharedEdge > s_EdgeTypes[] =
{
{ "TOOLWINDOW_NONE", TOOLWINDOW_NONE },
{ "TOOLWINDOW_LEFT", TOOLWINDOW_LEFT },
{ "TOOLWINDOW_TOP", TOOLWINDOW_TOP },
{ "TOOLWINDOW_RIGHT", TOOLWINDOW_RIGHT },
{ "TOOLWINDOW_BOTTOM", TOOLWINDOW_BOTTOM },
{ NULL, TOOLWINDOW_NONE }
};
void ToolWindow::MoveSibling( ToolWindow *pSibling, ESharedEdge eEdge, int dpixels )
{
int bounds[ 4 ];
GetParentSpaceExtents( pSibling, bounds );
bounds[ eEdge ] += dpixels;
// Convert back to local pos and width/height
bounds[ 2 ] = bounds[ 2 ] - bounds[ 0 ];
bounds[ 3 ] = bounds[ 3 ] - bounds[ 1 ];
pSibling->SetBounds( bounds[ 0 ], bounds[ 1 ], bounds[ 2 ], bounds[ 3 ] );
}
struct TWEdgePair_t
{
ToolWindow *m_pWindow;
ESharedEdge m_EdgeType;
static bool Less( const TWEdgePair_t &lhs, const TWEdgePair_t &rhs )
{
if ( lhs.m_pWindow < rhs.m_pWindow )
return true;
if ( lhs.m_pWindow > rhs.m_pWindow )
return false;
return lhs.m_EdgeType < rhs.m_EdgeType;
}
};
static bool SpanOverlaps( int line1[ 4 ], int line2[ 4 ] )
{
bool bXOverlap = ( MAX( line1[ 0 ], line2[ 0 ] ) - MIN( line1[ 2 ], line2[ 2 ] ) ) <= 0 ? true : false;
bool bYOverlap = ( MAX( line1[ 1 ], line2[ 1 ] ) - MIN( line1[ 3 ], line2[ 3 ] ) ) <= 0 ? true : false;
return bXOverlap && bYOverlap;
}
// Find any parallel edges that overlap and aren't already in the tree. If we add an edge, we recursively see if anything else overlaps it on the same line
void ToolWindow::FindOverlappingEdges_R( CUtlVector< ToolWindow * > &vecSiblings, CUtlRBTree< TWEdgePair_t > &rbCurrentEdges, ESharedEdge eEdgeType, int line[ 4 ] )
{
// L/R or T/B
for ( int et = 0; et < 2; ++et )
{
for ( int i = 0; i < vecSiblings.Count(); ++i )
{
ToolWindow *pSibling = vecSiblings[ i ];
int edgeline[ 4 ];
GetParentSpaceEdge( pSibling, edgeline, eEdgeType );
if ( SpanOverlaps( edgeline, line ) )
{
TWEdgePair_t ep;
ep.m_pWindow = pSibling;
ep.m_EdgeType = eEdgeType;
if ( rbCurrentEdges.Find( ep ) == rbCurrentEdges.InvalidIndex() )
{
/*
Msg( "Found overlap %d, %d, %d, %d with %d %d %d %d on edge %s\n",
edgeline[ 0 ],edgeline[ 1 ],edgeline[ 2 ],edgeline[ 3 ],
line[ 0 ],line[ 1 ],line[ 2 ],line[ 3 ],
s_EdgeTypes->GetNameByToken( eEdgeType ) );
*/
rbCurrentEdges.Insert( ep );
// Extend search out along the line!!!
FindOverlappingEdges_R( vecSiblings, rbCurrentEdges, eEdgeType, edgeline );
}
}
}
// Try opposite on next pass
eEdgeType = GetOppositeEdge( eEdgeType );
}
}
// Override Frame method in order to grow adjoining sibling tool windows if possible
void ToolWindow::OnGripPanelMoved( int nNewX, int nNewY, int nNewW, int nNewH )
{
bool bShiftDown = input()->IsKeyDown( KEY_LSHIFT ) || input()->IsKeyDown( KEY_RSHIFT );
if ( IsStickEdgesEnabled() && !bShiftDown )
{
// Snag old values
int x, y, w, h;
GetBounds( x, y, w, h );
// Get list of siblings
CUtlVector< ToolWindow * > vecSiblings;
GetSiblingToolWindows( vecSiblings );
// Msg( "%d siblings\n", vecSiblings.Count() );
if ( vecSiblings.Count() > 0 )
{
// Determine what type of grip panel movement(s) occurred
int d[ 4 ];
d[ 0 ] = nNewX - x; // L
d[ 1 ] = nNewY - y; // T
d[ 2 ] = ( nNewX + nNewW ) - ( x + w ); // R
d[ 3 ] = ( nNewY + nNewH ) - ( y + h ); // B
// For each moved edge, find and edges along the same line that overlap. If we find an overlap, check it for additional overlaps against the siblings:
//
// ------------------------------------ e.g: clicking at the base of panel B should find the "top" of panel C, which then recurses and gets the bottom line of panel A as well
// | | |
// | A | B |
// | | |
// | | |
// |-----------------xxxxxxxxxxxxxxxxxx|
// | C |
// | |
// -------------------------------------
//
for ( int edge = 0; edge < 4; ++edge )
{
if ( !d[ edge ] )
continue;
CUtlRBTree< TWEdgePair_t > rbEdges( 0, 0, TWEdgePair_t::Less );
ESharedEdge eEdge = (ESharedEdge)( edge );
// Msg( "movement on edge %s is %d pixels\n", s_EdgeTypes->GetNameByToken( eEdge ), d[ edge ] );
// Now find any overlapping ones along the same "line"
int edgeline[ 4 ];
GetParentSpaceEdge( this, edgeline, eEdge );
FindOverlappingEdges_R( vecSiblings, rbEdges, eEdge, edgeline );
FOR_EACH_UTLRBTREE( rbEdges, i )
{
TWEdgePair_t &ep = rbEdges[ i ];
// Msg( "moving tw %p on edge %s\n", ep.m_pWindow, s_EdgeTypes->GetNameByToken( ep.m_EdgeType ) );
// Move the sibling
MoveSibling( ep.m_pWindow, ep.m_EdgeType, d[ edge ] );
}
}
}
}
// Move panel in question
BaseClass::OnGripPanelMoved( nNewX, nNewY, nNewW, nNewH );
}
void ToolWindow::GetSiblingToolWindows( CUtlVector< ToolWindow * > &vecSiblings )
{
Panel *parent = GetParent();
if ( NULL == parent )
return;
int nChildCount = parent->GetChildCount();
for ( int i = 0 ; i < nChildCount; ++i )
{
ToolWindow *pChildTool = dynamic_cast< ToolWindow * >( parent->GetChild( i ) );
if ( !pChildTool ||
!pChildTool->IsVisible() ||
pChildTool == this )
continue;
vecSiblings.AddToTail( pChildTool );
}
}
void ToolWindow::OnGripPanelMoveFinished()
{
}
// Static method
void ToolWindow::EnableStickyEdges( bool bEnable )
{
m_bStickyEdges = bEnable;
}
bool ToolWindow::IsStickEdgesEnabled() const
{
return m_bStickyEdges;
}