//===== Copyright ?1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: 
//
// $NoKeywords: $
//===========================================================================//

#include <assert.h>
#include <math.h> // 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 <stdio.h>

// 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 );
			SetPaintBackgroundEnabled(false);
		}
		
		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 );
		SetPaintBackgroundEnabled(false);
	}
	
	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) : EditablePanel(parent, panelName)
{
	// frames start invisible, to avoid having window flicker in on taskbar
	SetVisible(false);
	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_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;
	m_bImageBackground = false;
	m_iOutOfFocusAlpha = -1;
	m_bCloseing = false;

	SetTitle("#Frame_Untitled", parent ? false : true);
	
	// add ourselves to the build group
	SetBuildGroup(GetBuildGroup());
	
	SetMinimumSize(128,66);
	
	_sysMenu = NULL;

	GetFocusNavGroup().SetFocusTopLevel(true);
	
	// 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());
	
	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;
		}
	}

	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 _title;
}

//-----------------------------------------------------------------------------
// Purpose: Setup the grips on the edges of the panel to resize it.
//-----------------------------------------------------------------------------
void Frame::SetupResizeCursors()
{
	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);
	}
}

//-----------------------------------------------------------------------------
// Purpose: Bring the frame to the front and requests focus, ensures it's not minimized
//-----------------------------------------------------------------------------
void Frame::Activate()
{
	MoveToFront();
	RequestFocus();
	SetVisible(true);
	SetEnabled(true);

	m_bCloseing = false;

	if (m_bFadingOut)
	{
		// we were fading out, make sure to fade back in
		m_bFadingOut = false;
		m_bPreviouslyVisible = false;
	}

	surface()->SetMinimized(GetVPanel(), false);
	input()->ClearCompositionString();
}


//-----------------------------------------------------------------------------
// 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)
	{
		if (m_bFadingOut)
		{
			// we're fading out, see if we're done so we can fully hide the window
			if (GetAlpha() < 1)
			{
				FinishClose();
			}
		}
		else if (!m_bPreviouslyVisible)
		{
			// need to fade-in
			m_bPreviouslyVisible = true;
			
			// fade in
			SetAlpha(0);
			GetAnimationController()->RunAnimationCommand(this, "alpha", 255.0f, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);
		}
	}

	// check for focus changes
	bool hasFocus = false;
	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)
{
	// 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();

	if (bHasFocus)
	{
		_title->SetColor(_titleBarFgColor);
	}
	else
	{
		_title->SetColor(_titleBarDisabledFgColor);
	}

	// set our background color
	if (bHasFocus)
	{
		if (m_flFocusTransitionEffectTime)
		{
			GetAnimationController()->RunAnimationCommand(this, "BgColor", m_InFocusBgColor, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);

			if (m_iOutOfFocusAlpha != -1)
				GetAnimationController()->RunAnimationCommand(this, "Alpha", 255, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);
		}
		else
		{
			if (m_iOutOfFocusAlpha != -1)
				SetAlpha(255);

			SetBgColor(m_InFocusBgColor);
		}
	}
	else
	{
		if (m_flFocusTransitionEffectTime)
		{
			GetAnimationController()->RunAnimationCommand(this, "BgColor", m_OutOfFocusBgColor, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);

			if (m_iOutOfFocusAlpha != -1 && m_bFadingOut != true)
				GetAnimationController()->RunAnimationCommand(this, "Alpha", m_iOutOfFocusAlpha, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);
		}
		else
		{
			if (m_iOutOfFocusAlpha != -1)
				SetAlpha(m_iOutOfFocusAlpha);

			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);
	
	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);

	float scale = 1;

	if(IsProportional())
	{
		int screenW, screenH;
		surface()->GetScreenSize( screenW, screenH );

		int proW,proH;
		surface()->GetProportionalBase( proW, proH );

		scale =	( (float)( screenH ) / (float)( proH ) );
	}
	
	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 );
	}


}

//-----------------------------------------------------------------------------
// 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_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 closeing of the panel
//-----------------------------------------------------------------------------
bool Frame::IsCloseing()
{
	return m_bCloseing;
}

//-----------------------------------------------------------------------------
// 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 ( _menuButton && _menuButton->IsVisible() )
			{
				int mw, mh;
				_menuButton->GetImageSize( mw, mh );
				nTitleX += mw;
				nTitleWidth -= mw;
			}

			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: draws a selection box
//-----------------------------------------------------------------------------
void Frame::DrawBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha, bool hollow /*=false*/ )
{
	if (!m_bImageBackground)
		return BaseClass::DrawBox(x, y, wide, tall, color, normalizedAlpha, hollow);

	if (surface()->IsLockAlpha())
	{
		if (m_bHasFocus)
		{
			int focusAlpha = surface()->GetFocusAlpha();

			if (focusAlpha == 0)
				surface()->SetBlendEnabled(false);
			else if (focusAlpha <= 255)
				surface()->DrawSetAlphaMultiplier(focusAlpha / 255.0);
		}
		else
		{
			int inactiveAlpha = surface()->GetInactiveAlpha();

			if (inactiveAlpha == 0)
				surface()->SetBlendEnabled(false);
			else if (inactiveAlpha <= 255)
				surface()->DrawSetAlphaMultiplier(inactiveAlpha / 255.0);
		}
	}

	const int iOffset = 10;
	const int iOffset2 = 10;

	m_pTopBackground[0]->SetPos(0, 0);
	m_pTopBackground[0]->SetSize(24, 24);
	m_pTopBackground[0]->Paint();
	m_pTopBackground[1]->SetPos(24, 0);
	m_pTopBackground[1]->SetSize(wide - 48 + iOffset, 24);
	m_pTopBackground[1]->Paint();
	m_pTopBackground[2]->SetPos(wide - 24 + iOffset, 0);
	m_pTopBackground[2]->SetSize(24, 24);
	m_pTopBackground[2]->Paint();

	m_pCenterBackground[0]->SetPos(0, 24);
	m_pCenterBackground[0]->SetSize(24, tall - 48 + iOffset2);
	m_pCenterBackground[0]->Paint();
	m_pCenterBackground[1]->SetPos(24, 24);
	m_pCenterBackground[1]->SetSize(wide - 48 + iOffset, tall - 48 + iOffset2);
	m_pCenterBackground[1]->Paint();
	m_pCenterBackground[2]->SetPos(wide - 24 + iOffset, 24);
	m_pCenterBackground[2]->SetSize(24, tall - 48 + iOffset2);
	m_pCenterBackground[2]->Paint();

	m_pBottomBackground[0]->SetPos(0, tall - 24 + iOffset2);
	m_pBottomBackground[0]->SetSize(24, 24);
	m_pBottomBackground[0]->Paint();
	m_pBottomBackground[1]->SetPos(24, tall - 24 + iOffset2);
	m_pBottomBackground[1]->SetSize(wide - 48 + iOffset, 24);
	m_pBottomBackground[1]->Paint();
	m_pBottomBackground[2]->SetPos(wide - 24 + iOffset, tall - 24 + iOffset2);
	m_pBottomBackground[2]->SetSize(24, 24);
	m_pBottomBackground[2]->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();

	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);

	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"));

	resourceString = pScheme->GetResourceString("Frame.TopLeft");

	if (resourceString[0])
	{
		m_bImageBackground = true;
		m_pTopBackground[0] = scheme()->GetImage(resourceString, true);
		m_pTopBackground[1] = scheme()->GetImage(pScheme->GetResourceString("Frame.TopCenter"), true);
		m_pTopBackground[2] = scheme()->GetImage(pScheme->GetResourceString("Frame.TopRight"), true);
		m_pCenterBackground[0] = scheme()->GetImage(pScheme->GetResourceString("Frame.MiddleLeft"), true);
		m_pCenterBackground[1] = scheme()->GetImage(pScheme->GetResourceString("Frame.MiddleCenter"), true);
		m_pCenterBackground[2] = scheme()->GetImage(pScheme->GetResourceString("Frame.MiddleRight"), true);
		m_pBottomBackground[0] = scheme()->GetImage(pScheme->GetResourceString("Frame.BottomLeft"), true);
		m_pBottomBackground[1] = scheme()->GetImage(pScheme->GetResourceString("Frame.BottomCenter"), true);
		m_pBottomBackground[2] = scheme()->GetImage(pScheme->GetResourceString("Frame.BottomRight"), true);
	}

	OnFrameFocusChanged( m_bHasFocus );

	resourceString = pScheme->GetResourceString("Frame.OutOfFocusAlpha");

	if (resourceString[0])
		m_iOutOfFocusAlpha = atoi(resourceString);
}

// 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;
	m_iOutOfFocusAlpha = -1;
}

//-----------------------------------------------------------------------------
// 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);
	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)
	{
		// 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();
	}

	m_bCloseing = true;
}

//-----------------------------------------------------------------------------
// 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 (!_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;
}

//-----------------------------------------------------------------------------
// Purpose: Set the system menu  
//-----------------------------------------------------------------------------
void Frame::SetSysMenu(Menu *menu)
{
	if (menu == _sysMenu)
		return;
	
	_sysMenu->MarkForDeletion();
	_sysMenu = menu;

	_menuButton->SetMenu(_sysMenu);
}


//-----------------------------------------------------------------------------
// Set the system menu images
//-----------------------------------------------------------------------------
void Frame::SetImages( const char *pEnabledImage, const char *pDisabledImage )
{
	_menuButton->SetImages( pEnabledImage, pDisabledImage );
}


//-----------------------------------------------------------------------------
// Purpose: Close the window 
//-----------------------------------------------------------------------------
void Frame::Close()
{
	OnClose();
}

//-----------------------------------------------------------------------------
// Purpose: Finishes closing the dialog
//-----------------------------------------------------------------------------
void Frame::FinishClose()
{
	SetVisible(false);
	m_bPreviouslyVisible = false;
	m_bFadingOut = false;
	m_bCloseing = false;

	if (m_bDeleteSelfOnClose)
	{
		MarkForDeletion();
	}

	OnFinishedClose();
}

//-----------------------------------------------------------------------------
// 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)
{
	_menuButton->SetVisible(state);
}

//-----------------------------------------------------------------------------
// 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)
{
	_menuButton->SetResponsive(state);
}

//-----------------------------------------------------------------------------
// Purpose: Toggle visibility of the minimize button
//-----------------------------------------------------------------------------
void Frame::SetMinimizeButtonVisible(bool state)
{
	_minimizeButton->SetVisible(state);
}

//-----------------------------------------------------------------------------
// Purpose: Toggle visibility of the maximize button
//-----------------------------------------------------------------------------
void Frame::SetMaximizeButtonVisible(bool state)
{
	_maximizeButton->SetVisible(state);
}

//-----------------------------------------------------------------------------
// Purpose: Toggles visibility of the minimize-to-systray icon (defaults to false)
//-----------------------------------------------------------------------------
void Frame::SetMinimizeToSysTrayButtonVisible(bool state)
{
	_minimizeToSysTrayButton->SetVisible(state);
}

//-----------------------------------------------------------------------------
// Purpose: Toggle visibility of the close button
//-----------------------------------------------------------------------------
void Frame::SetCloseButtonVisible(bool state)
{
	_closeButton->SetVisible(state);
}

//-----------------------------------------------------------------------------
// 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
//-----------------------------------------------------------------------------
bool Frame::LoadControlSettings( const char *dialogResourceName, const char *pathID, KeyValues *pPreloadedKeyValues )
{
	bool bSuccess = BaseClass::LoadControlSettings( dialogResourceName, pathID, pPreloadedKeyValues );

	if (bSuccess)
	{
		// set the focus on the default control
		Panel *defaultFocus = GetFocusNavGroup().GetDefaultPanel();
		if (defaultFocus)
		{
			defaultFocus->RequestFocus();
		}
	}

	return bSuccess;
}

//-----------------------------------------------------------------------------
// 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 ( 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_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 );
}