aboutsummaryrefslogtreecommitdiff
path: root/mp/src/vgui2/vgui_controls
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /mp/src/vgui2/vgui_controls
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/vgui2/vgui_controls')
-rw-r--r--mp/src/vgui2/vgui_controls/AnalogBar.cpp460
-rw-r--r--mp/src/vgui2/vgui_controls/AnimatingImagePanel.cpp216
-rw-r--r--mp/src/vgui2/vgui_controls/AnimationController.cpp1651
-rw-r--r--mp/src/vgui2/vgui_controls/BitmapImagePanel.cpp364
-rw-r--r--mp/src/vgui2/vgui_controls/BuildFactoryHelper.cpp104
-rw-r--r--mp/src/vgui2/vgui_controls/BuildGroup.cpp1448
-rw-r--r--mp/src/vgui2/vgui_controls/BuildModeDialog.cpp1441
-rw-r--r--mp/src/vgui2/vgui_controls/Button.cpp1095
-rw-r--r--mp/src/vgui2/vgui_controls/CheckButton.cpp205
-rw-r--r--mp/src/vgui2/vgui_controls/CheckButtonList.cpp212
-rw-r--r--mp/src/vgui2/vgui_controls/CircularProgressBar.cpp292
-rw-r--r--mp/src/vgui2/vgui_controls/ComboBox.cpp1041
-rw-r--r--mp/src/vgui2/vgui_controls/ControllerMap.cpp160
-rw-r--r--mp/src/vgui2/vgui_controls/DirectorySelectDialog.cpp594
-rw-r--r--mp/src/vgui2/vgui_controls/Divider.cpp41
-rw-r--r--mp/src/vgui2/vgui_controls/EditablePanel.cpp1069
-rw-r--r--mp/src/vgui2/vgui_controls/ExpandButton.cpp114
-rw-r--r--mp/src/vgui2/vgui_controls/FileOpenDialog.cpp1701
-rw-r--r--mp/src/vgui2/vgui_controls/FileOpenStateMachine.cpp497
-rw-r--r--mp/src/vgui2/vgui_controls/FocusNavGroup.cpp433
-rw-r--r--mp/src/vgui2/vgui_controls/Frame.cpp2396
-rw-r--r--mp/src/vgui2/vgui_controls/GraphPanel.cpp308
-rw-r--r--mp/src/vgui2/vgui_controls/HTML.cpp2278
-rw-r--r--mp/src/vgui2/vgui_controls/Image.cpp282
-rw-r--r--mp/src/vgui2/vgui_controls/ImageList.cpp106
-rw-r--r--mp/src/vgui2/vgui_controls/ImagePanel.cpp463
-rw-r--r--mp/src/vgui2/vgui_controls/InputDialog.cpp236
-rw-r--r--mp/src/vgui2/vgui_controls/KeyBindingHelpDialog.cpp355
-rw-r--r--mp/src/vgui2/vgui_controls/KeyBoardEditorDialog.cpp849
-rw-r--r--mp/src/vgui2/vgui_controls/KeyRepeat.cpp98
-rw-r--r--mp/src/vgui2/vgui_controls/Label.cpp1386
-rw-r--r--mp/src/vgui2/vgui_controls/ListPanel.cpp3283
-rw-r--r--mp/src/vgui2/vgui_controls/ListViewPanel.cpp1082
-rw-r--r--mp/src/vgui2/vgui_controls/Menu.cpp2703
-rw-r--r--mp/src/vgui2/vgui_controls/MenuBar.cpp252
-rw-r--r--mp/src/vgui2/vgui_controls/MenuButton.cpp351
-rw-r--r--mp/src/vgui2/vgui_controls/MenuItem.cpp647
-rw-r--r--mp/src/vgui2/vgui_controls/MessageBox.cpp395
-rw-r--r--mp/src/vgui2/vgui_controls/MessageDialog.cpp359
-rw-r--r--mp/src/vgui2/vgui_controls/Panel.cpp8451
-rw-r--r--mp/src/vgui2/vgui_controls/PanelListPanel.cpp473
-rw-r--r--mp/src/vgui2/vgui_controls/PerforceFileExplorer.cpp278
-rw-r--r--mp/src/vgui2/vgui_controls/PerforceFileList.cpp570
-rw-r--r--mp/src/vgui2/vgui_controls/ProgressBar.cpp427
-rw-r--r--mp/src/vgui2/vgui_controls/ProgressBox.cpp360
-rw-r--r--mp/src/vgui2/vgui_controls/PropertyDialog.cpp303
-rw-r--r--mp/src/vgui2/vgui_controls/PropertyPage.cpp114
-rw-r--r--mp/src/vgui2/vgui_controls/PropertySheet.cpp1674
-rw-r--r--mp/src/vgui2/vgui_controls/QueryBox.cpp222
-rw-r--r--mp/src/vgui2/vgui_controls/RadioButton.cpp419
-rw-r--r--mp/src/vgui2/vgui_controls/RichText.cpp2744
-rw-r--r--mp/src/vgui2/vgui_controls/RotatingProgressBar.cpp200
-rw-r--r--mp/src/vgui2/vgui_controls/ScalableImagePanel.cpp266
-rw-r--r--mp/src/vgui2/vgui_controls/ScrollBar.cpp802
-rw-r--r--mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp606
-rw-r--r--mp/src/vgui2/vgui_controls/ScrollableEditablePanel.cpp86
-rw-r--r--mp/src/vgui2/vgui_controls/SectionedListPanel.cpp2169
-rw-r--r--mp/src/vgui2/vgui_controls/Slider.cpp954
-rw-r--r--mp/src/vgui2/vgui_controls/Splitter.cpp765
-rw-r--r--mp/src/vgui2/vgui_controls/TextEntry.cpp4279
-rw-r--r--mp/src/vgui2/vgui_controls/TextImage.cpp985
-rw-r--r--mp/src/vgui2/vgui_controls/ToggleButton.cpp102
-rw-r--r--mp/src/vgui2/vgui_controls/ToolWindow.cpp478
-rw-r--r--mp/src/vgui2/vgui_controls/Tooltip.cpp406
-rw-r--r--mp/src/vgui2/vgui_controls/TreeView.cpp2854
-rw-r--r--mp/src/vgui2/vgui_controls/TreeViewListControl.cpp315
-rw-r--r--mp/src/vgui2/vgui_controls/URLLabel.cpp158
-rw-r--r--mp/src/vgui2/vgui_controls/WizardPanel.cpp720
-rw-r--r--mp/src/vgui2/vgui_controls/WizardSubPanel.cpp114
-rw-r--r--mp/src/vgui2/vgui_controls/consoledialog.cpp1256
-rw-r--r--mp/src/vgui2/vgui_controls/controls.cpp72
-rw-r--r--mp/src/vgui2/vgui_controls/cvartogglecheckbutton.cpp25
-rw-r--r--mp/src/vgui2/vgui_controls/perforcefilelistframe.cpp623
-rw-r--r--mp/src/vgui2/vgui_controls/savedocumentquery.cpp195
-rw-r--r--mp/src/vgui2/vgui_controls/subrectimage.cpp212
-rw-r--r--mp/src/vgui2/vgui_controls/vgui_controls-2010.vcxproj362
-rw-r--r--mp/src/vgui2/vgui_controls/vgui_controls-2010.vcxproj.filters539
-rw-r--r--mp/src/vgui2/vgui_controls/vgui_controls_linux32.mak782
-rw-r--r--mp/src/vgui2/vgui_controls/vgui_controls_osx32.mak786
79 files changed, 68113 insertions, 0 deletions
diff --git a/mp/src/vgui2/vgui_controls/AnalogBar.cpp b/mp/src/vgui2/vgui_controls/AnalogBar.cpp
new file mode 100644
index 00000000..49be39d4
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/AnalogBar.cpp
@@ -0,0 +1,460 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+
+#include <vgui_controls/AnalogBar.h>
+#include <vgui_controls/Controls.h>
+
+#include <vgui/ILocalize.h>
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <KeyValues.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( AnalogBar );
+
+
+#define ANALOG_BAR_HOME_SIZE 4
+#define ANALOG_BAR_HOME_GAP 2
+#define ANALOG_BAR_LESS_TALL ( ANALOG_BAR_HOME_SIZE + ANALOG_BAR_HOME_GAP )
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+AnalogBar::AnalogBar(Panel *parent, const char *panelName) : Panel(parent, panelName)
+{
+ _analogValue = 0.0f;
+ m_pszDialogVar = NULL;
+ SetSegmentInfo( 2, 6 );
+ SetBarInset( 0 );
+ m_iAnalogValueDirection = PROGRESS_EAST;
+
+ m_fHomeValue = 2.0f;
+ m_HomeColor = GetFgColor();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+AnalogBar::~AnalogBar()
+{
+ delete [] m_pszDialogVar;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void AnalogBar::SetSegmentInfo( int gap, int width )
+{
+ _segmentGap = gap;
+ _segmentWide = width;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the number of segment blocks drawn
+//-----------------------------------------------------------------------------
+int AnalogBar::GetDrawnSegmentCount()
+{
+ int wide, tall;
+ GetSize(wide, tall);
+ int segmentTotal = wide / (_segmentGap + _segmentWide);
+ return (int)(segmentTotal * _analogValue);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the total number of segment blocks drawn (active and inactive)
+//-----------------------------------------------------------------------------
+int AnalogBar::GetTotalSegmentCount()
+{
+ int wide, tall;
+ GetSize(wide, tall);
+ int segmentTotal = wide / (_segmentGap + _segmentWide);
+ return segmentTotal;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnalogBar::PaintBackground()
+{
+ // Don't draw a background
+}
+
+void AnalogBar::PaintSegment( int &x, int &y, int tall, int wide, Color color, bool bHome )
+{
+ switch( m_iAnalogValueDirection )
+ {
+ case PROGRESS_EAST:
+ x += _segmentGap;
+
+ if ( bHome )
+ {
+ surface()->DrawSetColor( GetHomeColor() );
+ surface()->DrawFilledRect(x, y, x + _segmentWide, y + ANALOG_BAR_HOME_SIZE );
+ surface()->DrawFilledRect(x, y + tall - (y * 2) - ANALOG_BAR_HOME_SIZE, x + _segmentWide, y + tall - (y * 2) );
+ }
+
+ surface()->DrawSetColor( color );
+ surface()->DrawFilledRect(x, y + ANALOG_BAR_LESS_TALL, x + _segmentWide, y + tall - (y * 2) - ANALOG_BAR_LESS_TALL );
+ x += _segmentWide;
+ break;
+
+ case PROGRESS_WEST:
+ x -= _segmentGap + _segmentWide;
+
+ if ( bHome )
+ {
+ surface()->DrawSetColor( GetHomeColor() );
+ surface()->DrawFilledRect(x, y, x + _segmentWide, y + ANALOG_BAR_HOME_SIZE );
+ surface()->DrawFilledRect(x, y + tall - (y * 2) - ANALOG_BAR_HOME_SIZE, x + _segmentWide, y + tall - (y * 2) );
+ }
+
+ surface()->DrawSetColor( color );
+ surface()->DrawFilledRect(x, y + ANALOG_BAR_LESS_TALL, x + _segmentWide, y + tall - (y * 2) - ANALOG_BAR_LESS_TALL );
+ break;
+
+ case PROGRESS_NORTH:
+ y -= _segmentGap + _segmentWide;
+
+ if ( bHome )
+ {
+ surface()->DrawSetColor( GetHomeColor() );
+ surface()->DrawFilledRect(x, y, x + ANALOG_BAR_HOME_SIZE, y + _segmentWide );
+ surface()->DrawFilledRect(x + wide - (x * 2) - ANALOG_BAR_HOME_SIZE, y, x + wide - (x * 2), y + _segmentWide );
+ }
+
+ surface()->DrawSetColor( color );
+ surface()->DrawFilledRect(x + ANALOG_BAR_LESS_TALL, y, x + wide - (x * 2) - ANALOG_BAR_LESS_TALL, y + _segmentWide);
+ break;
+
+ case PROGRESS_SOUTH:
+ y += _segmentGap;
+
+ if ( bHome )
+ {
+ surface()->DrawSetColor( GetHomeColor() );
+ surface()->DrawFilledRect(x, y, x + ANALOG_BAR_HOME_SIZE, y + _segmentWide );
+ surface()->DrawFilledRect(x + wide - (x * 2) - ANALOG_BAR_HOME_SIZE, y, x + wide - (x * 2), y + _segmentWide );
+ }
+
+ surface()->DrawSetColor( color );
+ surface()->DrawFilledRect(x + ANALOG_BAR_LESS_TALL, y, x + wide - (x * 2) - ANALOG_BAR_LESS_TALL, y + _segmentWide);
+ y += _segmentWide;
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnalogBar::Paint()
+{
+ int wide, tall;
+ GetSize(wide, tall);
+
+ // gaps
+ int segmentTotal = 0, segmentsDrawn = 0;
+ int x = 0, y = 0;
+
+ switch( m_iAnalogValueDirection )
+ {
+ case PROGRESS_WEST:
+ x = wide;
+ y = m_iBarInset;
+ segmentTotal = wide / (_segmentGap + _segmentWide);
+ segmentsDrawn = (int)(segmentTotal * _analogValue + 0.5f);
+ break;
+
+ case PROGRESS_EAST:
+ x = 0;
+ y = m_iBarInset;
+ segmentTotal = wide / (_segmentGap + _segmentWide);
+ segmentsDrawn = (int)(segmentTotal * _analogValue + 0.5f);
+ break;
+
+ case PROGRESS_NORTH:
+ x = m_iBarInset;
+ y = tall;
+ segmentTotal = tall / (_segmentGap + _segmentWide);
+ segmentsDrawn = (int)(segmentTotal * _analogValue + 0.5f);
+ break;
+
+ case PROGRESS_SOUTH:
+ x = m_iBarInset;
+ y = 0;
+ segmentTotal = tall / (_segmentGap + _segmentWide);
+ segmentsDrawn = (int)(segmentTotal * _analogValue + 0.5f);
+ break;
+ }
+
+ int iHomeIndex = (int)( segmentTotal * m_fHomeValue + 0.5f ) - 1;
+ if ( iHomeIndex < 0 )
+ iHomeIndex = 0;
+
+ for (int i = 0; i < segmentsDrawn; i++)
+ PaintSegment( x, y, tall, wide, GetFgColor(), i == iHomeIndex );
+
+ for (int i = segmentsDrawn; i < segmentTotal; i++)
+ PaintSegment( x, y, tall, wide, GetBgColor(), i == iHomeIndex );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnalogBar::SetAnalogValue(float analogValue)
+{
+ if (analogValue != _analogValue)
+ {
+ // clamp the analogValue value within the range
+ if (analogValue < 0.0f)
+ {
+ analogValue = 0.0f;
+ }
+ else if (analogValue > 1.0f)
+ {
+ analogValue = 1.0f;
+ }
+
+ _analogValue = analogValue;
+ Repaint();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+float AnalogBar::GetAnalogValue()
+{
+ return _analogValue;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnalogBar::ApplySchemeSettings(IScheme *pScheme)
+{
+ Panel::ApplySchemeSettings(pScheme);
+
+ SetBgColor( Color( 255 - GetFgColor().r(), 255 - GetFgColor().g(), 255 - GetFgColor().b(), GetFgColor().a() ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: utility function for calculating a time remaining string
+//-----------------------------------------------------------------------------
+bool AnalogBar::ConstructTimeRemainingString(wchar_t *output, int outputBufferSizeInBytes, float startTime, float currentTime, float currentAnalogValue, float lastAnalogValueUpdateTime, bool addRemainingSuffix)
+{
+ Assert( outputBufferSizeInBytes >= sizeof(output[0]) );
+ Assert(lastAnalogValueUpdateTime <= currentTime);
+ output[0] = 0;
+
+ // calculate pre-extrapolation values
+ float timeElapsed = lastAnalogValueUpdateTime - startTime;
+ float totalTime = timeElapsed / currentAnalogValue;
+
+ // calculate seconds
+ int secondsRemaining = (int)(totalTime - timeElapsed);
+ if (lastAnalogValueUpdateTime < currentTime)
+ {
+ // old update, extrapolate
+ float analogValueRate = currentAnalogValue / timeElapsed;
+ float extrapolatedAnalogValue = analogValueRate * (currentTime - startTime);
+ float extrapolatedTotalTime = (currentTime - startTime) / extrapolatedAnalogValue;
+ secondsRemaining = (int)(extrapolatedTotalTime - timeElapsed);
+ }
+ // if there's some time, make sure it's at least one second left
+ if ( secondsRemaining == 0 && ( ( totalTime - timeElapsed ) > 0 ) )
+ {
+ secondsRemaining = 1;
+ }
+
+ // calculate minutes
+ int minutesRemaining = 0;
+ while (secondsRemaining >= 60)
+ {
+ minutesRemaining++;
+ secondsRemaining -= 60;
+ }
+
+ char minutesBuf[16];
+ Q_snprintf(minutesBuf, sizeof( minutesBuf ), "%d", minutesRemaining);
+ char secondsBuf[16];
+ Q_snprintf(secondsBuf, sizeof( secondsBuf ), "%d", secondsRemaining);
+
+ if (minutesRemaining > 0)
+ {
+ wchar_t unicodeMinutes[16];
+ g_pVGuiLocalize->ConvertANSIToUnicode(minutesBuf, unicodeMinutes, sizeof( unicodeMinutes ));
+ wchar_t unicodeSeconds[16];
+ g_pVGuiLocalize->ConvertANSIToUnicode(secondsBuf, unicodeSeconds, sizeof( unicodeSeconds ));
+
+ const char *unlocalizedString = "#vgui_TimeLeftMinutesSeconds";
+ if (minutesRemaining == 1 && secondsRemaining == 1)
+ {
+ unlocalizedString = "#vgui_TimeLeftMinuteSecond";
+ }
+ else if (minutesRemaining == 1)
+ {
+ unlocalizedString = "#vgui_TimeLeftMinuteSeconds";
+ }
+ else if (secondsRemaining == 1)
+ {
+ unlocalizedString = "#vgui_TimeLeftMinutesSecond";
+ }
+
+ char unlocString[64];
+ Q_strncpy(unlocString, unlocalizedString,sizeof( unlocString ));
+ if (addRemainingSuffix)
+ {
+ Q_strncat(unlocString, "Remaining", sizeof(unlocString ), COPY_ALL_CHARACTERS);
+ }
+ g_pVGuiLocalize->ConstructString(output, outputBufferSizeInBytes, g_pVGuiLocalize->Find(unlocString), 2, unicodeMinutes, unicodeSeconds);
+
+ }
+ else if (secondsRemaining > 0)
+ {
+ wchar_t unicodeSeconds[16];
+ g_pVGuiLocalize->ConvertANSIToUnicode(secondsBuf, unicodeSeconds, sizeof( unicodeSeconds ));
+
+ const char *unlocalizedString = "#vgui_TimeLeftSeconds";
+ if (secondsRemaining == 1)
+ {
+ unlocalizedString = "#vgui_TimeLeftSecond";
+ }
+ char unlocString[64];
+ Q_strncpy(unlocString, unlocalizedString,sizeof(unlocString));
+ if (addRemainingSuffix)
+ {
+ Q_strncat(unlocString, "Remaining",sizeof(unlocString), COPY_ALL_CHARACTERS);
+ }
+ g_pVGuiLocalize->ConstructString(output, outputBufferSizeInBytes, g_pVGuiLocalize->Find(unlocString), 1, unicodeSeconds);
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void AnalogBar::SetBarInset( int pixels )
+{
+ m_iBarInset = pixels;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+int AnalogBar::GetBarInset( void )
+{
+ return m_iBarInset;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnalogBar::ApplySettings(KeyValues *inResourceData)
+{
+ _analogValue = inResourceData->GetFloat("analogValue", 0.0f);
+
+ const char *dialogVar = inResourceData->GetString("variable", "");
+ if (dialogVar && *dialogVar)
+ {
+ m_pszDialogVar = new char[strlen(dialogVar) + 1];
+ strcpy(m_pszDialogVar, dialogVar);
+ }
+
+ BaseClass::ApplySettings(inResourceData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnalogBar::GetSettings(KeyValues *outResourceData)
+{
+ BaseClass::GetSettings(outResourceData);
+ outResourceData->SetFloat("analogValue", _analogValue );
+
+ if (m_pszDialogVar)
+ {
+ outResourceData->SetString("variable", m_pszDialogVar);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a string description of the panel fields for use in the UI
+//-----------------------------------------------------------------------------
+const char *AnalogBar::GetDescription( void )
+{
+ static char buf[1024];
+ _snprintf(buf, sizeof(buf), "%s, string analogValue, string variable", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: updates analogValue bar bases on values
+//-----------------------------------------------------------------------------
+void AnalogBar::OnDialogVariablesChanged(KeyValues *dialogVariables)
+{
+ if (m_pszDialogVar)
+ {
+ int val = dialogVariables->GetInt(m_pszDialogVar, -1);
+ if (val >= 0.0f)
+ {
+ SetAnalogValue(val / 100.0f);
+ }
+ }
+}
+
+
+DECLARE_BUILD_FACTORY( ContinuousAnalogBar );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+ContinuousAnalogBar::ContinuousAnalogBar(Panel *parent, const char *panelName) : AnalogBar(parent, panelName)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ContinuousAnalogBar::Paint()
+{
+ int x = 0, y = 0;
+ int wide, tall;
+ GetSize(wide, tall);
+
+ surface()->DrawSetColor(GetFgColor());
+
+ switch( m_iAnalogValueDirection )
+ {
+ case PROGRESS_EAST:
+ surface()->DrawFilledRect( x, y, x + (int)( wide * _analogValue ), y + tall );
+ break;
+
+ case PROGRESS_WEST:
+ surface()->DrawFilledRect( x + (int)( wide * ( 1.0f - _analogValue ) ), y, x + wide, y + tall );
+ break;
+
+ case PROGRESS_NORTH:
+ surface()->DrawFilledRect( x, y + (int)( tall * ( 1.0f - _analogValue ) ), x + wide, y + tall );
+ break;
+
+ case PROGRESS_SOUTH:
+ surface()->DrawFilledRect( x, y, x + wide, y + (int)( tall * _analogValue ) );
+ break;
+ }
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/AnimatingImagePanel.cpp b/mp/src/vgui2/vgui_controls/AnimatingImagePanel.cpp
new file mode 100644
index 00000000..564219a5
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/AnimatingImagePanel.cpp
@@ -0,0 +1,216 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdio.h>
+#define PROTECTED_THINGS_DISABLE
+
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <vgui/ISystem.h>
+#include <vgui/IImage.h>
+#include <vgui/IVGui.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/AnimatingImagePanel.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( AnimatingImagePanel );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+AnimatingImagePanel::AnimatingImagePanel(Panel *parent, const char *name) : Panel(parent, name)
+{
+ m_iCurrentImage = 0;
+ m_iFrameTimeMillis = 100; // 10Hz frame rate
+ m_iNextFrameTime = 0;
+ m_pImageName = NULL;
+ m_bFiltered = false;
+ m_bScaleImage = false;
+ m_bAnimating = false;
+ ivgui()->AddTickSignal(GetVPanel());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Layout the panel for drawing.
+//-----------------------------------------------------------------------------
+void AnimatingImagePanel::PerformLayout()
+{
+ Panel::PerformLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add an image to the end of the list of animations
+//-----------------------------------------------------------------------------
+void AnimatingImagePanel::AddImage(IImage *image)
+{
+ m_Frames.AddToTail(image);
+
+ if ( !m_bScaleImage && image != NULL )
+ {
+ int wide,tall;
+ image->GetSize(wide,tall);
+ SetSize(wide,tall);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Load a set of animations by name.
+// Input:
+// baseName: is the name of the animations without their frame number or
+// file extension, (e.g. c1.tga becomes just c.)
+// framecount: number of frames in the animation
+//-----------------------------------------------------------------------------
+void AnimatingImagePanel::LoadAnimation(const char *baseName, int frameCount)
+{
+ m_Frames.RemoveAll();
+ for (int i = 1; i <= frameCount; i++)
+ {
+ char imageName[512];
+ Q_snprintf(imageName, sizeof( imageName ), "%s%d", baseName, i);
+ AddImage(scheme()->GetImage(imageName, m_bFiltered));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw the current image
+//-----------------------------------------------------------------------------
+void AnimatingImagePanel::PaintBackground()
+{
+ if ( m_Frames.IsValidIndex( m_iCurrentImage ) && m_Frames[m_iCurrentImage] != NULL )
+ {
+ IImage *pImage = m_Frames[m_iCurrentImage];
+
+ surface()->DrawSetColor( 255, 255, 255, 255 );
+ pImage->SetPos(0, 0);
+
+ if ( m_bScaleImage )
+ {
+ // Image size is stored in the bitmap, so temporarily set its size
+ // to our panel size and then restore after we draw it.
+
+ int imageWide, imageTall;
+ pImage->GetSize( imageWide, imageTall );
+
+ int wide, tall;
+ GetSize( wide, tall );
+ pImage->SetSize( wide, tall );
+
+ pImage->SetColor( Color( 255,255,255,255 ) );
+ pImage->Paint();
+
+ pImage->SetSize( imageWide, imageTall );
+ }
+ else
+ {
+ pImage->Paint();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called every frame the panel is visible
+//-----------------------------------------------------------------------------
+void AnimatingImagePanel::OnTick()
+{
+ if (m_bAnimating && system()->GetTimeMillis() >= m_iNextFrameTime)
+ {
+ m_iNextFrameTime = system()->GetTimeMillis() + m_iFrameTimeMillis;
+ m_iCurrentImage++;
+ if (!m_Frames.IsValidIndex(m_iCurrentImage))
+ {
+ m_iCurrentImage = 0;
+ }
+ Repaint();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get control settings for editing
+// Output: outResourceData- a set of keyvalues of imagenames.
+//-----------------------------------------------------------------------------
+void AnimatingImagePanel::GetSettings(KeyValues *outResourceData)
+{
+ BaseClass::GetSettings(outResourceData);
+ if (m_pImageName)
+ {
+ outResourceData->SetString("image", m_pImageName);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies resource settings
+//-----------------------------------------------------------------------------
+void AnimatingImagePanel::ApplySettings(KeyValues *inResourceData)
+{
+ BaseClass::ApplySettings(inResourceData);
+
+ const char *imageName = inResourceData->GetString("image", NULL);
+ if (imageName)
+ {
+ m_bScaleImage = ( inResourceData->GetInt( "scaleImage", 0 ) == 1 );
+
+ delete [] m_pImageName;
+ int len = Q_strlen(imageName) + 1;
+ m_pImageName = new char[len];
+ Q_strncpy(m_pImageName, imageName, len);
+
+ // add in the command
+ LoadAnimation(m_pImageName, inResourceData->GetInt("frames"));
+ }
+
+ m_iFrameTimeMillis = inResourceData->GetInt( "anim_framerate", 100 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get editing details
+//-----------------------------------------------------------------------------
+const char *AnimatingImagePanel::GetDescription()
+{
+ static char buf[1024];
+ Q_snprintf(buf, sizeof(buf), "%s, string image", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Starts the image doing its animation
+//-----------------------------------------------------------------------------
+void AnimatingImagePanel::StartAnimation()
+{
+ m_bAnimating = true;
+// ivgui()->AddTickSignal(GetVPanel());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stops the images animation
+//-----------------------------------------------------------------------------
+void AnimatingImagePanel::StopAnimation()
+{
+ m_bAnimating = false;
+// ivgui()->RemoveTickSignal(GetVPanel());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Resets the animation to the start of the sequence.
+//-----------------------------------------------------------------------------
+void AnimatingImagePanel::ResetAnimation(int frame)
+{
+ if(m_Frames.IsValidIndex(frame))
+ {
+ m_iCurrentImage = frame;
+ }
+ else
+ {
+ m_iCurrentImage = 0;
+ }
+ Repaint();
+}
diff --git a/mp/src/vgui2/vgui_controls/AnimationController.cpp b/mp/src/vgui2/vgui_controls/AnimationController.cpp
new file mode 100644
index 00000000..a618231c
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/AnimationController.cpp
@@ -0,0 +1,1651 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+#pragma warning( disable : 4244 ) // conversion from 'double' to 'float', possible loss of data
+
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <vgui/ISystem.h>
+#include <vgui/IVGui.h>
+#include <KeyValues.h>
+#include <vgui_controls/AnimationController.h>
+#include "filesystem.h"
+#include "filesystem_helpers.h"
+
+#include <stdio.h>
+#include <math.h>
+#include "mempool.h"
+#include "utldict.h"
+#include "mathlib/mathlib.h"
+#include "characterset.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/dbg.h>
+// for SRC
+#include <vstdlib/random.h>
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+static CUtlSymbolTable g_ScriptSymbols(0, 128, true);
+
+// singleton accessor for animation controller for use by the vgui controls
+namespace vgui
+{
+AnimationController *GetAnimationController()
+{
+ static AnimationController *s_pAnimationController = new AnimationController(NULL);
+ return s_pAnimationController;
+}
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+AnimationController::AnimationController(Panel *parent) : BaseClass(parent, NULL)
+{
+ m_hSizePanel = 0;
+ m_nScreenBounds[ 0 ] = m_nScreenBounds[ 1 ] = -1;
+ m_nScreenBounds[ 2 ] = m_nScreenBounds[ 3 ] = -1;
+
+ m_bAutoReloadScript = false;
+
+ // always invisible
+ SetVisible(false);
+
+ SetProportional(true);
+
+ // get the names of common types
+ m_sPosition = g_ScriptSymbols.AddString("position");
+ m_sSize = g_ScriptSymbols.AddString("size");
+ m_sFgColor = g_ScriptSymbols.AddString("fgcolor");
+ m_sBgColor = g_ScriptSymbols.AddString("bgcolor");
+
+ m_sXPos = g_ScriptSymbols.AddString("xpos");
+ m_sYPos = g_ScriptSymbols.AddString("ypos");
+ m_sWide = g_ScriptSymbols.AddString("wide");
+ m_sTall = g_ScriptSymbols.AddString("tall");
+
+ m_flCurrentTime = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+AnimationController::~AnimationController()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets which script file to use
+//-----------------------------------------------------------------------------
+bool AnimationController::SetScriptFile( VPANEL sizingPanel, const char *fileName, bool wipeAll /*=false*/ )
+{
+ m_hSizePanel = sizingPanel;
+
+ if ( wipeAll )
+ {
+ // clear the current script
+ m_Sequences.RemoveAll();
+ m_ScriptFileNames.RemoveAll();
+
+ CancelAllAnimations();
+ }
+
+ // Store off this filename for reloading later on (if we don't have it already)
+ UtlSymId_t sFilename = g_ScriptSymbols.AddString( fileName );
+ if ( m_ScriptFileNames.Find( sFilename ) == m_ScriptFileNames.InvalidIndex() )
+ {
+ m_ScriptFileNames.AddToTail( sFilename );
+ }
+
+ UpdateScreenSize();
+
+ // load the new script file
+ return LoadScriptFile( fileName );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: reloads the currently set script file
+//-----------------------------------------------------------------------------
+void AnimationController::ReloadScriptFile()
+{
+ // Clear all current sequences
+ m_Sequences.RemoveAll();
+
+ UpdateScreenSize();
+
+ // Reload each file we've loaded
+ for ( int i = 0; i < m_ScriptFileNames.Count(); i++ )
+ {
+ const char *lpszFilename = g_ScriptSymbols.String( m_ScriptFileNames[i] );
+ if ( strlen( lpszFilename ) > 0)
+ {
+ if ( LoadScriptFile( lpszFilename ) == false )
+ {
+ Assert( 0 );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: loads a script file from disk
+//-----------------------------------------------------------------------------
+bool AnimationController::LoadScriptFile(const char *fileName)
+{
+ FileHandle_t f = g_pFullFileSystem->Open(fileName, "rt");
+ if (!f)
+ {
+ Warning("Couldn't find script file %s\n", fileName);
+ return false;
+ }
+
+ // read the whole thing into memory
+ int size = g_pFullFileSystem->Size(f);
+ // read into temporary memory block
+ int nBufSize = size+1;
+ if ( IsXbox() )
+ {
+ nBufSize = AlignValue( nBufSize, 512 );
+ }
+ char *pMem = (char *)malloc(nBufSize);
+ int bytesRead = g_pFullFileSystem->ReadEx(pMem, nBufSize, size, f);
+ Assert(bytesRead <= size);
+ pMem[bytesRead] = 0;
+ g_pFullFileSystem->Close(f);
+ // parse
+ bool success = ParseScriptFile(pMem, bytesRead);
+ free(pMem);
+ return success;
+}
+
+AnimationController::RelativeAlignmentLookup AnimationController::g_AlignmentLookup[] =
+{
+ { AnimationController::a_northwest , "northwest" },
+ { AnimationController::a_north , "north" },
+ { AnimationController::a_northeast , "northeast" },
+ { AnimationController::a_west , "west" },
+ { AnimationController::a_center , "center" },
+ { AnimationController::a_east , "east" },
+ { AnimationController::a_southwest , "southwest" },
+ { AnimationController::a_south , "south" },
+ { AnimationController::a_southeast , "southeast" },
+
+ { AnimationController::a_northwest , "nw" },
+ { AnimationController::a_north , "n" },
+ { AnimationController::a_northeast , "ne" },
+ { AnimationController::a_west , "w" },
+ { AnimationController::a_center , "c" },
+ { AnimationController::a_east , "e" },
+ { AnimationController::a_southwest , "sw" },
+ { AnimationController::a_south , "s" },
+ { AnimationController::a_southeast , "se" },
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+AnimationController::RelativeAlignment AnimationController::LookupAlignment( char const *token )
+{
+ int c = ARRAYSIZE( g_AlignmentLookup );
+
+ for ( int i = 0; i < c; i++ )
+ {
+ if ( !Q_stricmp( token, g_AlignmentLookup[ i ].name ) )
+ {
+ return g_AlignmentLookup[ i ].align;
+ }
+ }
+
+ return AnimationController::a_northwest;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse position including right edge and center adjustment out of a
+// token. This is relative to the screen
+//-----------------------------------------------------------------------------
+void AnimationController::SetupPosition( AnimCmdAnimate_t& cmd, float *output, char const *psz, int screendimension )
+{
+ bool r = false, c = false;
+ int pos;
+ if ( psz[0] == '(' )
+ {
+ psz++;
+
+ if ( Q_strstr( psz, ")" ) )
+ {
+ char sz[ 256 ];
+ Q_strncpy( sz, psz, sizeof( sz ) );
+
+ char *colon = Q_strstr( sz, ":" );
+ if ( colon )
+ {
+ *colon = 0;
+
+ RelativeAlignment ra = LookupAlignment( sz );
+
+ colon++;
+
+ char *panelName = colon;
+ char *panelEnd = Q_strstr( panelName, ")" );
+ if ( panelEnd )
+ {
+ *panelEnd = 0;
+
+ if ( Q_strlen( panelName ) > 0 )
+ {
+ //
+ cmd.align.relativePosition = true;
+ cmd.align.alignPanel = g_ScriptSymbols.AddString(panelName);
+ cmd.align.alignment = ra;
+ }
+ }
+ }
+
+ psz = Q_strstr( psz, ")" ) + 1;
+ }
+ }
+ else if (psz[0] == 'r' || psz[0] == 'R')
+ {
+ r = true;
+ psz++;
+ }
+ else if (psz[0] == 'c' || psz[0] == 'C')
+ {
+ c = true;
+ psz++;
+ }
+
+ // get the number
+ pos = atoi(psz);
+
+ // scale the values
+ if (IsProportional())
+ {
+ pos = vgui::scheme()->GetProportionalScaledValueEx( GetScheme(), pos );
+ }
+
+ // adjust the positions
+ if (r)
+ {
+ pos = screendimension - pos;
+ }
+ if (c)
+ {
+ pos = (screendimension / 2) + pos;
+ }
+
+ // set the value
+ *output = static_cast<float>( pos );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: parses a script into sequences
+//-----------------------------------------------------------------------------
+bool AnimationController::ParseScriptFile(char *pMem, int length)
+{
+ // get the scheme (for looking up color names)
+ IScheme *scheme = vgui::scheme()->GetIScheme(GetScheme());
+
+ // get our screen size (for left/right/center alignment)
+ int screenWide = m_nScreenBounds[ 2 ];
+ int screenTall = m_nScreenBounds[ 3 ];
+
+ // start by getting the first token
+ char token[512];
+ pMem = ParseFile(pMem, token, NULL);
+ while (token[0])
+ {
+ bool bAccepted = true;
+
+ // should be 'event'
+ if (stricmp(token, "event"))
+ {
+ Warning("Couldn't parse script file: expected 'event', found '%s'\n", token);
+ return false;
+ }
+
+ // get the event name
+ pMem = ParseFile(pMem, token, NULL);
+ if (strlen(token) < 1)
+ {
+ Warning("Couldn't parse script file: expected <event name>, found nothing\n");
+ return false;
+ }
+
+ int seqIndex;
+ UtlSymId_t nameIndex = g_ScriptSymbols.AddString(token);
+
+ // Create a new sequence
+ seqIndex = m_Sequences.AddToTail();
+ AnimSequence_t &seq = m_Sequences[seqIndex];
+ seq.name = nameIndex;
+ seq.duration = 0.0f;
+
+ // get the open brace or a conditional
+ pMem = ParseFile(pMem, token, NULL);
+ if ( Q_stristr( token, "[$" ) )
+ {
+ bAccepted = EvaluateConditional( token );
+
+ // now get the open brace
+ pMem = ParseFile(pMem, token, NULL);
+ }
+
+ if (stricmp(token, "{"))
+ {
+ Warning("Couldn't parse script sequence '%s': expected '{', found '%s'\n", g_ScriptSymbols.String(seq.name), token);
+ return false;
+ }
+
+ // walk the commands
+ while (token && token[0])
+ {
+ // get the command type
+ pMem = ParseFile(pMem, token, NULL);
+
+ // skip out when we hit the end of the sequence
+ if (token[0] == '}')
+ break;
+
+ // create a new command
+ int cmdIndex = seq.cmdList.AddToTail();
+ AnimCommand_t &animCmd = seq.cmdList[cmdIndex];
+ memset(&animCmd, 0, sizeof(animCmd));
+ if (!stricmp(token, "animate"))
+ {
+ animCmd.commandType = CMD_ANIMATE;
+ // parse out the animation commands
+ AnimCmdAnimate_t &cmdAnimate = animCmd.cmdData.animate;
+ // panel to manipulate
+ pMem = ParseFile(pMem, token, NULL);
+ cmdAnimate.panel = g_ScriptSymbols.AddString(token);
+ // variable to change
+ pMem = ParseFile(pMem, token, NULL);
+ cmdAnimate.variable = g_ScriptSymbols.AddString(token);
+ // target value
+ pMem = ParseFile(pMem, token, NULL);
+ if (cmdAnimate.variable == m_sPosition)
+ {
+ // Get first token
+ SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenWide );
+
+ // Get second token from "token"
+ char token2[32];
+ char *psz = ParseFile(token, token2, NULL);
+ psz = ParseFile(psz, token2, NULL);
+ psz = token2;
+
+ // Position Y goes into ".b"
+ SetupPosition( cmdAnimate, &cmdAnimate.target.b, psz, screenTall );
+ }
+ else if ( cmdAnimate.variable == m_sXPos )
+ {
+ // XPos and YPos both use target ".a"
+ SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenWide );
+ }
+ else if ( cmdAnimate.variable == m_sYPos )
+ {
+ // XPos and YPos both use target ".a"
+ SetupPosition( cmdAnimate, &cmdAnimate.target.a, token, screenTall );
+ }
+ else
+ {
+ // parse the floating point values right out
+ if (0 == sscanf(token, "%f %f %f %f", &cmdAnimate.target.a, &cmdAnimate.target.b, &cmdAnimate.target.c, &cmdAnimate.target.d))
+ {
+ //=============================================================================
+ // HPE_BEGIN:
+ // [pfreese] Improved handling colors not defined in scheme
+ //=============================================================================
+
+ // could be referencing a value in the scheme file, lookup
+ Color default_invisible_black(0, 0, 0, 0);
+ Color col = scheme->GetColor(token, default_invisible_black);
+
+ // we don't have a way of seeing if the color is not declared in the scheme, so we use this
+ // silly method of trying again with a different default to see if we get the fallback again
+ if (col == default_invisible_black)
+ {
+ Color error_pink(255, 0, 255, 255); // make it extremely obvious if a scheme lookup fails
+ col = scheme->GetColor(token, error_pink);
+
+ // commented out for Soldier/Demo release...(getting spammed in console)
+ // we'll try to figure this out after the update is out
+// if (col == error_pink)
+// {
+// Warning("Missing color in scheme: %s\n", token);
+// }
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ cmdAnimate.target.a = col[0];
+ cmdAnimate.target.b = col[1];
+ cmdAnimate.target.c = col[2];
+ cmdAnimate.target.d = col[3];
+ }
+ }
+
+ // fix up scale
+ if (cmdAnimate.variable == m_sSize)
+ {
+ if (IsProportional())
+ {
+ cmdAnimate.target.a = static_cast<float>( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.a) );
+ cmdAnimate.target.b = static_cast<float>( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.b) );
+ }
+ }
+ else if (cmdAnimate.variable == m_sWide ||
+ cmdAnimate.variable == m_sTall )
+ {
+ if (IsProportional())
+ {
+ // Wide and tall both use.a
+ cmdAnimate.target.a = static_cast<float>( vgui::scheme()->GetProportionalScaledValueEx(GetScheme(), cmdAnimate.target.a) );
+ }
+ }
+
+ // interpolation function
+ pMem = ParseFile(pMem, token, NULL);
+ if (!stricmp(token, "Accel"))
+ {
+ cmdAnimate.interpolationFunction = INTERPOLATOR_ACCEL;
+ }
+ else if (!stricmp(token, "Deaccel"))
+ {
+ cmdAnimate.interpolationFunction = INTERPOLATOR_DEACCEL;
+ }
+ else if ( !stricmp(token, "Spline"))
+ {
+ cmdAnimate.interpolationFunction = INTERPOLATOR_SIMPLESPLINE;
+ }
+ else if (!stricmp(token,"Pulse"))
+ {
+ cmdAnimate.interpolationFunction = INTERPOLATOR_PULSE;
+ // frequencey
+ pMem = ParseFile(pMem, token, NULL);
+ cmdAnimate.interpolationParameter = (float)atof(token);
+ }
+ else if ( !stricmp( token, "Flicker"))
+ {
+ cmdAnimate.interpolationFunction = INTERPOLATOR_FLICKER;
+ // noiseamount
+ pMem = ParseFile(pMem, token, NULL);
+ cmdAnimate.interpolationParameter = (float)atof(token);
+ }
+ else if (!stricmp(token, "Bounce"))
+ {
+ cmdAnimate.interpolationFunction = INTERPOLATOR_BOUNCE;
+ }
+ else
+ {
+ cmdAnimate.interpolationFunction = INTERPOLATOR_LINEAR;
+ }
+ // start time
+ pMem = ParseFile(pMem, token, NULL);
+ cmdAnimate.startTime = (float)atof(token);
+ // duration
+ pMem = ParseFile(pMem, token, NULL);
+ cmdAnimate.duration = (float)atof(token);
+ // check max duration
+ if (cmdAnimate.startTime + cmdAnimate.duration > seq.duration)
+ {
+ seq.duration = cmdAnimate.startTime + cmdAnimate.duration;
+ }
+ }
+ else if (!stricmp(token, "runevent"))
+ {
+ animCmd.commandType = CMD_RUNEVENT;
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
+ }
+ else if (!stricmp(token, "stopevent"))
+ {
+ animCmd.commandType = CMD_STOPEVENT;
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
+ }
+ else if (!stricmp(token, "StopPanelAnimations"))
+ {
+ animCmd.commandType = CMD_STOPPANELANIMATIONS;
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
+ }
+ else if (!stricmp(token, "stopanimation"))
+ {
+ animCmd.commandType = CMD_STOPANIMATION;
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token);
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
+ }
+ else if ( !stricmp( token, "SetFont" ))
+ {
+ animCmd.commandType = CMD_SETFONT;
+ // Panel name
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
+ // Font parameter
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token);
+ // Font name from scheme
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.variable2 = g_ScriptSymbols.AddString(token);
+
+ // Set time
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
+ }
+ else if ( !stricmp( token, "SetTexture" ))
+ {
+ animCmd.commandType = CMD_SETTEXTURE;
+ // Panel name
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
+ // Texture Id
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token);
+ // material name
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.variable2 = g_ScriptSymbols.AddString(token);
+
+ // Set time
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
+ }
+ else if ( !stricmp( token, "SetString" ))
+ {
+ animCmd.commandType = CMD_SETSTRING;
+ // Panel name
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.event = g_ScriptSymbols.AddString(token);
+ // String variable name
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.variable = g_ScriptSymbols.AddString(token);
+ // String value to set
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.variable2 = g_ScriptSymbols.AddString(token);
+
+ // Set time
+ pMem = ParseFile(pMem, token, NULL);
+ animCmd.cmdData.runEvent.timeDelay = (float)atof(token);
+ }
+ else
+ {
+ Warning("Couldn't parse script sequence '%s': expected <anim command>, found '%s'\n", g_ScriptSymbols.String(seq.name), token);
+ return false;
+ }
+
+ // Look ahead one token for a conditional
+ char *peek = ParseFile(pMem, token, NULL);
+ if ( Q_stristr( token, "[$" ) )
+ {
+ if ( !EvaluateConditional( token ) )
+ {
+ seq.cmdList.Remove( cmdIndex );
+ }
+ pMem = peek;
+ }
+ }
+
+ if ( bAccepted )
+ {
+ // Attempt to find a collision in the sequences, replacing the old one if found
+ int seqIterator;
+ for ( seqIterator = 0; seqIterator < m_Sequences.Count()-1; seqIterator++ )
+ {
+ if ( m_Sequences[seqIterator].name == nameIndex )
+ {
+ // Get rid of it, we're overriding it
+ m_Sequences.Remove( seqIndex );
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Dump the entire sequence
+ m_Sequences.Remove( seqIndex );
+ }
+
+ // get the next token, if any
+ pMem = ParseFile(pMem, token, NULL);
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: checks all posted animation events, firing if time
+//-----------------------------------------------------------------------------
+void AnimationController::UpdatePostedMessages(bool bRunToCompletion)
+{
+ CUtlVector<RanEvent_t> eventsRanThisFrame;
+
+ // check all posted messages
+ for (int i = 0; i < m_PostedMessages.Count(); i++)
+ {
+ PostedMessage_t &msgRef = m_PostedMessages[i];
+ if (m_flCurrentTime < msgRef.startTime && !bRunToCompletion)
+ continue;
+
+ // take a copy of th message
+ PostedMessage_t msg = msgRef;
+
+ // remove the event
+ // do this before handling the message because the message queue may be messed with
+ m_PostedMessages.Remove(i);
+ // reset the count, start the whole queue again
+ i = -1;
+
+ // handle the event
+ switch (msg.commandType)
+ {
+ case CMD_RUNEVENT:
+ {
+ RanEvent_t curEvent;
+ curEvent.event = msg.event;
+ curEvent.pParent = msg.parent.Get();
+
+ // run the event, but only if we haven't already run it this frame, for this parent
+ if (!eventsRanThisFrame.HasElement(curEvent))
+ {
+ eventsRanThisFrame.AddToTail(curEvent);
+ RunCmd_RunEvent(msg);
+ }
+ }
+ break;
+ case CMD_STOPEVENT:
+ RunCmd_StopEvent(msg);
+ break;
+ case CMD_STOPPANELANIMATIONS:
+ RunCmd_StopPanelAnimations(msg);
+ break;
+ case CMD_STOPANIMATION:
+ RunCmd_StopAnimation(msg);
+ break;
+ case CMD_SETFONT:
+ RunCmd_SetFont(msg);
+ break;
+ case CMD_SETTEXTURE:
+ RunCmd_SetTexture(msg);
+ break;
+ case CMD_SETSTRING:
+ RunCmd_SetString( msg );
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: runs the current animations
+//-----------------------------------------------------------------------------
+void AnimationController::UpdateActiveAnimations(bool bRunToCompletion)
+{
+ // iterate all the currently active animations
+ for (int i = 0; i < m_ActiveAnimations.Count(); i++)
+ {
+ ActiveAnimation_t &anim = m_ActiveAnimations[i];
+
+ // see if the anim is ready to start
+ if (m_flCurrentTime < anim.startTime && !bRunToCompletion)
+ continue;
+
+ if (!anim.panel.Get())
+ {
+ // panel is gone, remove the animation
+ m_ActiveAnimations.Remove(i);
+ --i;
+ continue;
+ }
+
+ if (!anim.started && !bRunToCompletion)
+ {
+ // start the animation from the current value
+ anim.startValue = GetValue(anim, anim.panel, anim.variable);
+ anim.started = true;
+
+ // Msg( "Starting animation of %s => %.2f (seq: %s) (%s)\n", g_ScriptSymbols.String(anim.variable), anim.endValue.a, g_ScriptSymbols.String(anim.seqName), anim.panel->GetName());
+ }
+
+ // get the interpolated value
+ Value_t val;
+ if (m_flCurrentTime >= anim.endTime || bRunToCompletion)
+ {
+ // animation is done, use the last value
+ val = anim.endValue;
+ }
+ else
+ {
+ // get the interpolated value
+ val = GetInterpolatedValue(anim.interpolator, anim.interpolatorParam, m_flCurrentTime, anim.startTime, anim.endTime, anim.startValue, anim.endValue);
+ }
+
+ // apply the new value to the panel
+ SetValue(anim, anim.panel, anim.variable, val);
+
+ // Msg( "Animate value: %s => %.2f for panel '%s'\n", g_ScriptSymbols.String(anim.variable), val.a, anim.panel->GetName());
+
+ // see if we can remove the animation
+ if (m_flCurrentTime >= anim.endTime || bRunToCompletion)
+ {
+ m_ActiveAnimations.Remove(i);
+ --i;
+ }
+ }
+}
+
+bool AnimationController::UpdateScreenSize()
+{
+ // get our screen size (for left/right/center alignment)
+ int screenWide, screenTall;
+ int sx = 0, sy = 0;
+ if ( m_hSizePanel != 0 )
+ {
+ ipanel()->GetSize( m_hSizePanel, screenWide, screenTall );
+ ipanel()->GetPos( m_hSizePanel, sx, sy );
+ }
+ else
+ {
+ surface()->GetScreenSize(screenWide, screenTall);
+ }
+
+ bool changed = m_nScreenBounds[ 0 ] != sx ||
+ m_nScreenBounds[ 1 ] != sy ||
+ m_nScreenBounds[ 2 ] != screenWide ||
+ m_nScreenBounds[ 3 ] != screenTall;
+
+ m_nScreenBounds[ 0 ] = sx;
+ m_nScreenBounds[ 1 ] = sy;
+ m_nScreenBounds[ 2 ] = screenWide;
+ m_nScreenBounds[ 3 ] = screenTall;
+
+ return changed;
+}
+//-----------------------------------------------------------------------------
+// Purpose: runs a frame of animation
+//-----------------------------------------------------------------------------
+void AnimationController::UpdateAnimations( float currentTime )
+{
+ m_flCurrentTime = currentTime;
+
+ if ( UpdateScreenSize() && m_ScriptFileNames.Count() )
+ {
+ RunAllAnimationsToCompletion();
+ ReloadScriptFile();
+ }
+
+ UpdatePostedMessages(false);
+ UpdateActiveAnimations(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: plays all animations to completion instantly
+//-----------------------------------------------------------------------------
+void AnimationController::RunAllAnimationsToCompletion()
+{
+ // Msg( "AnimationController::RunAllAnimationsToCompletion()\n" );
+ UpdatePostedMessages(true);
+ UpdateActiveAnimations(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stops all current animations
+//-----------------------------------------------------------------------------
+void AnimationController::CancelAllAnimations()
+{
+ // Msg( "AnimationController::CancelAllAnimations()\n" );
+
+ m_ActiveAnimations.RemoveAll();
+ m_PostedMessages.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: produces an interpolated value
+//-----------------------------------------------------------------------------
+AnimationController::Value_t AnimationController::GetInterpolatedValue(int interpolator, float interpolatorParam, float currentTime, float startTime, float endTime, Value_t &startValue, Value_t &endValue)
+{
+ // calculate how far we are into the animation
+ float pos = (currentTime - startTime) / (endTime - startTime);
+
+ // adjust the percentage through by the interpolation function
+ switch (interpolator)
+ {
+ case INTERPOLATOR_ACCEL:
+ pos *= pos;
+ break;
+ case INTERPOLATOR_DEACCEL:
+ pos = sqrtf(pos);
+ break;
+ case INTERPOLATOR_SIMPLESPLINE:
+ pos = SimpleSpline( pos );
+ break;
+ case INTERPOLATOR_PULSE:
+ // Make sure we end at 1.0, so use cosine
+ pos = 0.5f + 0.5f * ( cos( pos * 2.0f * M_PI * interpolatorParam ) );
+ break;
+ case INTERPOLATOR_FLICKER:
+ if ( RandomFloat( 0.0f, 1.0f ) < interpolatorParam )
+ {
+ pos = 1.0f;
+ }
+ else
+ {
+ pos = 0.0f;
+ }
+ break;
+ case INTERPOLATOR_BOUNCE:
+ {
+ // fall from startValue to endValue, bouncing a few times and settling out at endValue
+ const float hit1 = 0.33f;
+ const float hit2 = 0.67f;
+ const float hit3 = 1.0f;
+
+ if ( pos < hit1 )
+ {
+ pos = 1.0f - sin( M_PI * pos / hit1 );
+ }
+ else if ( pos < hit2 )
+ {
+ pos = 0.5f + 0.5f * ( 1.0f - sin( M_PI * ( pos - hit1 ) / ( hit2 - hit1 ) ) );
+ }
+ else
+ {
+ pos = 0.8f + 0.2f * ( 1.0f - sin( M_PI * ( pos - hit2 ) / ( hit3 - hit2 ) ) );
+ }
+ break;
+ }
+ case INTERPOLATOR_LINEAR:
+ default:
+ break;
+ }
+
+ // calculate the value
+ Value_t val;
+ val.a = ((endValue.a - startValue.a) * pos) + startValue.a;
+ val.b = ((endValue.b - startValue.b) * pos) + startValue.b;
+ val.c = ((endValue.c - startValue.c) * pos) + startValue.c;
+ val.d = ((endValue.d - startValue.d) * pos) + startValue.d;
+ return val;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets that the script file should be reloaded each time a script is ran
+// used for development
+//-----------------------------------------------------------------------------
+void AnimationController::SetAutoReloadScript(bool state)
+{
+ m_bAutoReloadScript = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: starts an animation sequence script
+//-----------------------------------------------------------------------------
+bool AnimationController::StartAnimationSequence(const char *sequenceName)
+{
+ // We support calling an animation on elements that are not the calling
+ // panel's children. Use the base parent to start the search.
+
+ return StartAnimationSequence( GetParent(), sequenceName );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: starts an animation sequence script
+//-----------------------------------------------------------------------------
+bool AnimationController::StartAnimationSequence(Panel *pWithinParent, const char *sequenceName)
+{
+ Assert( pWithinParent );
+
+ if (m_bAutoReloadScript)
+ {
+ // Reload the script files
+ ReloadScriptFile();
+ }
+
+ // lookup the symbol for the name
+ UtlSymId_t seqName = g_ScriptSymbols.Find(sequenceName);
+ if (seqName == UTL_INVAL_SYMBOL)
+ return false;
+
+ // Msg("Starting animation sequence %s\n", sequenceName);
+
+ // remove the existing command from the queue
+ RemoveQueuedAnimationCommands(seqName, pWithinParent);
+
+ // look through for the sequence
+ int i;
+ for (i = 0; i < m_Sequences.Count(); i++)
+ {
+ if (m_Sequences[i].name == seqName)
+ break;
+ }
+ if (i >= m_Sequences.Count())
+ return false;
+
+ // execute the sequence
+ for (int cmdIndex = 0; cmdIndex < m_Sequences[i].cmdList.Count(); cmdIndex++)
+ {
+ ExecAnimationCommand(seqName, m_Sequences[i].cmdList[cmdIndex], pWithinParent);
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Runs a custom command from code, not from a script file
+//-----------------------------------------------------------------------------
+void AnimationController::RunAnimationCommand(vgui::Panel *panel, const char *variable, float targetValue, float startDelaySeconds, float duration, Interpolators_e interpolator, float animParameter /* = 0 */ )
+{
+ // clear any previous animations of this variable
+ UtlSymId_t var = g_ScriptSymbols.AddString(variable);
+ RemoveQueuedAnimationByType(panel, var, UTL_INVAL_SYMBOL);
+
+ // build a new animation
+ AnimCmdAnimate_t animateCmd;
+ memset(&animateCmd, 0, sizeof(animateCmd));
+ animateCmd.panel = 0;
+ animateCmd.variable = var;
+ animateCmd.target.a = targetValue;
+ animateCmd.interpolationFunction = interpolator;
+ animateCmd.interpolationParameter = animParameter;
+ animateCmd.startTime = startDelaySeconds;
+ animateCmd.duration = duration;
+
+ // start immediately
+ StartCmd_Animate(panel, 0, animateCmd);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Runs a custom command from code, not from a script file
+//-----------------------------------------------------------------------------
+void AnimationController::RunAnimationCommand(vgui::Panel *panel, const char *variable, Color targetValue, float startDelaySeconds, float duration, Interpolators_e interpolator, float animParameter /* = 0 */ )
+{
+ // clear any previous animations of this variable
+ UtlSymId_t var = g_ScriptSymbols.AddString(variable);
+ RemoveQueuedAnimationByType(panel, var, UTL_INVAL_SYMBOL);
+
+ // build a new animation
+ AnimCmdAnimate_t animateCmd;
+ memset(&animateCmd, 0, sizeof(animateCmd));
+ animateCmd.panel = 0;
+ animateCmd.variable = var;
+ animateCmd.target.a = targetValue[0];
+ animateCmd.target.b = targetValue[1];
+ animateCmd.target.c = targetValue[2];
+ animateCmd.target.d = targetValue[3];
+ animateCmd.interpolationFunction = interpolator;
+ animateCmd.interpolationParameter = animParameter;
+ animateCmd.startTime = startDelaySeconds;
+ animateCmd.duration = duration;
+
+ // start immediately
+ StartCmd_Animate(panel, 0, animateCmd);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the length of an animation sequence, in seconds
+//-----------------------------------------------------------------------------
+float AnimationController::GetAnimationSequenceLength(const char *sequenceName)
+{
+ // lookup the symbol for the name
+ UtlSymId_t seqName = g_ScriptSymbols.Find(sequenceName);
+ if (seqName == UTL_INVAL_SYMBOL)
+ return 0.0f;
+
+ // look through for the sequence
+ int i;
+ for (i = 0; i < m_Sequences.Count(); i++)
+ {
+ if (m_Sequences[i].name == seqName)
+ break;
+ }
+ if (i >= m_Sequences.Count())
+ return 0.0f;
+
+ // sequence found
+ return m_Sequences[i].duration;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: removes an existing set of commands from the queue
+//-----------------------------------------------------------------------------
+void AnimationController::RemoveQueuedAnimationCommands(UtlSymId_t seqName, Panel *pWithinParent)
+{
+ // Msg("Removing queued anims for sequence %s\n", g_ScriptSymbols.String(seqName));
+
+ // remove messages posted by this sequence
+ // if pWithinParent is specified, remove only messages under that parent
+ {for (int i = 0; i < m_PostedMessages.Count(); i++)
+ {
+ if ( ( m_PostedMessages[i].seqName == seqName ) &&
+ ( !pWithinParent || ( m_PostedMessages[i].parent == pWithinParent ) ) )
+ {
+ m_PostedMessages.Remove(i);
+ --i;
+ }
+ }}
+
+ // remove all animations
+ // if pWithinParent is specified, remove only animations under that parent
+ for (int i = 0; i < m_ActiveAnimations.Count(); i++)
+ {
+ if ( m_ActiveAnimations[i].seqName != seqName )
+ continue;
+
+ // panel this anim is on, m_ActiveAnimations[i].panel
+ if ( pWithinParent )
+ {
+ Panel *animPanel = m_ActiveAnimations[i].panel;
+
+ if ( !animPanel )
+ continue;
+
+ Panel *foundPanel = pWithinParent->FindChildByName(animPanel->GetName(),true);
+
+ if ( foundPanel != animPanel )
+ continue;
+ }
+
+ m_ActiveAnimations.Remove(i);
+ --i;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: removes the specified queued animation
+//-----------------------------------------------------------------------------
+void AnimationController::RemoveQueuedAnimationByType(vgui::Panel *panel, UtlSymId_t variable, UtlSymId_t sequenceToIgnore)
+{
+ for (int i = 0; i < m_ActiveAnimations.Count(); i++)
+ {
+ if (m_ActiveAnimations[i].panel == panel && m_ActiveAnimations[i].variable == variable && m_ActiveAnimations[i].seqName != sequenceToIgnore)
+ {
+ // Msg("Removing queued anim %s::%s::%s\n", g_ScriptSymbols.String(m_ActiveAnimations[i].seqName), panel->GetName(), g_ScriptSymbols.String(variable));
+ m_ActiveAnimations.Remove(i);
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: runs a single line of the script
+//-----------------------------------------------------------------------------
+void AnimationController::ExecAnimationCommand(UtlSymId_t seqName, AnimCommand_t &animCommand, Panel *pWithinParent)
+{
+ if (animCommand.commandType == CMD_ANIMATE)
+ {
+ StartCmd_Animate(seqName, animCommand.cmdData.animate, pWithinParent);
+ }
+ else
+ {
+ // post the command to happen at the specified time
+ PostedMessage_t &msg = m_PostedMessages[m_PostedMessages.AddToTail()];
+ msg.seqName = seqName;
+ msg.commandType = animCommand.commandType;
+ msg.event = animCommand.cmdData.runEvent.event;
+ msg.variable = animCommand.cmdData.runEvent.variable;
+ msg.variable2 = animCommand.cmdData.runEvent.variable2;
+ msg.startTime = m_flCurrentTime + animCommand.cmdData.runEvent.timeDelay;
+ msg.parent = pWithinParent;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: starts a variable animation
+//-----------------------------------------------------------------------------
+void AnimationController::StartCmd_Animate(UtlSymId_t seqName, AnimCmdAnimate_t &cmd, Panel *pWithinParent)
+{
+ Assert( pWithinParent );
+ if ( !pWithinParent )
+ return;
+
+ // make sure the child exists
+ Panel *panel = pWithinParent->FindChildByName(g_ScriptSymbols.String(cmd.panel),true);
+ if ( !panel )
+ {
+ // Check the parent
+ Panel *parent = GetParent();
+ if ( !Q_stricmp( parent->GetName(), g_ScriptSymbols.String(cmd.panel) ) )
+ {
+ panel = parent;
+ }
+ }
+ if (!panel)
+ return;
+
+ StartCmd_Animate(panel, seqName, cmd);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Starts an animation command for the specified panel
+//-----------------------------------------------------------------------------
+void AnimationController::StartCmd_Animate(Panel *panel, UtlSymId_t seqName, AnimCmdAnimate_t &cmd)
+{
+ // build a command to add to the animation queue
+ ActiveAnimation_t &anim = m_ActiveAnimations[m_ActiveAnimations.AddToTail()];
+ anim.panel = panel;
+ anim.seqName = seqName;
+ anim.variable = cmd.variable;
+ anim.interpolator = cmd.interpolationFunction;
+ anim.interpolatorParam = cmd.interpolationParameter;
+ // timings
+ anim.startTime = m_flCurrentTime + cmd.startTime;
+ anim.endTime = anim.startTime + cmd.duration;
+ // values
+ anim.started = false;
+ anim.endValue = cmd.target;
+
+ anim.align = cmd.align;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: a posted message to run another event
+//-----------------------------------------------------------------------------
+void AnimationController::RunCmd_RunEvent(PostedMessage_t &msg)
+{
+ StartAnimationSequence(msg.parent.Get(), g_ScriptSymbols.String(msg.event));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: a posted message to stop another event
+//-----------------------------------------------------------------------------
+void AnimationController::RunCmd_StopEvent(PostedMessage_t &msg)
+{
+ RemoveQueuedAnimationCommands(msg.event, msg.parent);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: a posted message to stop all animations relevant to a specified panel
+//-----------------------------------------------------------------------------
+void AnimationController::RunCmd_StopPanelAnimations(PostedMessage_t &msg)
+{
+ Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event));
+ Assert(panel != NULL);
+ if (!panel)
+ return;
+
+ // loop through all the active animations cancelling any that
+ // are operating on said panel, except for the event specified
+ for (int i = 0; i < m_ActiveAnimations.Count(); i++)
+ {
+ if (m_ActiveAnimations[i].panel == panel && m_ActiveAnimations[i].seqName != msg.seqName)
+ {
+ m_ActiveAnimations.Remove(i);
+ --i;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: a posted message to stop animations of a specific type
+//-----------------------------------------------------------------------------
+void AnimationController::RunCmd_StopAnimation(PostedMessage_t &msg)
+{
+ Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event));
+ Assert(panel != NULL);
+ if (!panel)
+ return;
+
+ RemoveQueuedAnimationByType(panel, msg.variable, msg.seqName);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationController::RunCmd_SetFont( PostedMessage_t &msg )
+{
+ Panel *parent = msg.parent.Get();
+
+ if ( !parent )
+ {
+ parent = GetParent();
+ }
+
+ Panel *panel = parent->FindChildByName(g_ScriptSymbols.String(msg.event), true);
+ Assert(panel != NULL);
+ if (!panel)
+ return;
+
+ KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(msg.variable));
+ inputData->SetString(g_ScriptSymbols.String(msg.variable), g_ScriptSymbols.String(msg.variable2));
+ if (!panel->SetInfo(inputData))
+ {
+ // Assert(!("Unhandlable var in AnimationController::SetValue())"));
+ }
+ inputData->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationController::RunCmd_SetTexture( PostedMessage_t &msg )
+{
+ Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event));
+ Assert(panel != NULL);
+ if (!panel)
+ return;
+
+ KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(msg.variable));
+ inputData->SetString(g_ScriptSymbols.String(msg.variable), g_ScriptSymbols.String(msg.variable2));
+ if (!panel->SetInfo(inputData))
+ {
+ // Assert(!("Unhandlable var in AnimationController::SetValue())"));
+ }
+ inputData->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationController::RunCmd_SetString( PostedMessage_t &msg )
+{
+ Panel *panel = FindSiblingByName(g_ScriptSymbols.String(msg.event));
+ Assert(panel != NULL);
+ if (!panel)
+ return;
+
+ KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(msg.variable));
+ inputData->SetString(g_ScriptSymbols.String(msg.variable), g_ScriptSymbols.String(msg.variable2));
+ if (!panel->SetInfo(inputData))
+ {
+ // Assert(!("Unhandlable var in AnimationController::SetValue())"));
+ }
+ inputData->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int AnimationController::GetRelativeOffset( AnimAlign_t& align, bool xcoord )
+{
+ if ( !align.relativePosition )
+ return 0;
+
+ Panel *panel = GetParent()->FindChildByName(g_ScriptSymbols.String(align.alignPanel), true);
+ if ( !panel )
+ return 0;
+
+ int x, y, w, h;
+ panel->GetBounds( x, y, w, h );
+
+ int offset =0;
+ switch ( align.alignment )
+ {
+ default:
+ case a_northwest:
+ offset = xcoord ? x : y;
+ break;
+ case a_north:
+ offset = xcoord ? ( x + w ) / 2 : y;
+ break;
+ case a_northeast:
+ offset = xcoord ? ( x + w ) : y;
+ break;
+ case a_west:
+ offset = xcoord ? x : ( y + h ) / 2;
+ break;
+ case a_center:
+ offset = xcoord ? ( x + w ) / 2 : ( y + h ) / 2;
+ break;
+ case a_east:
+ offset = xcoord ? ( x + w ) : ( y + h ) / 2;
+ break;
+ case a_southwest:
+ offset = xcoord ? x : ( y + h );
+ break;
+ case a_south:
+ offset = xcoord ? ( x + w ) / 2 : ( y + h );
+ break;
+ case a_southeast:
+ offset = xcoord ? ( x + w ) : ( y + h );
+ break;
+ }
+
+ return offset;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the specified value from a panel
+//-----------------------------------------------------------------------------
+AnimationController::Value_t AnimationController::GetValue(ActiveAnimation_t& anim, Panel *panel, UtlSymId_t var)
+{
+ Value_t val = { 0, 0, 0, 0 };
+ if (var == m_sPosition)
+ {
+ int x, y;
+ panel->GetPos(x, y);
+ val.a = (float)(x - GetRelativeOffset( anim.align, true ) );
+ val.b = (float)(y - GetRelativeOffset( anim.align, false ) );
+ }
+ else if (var == m_sSize)
+ {
+ int w, t;
+ panel->GetSize(w, t);
+ val.a = (float)w;
+ val.b = (float)t;
+ }
+ else if (var == m_sFgColor)
+ {
+ Color col = panel->GetFgColor();
+ val.a = col[0];
+ val.b = col[1];
+ val.c = col[2];
+ val.d = col[3];
+ }
+ else if (var == m_sBgColor)
+ {
+ Color col = panel->GetBgColor();
+ val.a = col[0];
+ val.b = col[1];
+ val.c = col[2];
+ val.d = col[3];
+ }
+ else if ( var == m_sXPos )
+ {
+ int x, y;
+ panel->GetPos(x, y);
+ val.a = (float)( x - GetRelativeOffset( anim.align, true ) );
+ }
+ else if ( var == m_sYPos )
+ {
+ int x, y;
+ panel->GetPos(x, y);
+ val.a = (float)( y - GetRelativeOffset( anim.align, false ) );
+ }
+ else if ( var == m_sWide )
+ {
+ int w, h;
+ panel->GetSize(w, h);
+ val.a = (float)w;
+ }
+ else if ( var == m_sTall )
+ {
+ int w, h;
+ panel->GetSize(w, h);
+ val.a = (float)h;
+ }
+ else
+ {
+ KeyValues *outputData = new KeyValues(g_ScriptSymbols.String(var));
+ if (panel->RequestInfo(outputData))
+ {
+ // find the var and lookup it's type
+ KeyValues *kv = outputData->FindKey(g_ScriptSymbols.String(var));
+ if (kv && kv->GetDataType() == KeyValues::TYPE_FLOAT)
+ {
+ val.a = kv->GetFloat();
+ val.b = 0.0f;
+ val.c = 0.0f;
+ val.d = 0.0f;
+ }
+ else if (kv && kv->GetDataType() == KeyValues::TYPE_COLOR)
+ {
+ Color col = kv->GetColor();
+ val.a = col[0];
+ val.b = col[1];
+ val.c = col[2];
+ val.d = col[3];
+ }
+ }
+ else
+ {
+ // Assert(!("Unhandlable var in AnimationController::GetValue())"));
+ }
+ outputData->deleteThis();
+ }
+ return val;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets a value in a panel
+//-----------------------------------------------------------------------------
+void AnimationController::SetValue(ActiveAnimation_t& anim, Panel *panel, UtlSymId_t var, Value_t &value)
+{
+ if (var == m_sPosition)
+ {
+ int x = (int)value.a + GetRelativeOffset( anim.align, true );
+ int y = (int)value.b + GetRelativeOffset( anim.align, false );
+ panel->SetPos(x, y);
+ }
+ else if (var == m_sSize)
+ {
+ panel->SetSize((int)value.a, (int)value.b);
+ }
+ else if (var == m_sFgColor)
+ {
+ Color col = panel->GetFgColor();
+ col[0] = (unsigned char)value.a;
+ col[1] = (unsigned char)value.b;
+ col[2] = (unsigned char)value.c;
+ col[3] = (unsigned char)value.d;
+ panel->SetFgColor(col);
+ }
+ else if (var == m_sBgColor)
+ {
+ Color col = panel->GetBgColor();
+ col[0] = (unsigned char)value.a;
+ col[1] = (unsigned char)value.b;
+ col[2] = (unsigned char)value.c;
+ col[3] = (unsigned char)value.d;
+ panel->SetBgColor(col);
+ }
+ else if (var == m_sXPos)
+ {
+ int newx = (int)value.a + GetRelativeOffset( anim.align, true );
+ int x, y;
+ panel->GetPos( x, y );
+ x = newx;
+ panel->SetPos(x, y);
+ }
+ else if (var == m_sYPos)
+ {
+ int newy = (int)value.a + GetRelativeOffset( anim.align, false );
+ int x, y;
+ panel->GetPos( x, y );
+ y = newy;
+ panel->SetPos(x, y);
+ }
+ else if (var == m_sWide)
+ {
+ int neww = (int)value.a;
+ int w, h;
+ panel->GetSize( w, h );
+ w = neww;
+ panel->SetSize(w, h);
+ }
+ else if (var == m_sTall)
+ {
+ int newh = (int)value.a;
+ int w, h;
+ panel->GetSize( w, h );
+ h = newh;
+ panel->SetSize(w, h);
+ }
+ else
+ {
+ KeyValues *inputData = new KeyValues(g_ScriptSymbols.String(var));
+ // set the custom value
+ if (value.b == 0.0f && value.c == 0.0f && value.d == 0.0f)
+ {
+ // only the first value is non-zero, so probably just a float value
+ inputData->SetFloat(g_ScriptSymbols.String(var), value.a);
+ }
+ else
+ {
+ // multivalue, set the color
+ Color col((unsigned char)value.a, (unsigned char)value.b, (unsigned char)value.c, (unsigned char)value.d);
+ inputData->SetColor(g_ScriptSymbols.String(var), col);
+ }
+ if (!panel->SetInfo(inputData))
+ {
+ // Assert(!("Unhandlable var in AnimationController::SetValue())"));
+ }
+ inputData->deleteThis();
+ }
+}
+// Hooks between panels and animation controller system
+
+class CPanelAnimationDictionary
+{
+public:
+ CPanelAnimationDictionary() : m_PanelAnimationMapPool( 32 )
+ {
+ }
+
+ ~CPanelAnimationDictionary()
+ {
+ m_PanelAnimationMapPool.Clear();
+ }
+
+ PanelAnimationMap *FindOrAddPanelAnimationMap( char const *className );
+ PanelAnimationMap *FindPanelAnimationMap( char const *className );
+ void PanelAnimationDumpVars( char const *className );
+private:
+
+ struct PanelAnimationMapDictionaryEntry
+ {
+ PanelAnimationMap *map;
+ };
+
+ char const *StripNamespace( char const *className );
+ void PanelAnimationDumpMap( PanelAnimationMap *map, bool recursive );
+
+ CClassMemoryPool< PanelAnimationMap > m_PanelAnimationMapPool;
+ CUtlDict< PanelAnimationMapDictionaryEntry, int > m_AnimationMaps;
+};
+
+
+char const *CPanelAnimationDictionary::StripNamespace( char const *className )
+{
+ if ( !Q_strnicmp( className, "vgui::", 6 ) )
+ {
+ return className + 6;
+ }
+ return className;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find but don't add mapping
+//-----------------------------------------------------------------------------
+PanelAnimationMap *CPanelAnimationDictionary::FindPanelAnimationMap( char const *className )
+{
+ int lookup = m_AnimationMaps.Find( StripNamespace( className ) );
+ if ( lookup != m_AnimationMaps.InvalidIndex() )
+ {
+ return m_AnimationMaps[ lookup ].map;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+PanelAnimationMap *CPanelAnimationDictionary::FindOrAddPanelAnimationMap( char const *className )
+{
+ PanelAnimationMap *map = FindPanelAnimationMap( className );
+ if ( map )
+ return map;
+
+ Panel::InitPropertyConverters();
+
+ PanelAnimationMapDictionaryEntry entry;
+ entry.map = (PanelAnimationMap *)m_PanelAnimationMapPool.Alloc();
+ m_AnimationMaps.Insert( StripNamespace( className ), entry );
+ return entry.map;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPanelAnimationDictionary::PanelAnimationDumpMap( PanelAnimationMap *map, bool recursive )
+{
+ if ( map->pfnClassName )
+ {
+ Msg( "%s\n", (*map->pfnClassName)() );
+ }
+ int c = map->entries.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ PanelAnimationMapEntry *e = &map->entries[ i ];
+ Msg( " %s %s\n", e->type(), e->name() );
+ }
+
+ if ( recursive && map->baseMap )
+ {
+ PanelAnimationDumpMap( map->baseMap, recursive );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPanelAnimationDictionary::PanelAnimationDumpVars( char const *className )
+{
+ if ( className == NULL )
+ {
+ for ( int i = 0; i < (int)m_AnimationMaps.Count(); i++ )
+ {
+ PanelAnimationDumpMap( m_AnimationMaps[ i ].map, false );
+ }
+ }
+ else
+ {
+ PanelAnimationMap *map = FindPanelAnimationMap( className );
+ if ( map )
+ {
+ PanelAnimationDumpMap( map, true );
+ }
+ else
+ {
+ Msg( "No such Panel Animation class %s\n", className );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: singleton accessor
+//-----------------------------------------------------------------------------
+CPanelAnimationDictionary& GetPanelAnimationDictionary()
+{
+ static CPanelAnimationDictionary dictionary;
+ return dictionary;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+PanelAnimationMap *FindOrAddPanelAnimationMap( char const *className )
+{
+ return GetPanelAnimationDictionary().FindOrAddPanelAnimationMap( className );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find but don't add mapping
+//-----------------------------------------------------------------------------
+PanelAnimationMap *FindPanelAnimationMap( char const *className )
+{
+ return GetPanelAnimationDictionary().FindPanelAnimationMap( className );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PanelAnimationDumpVars( char const *className )
+{
+ GetPanelAnimationDictionary().PanelAnimationDumpVars( className );
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/BitmapImagePanel.cpp b/mp/src/vgui2/vgui_controls/BitmapImagePanel.cpp
new file mode 100644
index 00000000..e6f40128
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/BitmapImagePanel.cpp
@@ -0,0 +1,364 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdio.h>
+
+#include "vgui_controls/BitmapImagePanel.h"
+#include "vgui/ISurface.h"
+#include "vgui/IScheme.h"
+#include "vgui/IBorder.h"
+#include "KeyValues.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#ifndef min
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+/**
+ * Simple utility function to allocate memory and duplicate a string
+ */
+static inline char *CloneString( const char *str )
+{
+ char *cloneStr = new char [ strlen(str)+1 ];
+ strcpy( cloneStr, str );
+ return cloneStr;
+}
+
+DECLARE_BUILD_FACTORY_DEFAULT_TEXT( CBitmapImagePanel, BitmapImagePanel );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CBitmapImagePanel::CBitmapImagePanel( Panel *parent, char const *panelName,
+ char const *filename /*= NULL*/ ) : Panel( parent, panelName )
+{
+ m_pImage = NULL;
+
+ SetBounds( 0, 0, 100, 100 );
+
+ m_pszImageName = NULL;
+ m_pszColorName = NULL;
+
+ m_hardwareFiltered = false;
+ m_preserveAspectRatio = false;
+ m_contentAlignment = Label::a_center;
+
+ if ( filename && filename[ 0 ] )
+ {
+ m_pImage = scheme()->GetImage( filename, NULL );
+ m_pszImageName = CloneString( filename );
+ }
+
+ m_bgColor = Color(255, 255, 255, 255);
+}
+CBitmapImagePanel::~CBitmapImagePanel()
+{
+ delete [] m_pszImageName;
+ delete [] m_pszColorName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBitmapImagePanel::ComputeImagePosition(int &x, int &y, int &w, int &h)
+{
+ if (!m_pImage)
+ {
+ x = y = w = h = 0;
+ return;
+ }
+
+ if ( !m_preserveAspectRatio )
+ {
+ x = y = 0;
+ GetSize( w, h );
+ return;
+ }
+
+ int panelWide, panelTall;
+ GetSize( panelWide, panelTall );
+
+ int imageWide, imageTall;
+ m_pImage->GetSize( imageWide, imageTall );
+
+ if ( panelWide > 0 && panelTall > 0 && imageWide > 0 && imageTall > 0 )
+ {
+ float xScale = (float)panelWide / (float)imageWide;
+ float yScale = (float)panelTall / (float)imageTall;
+ float scale = min( xScale, yScale );
+
+ w = (int) (imageWide * scale);
+ h = (int) (imageTall * scale);
+
+ switch (m_contentAlignment)
+ {
+ case Label::a_northwest:
+ x = y = 0;
+ break;
+ case Label::a_north:
+ x = (panelWide - w) / 2;
+ y = 0;
+ break;
+ case Label::a_northeast:
+ x = (panelWide - w);
+ y = 0;
+ break;
+ case Label::a_west:
+ x = 0;
+ y = (panelTall - h) / 2;
+ break;
+ case Label::a_center:
+ x = (panelWide - w) / 2;
+ y = (panelTall - h) / 2;
+ break;
+ case Label::a_east:
+ x = (panelWide - w);
+ y = (panelTall - h) / 2;
+ break;
+ case Label::a_southwest:
+ x = (panelWide - w);
+ y = 0;
+ break;
+ case Label::a_south:
+ x = (panelWide - w);
+ y = (panelTall - h) / 2;
+ break;
+ case Label::a_southeast:
+ x = (panelWide - w);
+ y = (panelTall - h);
+ break;
+ default:
+ x = y = 0;
+ break;
+ }
+ }
+ else
+ {
+ x = y = 0;
+ w = panelWide;
+ h = panelTall;
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBitmapImagePanel::PaintBorder()
+{
+ int x, y, w, h;
+ ComputeImagePosition(x, y, w, h);
+
+ IBorder *pBorder = GetBorder();
+ if ( pBorder )
+ pBorder->Paint( x, y, x+w, y+h, -1, 0, 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBitmapImagePanel::PaintBackground()
+{
+ if (!m_pImage)
+ return;
+
+ int x, y, w, h;
+ ComputeImagePosition(x, y, w, h);
+
+ m_pImage->SetPos(x, y);
+ m_pImage->SetSize( w, h );
+ m_pImage->SetColor( m_bgColor );
+ surface()->DrawSetColor( m_bgColor );
+ m_pImage->Paint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBitmapImagePanel::setTexture( char const *filename, bool hardwareFiltered )
+{
+ m_hardwareFiltered = hardwareFiltered;
+
+ if ( m_pszImageName )
+ {
+ delete[] m_pszImageName;
+ m_pszImageName = NULL;
+ }
+ if ( filename && filename[ 0 ] )
+ {
+ m_pImage = scheme()->GetImage( filename, m_hardwareFiltered );
+ m_pszImageName = CloneString( filename );
+ }
+ else
+ {
+ m_pImage = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBitmapImagePanel::SetContentAlignment(Label::Alignment alignment)
+{
+ m_contentAlignment=alignment;
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets control settings for editing
+//-----------------------------------------------------------------------------
+void CBitmapImagePanel::GetSettings(KeyValues *outResourceData)
+{
+ BaseClass::GetSettings(outResourceData);
+ if (m_pszImageName)
+ {
+ outResourceData->SetString("image", m_pszImageName);
+ }
+ if (m_pszColorName)
+ {
+ outResourceData->SetString("imagecolor", m_pszColorName);
+ }
+ const char *alignmentString = "";
+ switch ( m_contentAlignment )
+ {
+ case Label::a_northwest: alignmentString = "north-west"; break;
+ case Label::a_north: alignmentString = "north"; break;
+ case Label::a_northeast: alignmentString = "north-east"; break;
+ case Label::a_center: alignmentString = "center"; break;
+ case Label::a_east: alignmentString = "east"; break;
+ case Label::a_southwest: alignmentString = "south-west"; break;
+ case Label::a_south: alignmentString = "south"; break;
+ case Label::a_southeast: alignmentString = "south-east"; break;
+ case Label::a_west:
+ default: alignmentString = "center"; break;
+ }
+ outResourceData->SetString( "imageAlignment", alignmentString );
+ outResourceData->SetInt("preserveAspectRatio", m_preserveAspectRatio);
+ outResourceData->SetInt("filtered", m_hardwareFiltered);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies designer settings from res file
+//-----------------------------------------------------------------------------
+void CBitmapImagePanel::ApplySettings(KeyValues *inResourceData)
+{
+ if ( m_pszImageName )
+ {
+ delete[] m_pszImageName;
+ m_pszImageName = NULL;
+ }
+
+ if ( m_pszColorName )
+ {
+ delete[] m_pszColorName;
+ m_pszColorName = NULL;
+ }
+
+ const char *imageName = inResourceData->GetString("image", "");
+ if (*imageName)
+ {
+ setTexture( imageName );
+ }
+
+ const char *colorName = inResourceData->GetString("imagecolor", "");
+ if (*colorName)
+ {
+ m_pszColorName = CloneString( colorName );
+ InvalidateLayout(false,true); // force ApplySchemeSettings to run
+ }
+
+ const char *keyString = inResourceData->GetString("imageAlignment", "");
+ if (keyString && *keyString)
+ {
+ int align = -1;
+
+ if ( !stricmp(keyString, "north-west") )
+ {
+ align = Label::a_northwest;
+ }
+ else if ( !stricmp(keyString, "north") )
+ {
+ align = Label::a_north;
+ }
+ else if ( !stricmp(keyString, "north-east") )
+ {
+ align = Label::a_northeast;
+ }
+ else if ( !stricmp(keyString, "west") )
+ {
+ align = Label::a_west;
+ }
+ else if ( !stricmp(keyString, "center") )
+ {
+ align = Label::a_center;
+ }
+ else if ( !stricmp(keyString, "east") )
+ {
+ align = Label::a_east;
+ }
+ else if ( !stricmp(keyString, "south-west") )
+ {
+ align = Label::a_southwest;
+ }
+ else if ( !stricmp(keyString, "south") )
+ {
+ align = Label::a_south;
+ }
+ else if ( !stricmp(keyString, "south-east") )
+ {
+ align = Label::a_southeast;
+ }
+
+ if ( align != -1 )
+ {
+ SetContentAlignment( (Label::Alignment)align );
+ }
+ }
+
+ keyString = inResourceData->GetString("preserveAspectRatio", "");
+ if (keyString && *keyString)
+ {
+ m_preserveAspectRatio = atoi( keyString ) != 0;
+ }
+
+ keyString = inResourceData->GetString("filtered", "");
+ if (keyString && *keyString)
+ {
+ m_hardwareFiltered = atoi( keyString ) != 0;
+ }
+
+ BaseClass::ApplySettings(inResourceData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: load the image, this is done just before this control is displayed
+//-----------------------------------------------------------------------------
+void CBitmapImagePanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ if ( m_pszColorName )
+ {
+ setImageColor( pScheme->GetColor( m_pszColorName, m_bgColor ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Describes editing details
+//-----------------------------------------------------------------------------
+const char *CBitmapImagePanel::GetDescription()
+{
+ static char buf[1024];
+ _snprintf(buf, sizeof(buf), "%s, string image, string imagecolor, alignment imageAlignment, int preserveAspectRatio, int filtered", BaseClass::GetDescription());
+ return buf;
+}
diff --git a/mp/src/vgui2/vgui_controls/BuildFactoryHelper.cpp b/mp/src/vgui2/vgui_controls/BuildFactoryHelper.cpp
new file mode 100644
index 00000000..6d3743d3
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/BuildFactoryHelper.cpp
@@ -0,0 +1,104 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Helper for the CHudElement class to add themselves to the list of hud elements
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "vgui/IVGui.h"
+#include "vgui_controls/MessageMap.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+// Start with empty list
+CBuildFactoryHelper *CBuildFactoryHelper::m_sHelpers = NULL;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructs a panel factory
+// Input : pfnCreate - fn Ptr to a function which generates a panel
+//-----------------------------------------------------------------------------
+CBuildFactoryHelper::CBuildFactoryHelper( char const *className, PANELCREATEFUNC func )
+{
+ // Make this fatal
+ if ( HasFactory( className ) )
+ {
+ Error( "CBuildFactoryHelper: Factory for '%s' already exists!!!!\n", className );
+ }
+
+ //List is empty, or element belongs at front, insert here
+ m_pNext = m_sHelpers;
+ m_sHelpers = this;
+
+ Assert( func );
+ m_CreateFunc = func;
+ Assert( className );
+ m_pClassName = className;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns next object in list
+// Output : CBuildFactoryHelper
+//-----------------------------------------------------------------------------
+CBuildFactoryHelper *CBuildFactoryHelper::GetNext( void )
+{
+ return m_pNext;
+}
+
+char const *CBuildFactoryHelper::GetClassName() const
+{
+ return m_pClassName;
+}
+
+vgui::Panel *CBuildFactoryHelper::CreatePanel()
+{
+ if ( !m_CreateFunc )
+ return NULL;
+
+ return ( *m_CreateFunc )();
+}
+
+// private static meethod
+bool CBuildFactoryHelper::HasFactory( char const *className )
+{
+ CBuildFactoryHelper *p = m_sHelpers;
+ while ( p )
+ {
+ if ( !Q_stricmp( className, p->GetClassName() ) )
+ return true;
+
+ p = p->GetNext();
+ }
+ return false;
+}
+
+// static method
+vgui::Panel *CBuildFactoryHelper::InstancePanel( char const *className )
+{
+ CBuildFactoryHelper *p = m_sHelpers;
+ while ( p )
+ {
+ if ( !Q_stricmp( className, p->GetClassName() ) )
+ return p->CreatePanel();
+
+ p = p->GetNext();
+ }
+ return NULL;
+}
+
+// static method
+void CBuildFactoryHelper::GetFactoryNames( CUtlVector< char const * >& list )
+{
+ list.RemoveAll();
+
+ CBuildFactoryHelper *p = m_sHelpers;
+ while ( p )
+ {
+ list.AddToTail( p->GetClassName() );
+ p = p->GetNext();
+ }
+}
+
+
+
diff --git a/mp/src/vgui2/vgui_controls/BuildGroup.cpp b/mp/src/vgui2/vgui_controls/BuildGroup.cpp
new file mode 100644
index 00000000..cf9cae8e
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/BuildGroup.cpp
@@ -0,0 +1,1448 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+ //========= Copyright � 1996-2003, Valve LLC, All rights reserved. ============
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+
+#include <stdio.h>
+#define PROTECTED_THINGS_DISABLE
+
+#include "utldict.h"
+
+#include <vgui/KeyCode.h>
+#include <vgui/Cursor.h>
+#include <vgui/MouseCode.h>
+#include <KeyValues.h>
+#include <vgui/IInput.h>
+#include <vgui/ISystem.h>
+#include <vgui/IVGui.h>
+#include <vgui/ISurface.h>
+
+#include <vgui_controls/BuildGroup.h>
+#include <vgui_controls/Panel.h>
+#include <vgui_controls/PHandle.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/EditablePanel.h>
+#include <vgui_controls/MessageBox.h>
+#include "filesystem.h"
+
+#if defined( _X360 )
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Handle table
+//-----------------------------------------------------------------------------
+IMPLEMENT_HANDLES( BuildGroup, 20 )
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+BuildGroup::BuildGroup(Panel *parentPanel, Panel *contextPanel)
+{
+ CONSTRUCT_HANDLE( );
+
+ _enabled=false;
+ _snapX=1;
+ _snapY=1;
+ _cursor_sizenwse = dc_sizenwse;
+ _cursor_sizenesw = dc_sizenesw;
+ _cursor_sizewe = dc_sizewe;
+ _cursor_sizens = dc_sizens;
+ _cursor_sizeall = dc_sizeall;
+ _currentPanel=null;
+ _dragging=false;
+ m_pResourceName=NULL;
+ m_pResourcePathID = NULL;
+ m_hBuildDialog=NULL;
+ m_pParentPanel=parentPanel;
+ for (int i=0; i<4; ++i)
+ _rulerNumber[i] = NULL;
+ SetContextPanel(contextPanel);
+ _showRulers = false;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+BuildGroup::~BuildGroup()
+{
+ if (m_hBuildDialog)
+ delete m_hBuildDialog.Get();
+ m_hBuildDialog = NULL;
+
+ delete [] m_pResourceName;
+ delete [] m_pResourcePathID;
+
+ for (int i=0; i <4; ++i)
+ {
+ if (_rulerNumber[i])
+ {
+ delete _rulerNumber[i];
+ _rulerNumber[i]= NULL;
+ }
+ }
+
+ DESTRUCT_HANDLE();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggles build mode on/off
+// Input : state - new state
+//-----------------------------------------------------------------------------
+void BuildGroup::SetEnabled(bool state)
+{
+ if(_enabled != state)
+ {
+ _enabled = state;
+ _currentPanel = NULL;
+
+ if ( state )
+ {
+ ActivateBuildDialog();
+ }
+ else
+ {
+ // hide the build dialog
+ if ( m_hBuildDialog )
+ {
+ m_hBuildDialog->OnCommand("Close");
+ }
+
+ // request focus for our main panel
+ m_pParentPanel->RequestFocus();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if buildgroup is enabled
+//-----------------------------------------------------------------------------
+bool BuildGroup::IsEnabled()
+{
+ return _enabled;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the list of panels that are currently selected
+//-----------------------------------------------------------------------------
+CUtlVector<PHandle> *BuildGroup::GetControlGroup()
+{
+ return &_controlGroup;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if ruler display is activated
+//-----------------------------------------------------------------------------
+bool BuildGroup::HasRulersOn()
+{
+ return _showRulers;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle ruler display
+//-----------------------------------------------------------------------------
+void BuildGroup::ToggleRulerDisplay()
+{
+ _showRulers = !_showRulers;
+
+ if (_rulerNumber[0] == NULL) // rulers haven't been initialized
+ {
+ _rulerNumber[0] = new Label(m_pBuildContext, NULL, "");
+ _rulerNumber[1] = new Label(m_pBuildContext, NULL, "");
+ _rulerNumber[2] = new Label(m_pBuildContext, NULL, "");
+ _rulerNumber[3] = new Label(m_pBuildContext, NULL, "");
+ }
+ SetRulerLabelsVisible(_showRulers);
+
+ m_pBuildContext->Repaint();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Tobble visibility of ruler number labels
+//-----------------------------------------------------------------------------
+void BuildGroup::SetRulerLabelsVisible(bool state)
+{
+ _rulerNumber[0]->SetVisible(state);
+ _rulerNumber[1]->SetVisible(state);
+ _rulerNumber[2]->SetVisible(state);
+ _rulerNumber[3]->SetVisible(state);
+}
+
+void BuildGroup::ApplySchemeSettings( IScheme *pScheme )
+{
+ DrawRulers();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw Rulers on screen if conditions are right
+//-----------------------------------------------------------------------------
+void BuildGroup::DrawRulers()
+{
+ // don't draw if visibility is off
+ if (!_showRulers)
+ {
+ return;
+ }
+
+ // no drawing if we selected the context panel
+ if (m_pBuildContext == _currentPanel)
+ {
+ SetRulerLabelsVisible(false);
+ return;
+ }
+ else
+ SetRulerLabelsVisible(true);
+
+ int x, y, wide, tall;
+ // get base panel's postition
+ m_pBuildContext->GetBounds(x, y, wide, tall);
+ m_pBuildContext->ScreenToLocal(x,y);
+
+ int cx, cy, cwide, ctall;
+ _currentPanel->GetBounds (cx, cy, cwide, ctall);
+
+ surface()->PushMakeCurrent(m_pBuildContext->GetVPanel(), false);
+
+ // draw rulers
+ surface()->DrawSetColor(255, 255, 255, 255); // white color
+
+ surface()->DrawFilledRect(0, cy, cx, cy+1); //top horiz left
+ surface()->DrawFilledRect(cx+cwide, cy, wide, cy+1); //top horiz right
+
+ surface()->DrawFilledRect(0, cy+ctall-1, cx, cy+ctall); //bottom horiz left
+ surface()->DrawFilledRect(cx+cwide, cy+ctall-1, wide, cy+ctall); //bottom horiz right
+
+ surface()->DrawFilledRect(cx,0,cx+1,cy); //top vert left
+ surface()->DrawFilledRect(cx+cwide-1,0, cx+cwide, cy); //top vert right
+
+ surface()->DrawFilledRect(cx,cy+ctall, cx+1, tall); //bottom vert left
+ surface()->DrawFilledRect(cx+cwide-1, cy+ctall, cx+cwide, tall); //bottom vert right
+
+ surface()->PopMakeCurrent(m_pBuildContext->GetVPanel());
+
+ // now let's put numbers with the rulers
+ char textstring[20];
+ Q_snprintf (textstring, sizeof( textstring ), "%d", cx);
+ _rulerNumber[0]->SetText(textstring);
+ int twide, ttall;
+ _rulerNumber[0]->GetContentSize(twide,ttall);
+ _rulerNumber[0]->SetSize(twide,ttall);
+ _rulerNumber[0]->SetPos(cx/2-twide/2, cy-ttall+3);
+
+ Q_snprintf (textstring, sizeof( textstring ), "%d", cy);
+ _rulerNumber[1]->SetText(textstring);
+ _rulerNumber[1]->GetContentSize(twide,ttall);
+ _rulerNumber[1]->SetSize(twide,ttall);
+ _rulerNumber[1]->GetSize(twide,ttall);
+ _rulerNumber[1]->SetPos(cx-twide + 3, cy/2-ttall/2);
+
+ Q_snprintf (textstring, sizeof( textstring ), "%d", cy);
+ _rulerNumber[2]->SetText(textstring);
+ _rulerNumber[2]->GetContentSize(twide,ttall);
+ _rulerNumber[2]->SetSize(twide,ttall);
+ _rulerNumber[2]->SetPos(cx+cwide+(wide-cx-cwide)/2 - twide/2, cy+ctall-3);
+
+ Q_snprintf (textstring, sizeof( textstring ), "%d", cy);
+ _rulerNumber[3]->SetText(textstring);
+ _rulerNumber[3]->GetContentSize(twide,ttall);
+ _rulerNumber[3]->SetSize(twide,ttall);
+ _rulerNumber[3]->SetPos(cx+cwide, cy+ctall+(tall-cy-ctall)/2 - ttall/2);
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: respond to cursor movments
+//-----------------------------------------------------------------------------
+bool BuildGroup::CursorMoved(int x, int y, Panel *panel)
+{
+ Assert(panel);
+
+ if ( !m_hBuildDialog.Get() )
+ {
+ if ( panel->GetParent() )
+ {
+ EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() );
+ if ( ep )
+ {
+ BuildGroup *bg = ep->GetBuildGroup();
+ if ( bg && bg != this )
+ {
+ bg->CursorMoved( x, y, panel );
+ }
+ }
+ }
+ return false;
+ }
+
+ // no moving uneditable panels
+ // commented out because this has issues with panels moving
+ // to front and obscuring other panels
+ //if (!panel->IsBuildModeEditable())
+ // return;
+
+ if (_dragging)
+ {
+ input()->GetCursorPos(x, y);
+
+ if (_dragMouseCode == MOUSE_RIGHT)
+ {
+ int newW = max( 1, _dragStartPanelSize[ 0 ] + x - _dragStartCursorPos[0] );
+ int newH = max( 1, _dragStartPanelSize[ 1 ] + y - _dragStartCursorPos[1] );
+
+ bool shift = ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) );
+ bool ctrl = ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) );
+
+ if ( shift )
+ {
+ newW = _dragStartPanelSize[ 0 ];
+ }
+ if ( ctrl )
+ {
+ newH = _dragStartPanelSize[ 1 ];
+ }
+
+ panel->SetSize( newW, newH );
+ ApplySnap(panel);
+ }
+ else
+ {
+ for (int i=0; i < _controlGroup.Count(); ++i)
+ {
+ // now fix offset of member panels with respect to the one we are dragging
+ Panel *groupMember = _controlGroup[i].Get();
+ groupMember->SetPos(_dragStartPanelPos[0] + _groupDeltaX[i] +(x-_dragStartCursorPos[0]), _dragStartPanelPos[1] + _groupDeltaY[i] +(y-_dragStartCursorPos[1]));
+ ApplySnap(groupMember);
+ }
+ }
+
+ // update the build dialog
+ if (m_hBuildDialog)
+ {
+ KeyValues *keyval = new KeyValues("UpdateControlData");
+ keyval->SetPtr("panel", GetCurrentPanel());
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL);
+
+ keyval = new KeyValues("EnableSaveButton");
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL);
+ }
+
+ panel->Repaint();
+ panel->CallParentFunction(new KeyValues("Repaint"));
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool BuildGroup::MousePressed(MouseCode code, Panel *panel)
+{
+ Assert(panel);
+
+ if ( !m_hBuildDialog.Get() )
+ {
+ if ( panel->GetParent() )
+ {
+ EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() );
+ if ( ep )
+ {
+ BuildGroup *bg = ep->GetBuildGroup();
+ if ( bg && bg != this )
+ {
+ bg->MousePressed( code, panel );
+ }
+ }
+ }
+ return false;
+ }
+
+ // if people click on the base build dialog panel.
+ if (panel == m_hBuildDialog)
+ {
+ // hide the click menu if its up
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("HideNewControlMenu"), NULL);
+ return true;
+ }
+
+ // don't select unnamed items
+ if (strlen(panel->GetName()) < 1)
+ return true;
+
+ bool shift = ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) );
+ if (!shift)
+ {
+ _controlGroup.RemoveAll();
+ }
+
+ // Show new ctrl menu if they click on the bg (not on a subcontrol)
+ if ( code == MOUSE_RIGHT && panel == GetContextPanel())
+ {
+ // trigger a drop down menu to create new controls
+ ivgui()->PostMessage (m_hBuildDialog->GetVPanel(), new KeyValues("ShowNewControlMenu"), NULL);
+ }
+ else
+ {
+ // don't respond if we click on ruler numbers
+ if (_showRulers) // rulers are visible
+ {
+ for ( int i=0; i < 4; i++)
+ {
+ if ( panel == _rulerNumber[i])
+ return true;
+ }
+ }
+
+ _dragging = true;
+ _dragMouseCode = code;
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("HideNewControlMenu"), NULL);
+
+ int x, y;
+ input()->GetCursorPos(x, y);
+
+ _dragStartCursorPos[0] = x;
+ _dragStartCursorPos[1] = y;
+
+
+ input()->SetMouseCapture(panel->GetVPanel());
+
+ _groupDeltaX.RemoveAll();
+ _groupDeltaY.RemoveAll();
+
+ // basepanel is the panel that all the deltas will be calculated from.
+ // it is the last panel we clicked in because if we move the panels as a group
+ // it would be from that one
+ Panel *basePanel = NULL;
+ // find the panel we clicked in, that is the base panel
+ // it might already be in the group
+ for (int i=0; i< _controlGroup.Count(); ++i)
+ {
+ if (panel == _controlGroup[i].Get())
+ {
+ basePanel = panel;
+ break;
+ }
+ }
+
+ // if its not in the group we just added this panel. get it in the group
+ if (basePanel == NULL)
+ {
+ PHandle temp;
+ temp = panel;
+ _controlGroup.AddToTail(temp);
+ basePanel = panel;
+ }
+
+ basePanel->GetPos(x,y);
+ _dragStartPanelPos[0]=x;
+ _dragStartPanelPos[1]=y;
+
+ basePanel->GetSize( _dragStartPanelSize[ 0 ], _dragStartPanelSize[ 1 ] );
+
+ // figure out the deltas of the other panels from the base panel
+ for (int i=0; i<_controlGroup.Count(); ++i)
+ {
+ int cx, cy;
+ _controlGroup[i].Get()->GetPos(cx, cy);
+ _groupDeltaX.AddToTail(cx - x);
+ _groupDeltaY.AddToTail(cy - y);
+ }
+
+ // if this panel wasn't already selected update the buildmode dialog controls to show its info
+ if(_currentPanel != panel)
+ {
+ _currentPanel = panel;
+
+ if ( m_hBuildDialog )
+ {
+ // think this is taken care of by SetActiveControl.
+ //ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("ApplyDataToControls"), NULL);
+
+ KeyValues *keyval = new KeyValues("SetActiveControl");
+ keyval->SetPtr("PanelPtr", GetCurrentPanel());
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL);
+ }
+ }
+
+ // store undo information upon panel selection.
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("StoreUndo"), NULL);
+
+ panel->RequestFocus();
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool BuildGroup::MouseReleased(MouseCode code, Panel *panel)
+{
+ if ( !m_hBuildDialog.Get() )
+ {
+ if ( panel->GetParent() )
+ {
+ EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() );
+ if ( ep )
+ {
+ BuildGroup *bg = ep->GetBuildGroup();
+ if ( bg && bg != this )
+ {
+ bg->MouseReleased( code, panel );
+ }
+ }
+ }
+ return false;
+ }
+
+ Assert(panel);
+
+ _dragging=false;
+ input()->SetMouseCapture(null);
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool BuildGroup::MouseDoublePressed(MouseCode code, Panel *panel)
+{
+ Assert(panel);
+ return MousePressed( code, panel );
+}
+
+bool BuildGroup::KeyTyped( wchar_t unichar, Panel *panel )
+{
+ if ( !m_hBuildDialog.Get() )
+ {
+ if ( panel->GetParent() )
+ {
+ EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() );
+ if ( ep )
+ {
+ BuildGroup *bg = ep->GetBuildGroup();
+ if ( bg && bg != this )
+ {
+ bg->KeyTyped( unichar, panel );
+ }
+ }
+ }
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool BuildGroup::KeyCodeTyped(KeyCode code, Panel *panel)
+{
+ if ( !m_hBuildDialog.Get() )
+ {
+ if ( panel->GetParent() )
+ {
+ EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() );
+ if ( ep )
+ {
+ BuildGroup *bg = ep->GetBuildGroup();
+ if ( bg && bg != this )
+ {
+ bg->KeyCodeTyped( code, panel );
+ }
+ }
+ }
+ return false;
+ }
+
+ Assert(panel);
+
+ int dx=0;
+ int dy=0;
+
+ bool shift = ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) );
+ bool ctrl = ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) );
+ bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
+
+
+ if ( ctrl && shift && alt && code == KEY_B)
+ {
+ // enable build mode
+ EditablePanel *ep = dynamic_cast< EditablePanel * >( panel );
+ if ( ep )
+ {
+ ep->ActivateBuildMode();
+ }
+ return true;
+ }
+
+ switch (code)
+ {
+ case KEY_LEFT:
+ {
+ dx-=_snapX;
+ break;
+ }
+ case KEY_RIGHT:
+ {
+ dx+=_snapX;
+ break;
+ }
+ case KEY_UP:
+ {
+ dy-=_snapY;
+ break;
+ }
+ case KEY_DOWN:
+ {
+ dy+=_snapY;
+ break;
+ }
+ case KEY_DELETE:
+ {
+ // delete the panel we have selected
+ ivgui()->PostMessage (m_hBuildDialog->GetVPanel(), new KeyValues ("DeletePanel"), NULL);
+ break;
+ }
+
+ }
+
+ if (ctrl)
+ {
+ switch (code)
+ {
+ case KEY_Z:
+ {
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("Undo"), NULL);
+ break;
+ }
+
+ case KEY_C:
+ {
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("Copy"), NULL);
+ break;
+ }
+ case KEY_V:
+ {
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("Paste"), NULL);
+ break;
+ }
+ }
+ }
+
+ if(dx||dy)
+ {
+ //TODO: make this stuff actually snap
+
+ int x,y,wide,tall;
+
+ panel->GetBounds(x,y,wide,tall);
+
+ if(shift)
+ {
+ panel->SetSize(wide+dx,tall+dy);
+ }
+ else
+ {
+ panel->SetPos(x+dx,y+dy);
+ }
+
+ ApplySnap(panel);
+
+ panel->Repaint();
+ if (panel->GetVParent() != 0)
+ {
+ panel->PostMessage(panel->GetVParent(), new KeyValues("Repaint"));
+ }
+
+
+ // update the build dialog
+ if (m_hBuildDialog)
+ {
+ // post that it's active
+ KeyValues *keyval = new KeyValues("SetActiveControl");
+ keyval->SetPtr("PanelPtr", GetCurrentPanel());
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL);
+
+ // post that it's been changed
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), new KeyValues("PanelMoved"), NULL);
+ }
+ }
+
+ // If holding key while dragging, simulate moving cursor so shift/ctrl key changes take effect
+ if ( _dragging && panel != GetContextPanel() )
+ {
+ int x, y;
+ input()->GetCursorPos( x, y );
+ CursorMoved( x, y, panel );
+ }
+
+ return true;
+}
+
+bool BuildGroup::KeyCodeReleased(KeyCode code, Panel *panel )
+{
+ if ( !m_hBuildDialog.Get() )
+ {
+ if ( panel->GetParent() )
+ {
+ EditablePanel *ep = dynamic_cast< EditablePanel * >( panel->GetParent() );
+ if ( ep )
+ {
+ BuildGroup *bg = ep->GetBuildGroup();
+ if ( bg && bg != this )
+ {
+ bg->KeyCodeTyped( code, panel );
+ }
+ }
+ }
+ return false;
+ }
+
+ // If holding key while dragging, simulate moving cursor so shift/ctrl key changes take effect
+ if ( _dragging && panel != GetContextPanel() )
+ {
+ int x, y;
+ input()->GetCursorPos( x, y );
+ CursorMoved( x, y, panel );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Searches for a BuildModeDialog in the hierarchy
+//-----------------------------------------------------------------------------
+Panel *BuildGroup::CreateBuildDialog( void )
+{
+ // request the panel
+ Panel *buildDialog = NULL;
+ KeyValues *data = new KeyValues("BuildDialog");
+ data->SetPtr("BuildGroupPtr", this);
+ if (m_pBuildContext->RequestInfo(data))
+ {
+ buildDialog = (Panel *)data->GetPtr("PanelPtr");
+ }
+
+ // initialize the build dialog if found
+ if ( buildDialog )
+ {
+ input()->ReleaseAppModalSurface();
+ }
+
+ return buildDialog;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activates the build mode settings dialog
+//-----------------------------------------------------------------------------
+void BuildGroup::ActivateBuildDialog( void )
+{
+ // create the build mode dialog first time through
+ if (!m_hBuildDialog.Get())
+ {
+ m_hBuildDialog = CreateBuildDialog();
+
+ if (!m_hBuildDialog.Get())
+ return;
+ }
+
+ m_hBuildDialog->SetVisible( true );
+
+ // send a message to set the initial dialog controls info
+ _currentPanel = m_pParentPanel;
+ KeyValues *keyval = new KeyValues("SetActiveControl");
+ keyval->SetPtr("PanelPtr", GetCurrentPanel());
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+HCursor BuildGroup::GetCursor(Panel *panel)
+{
+ Assert(panel);
+
+ int x,y,wide,tall;
+ input()->GetCursorPos(x,y);
+ panel->ScreenToLocal(x,y);
+ panel->GetSize(wide,tall);
+
+ if(x < 2)
+ {
+ if(y < 4)
+ {
+ return _cursor_sizenwse;
+ }
+ else
+ if(y<(tall-4))
+ {
+ return _cursor_sizewe;
+ }
+ else
+ {
+ return _cursor_sizenesw;
+ }
+ }
+
+ return _cursor_sizeall;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void BuildGroup::ApplySnap(Panel *panel)
+{
+ Assert(panel);
+
+ int x,y,wide,tall;
+ panel->GetBounds(x,y,wide,tall);
+
+ x=(x/_snapX)*_snapX;
+ y=(y/_snapY)*_snapY;
+ panel->SetPos(x,y);
+
+ int xx,yy;
+ xx=x+wide;
+ yy=y+tall;
+
+ xx=(xx/_snapX)*_snapX;
+ yy=(yy/_snapY)*_snapY;
+ panel->SetSize(xx-x,yy-y);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the currently selected panel
+//-----------------------------------------------------------------------------
+Panel *BuildGroup::GetCurrentPanel()
+{
+ return _currentPanel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add panel the list of panels that are in the build group
+//-----------------------------------------------------------------------------
+void BuildGroup::PanelAdded(Panel *panel)
+{
+ Assert(panel);
+
+ PHandle temp;
+ temp = panel;
+ int c = _panelDar.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ if ( _panelDar[ i ] == temp )
+ {
+ return;
+ }
+ }
+ _panelDar.AddToTail(temp);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: loads the control settings from file
+//-----------------------------------------------------------------------------
+void BuildGroup::LoadControlSettings(const char *controlResourceName, const char *pathID, KeyValues *pPreloadedKeyValues, KeyValues *pConditions)
+{
+ // make sure the file is registered
+ RegisterControlSettingsFile(controlResourceName, pathID);
+
+ // Use the keyvalues they passed in or load them.
+ KeyValues *rDat = pPreloadedKeyValues;
+ if ( !rDat )
+ {
+ // load the resource data from the file
+ rDat = new KeyValues(controlResourceName);
+
+ // check the skins directory first, if an explicit pathID hasn't been set
+ bool bSuccess = false;
+ if (!pathID)
+ {
+ bSuccess = rDat->LoadFromFile(g_pFullFileSystem, controlResourceName, "SKIN");
+ }
+ if (!bSuccess)
+ {
+ bSuccess = rDat->LoadFromFile(g_pFullFileSystem, controlResourceName, pathID);
+ }
+
+ if ( bSuccess )
+ {
+ if ( IsX360() )
+ {
+ rDat->ProcessResolutionKeys( surface()->GetResolutionKey() );
+ }
+ if ( IsPC() )
+ {
+ ConVarRef cl_hud_minmode( "cl_hud_minmode", true );
+ if ( cl_hud_minmode.IsValid() && cl_hud_minmode.GetBool() )
+ {
+ rDat->ProcessResolutionKeys( "_minmode" );
+ }
+ }
+
+ if ( pConditions && pConditions->GetFirstSubKey() )
+ {
+ ProcessConditionalKeys( rDat, pConditions );
+ }
+ }
+ }
+
+ // save off the resource name
+ delete [] m_pResourceName;
+ m_pResourceName = new char[strlen(controlResourceName) + 1];
+ strcpy(m_pResourceName, controlResourceName);
+
+ if (pathID)
+ {
+ delete [] m_pResourcePathID;
+ m_pResourcePathID = new char[strlen(pathID) + 1];
+ strcpy(m_pResourcePathID, pathID);
+ }
+
+ // delete any controls not in both files
+ DeleteAllControlsCreatedByControlSettingsFile();
+
+ // loop through the resource data sticking info into controls
+ ApplySettings(rDat);
+
+ if (m_pParentPanel)
+ {
+ m_pParentPanel->InvalidateLayout();
+ m_pParentPanel->Repaint();
+ }
+
+ if ( rDat != pPreloadedKeyValues )
+ {
+ rDat->deleteThis();
+ }
+}
+
+void BuildGroup::ProcessConditionalKeys( KeyValues *pData, KeyValues *pConditions )
+{
+ // for each condition, look for it in keys
+ // if its a positive condition, promote all of its children, replacing values
+
+ if ( pData )
+ {
+ KeyValues *pSubKey = pData->GetFirstSubKey();
+ if ( !pSubKey )
+ {
+ // not a block
+ return;
+ }
+
+ for ( ; pSubKey != NULL; pSubKey = pSubKey->GetNextKey() )
+ {
+ // recursively descend each sub block
+ ProcessConditionalKeys( pSubKey, pConditions );
+
+ KeyValues *pCondition = pConditions->GetFirstSubKey();
+ for ( ; pCondition != NULL; pCondition = pCondition->GetNextKey() )
+ {
+ // if we match any conditions in this sub block, copy up
+ KeyValues *pConditionBlock = pSubKey->FindKey( pCondition->GetName() );
+ if ( pConditionBlock )
+ {
+ KeyValues *pOverridingKey;
+ for ( pOverridingKey = pConditionBlock->GetFirstSubKey(); pOverridingKey != NULL; pOverridingKey = pOverridingKey->GetNextKey() )
+ {
+ KeyValues *pExistingKey = pSubKey->FindKey( pOverridingKey->GetName() );
+ if ( pExistingKey )
+ {
+ pExistingKey->SetStringValue( pOverridingKey->GetString() );
+ }
+ else
+ {
+ KeyValues *copy = pOverridingKey->MakeCopy();
+ pSubKey->AddSubKey( copy );
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: registers that a control settings file may be loaded
+// use when the dialog may have multiple states and the editor will need to be able to switch between them
+//-----------------------------------------------------------------------------
+void BuildGroup::RegisterControlSettingsFile(const char *controlResourceName, const char *pathID)
+{
+ // add the file into a list for build mode
+ CUtlSymbol sym(controlResourceName);
+ if (!m_RegisteredControlSettingsFiles.IsValidIndex(m_RegisteredControlSettingsFiles.Find(sym)))
+ {
+ m_RegisteredControlSettingsFiles.AddToTail(sym);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor / iterator
+//-----------------------------------------------------------------------------
+int BuildGroup::GetRegisteredControlSettingsFileCount()
+{
+ return m_RegisteredControlSettingsFiles.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+const char *BuildGroup::GetRegisteredControlSettingsFileByIndex(int index)
+{
+ return m_RegisteredControlSettingsFiles[index].String();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: reloads the control settings from file
+//-----------------------------------------------------------------------------
+void BuildGroup::ReloadControlSettings()
+{
+ delete m_hBuildDialog.Get();
+ m_hBuildDialog = NULL;
+
+ // loop though objects in the current control group and remove them all
+ // the 0th panel is always the contextPanel which is not deletable
+ for( int i = 1; i < _panelDar.Count(); i++ )
+ {
+ if (!_panelDar[i].Get()) // this can happen if we had two of the same handle in the list
+ {
+ _panelDar.Remove(i);
+ --i;
+ continue;
+ }
+
+ // only delete deletable panels, as the only deletable panels
+ // are the ones created using the resource file
+ if ( _panelDar[i].Get()->IsBuildModeDeletable())
+ {
+ delete _panelDar[i].Get();
+ _panelDar.Remove(i);
+ --i;
+ }
+ }
+
+ if (m_pResourceName)
+ {
+ EditablePanel *edit = dynamic_cast<EditablePanel *>(m_pParentPanel);
+ if (edit)
+ {
+ edit->LoadControlSettings(m_pResourceName, m_pResourcePathID);
+ }
+ else
+ {
+ LoadControlSettings(m_pResourceName, m_pResourcePathID);
+ }
+ }
+
+ _controlGroup.RemoveAll();
+
+ ActivateBuildDialog();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: changes which control settings are currently loaded
+//-----------------------------------------------------------------------------
+void BuildGroup::ChangeControlSettingsFile(const char *controlResourceName)
+{
+ // clear any current state
+ _controlGroup.RemoveAll();
+ _currentPanel = m_pParentPanel;
+
+ // load the new state, via the dialog if possible
+ EditablePanel *edit = dynamic_cast<EditablePanel *>(m_pParentPanel);
+ if (edit)
+ {
+ edit->LoadControlSettings(controlResourceName, m_pResourcePathID);
+ }
+ else
+ {
+ LoadControlSettings(controlResourceName, m_pResourcePathID);
+ }
+
+ // force it to update
+ KeyValues *keyval = new KeyValues("SetActiveControl");
+ keyval->SetPtr("PanelPtr", GetCurrentPanel());
+ ivgui()->PostMessage(m_hBuildDialog->GetVPanel(), keyval, NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: saves control settings to file
+//-----------------------------------------------------------------------------
+bool BuildGroup::SaveControlSettings( void )
+{
+ bool bSuccess = false;
+ if ( m_pResourceName )
+ {
+ KeyValues *rDat = new KeyValues( m_pResourceName );
+
+ // get the data from our controls
+ GetSettings( rDat );
+
+ char fullpath[ 512 ];
+ g_pFullFileSystem->RelativePathToFullPath( m_pResourceName, m_pResourcePathID, fullpath, sizeof( fullpath ) );
+
+ // save the data out to a file
+ bSuccess = rDat->SaveToFile( g_pFullFileSystem, fullpath, NULL );
+ if (!bSuccess)
+ {
+ MessageBox *dlg = new MessageBox("BuildMode - Error saving file", "Error: Could not save changes. File is most likely read only.");
+ dlg->DoModal();
+ }
+
+ rDat->deleteThis();
+ }
+
+ return bSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deletes all the controls not created by the code
+//-----------------------------------------------------------------------------
+void BuildGroup::DeleteAllControlsCreatedByControlSettingsFile()
+{
+ // loop though objects in the current control group and remove them all
+ // the 0th panel is always the contextPanel which is not deletable
+ for ( int i = 1; i < _panelDar.Count(); i++ )
+ {
+ if (!_panelDar[i].Get()) // this can happen if we had two of the same handle in the list
+ {
+ _panelDar.Remove(i);
+ --i;
+ continue;
+ }
+
+ // only delete deletable panels, as the only deletable panels
+ // are the ones created using the resource file
+ if ( _panelDar[i].Get()->IsBuildModeDeletable())
+ {
+ delete _panelDar[i].Get();
+ _panelDar.Remove(i);
+ --i;
+ }
+ }
+
+ _currentPanel = m_pBuildContext;
+ _currentPanel->InvalidateLayout();
+ m_pBuildContext->Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: serializes settings from a resource data container
+//-----------------------------------------------------------------------------
+void BuildGroup::ApplySettings( KeyValues *resourceData )
+{
+ // loop through all the keys, applying them wherever
+ for (KeyValues *controlKeys = resourceData->GetFirstSubKey(); controlKeys != NULL; controlKeys = controlKeys->GetNextKey())
+ {
+ bool bFound = false;
+
+ // Skip keys that are atomic..
+ if (controlKeys->GetDataType() != KeyValues::TYPE_NONE)
+ continue;
+
+ char const *keyName = controlKeys->GetName();
+
+ // check to see if any buildgroup panels have this name
+ for ( int i = 0; i < _panelDar.Count(); i++ )
+ {
+ Panel *panel = _panelDar[i].Get();
+
+ if (!panel) // this can happen if we had two of the same handle in the list
+ {
+ _panelDar.Remove(i);
+ --i;
+ continue;
+ }
+
+
+ Assert (panel);
+
+ // make the control name match CASE INSENSITIVE!
+ char const *panelName = panel->GetName();
+
+ if (!Q_stricmp(panelName, keyName))
+ {
+ // apply the settings
+ panel->ApplySettings(controlKeys);
+ bFound = true;
+ break;
+ }
+ }
+
+ if ( !bFound )
+ {
+ // the key was not found in the registered list, check to see if we should create it
+ if ( keyName /*controlKeys->GetInt("AlwaysCreate", false)*/ )
+ {
+ // create the control even though it wasn't registered
+ NewControl( controlKeys );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a new control in the context panel
+// Input: name: class name of control to create
+// controlKeys: keyvalues of settings for the panel.
+// name OR controlKeys should be set, not both.
+// x,y position relative to base panel
+// Output: Panel *newPanel, NULL if failed to create new control.
+//-----------------------------------------------------------------------------
+Panel *BuildGroup::NewControl( const char *name, int x, int y)
+{
+ Assert (name);
+
+ Panel *newPanel = NULL;
+ // returns NULL on failure
+ newPanel = static_cast<EditablePanel *>(m_pParentPanel)->CreateControlByName(name);
+
+ if (newPanel)
+ {
+ // panel successfully created
+ newPanel->SetParent(m_pParentPanel);
+ newPanel->SetBuildGroup(this);
+ newPanel->SetPos(x, y);
+
+ char newFieldName[255];
+ GetNewFieldName(newFieldName, sizeof(newFieldName), newPanel);
+ newPanel->SetName(newFieldName);
+
+ newPanel->AddActionSignalTarget(m_pParentPanel);
+ newPanel->SetBuildModeEditable(true);
+ newPanel->SetBuildModeDeletable(true);
+
+ // make sure it gets freed
+ newPanel->SetAutoDelete(true);
+ }
+
+ return newPanel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a new control in the context panel
+// Input: controlKeys: keyvalues of settings for the panel only works when applying initial settings.
+// Output: Panel *newPanel, NULL if failed to create new control.
+//-----------------------------------------------------------------------------
+Panel *BuildGroup::NewControl( KeyValues *controlKeys, int x, int y)
+{
+ Assert (controlKeys);
+
+ Panel *newPanel = NULL;
+ if (controlKeys)
+ {
+// Warning( "Creating new control \"%s\" of type \"%s\"\n", controlKeys->GetString( "fieldName" ), controlKeys->GetString( "ControlName" ) );
+ KeyValues *keyVal = new KeyValues("ControlFactory", "ControlName", controlKeys->GetString("ControlName"));
+ m_pBuildContext->RequestInfo(keyVal);
+ // returns NULL on failure
+ newPanel = (Panel *)keyVal->GetPtr("PanelPtr");
+ keyVal->deleteThis();
+ }
+ else
+ {
+ return NULL;
+ }
+
+ if (newPanel)
+ {
+ // panel successfully created
+ newPanel->SetParent(m_pParentPanel);
+ newPanel->SetBuildGroup(this);
+ newPanel->SetPos(x, y);
+
+ newPanel->SetName(controlKeys->GetName()); // name before applysettings :)
+ newPanel->ApplySettings(controlKeys);
+
+ newPanel->AddActionSignalTarget(m_pParentPanel);
+ newPanel->SetBuildModeEditable(true);
+ newPanel->SetBuildModeDeletable(true);
+
+ // make sure it gets freed
+ newPanel->SetAutoDelete(true);
+ }
+
+ return newPanel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a new unique fieldname for a new control
+//-----------------------------------------------------------------------------
+void BuildGroup::GetNewFieldName(char *newFieldName, int newFieldNameSize, Panel *newPanel)
+{
+ int fieldNameNumber=1;
+ char defaultName[25];
+
+ Q_strncpy( defaultName, newPanel->GetClassName(), sizeof( defaultName ) );
+
+ while (1)
+ {
+ Q_snprintf (newFieldName, newFieldNameSize, "%s%d", defaultName, fieldNameNumber);
+ if ( FieldNameTaken(newFieldName) == NULL)
+ break;
+ ++fieldNameNumber;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: check to see if any buildgroup panels have this fieldname
+// Input : fieldName, name to check
+// Output : ptr to a panel that has the name if it is taken
+//-----------------------------------------------------------------------------
+Panel *BuildGroup::FieldNameTaken(const char *fieldName)
+{
+ for ( int i = 0; i < _panelDar.Count(); i++ )
+ {
+ Panel *panel = _panelDar[i].Get();
+ if ( !panel )
+ continue;
+
+ if (!stricmp(panel->GetName(), fieldName) )
+ {
+ return panel;
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: serializes settings to a resource data container
+//-----------------------------------------------------------------------------
+void BuildGroup::GetSettings( KeyValues *resourceData )
+{
+ // loop through all the objects getting their settings
+ for( int i = 0; i < _panelDar.Count(); i++ )
+ {
+ Panel *panel = _panelDar[i].Get();
+ if (!panel)
+ continue;
+
+ bool isRuler = false;
+ // do not get setting for ruler labels.
+ if (_showRulers) // rulers are visible
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ if (panel == _rulerNumber[i])
+ {
+ isRuler = true;
+ break;
+ }
+ }
+ if (isRuler)
+ {
+ isRuler = false;
+ continue;
+ }
+ }
+
+ // Don't save the setting of the buildmodedialog
+ if (!stricmp(panel->GetName(), "BuildDialog"))
+ continue;
+
+ // get the keys section from the data file
+ if (panel->GetName() && *panel->GetName())
+ {
+ KeyValues *datKey = resourceData->FindKey(panel->GetName(), true);
+
+ // get the settings
+ panel->GetSettings(datKey);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: loop though objects in the current control group and remove them all
+//-----------------------------------------------------------------------------
+void BuildGroup::RemoveSettings()
+{
+ // loop though objects in the current control group and remove them all
+ int i;
+ for( i = 0; i < _controlGroup.Count(); i++ )
+ {
+ // only delete delatable panels
+ if ( _controlGroup[i].Get()->IsBuildModeDeletable())
+ {
+ delete _controlGroup[i].Get();
+ _controlGroup.Remove(i);
+ --i;
+ }
+ }
+
+ // remove deleted panels from the handle list
+ for( i = 0; i < _panelDar.Count(); i++ )
+ {
+ if ( !_panelDar[i].Get() )
+ {
+ _panelDar.Remove(i);
+ --i;
+ }
+ }
+
+ _currentPanel = m_pBuildContext;
+ _currentPanel->InvalidateLayout();
+ m_pBuildContext->Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the panel from which the build group gets all it's object creation info
+//-----------------------------------------------------------------------------
+void BuildGroup::SetContextPanel(Panel *contextPanel)
+{
+ m_pBuildContext = contextPanel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the panel from which the build group gets all it's object creation info
+//-----------------------------------------------------------------------------
+Panel *BuildGroup::GetContextPanel()
+{
+ return m_pBuildContext;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: get the list of panels in the buildgroup
+//-----------------------------------------------------------------------------
+CUtlVector<PHandle> *BuildGroup::GetPanelList()
+{
+ return &_panelDar;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: dialog variables
+//-----------------------------------------------------------------------------
+KeyValues *BuildGroup::GetDialogVariables()
+{
+ EditablePanel *edit = dynamic_cast<EditablePanel *>(m_pParentPanel);
+ if (edit)
+ {
+ return edit->GetDialogVariables();
+ }
+
+ return NULL;
+}
diff --git a/mp/src/vgui2/vgui_controls/BuildModeDialog.cpp b/mp/src/vgui2/vgui_controls/BuildModeDialog.cpp
new file mode 100644
index 00000000..9ae8c8c5
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/BuildModeDialog.cpp
@@ -0,0 +1,1441 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <ctype.h>
+#include <stdio.h>
+#include <utlvector.h>
+
+#include <vgui/IInput.h>
+#include <vgui/ILocalize.h>
+#include <vgui/ISurface.h>
+#include <vgui/ISystem.h>
+#include <vgui/KeyCode.h>
+#include <KeyValues.h>
+#include <vgui/MouseCode.h>
+
+#include <vgui_controls/BuildModeDialog.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/TextEntry.h>
+#include <vgui_controls/Button.h>
+#include <vgui_controls/CheckButton.h>
+#include <vgui_controls/RadioButton.h>
+#include <vgui_controls/MenuButton.h>
+#include <vgui_controls/ComboBox.h>
+#include <vgui_controls/BuildGroup.h>
+#include <vgui_controls/MessageBox.h>
+#include <vgui_controls/Menu.h>
+#include <vgui_controls/Divider.h>
+#include <vgui_controls/PanelListPanel.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+struct PanelItem_t
+{
+ PanelItem_t() : m_EditLabel(NULL) {}
+
+ Panel *m_EditLabel;
+ TextEntry *m_EditPanel;
+ ComboBox *m_pCombo;
+ Button *m_EditButton;
+ char m_szName[64];
+ int m_iType;
+};
+
+class CSmallTextEntry : public TextEntry
+{
+ DECLARE_CLASS_SIMPLE( CSmallTextEntry, TextEntry );
+public:
+
+ CSmallTextEntry( Panel *parent, char const *panelName ) :
+ BaseClass( parent, panelName )
+ {
+ }
+
+ virtual void ApplySchemeSettings( IScheme *scheme )
+ {
+ BaseClass::ApplySchemeSettings( scheme );
+
+ SetFont( scheme->GetFont( "DefaultVerySmall" ) );
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Holds a list of all the edit fields for the currently selected panel
+//-----------------------------------------------------------------------------
+class BuildModeDialog::PanelList
+{
+public:
+
+ CUtlVector<PanelItem_t> m_PanelList;
+
+ void AddItem( Panel *label, TextEntry *edit, ComboBox *combo, Button *button, const char *name, int type )
+ {
+ PanelItem_t item;
+ item.m_EditLabel = label;
+ item.m_EditPanel = edit;
+ Q_strncpy(item.m_szName, name, sizeof(item.m_szName));
+ item.m_iType = type;
+ item.m_pCombo = combo;
+ item.m_EditButton = button;
+
+ m_PanelList.AddToTail( item );
+ }
+
+ void RemoveAll( void )
+ {
+ for ( int i = 0; i < m_PanelList.Size(); i++ )
+ {
+ PanelItem_t *item = &m_PanelList[i];
+ delete item->m_EditLabel;
+ delete item->m_EditPanel;
+ delete item->m_EditButton;
+ }
+
+ m_PanelList.RemoveAll();
+ m_pControls->RemoveAll();
+ }
+
+ KeyValues *m_pResourceData;
+ PanelListPanel *m_pControls;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Dialog for adding localized strings
+//-----------------------------------------------------------------------------
+class BuildModeLocalizedStringEditDialog : public Frame
+{
+ DECLARE_CLASS_SIMPLE(BuildModeLocalizedStringEditDialog, Frame);
+
+public:
+
+#pragma warning( disable : 4355 )
+ BuildModeLocalizedStringEditDialog() : Frame(this, NULL)
+ {
+ m_pTokenEntry = new TextEntry(this, NULL);
+ m_pValueEntry = new TextEntry(this, NULL);
+ m_pFileCombo = new ComboBox(this, NULL, 12, false);
+ m_pOKButton = new Button(this, NULL, "OK");
+ m_pCancelButton = new Button(this, NULL, "Cancel");
+
+ m_pCancelButton->SetCommand("Close");
+ m_pOKButton->SetCommand("OK");
+
+ // add the files to the combo
+ for (int i = 0; i < g_pVGuiLocalize->GetLocalizationFileCount(); i++)
+ {
+ m_pFileCombo->AddItem(g_pVGuiLocalize->GetLocalizationFileName(i), NULL);
+ }
+ }
+#pragma warning( default : 4355 )
+
+ virtual void DoModal(const char *token)
+ {
+ input()->SetAppModalSurface(GetVPanel());
+
+ // setup data
+ m_pTokenEntry->SetText(token);
+
+ // lookup the value
+ StringIndex_t val = g_pVGuiLocalize->FindIndex(token);
+ if (val != INVALID_LOCALIZE_STRING_INDEX)
+ {
+ m_pValueEntry->SetText(g_pVGuiLocalize->GetValueByIndex(val));
+
+ // set the place in the file combo
+ m_pFileCombo->SetText(g_pVGuiLocalize->GetFileNameByIndex(val));
+ }
+ else
+ {
+ m_pValueEntry->SetText("");
+ }
+ }
+
+private:
+ virtual void PerformLayout()
+ {
+ }
+
+ virtual void OnClose()
+ {
+ input()->SetAppModalSurface(NULL);
+ BaseClass::OnClose();
+ //PostActionSignal(new KeyValues("Command"
+ }
+
+ virtual void OnCommand(const char *command)
+ {
+ if (!stricmp(command, "OK"))
+ {
+ //!! apply changes
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+ }
+
+ vgui::TextEntry *m_pTokenEntry;
+ vgui::TextEntry *m_pValueEntry;
+ vgui::ComboBox *m_pFileCombo;
+ vgui::Button *m_pOKButton;
+ vgui::Button *m_pCancelButton;
+};
+
+class CBuildModeDialogMgr
+{
+public:
+
+ void Add( BuildModeDialog *pDlg );
+ void Remove( BuildModeDialog *pDlg );
+
+ int Count() const;
+
+private:
+ CUtlVector< BuildModeDialog * > m_vecBuildDialogs;
+};
+
+static CBuildModeDialogMgr g_BuildModeDialogMgr;
+
+void CBuildModeDialogMgr::Add( BuildModeDialog *pDlg )
+{
+ if ( m_vecBuildDialogs.Find( pDlg ) == m_vecBuildDialogs.InvalidIndex() )
+ {
+ m_vecBuildDialogs.AddToTail( pDlg );
+ }
+}
+
+void CBuildModeDialogMgr::Remove( BuildModeDialog *pDlg )
+{
+ m_vecBuildDialogs.FindAndRemove( pDlg );
+}
+
+int CBuildModeDialogMgr::Count() const
+{
+ return m_vecBuildDialogs.Count();
+}
+
+int GetBuildModeDialogCount()
+{
+ return g_BuildModeDialogMgr.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+BuildModeDialog::BuildModeDialog(BuildGroup *buildGroup) : Frame(buildGroup->GetContextPanel(), "BuildModeDialog")
+{
+ SetMinimumSize(300, 256);
+ SetSize(300, 420);
+ m_pCurrentPanel = NULL;
+ m_pEditableParents = NULL;
+ m_pEditableChildren = NULL;
+ m_pNextChild = NULL;
+ m_pPrevChild = NULL;
+ m_pBuildGroup = buildGroup;
+ _undoSettings = NULL;
+ _copySettings = NULL;
+ _autoUpdate = false;
+ MakePopup();
+ SetTitle("VGUI Build Mode Editor", true);
+
+ CreateControls();
+ LoadUserConfig("BuildModeDialog");
+
+ g_BuildModeDialogMgr.Add( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+BuildModeDialog::~BuildModeDialog()
+{
+ g_BuildModeDialogMgr.Remove( this );
+
+ m_pPanelList->m_pResourceData->deleteThis();
+ m_pPanelList->m_pControls->DeleteAllItems();
+ if (_undoSettings)
+ _undoSettings->deleteThis();
+ if (_copySettings)
+ _copySettings->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: makes sure build mode has been shut down properly
+//-----------------------------------------------------------------------------
+void BuildModeDialog::OnClose()
+{
+ if (m_pBuildGroup->IsEnabled())
+ {
+ m_pBuildGroup->SetEnabled(false);
+ }
+ else
+ {
+ BaseClass::OnClose();
+ MarkForDeletion();
+ }
+}
+
+class CBuildModeNavCombo : public ComboBox
+{
+ DECLARE_CLASS_SIMPLE( CBuildModeNavCombo, ComboBox );
+public:
+
+ CBuildModeNavCombo(Panel *parent, const char *panelName, int numLines, bool allowEdit, bool getParents, Panel *context ) :
+ BaseClass( parent, panelName, numLines, allowEdit ),
+ m_bParents( getParents )
+ {
+ m_hContext = context;
+ }
+
+ virtual void OnShowMenu(Menu *menu)
+ {
+ menu->DeleteAllItems();
+ if ( !m_hContext.Get() )
+ return;
+
+ if ( m_bParents )
+ {
+ Panel *p = m_hContext->GetParent();
+ while ( p )
+ {
+ EditablePanel *ep = dynamic_cast < EditablePanel * >( p );
+ if ( ep && ep->GetBuildGroup() )
+ {
+ KeyValues *kv = new KeyValues( "Panel" );
+ kv->SetPtr( "ptr", p );
+ char const *text = ep->GetName() ? ep->GetName() : "unnamed";
+ menu->AddMenuItem( text, new KeyValues("SetText", "text", text), GetParent(), kv );
+ }
+ p = p->GetParent();
+ }
+ }
+ else
+ {
+ int i;
+ int c = m_hContext->GetChildCount();
+ for ( i = 0; i < c; ++i )
+ {
+ EditablePanel *ep = dynamic_cast < EditablePanel * >( m_hContext->GetChild( i ) );
+ if ( ep && ep->IsVisible() && ep->GetBuildGroup() )
+ {
+ KeyValues *kv = new KeyValues( "Panel" );
+ kv->SetPtr( "ptr", ep );
+ char const *text = ep->GetName() ? ep->GetName() : "unnamed";
+ menu->AddMenuItem( text, new KeyValues("SetText", "text", text), GetParent(), kv );
+ }
+ }
+ }
+ }
+private:
+ bool m_bParents;
+ vgui::PHandle m_hContext;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates the build mode editing controls
+//-----------------------------------------------------------------------------
+void BuildModeDialog::CreateControls()
+{
+ int i;
+ m_pPanelList = new PanelList;
+ m_pPanelList->m_pResourceData = new KeyValues( "BuildDialog" );
+ m_pPanelList->m_pControls = new PanelListPanel(this, "BuildModeControls");
+
+ // file to edit combo box is first
+ m_pFileSelectionCombo = new ComboBox(this, "FileSelectionCombo", 10, false);
+ for ( i = 0; i < m_pBuildGroup->GetRegisteredControlSettingsFileCount(); i++)
+ {
+ m_pFileSelectionCombo->AddItem(m_pBuildGroup->GetRegisteredControlSettingsFileByIndex(i), NULL);
+ }
+ if (m_pFileSelectionCombo->GetItemCount() < 2)
+ {
+ m_pFileSelectionCombo->SetEnabled(false);
+ }
+
+ int buttonH = 18;
+
+ // status info at top of dialog
+ m_pStatusLabel = new Label(this, "StatusLabel", "[nothing currently selected]");
+ m_pStatusLabel->SetTextColorState(Label::CS_DULL);
+ m_pStatusLabel->SetTall( buttonH );
+ m_pDivider = new Divider(this, "Divider");
+ // drop-down combo box for adding new controls
+ m_pAddNewControlCombo = new ComboBox(this, NULL, 30, false);
+ m_pAddNewControlCombo->SetSize(116, buttonH);
+ m_pAddNewControlCombo->SetOpenDirection(Menu::DOWN);
+
+ m_pEditableParents = new CBuildModeNavCombo( this, NULL, 15, false, true, m_pBuildGroup->GetContextPanel() );
+ m_pEditableParents->SetSize(116, buttonH);
+ m_pEditableParents->SetOpenDirection(Menu::DOWN);
+
+ m_pEditableChildren = new CBuildModeNavCombo( this, NULL, 15, false, false, m_pBuildGroup->GetContextPanel() );
+ m_pEditableChildren->SetSize(116, buttonH);
+ m_pEditableChildren->SetOpenDirection(Menu::DOWN);
+
+ m_pNextChild = new Button( this, "NextChild", "Next", this );
+ m_pNextChild->SetCommand( new KeyValues( "OnChangeChild", "direction", 1 ) );
+
+ m_pPrevChild = new Button( this, "PrevChild", "Prev", this );
+ m_pPrevChild->SetCommand( new KeyValues( "OnChangeChild", "direction", -1 ) );
+
+ // controls that can be added
+ // this list comes from controls EditablePanel can create by name.
+ int defaultItem = m_pAddNewControlCombo->AddItem("None", NULL);
+
+ CUtlVector< char const * > names;
+ CBuildFactoryHelper::GetFactoryNames( names );
+ // Sort the names
+ CUtlRBTree< char const *, int > sorted( 0, 0, StringLessThan );
+
+ for ( i = 0; i < names.Count(); ++i )
+ {
+ sorted.Insert( names[ i ] );
+ }
+
+ for ( i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) )
+ {
+ m_pAddNewControlCombo->AddItem( sorted[ i ], NULL );
+ }
+
+ m_pAddNewControlCombo->ActivateItem(defaultItem);
+
+ m_pExitButton = new Button(this, "ExitButton", "&Exit");
+ m_pExitButton->SetSize(64, buttonH);
+
+ m_pSaveButton = new Button(this, "SaveButton", "&Save");
+ m_pSaveButton->SetSize(64, buttonH);
+
+ m_pApplyButton = new Button(this, "ApplyButton", "&Apply");
+ m_pApplyButton->SetSize(64, buttonH);
+
+ m_pReloadLocalization = new Button( this, "Localization", "&Reload Localization" );
+ m_pReloadLocalization->SetSize( 100, buttonH );
+
+ m_pExitButton->SetCommand("Exit");
+ m_pSaveButton->SetCommand("Save");
+ m_pApplyButton->SetCommand("Apply");
+ m_pReloadLocalization->SetCommand( new KeyValues( "ReloadLocalization" ) );
+
+ m_pDeleteButton = new Button(this, "DeletePanelButton", "Delete");
+ m_pDeleteButton->SetSize(64, buttonH);
+ m_pDeleteButton->SetCommand("DeletePanel");
+
+ m_pVarsButton = new MenuButton(this, "VarsButton", "Variables");
+ m_pVarsButton->SetSize(72, buttonH);
+ m_pVarsButton->SetOpenDirection(Menu::UP);
+
+ // iterate the vars
+ KeyValues *vars = m_pBuildGroup->GetDialogVariables();
+ if (vars && vars->GetFirstSubKey())
+ {
+ // create the menu
+ m_pVarsButton->SetEnabled(true);
+ Menu *menu = new Menu(m_pVarsButton, "VarsMenu");
+
+ // set all the variables to be copied to the clipboard when selected
+ for (KeyValues *kv = vars->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey())
+ {
+ char buf[32];
+ _snprintf(buf, sizeof(buf), "%%%s%%", kv->GetName());
+ menu->AddMenuItem(kv->GetName(), new KeyValues("SetClipboardText", "text", buf), this);
+ }
+
+ m_pVarsButton->SetMenu(menu);
+ }
+ else
+ {
+ // no variables
+ m_pVarsButton->SetEnabled(false);
+ }
+
+ m_pApplyButton->SetTabPosition(1);
+ m_pPanelList->m_pControls->SetTabPosition(2);
+ m_pVarsButton->SetTabPosition(3);
+ m_pDeleteButton->SetTabPosition(4);
+ m_pAddNewControlCombo->SetTabPosition(5);
+ m_pSaveButton->SetTabPosition(6);
+ m_pExitButton->SetTabPosition(7);
+
+ m_pEditableParents->SetTabPosition( 8 );
+ m_pEditableChildren->SetTabPosition( 9 );
+
+ m_pPrevChild->SetTabPosition( 10 );
+ m_pNextChild->SetTabPosition( 11 );
+
+ m_pReloadLocalization->SetTabPosition( 12 );
+}
+
+void BuildModeDialog::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ HFont font = pScheme->GetFont( "DefaultVerySmall" );
+ m_pStatusLabel->SetFont( font );
+ m_pReloadLocalization->SetFont( font );
+ m_pExitButton->SetFont( font );
+ m_pSaveButton->SetFont( font );
+ m_pApplyButton->SetFont( font );
+ m_pAddNewControlCombo->SetFont( font );
+ m_pEditableParents->SetFont( font );
+ m_pEditableChildren->SetFont( font );
+ m_pDeleteButton->SetFont( font );
+ m_pVarsButton->SetFont( font );
+ m_pPrevChild->SetFont( font );
+ m_pNextChild->SetFont( font );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: lays out controls
+//-----------------------------------------------------------------------------
+void BuildModeDialog::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ // layout parameters
+ const int BORDER_GAP = 16, YGAP_SMALL = 4, YGAP_LARGE = 8, TITLE_HEIGHT = 24, BOTTOM_CONTROLS_HEIGHT = 145, XGAP = 6;
+
+ int wide, tall;
+ GetSize(wide, tall);
+
+ int xpos = BORDER_GAP;
+ int ypos = BORDER_GAP + TITLE_HEIGHT;
+
+ // controls from top down
+ // selection combo
+ m_pFileSelectionCombo->SetBounds(xpos, ypos, wide - (BORDER_GAP * 2), m_pStatusLabel->GetTall());
+ ypos += (m_pStatusLabel->GetTall() + YGAP_SMALL);
+
+ // status
+ m_pStatusLabel->SetBounds(xpos, ypos, wide - (BORDER_GAP * 2), m_pStatusLabel->GetTall());
+ ypos += (m_pStatusLabel->GetTall() + YGAP_SMALL);
+
+ // center control
+ m_pPanelList->m_pControls->SetPos(xpos, ypos);
+ m_pPanelList->m_pControls->SetSize(wide - (BORDER_GAP * 2), tall - (ypos + BOTTOM_CONTROLS_HEIGHT));
+
+ // controls from bottom-right
+ ypos = tall - BORDER_GAP;
+ xpos = BORDER_GAP + m_pVarsButton->GetWide() + m_pDeleteButton->GetWide() + m_pAddNewControlCombo->GetWide() + (XGAP * 2);
+
+ // bottom row of buttons
+ ypos -= m_pApplyButton->GetTall();
+ xpos -= m_pApplyButton->GetWide();
+ m_pApplyButton->SetPos(xpos, ypos);
+
+ xpos -= m_pExitButton->GetWide();
+ xpos -= XGAP;
+ m_pExitButton->SetPos(xpos, ypos);
+
+ xpos -= m_pSaveButton->GetWide();
+ xpos -= XGAP;
+ m_pSaveButton->SetPos(xpos, ypos);
+
+ // divider
+ xpos = BORDER_GAP;
+ ypos -= (YGAP_LARGE + m_pDivider->GetTall());
+ m_pDivider->SetBounds(xpos, ypos, wide - (xpos + BORDER_GAP), 2);
+
+ ypos -= (YGAP_LARGE + m_pVarsButton->GetTall());
+
+ xpos = BORDER_GAP;
+ m_pEditableParents->SetPos( xpos, ypos );
+ m_pEditableChildren->SetPos( xpos + 150, ypos );
+
+ ypos -= (YGAP_LARGE + 18 );
+ xpos = BORDER_GAP;
+ m_pReloadLocalization->SetPos( xpos, ypos );
+
+ xpos += ( XGAP ) + m_pReloadLocalization->GetWide();
+
+ m_pPrevChild->SetPos( xpos, ypos );
+ m_pPrevChild->SetSize( 64, m_pReloadLocalization->GetTall() );
+ xpos += ( XGAP ) + m_pPrevChild->GetWide();
+
+ m_pNextChild->SetPos( xpos, ypos );
+ m_pNextChild->SetSize( 64, m_pReloadLocalization->GetTall() );
+
+ ypos -= (YGAP_LARGE + m_pVarsButton->GetTall());
+ xpos = BORDER_GAP;
+
+ // edit buttons
+ m_pVarsButton->SetPos(xpos, ypos);
+ xpos += (XGAP + m_pVarsButton->GetWide());
+ m_pDeleteButton->SetPos(xpos, ypos);
+ xpos += (XGAP + m_pDeleteButton->GetWide());
+ m_pAddNewControlCombo->SetPos(xpos, ypos);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Deletes all the controls from the panel
+//-----------------------------------------------------------------------------
+void BuildModeDialog::RemoveAllControls( void )
+{
+ // free the array
+ m_pPanelList->RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: simple helper function to get a token from a string
+// Input : char **string - pointer to the string pointer, which will be incremented
+// Output : const char * - pointer to the token
+//-----------------------------------------------------------------------------
+const char *ParseTokenFromString( const char **string )
+{
+ static char buf[128];
+ buf[0] = 0;
+
+ // find the first alnum character
+ const char *tok = *string;
+ while ( !V_isalnum(*tok) && *tok != 0 )
+ {
+ tok++;
+ }
+
+ // read in all the alnum characters
+ int pos = 0;
+ while ( V_isalnum(tok[pos]) )
+ {
+ buf[pos] = tok[pos];
+ pos++;
+ }
+
+ // null terminate the token
+ buf[pos] = 0;
+
+ // update the main string pointer
+ *string = &(tok[pos]);
+
+ // return a pointer to the static buffer
+ return buf;
+}
+
+void BuildModeDialog::OnTextKillFocus()
+{
+ if ( !m_pCurrentPanel )
+ return;
+
+ ApplyDataToControls();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: sets up the current control to edit
+//-----------------------------------------------------------------------------
+void BuildModeDialog::SetActiveControl(Panel *controlToEdit)
+{
+ if (m_pCurrentPanel == controlToEdit)
+ {
+ // it's already set, so just update the property data and quit
+ if (m_pCurrentPanel)
+ {
+ UpdateControlData(m_pCurrentPanel);
+ }
+ return;
+ }
+
+ // reset the data
+ m_pCurrentPanel = controlToEdit;
+ RemoveAllControls();
+ m_pPanelList->m_pControls->MoveScrollBarToTop();
+
+ if (!m_pCurrentPanel)
+ {
+ m_pStatusLabel->SetText("[nothing currently selected]");
+ m_pStatusLabel->SetTextColorState(Label::CS_DULL);
+ RemoveAllControls();
+ return;
+ }
+
+ // get the control description string
+ const char *controlDesc = m_pCurrentPanel->GetDescription();
+
+ // parse out the control description
+ int tabPosition = 1;
+ while (1)
+ {
+ const char *dataType = ParseTokenFromString(&controlDesc);
+
+ // finish when we have no more tokens
+ if (*dataType == 0)
+ break;
+
+ // default the data type to a string
+ int datat = TYPE_STRING;
+
+ if (!stricmp(dataType, "int"))
+ {
+ datat = TYPE_STRING; //!! just for now
+ }
+ else if (!stricmp(dataType, "alignment"))
+ {
+ datat = TYPE_ALIGNMENT;
+ }
+ else if (!stricmp(dataType, "autoresize"))
+ {
+ datat = TYPE_AUTORESIZE;
+ }
+ else if (!stricmp(dataType, "corner"))
+ {
+ datat = TYPE_CORNER;
+ }
+ else if (!stricmp(dataType, "localize"))
+ {
+ datat = TYPE_LOCALIZEDSTRING;
+ }
+
+ // get the field name
+ const char *fieldName = ParseTokenFromString(&controlDesc);
+
+ int itemHeight = 18;
+
+ // build a control & label
+ Label *label = new Label(this, NULL, fieldName);
+ label->SetSize(96, itemHeight);
+ label->SetContentAlignment(Label::a_east);
+
+ TextEntry *edit = NULL;
+ ComboBox *editCombo = NULL;
+ Button *editButton = NULL;
+ if (datat == TYPE_ALIGNMENT)
+ {
+ // drop-down combo box
+ editCombo = new ComboBox(this, NULL, 9, false);
+ editCombo->AddItem("north-west", NULL);
+ editCombo->AddItem("north", NULL);
+ editCombo->AddItem("north-east", NULL);
+ editCombo->AddItem("west", NULL);
+ editCombo->AddItem("center", NULL);
+ editCombo->AddItem("east", NULL);
+ editCombo->AddItem("south-west", NULL);
+ editCombo->AddItem("south", NULL);
+ editCombo->AddItem("south-east", NULL);
+
+ edit = editCombo;
+ }
+ else if (datat == TYPE_AUTORESIZE)
+ {
+ // drop-down combo box
+ editCombo = new ComboBox(this, NULL, 4, false);
+ editCombo->AddItem( "0 - no auto-resize", NULL);
+ editCombo->AddItem( "1 - resize right", NULL);
+ editCombo->AddItem( "2 - resize down", NULL);
+ editCombo->AddItem( "3 - down & right", NULL);
+
+ edit = editCombo;
+ }
+ else if (datat == TYPE_CORNER)
+ {
+ // drop-down combo box
+ editCombo = new ComboBox(this, NULL, 4, false);
+ editCombo->AddItem("0 - top-left", NULL);
+ editCombo->AddItem("1 - top-right", NULL);
+ editCombo->AddItem("2 - bottom-left", NULL);
+ editCombo->AddItem("3 - bottom-right", NULL);
+
+ edit = editCombo;
+ }
+ else if (datat == TYPE_LOCALIZEDSTRING)
+ {
+ editButton = new Button(this, NULL, "...");
+ editButton->SetParent(this);
+ editButton->AddActionSignalTarget(this);
+ editButton->SetTabPosition(tabPosition++);
+ editButton->SetTall( itemHeight );
+ label->SetAssociatedControl(editButton);
+ }
+ else
+ {
+ // normal string edit
+ edit = new CSmallTextEntry(this, NULL);
+ }
+
+ if (edit)
+ {
+ edit->SetTall( itemHeight );
+ edit->SetParent(this);
+ edit->AddActionSignalTarget(this);
+ edit->SetTabPosition(tabPosition++);
+ label->SetAssociatedControl(edit);
+ }
+
+ HFont smallFont = scheme()->GetIScheme( GetScheme() )->GetFont( "DefaultVerySmall" );
+
+ if ( label )
+ {
+ label->SetFont( smallFont );
+ }
+ if ( edit )
+ {
+ edit->SetFont( smallFont );
+ }
+ if ( editCombo )
+ {
+ editCombo->SetFont( smallFont );
+ }
+ if ( editButton )
+ {
+ editButton->SetFont( smallFont );
+ }
+
+ // add to our control list
+ m_pPanelList->AddItem(label, edit, editCombo, editButton, fieldName, datat);
+
+ if ( edit )
+ {
+ m_pPanelList->m_pControls->AddItem(label, edit);
+ }
+ else
+ {
+ m_pPanelList->m_pControls->AddItem(label, editButton);
+ }
+ }
+
+ // check and see if the current panel is a Label
+ // iterate through the class hierarchy
+ if ( controlToEdit->IsBuildModeDeletable() )
+ {
+ m_pDeleteButton->SetEnabled(true);
+ }
+ else
+ {
+ m_pDeleteButton->SetEnabled(false);
+ }
+
+ // update the property data in the dialog
+ UpdateControlData(m_pCurrentPanel);
+
+ // set our title
+ if ( m_pBuildGroup->GetResourceName() )
+ {
+ m_pFileSelectionCombo->SetText(m_pBuildGroup->GetResourceName());
+ }
+ else
+ {
+ m_pFileSelectionCombo->SetText("[ no resource file associated with dialog ]");
+ }
+
+ m_pApplyButton->SetEnabled(false);
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the edit fields with information about the control
+//-----------------------------------------------------------------------------
+void BuildModeDialog::UpdateControlData(Panel *control)
+{
+ KeyValues *dat = m_pPanelList->m_pResourceData->FindKey( control->GetName(), true );
+ control->GetSettings( dat );
+
+ // apply the settings to the edit panels
+ for ( int i = 0; i < m_pPanelList->m_PanelList.Size(); i++ )
+ {
+ const char *name = m_pPanelList->m_PanelList[i].m_szName;
+ const char *datstring = dat->GetString( name, "" );
+
+ UpdateEditControl(m_pPanelList->m_PanelList[i], datstring);
+ }
+
+ char statusText[512];
+ Q_snprintf(statusText, sizeof(statusText), "%s: \'%s\'", control->GetClassName(), control->GetName());
+ m_pStatusLabel->SetText(statusText);
+ m_pStatusLabel->SetTextColorState(Label::CS_NORMAL);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the data in a single edit control
+//-----------------------------------------------------------------------------
+void BuildModeDialog::UpdateEditControl(PanelItem_t &panelItem, const char *datstring)
+{
+ switch (panelItem.m_iType)
+ {
+ case TYPE_AUTORESIZE:
+ case TYPE_CORNER:
+ {
+ int dat = atoi(datstring);
+ panelItem.m_pCombo->ActivateItemByRow(dat);
+ }
+ break;
+
+ case TYPE_LOCALIZEDSTRING:
+ {
+ panelItem.m_EditButton->SetText(datstring);
+ }
+ break;
+
+ default:
+ {
+ wchar_t unicode[512];
+ g_pVGuiLocalize->ConvertANSIToUnicode(datstring, unicode, sizeof(unicode));
+ panelItem.m_EditPanel->SetText(unicode);
+ }
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when one of the buttons is pressed
+//-----------------------------------------------------------------------------
+void BuildModeDialog::OnCommand(const char *command)
+{
+ if (!stricmp(command, "Save"))
+ {
+ // apply the current data and save it to disk
+ ApplyDataToControls();
+ if (m_pBuildGroup->SaveControlSettings())
+ {
+ // disable save button until another change has been made
+ m_pSaveButton->SetEnabled(false);
+ }
+ }
+ else if (!stricmp(command, "Exit"))
+ {
+ // exit build mode
+ ExitBuildMode();
+ }
+ else if (!stricmp(command, "Apply"))
+ {
+ // apply data to controls
+ ApplyDataToControls();
+ }
+ else if (!stricmp(command, "DeletePanel"))
+ {
+ OnDeletePanel();
+ }
+ else if (!stricmp(command, "RevertToSaved"))
+ {
+ RevertToSaved();
+ }
+ else if (!stricmp(command, "ShowHelp"))
+ {
+ ShowHelp();
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Deletes a panel from the buildgroup
+//-----------------------------------------------------------------------------
+void BuildModeDialog::OnDeletePanel()
+{
+ if (!m_pCurrentPanel->IsBuildModeEditable())
+ {
+ return;
+ }
+
+ m_pBuildGroup->RemoveSettings();
+ SetActiveControl(m_pBuildGroup->GetCurrentPanel());
+
+ _undoSettings->deleteThis();
+ _undoSettings = NULL;
+ m_pSaveButton->SetEnabled(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies the current settings to the build controls
+//-----------------------------------------------------------------------------
+void BuildModeDialog::ApplyDataToControls()
+{
+ // don't apply if the panel is not editable
+ if ( !m_pCurrentPanel->IsBuildModeEditable())
+ {
+ UpdateControlData( m_pCurrentPanel );
+ return; // return success, since we are behaving as expected.
+ }
+
+ char fieldName[512];
+ if (m_pPanelList->m_PanelList[0].m_EditPanel)
+ {
+ m_pPanelList->m_PanelList[0].m_EditPanel->GetText(fieldName, sizeof(fieldName));
+ }
+ else
+ {
+ m_pPanelList->m_PanelList[0].m_EditButton->GetText(fieldName, sizeof(fieldName));
+ }
+
+ // check to see if any buildgroup panels have this name
+ Panel *panel = m_pBuildGroup->FieldNameTaken(fieldName);
+ if (panel)
+ {
+ if (panel != m_pCurrentPanel)// make sure name is taken by some other panel not this one
+ {
+ char messageString[255];
+ Q_snprintf(messageString, sizeof( messageString ), "Fieldname is not unique: %s\nRename it and try again.", fieldName);
+ MessageBox *errorBox = new MessageBox("Cannot Apply", messageString);
+ errorBox->DoModal();
+ UpdateControlData(m_pCurrentPanel);
+ m_pApplyButton->SetEnabled(false);
+ return;
+ }
+ }
+
+ // create a section to store settings
+ // m_pPanelList->m_pResourceData->getSection( m_pCurrentPanel->GetName(), true );
+ KeyValues *dat = new KeyValues( m_pCurrentPanel->GetName() );
+
+ // loop through the textedit filling in settings
+ for ( int i = 0; i < m_pPanelList->m_PanelList.Size(); i++ )
+ {
+ const char *name = m_pPanelList->m_PanelList[i].m_szName;
+ char buf[512];
+ if (m_pPanelList->m_PanelList[i].m_EditPanel)
+ {
+ m_pPanelList->m_PanelList[i].m_EditPanel->GetText(buf, sizeof(buf));
+ }
+ else
+ {
+ m_pPanelList->m_PanelList[i].m_EditButton->GetText(buf, sizeof(buf));
+ }
+
+ switch (m_pPanelList->m_PanelList[i].m_iType)
+ {
+ case TYPE_CORNER:
+ case TYPE_AUTORESIZE:
+ // the integer value is assumed to be the first part of the string for these items
+ dat->SetInt(name, atoi(buf));
+ break;
+
+ default:
+ dat->SetString(name, buf);
+ break;
+ }
+ }
+
+ // dat is built, hand it back to the control
+ m_pCurrentPanel->ApplySettings( dat );
+
+ if ( m_pBuildGroup->GetContextPanel() )
+ {
+ m_pBuildGroup->GetContextPanel()->Repaint();
+ }
+
+ m_pApplyButton->SetEnabled(false);
+ m_pSaveButton->SetEnabled(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Store the settings of the current panel in a KeyValues
+//-----------------------------------------------------------------------------
+void BuildModeDialog::StoreUndoSettings()
+{
+ // don't save if the planel is not editable
+ if ( !m_pCurrentPanel->IsBuildModeEditable())
+ {
+ if (_undoSettings)
+ _undoSettings->deleteThis();
+ _undoSettings = NULL;
+ return;
+ }
+
+ if (_undoSettings)
+ {
+ _undoSettings->deleteThis();
+ _undoSettings = NULL;
+ }
+
+ _undoSettings = StoreSettings();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Revert to the stored the settings of the current panel in a keyValues
+//-----------------------------------------------------------------------------
+void BuildModeDialog::DoUndo()
+{
+ if ( _undoSettings )
+ {
+ m_pCurrentPanel->ApplySettings( _undoSettings );
+ UpdateControlData(m_pCurrentPanel);
+ _undoSettings->deleteThis();
+ _undoSettings = NULL;
+ }
+
+ m_pSaveButton->SetEnabled(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Copy the settings of the current panel into a keyValues
+//-----------------------------------------------------------------------------
+void BuildModeDialog::DoCopy()
+{
+ if (_copySettings)
+ {
+ _copySettings->deleteThis();
+ _copySettings = NULL;
+ }
+
+ _copySettings = StoreSettings();
+ Q_strncpy (_copyClassName, m_pCurrentPanel->GetClassName(), sizeof( _copyClassName ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a new Panel with the _copySettings applied
+//-----------------------------------------------------------------------------
+void BuildModeDialog::DoPaste()
+{
+ // Make a new control located where you had the mouse
+ int x, y;
+ input()->GetCursorPos(x, y);
+ m_pBuildGroup->GetContextPanel()->ScreenToLocal(x,y);
+
+ Panel *newPanel = OnNewControl(_copyClassName, x, y);
+ if (newPanel)
+ {
+ newPanel->ApplySettings(_copySettings);
+ newPanel->SetPos(x, y);
+ char name[255];
+ m_pBuildGroup->GetNewFieldName(name, sizeof(name), newPanel);
+ newPanel->SetName(name);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Store the settings of the current panel in a keyValues
+//-----------------------------------------------------------------------------
+KeyValues *BuildModeDialog::StoreSettings()
+{
+ KeyValues *storedSettings;
+ storedSettings = new KeyValues( m_pCurrentPanel->GetName() );
+
+ // loop through the textedit filling in settings
+ for ( int i = 0; i < m_pPanelList->m_PanelList.Size(); i++ )
+ {
+ const char *name = m_pPanelList->m_PanelList[i].m_szName;
+ char buf[512];
+ if (m_pPanelList->m_PanelList[i].m_EditPanel)
+ {
+ m_pPanelList->m_PanelList[i].m_EditPanel->GetText(buf, sizeof(buf));
+ }
+ else
+ {
+ m_pPanelList->m_PanelList[i].m_EditButton->GetText(buf, sizeof(buf));
+ }
+
+ switch (m_pPanelList->m_PanelList[i].m_iType)
+ {
+ case TYPE_CORNER:
+ case TYPE_AUTORESIZE:
+ // the integer value is assumed to be the first part of the string for these items
+ storedSettings->SetInt(name, atoi(buf));
+ break;
+
+ default:
+ storedSettings->SetString(name, buf);
+ break;
+ }
+ }
+
+ return storedSettings;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void BuildModeDialog::OnKeyCodeTyped(KeyCode code)
+{
+ if (code == KEY_ENTER) // if someone hits return apply the changes
+ {
+ ApplyDataToControls();
+ }
+ else
+ {
+ Frame::OnKeyCodeTyped(code);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks to see if any text has changed
+//-----------------------------------------------------------------------------
+void BuildModeDialog::OnTextChanged( Panel *panel )
+{
+ if (panel == m_pFileSelectionCombo)
+ {
+ // reload file if it's changed
+ char newFile[512];
+ m_pFileSelectionCombo->GetText(newFile, sizeof(newFile));
+
+ if (stricmp(newFile, m_pBuildGroup->GetResourceName()) != 0)
+ {
+ // file has changed, reload
+ SetActiveControl(NULL);
+ m_pBuildGroup->ChangeControlSettingsFile(newFile);
+ }
+ return;
+ }
+
+ if (panel == m_pAddNewControlCombo)
+ {
+ char buf[40];
+ m_pAddNewControlCombo->GetText(buf, 40);
+ if (stricmp(buf, "None") != 0)
+ {
+ OnNewControl(buf);
+ // reset box back to None
+ m_pAddNewControlCombo->ActivateItemByRow( 0 );
+ }
+ }
+
+ if ( panel == m_pEditableChildren )
+ {
+ KeyValues *kv = m_pEditableChildren->GetActiveItemUserData();
+ if ( kv )
+ {
+ EditablePanel *ep = reinterpret_cast< EditablePanel * >( kv->GetPtr( "ptr" ) );
+ if ( ep )
+ {
+ ep->ActivateBuildMode();
+ }
+ }
+ }
+
+ if ( panel == m_pEditableParents )
+ {
+ KeyValues *kv = m_pEditableParents->GetActiveItemUserData();
+ if ( kv )
+ {
+ EditablePanel *ep = reinterpret_cast< EditablePanel * >( kv->GetPtr( "ptr" ) );
+ if ( ep )
+ {
+ ep->ActivateBuildMode();
+ }
+ }
+ }
+
+ if (m_pCurrentPanel && m_pCurrentPanel->IsBuildModeEditable())
+ {
+ m_pApplyButton->SetEnabled(true);
+ }
+
+ if (_autoUpdate)
+ {
+ ApplyDataToControls();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void BuildModeDialog::ExitBuildMode( void )
+{
+ // make sure rulers are off
+ if (m_pBuildGroup->HasRulersOn())
+ {
+ m_pBuildGroup->ToggleRulerDisplay();
+ }
+ m_pBuildGroup->SetEnabled(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a new control in the context panel
+//-----------------------------------------------------------------------------
+Panel *BuildModeDialog::OnNewControl( const char *name, int x, int y)
+{
+ // returns NULL on failure
+ Panel *newPanel = m_pBuildGroup->NewControl(name, x, y);
+ if (newPanel)
+ {
+ // call mouse commands to simulate selecting the new
+ // panel. This will set everything up correctly in the buildGroup.
+ m_pBuildGroup->MousePressed(MOUSE_LEFT, newPanel);
+ m_pBuildGroup->MouseReleased(MOUSE_LEFT, newPanel);
+ }
+
+ m_pSaveButton->SetEnabled(true);
+
+ return newPanel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: enable the save button, useful when buildgroup needs to Activate it.
+//-----------------------------------------------------------------------------
+void BuildModeDialog::EnableSaveButton()
+{
+ m_pSaveButton->SetEnabled(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Revert to the saved settings in the .res file
+//-----------------------------------------------------------------------------
+void BuildModeDialog::RevertToSaved()
+{
+ // hide the dialog as reloading will destroy it
+ surface()->SetPanelVisible(this->GetVPanel(), false);
+ m_pBuildGroup->ReloadControlSettings();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Display some information about the editor
+//-----------------------------------------------------------------------------
+void BuildModeDialog::ShowHelp()
+{
+ char helpText[]= "In the Build Mode Dialog Window:\n"
+ "Delete button - deletes the currently selected panel if it is deletable.\n"
+ "Apply button - applies changes to the Context Panel.\n"
+ "Save button - saves all settings to file. \n"
+ "Revert to saved- reloads the last saved file.\n"
+ "Auto Update - any changes apply instantly.\n"
+ "Typing Enter in any text field applies changes.\n"
+ "New Control menu - creates a new panel in the upper left corner.\n\n"
+ "In the Context Panel:\n"
+ "After selecting and moving a panel Ctrl-z will undo the move.\n"
+ "Shift clicking panels allows multiple panels to be selected into a group.\n"
+ "Ctrl-c copies the settings of the last selected panel.\n"
+ "Ctrl-v creates a new panel with the copied settings at the location of the mouse pointer.\n"
+ "Arrow keys slowly move panels, holding shift + arrow will slowly resize it.\n"
+ "Holding right mouse button down opens a dropdown panel creation menu.\n"
+ " Panel will be created where the menu was opened.\n"
+ "Delete key deletes the currently selected panel if it is deletable.\n"
+ " Does nothing to multiple selections.";
+
+ MessageBox *helpDlg = new MessageBox ("Build Mode Help", helpText, this);
+ helpDlg->AddActionSignalTarget(this);
+ helpDlg->DoModal();
+}
+
+
+void BuildModeDialog::ShutdownBuildMode()
+{
+ m_pBuildGroup->SetEnabled(false);
+}
+
+void BuildModeDialog::OnPanelMoved()
+{
+ m_pApplyButton->SetEnabled(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: message handles thats sets the text in the clipboard
+//-----------------------------------------------------------------------------
+void BuildModeDialog::OnSetClipboardText(const char *text)
+{
+ system()->SetClipboardText(text, strlen(text));
+}
+
+void BuildModeDialog::OnCreateNewControl( char const *text )
+{
+ if ( !Q_stricmp( text, "None" ) )
+ return;
+
+ OnNewControl( text, m_nClick[ 0 ], m_nClick[ 1 ] );
+}
+
+void BuildModeDialog::OnShowNewControlMenu()
+{
+ if ( !m_pBuildGroup )
+ return;
+
+ int i;
+
+ input()->GetCursorPos( m_nClick[ 0 ], m_nClick[ 1 ] );
+ m_pBuildGroup->GetContextPanel()->ScreenToLocal( m_nClick[ 0 ], m_nClick[ 1 ] );
+
+ if ( m_hContextMenu )
+ delete m_hContextMenu.Get();
+
+ m_hContextMenu = new Menu( this, "NewControls" );
+
+ // Show popup menu
+ m_hContextMenu->AddMenuItem( "None", "None", new KeyValues( "CreateNewControl", "text", "None" ), this );
+
+ CUtlVector< char const * > names;
+ CBuildFactoryHelper::GetFactoryNames( names );
+ // Sort the names
+ CUtlRBTree< char const *, int > sorted( 0, 0, StringLessThan );
+
+ for ( i = 0; i < names.Count(); ++i )
+ {
+ sorted.Insert( names[ i ] );
+ }
+
+ for ( i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) )
+ {
+ m_hContextMenu->AddMenuItem( sorted[ i ], sorted[ i ], new KeyValues( "CreateNewControl", "text", sorted[ i ] ), this );
+ }
+
+ Menu::PlaceContextMenu( this, m_hContextMenu );
+}
+
+void BuildModeDialog::OnReloadLocalization()
+{
+ // reload localization files
+ g_pVGuiLocalize->ReloadLocalizationFiles( );
+}
+
+bool BuildModeDialog::IsBuildGroupEnabled()
+{
+ // Don't ever edit the actual build dialog!!!
+ return false;
+}
+
+void BuildModeDialog::OnChangeChild( int direction )
+{
+ Assert( direction == 1 || direction == -1 );
+ if ( !m_pBuildGroup )
+ return;
+
+ Panel *current = m_pCurrentPanel;
+ Panel *context = m_pBuildGroup->GetContextPanel();
+
+ if ( !current || current == context )
+ {
+ current = NULL;
+ if ( context->GetChildCount() > 0 )
+ {
+ current = context->GetChild( 0 );
+ }
+ }
+ else
+ {
+ int i;
+ // Move in direction requested
+ int children = context->GetChildCount();
+ for ( i = 0; i < children; ++i )
+ {
+ Panel *child = context->GetChild( i );
+ if ( child == current )
+ {
+ break;
+ }
+ }
+
+ if ( i < children )
+ {
+ for ( int offset = 1; offset < children; ++offset )
+ {
+ int test = ( i + ( direction * offset ) ) % children;
+ if ( test < 0 )
+ test += children;
+ if ( test == i )
+ continue;
+
+ Panel *check = context->GetChild( test );
+ BuildModeDialog *bm = dynamic_cast< BuildModeDialog * >( check );
+ if ( bm )
+ continue;
+
+ current = check;
+ break;
+ }
+ }
+ }
+
+ if ( !current )
+ {
+ return;
+ }
+
+ SetActiveControl( current );
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/Button.cpp b/mp/src/vgui2/vgui_controls/Button.cpp
new file mode 100644
index 00000000..2a70e8fa
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/Button.cpp
@@ -0,0 +1,1095 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Basic button control
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdio.h>
+#include <utlsymbol.h>
+
+#include <vgui/IBorder.h>
+#include <vgui/IInput.h>
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <vgui/ISystem.h>
+#include <vgui/IVGui.h>
+#include <vgui/MouseCode.h>
+#include <vgui/KeyCode.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/Button.h>
+#include <vgui_controls/FocusNavGroup.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+// global list of all the names of all the sounds played by buttons
+CUtlSymbolTable g_ButtonSoundNames;
+
+DECLARE_BUILD_FACTORY_DEFAULT_TEXT( Button, Button );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+Button::Button(Panel *parent, const char *panelName, const char *text, Panel *pActionSignalTarget, const char *pCmd ) : Label(parent, panelName, text)
+{
+ Init();
+ if ( pActionSignalTarget && pCmd )
+ {
+ AddActionSignalTarget( pActionSignalTarget );
+ SetCommand( pCmd );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+Button::Button(Panel *parent, const char *panelName, const wchar_t *wszText, Panel *pActionSignalTarget, const char *pCmd ) : Label(parent, panelName, wszText)
+{
+ Init();
+ if ( pActionSignalTarget && pCmd )
+ {
+ AddActionSignalTarget( pActionSignalTarget );
+ SetCommand( pCmd );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::Init()
+{
+ _buttonFlags.SetFlag( USE_CAPTURE_MOUSE | BUTTON_BORDER_ENABLED );
+
+ _mouseClickMask = 0;
+ _actionMessage = NULL;
+ _defaultBorder = NULL;
+ _depressedBorder = NULL;
+ _keyFocusBorder = NULL;
+ m_bSelectionStateSaved = false;
+ m_bStaySelectedOnClick = false;
+ m_sArmedSoundName = UTL_INVAL_SYMBOL;
+ m_sDepressedSoundName = UTL_INVAL_SYMBOL;
+ m_sReleasedSoundName = UTL_INVAL_SYMBOL;
+ SetTextInset(6, 0);
+ SetMouseClickEnabled( MOUSE_LEFT, true );
+ SetButtonActivationType(ACTIVATE_ONPRESSEDANDRELEASED);
+
+ // labels have this off by default, but we need it on
+ SetPaintBackgroundEnabled( true );
+
+ _paint = true;
+
+ REGISTER_COLOR_AS_OVERRIDABLE( _defaultFgColor, "defaultFgColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _defaultBgColor, "defaultBgColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _armedFgColor, "armedFgColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _armedBgColor, "armedBgColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _depressedFgColor, "depressedFgColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _depressedBgColor, "depressedBgColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _selectedFgColor, "selectedFgColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _selectedBgColor, "selectedBgColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _keyboardFocusColor, "keyboardFocusColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _blinkFgColor, "blinkFgColor_override" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+Button::~Button()
+{
+ if (_actionMessage)
+ {
+ _actionMessage->deleteThis();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::SetButtonActivationType(ActivationType_t activationType)
+{
+ _activationType = activationType;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set button border attribute enabled.
+//-----------------------------------------------------------------------------
+void Button::SetButtonBorderEnabled( bool state )
+{
+ if ( state != _buttonFlags.IsFlagSet( BUTTON_BORDER_ENABLED ) )
+ {
+ _buttonFlags.SetFlag( BUTTON_BORDER_ENABLED, state );
+ InvalidateLayout(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set button selected state.
+//-----------------------------------------------------------------------------
+void Button::SetSelected( bool state )
+{
+ if ( _buttonFlags.IsFlagSet( SELECTED ) != state )
+ {
+ _buttonFlags.SetFlag( SELECTED, state );
+ RecalculateDepressedState();
+ InvalidateLayout(false);
+ }
+
+ if ( state && _buttonFlags.IsFlagSet( ARMED ) )
+ {
+ _buttonFlags.SetFlag( ARMED, false );
+ InvalidateLayout(false);
+ }
+}
+
+void Button::SetBlink( bool state )
+{
+ if ( _buttonFlags.IsFlagSet( BLINK ) != state )
+ {
+ _buttonFlags.SetFlag( BLINK, state );
+ RecalculateDepressedState();
+ InvalidateLayout(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set button force depressed state.
+//-----------------------------------------------------------------------------
+void Button::ForceDepressed(bool state)
+{
+ if ( _buttonFlags.IsFlagSet( FORCE_DEPRESSED ) != state )
+ {
+ _buttonFlags.SetFlag( FORCE_DEPRESSED, state );
+ RecalculateDepressedState();
+ InvalidateLayout(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set button depressed state with respect to the force depressed state.
+//-----------------------------------------------------------------------------
+void Button::RecalculateDepressedState( void )
+{
+ bool newState;
+ if (!IsEnabled())
+ {
+ newState = false;
+ }
+ else
+ {
+ if ( m_bStaySelectedOnClick && _buttonFlags.IsFlagSet( SELECTED ) )
+ {
+ newState = false;
+ }
+ else
+ {
+ newState = _buttonFlags.IsFlagSet( FORCE_DEPRESSED ) ? true : (_buttonFlags.IsFlagSet(ARMED) && _buttonFlags.IsFlagSet( SELECTED ) );
+ }
+ }
+
+ _buttonFlags.SetFlag( DEPRESSED, newState );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets whether or not the button captures all mouse input when depressed
+// Defaults to true
+// Should be set to false for things like menu items where there is a higher-level mouse capture
+//-----------------------------------------------------------------------------
+void Button::SetUseCaptureMouse( bool state )
+{
+ _buttonFlags.SetFlag( USE_CAPTURE_MOUSE, state );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if mouse capture is enabled.
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Button::IsUseCaptureMouseEnabled( void )
+{
+ return _buttonFlags.IsFlagSet( USE_CAPTURE_MOUSE );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set armed state.
+//-----------------------------------------------------------------------------
+void Button::SetArmed(bool state)
+{
+ if ( _buttonFlags.IsFlagSet( ARMED ) != state )
+ {
+ _buttonFlags.SetFlag( ARMED, state );
+ RecalculateDepressedState();
+ InvalidateLayout(false);
+
+ // play any sounds specified
+ if (state && m_sArmedSoundName != UTL_INVAL_SYMBOL)
+ {
+ surface()->PlaySound(g_ButtonSoundNames.String(m_sArmedSoundName));
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check armed state
+//-----------------------------------------------------------------------------
+bool Button::IsArmed()
+{
+ return _buttonFlags.IsFlagSet( ARMED );
+}
+
+
+KeyValues *Button::GetActionMessage()
+{
+ return _actionMessage->MakeCopy();
+}
+
+void Button::PlayButtonReleasedSound()
+{
+ // check for playing a transition sound
+ if ( m_sReleasedSoundName != UTL_INVAL_SYMBOL )
+ {
+ surface()->PlaySound( g_ButtonSoundNames.String( m_sReleasedSoundName ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activate a button click.
+//-----------------------------------------------------------------------------
+void Button::DoClick()
+{
+ SetSelected(true);
+ FireActionSignal();
+ PlayButtonReleasedSound();
+
+ static ConVarRef vgui_nav_lock( "vgui_nav_lock" );
+ if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateActivate() )
+ {
+ vgui_nav_lock.SetValue( 1 );
+ }
+
+ if ( !m_bStaySelectedOnClick )
+ {
+ SetSelected(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check selected state
+//-----------------------------------------------------------------------------
+bool Button::IsSelected()
+{
+ return _buttonFlags.IsFlagSet( SELECTED );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check depressed state
+//-----------------------------------------------------------------------------
+bool Button::IsDepressed()
+{
+ return _buttonFlags.IsFlagSet( DEPRESSED );
+}
+
+bool Button::IsBlinking( void )
+{
+ return _buttonFlags.IsFlagSet( BLINK );
+}
+
+
+//-----------------------------------------------------------------------------
+// Drawing focus box?
+//-----------------------------------------------------------------------------
+bool Button::IsDrawingFocusBox()
+{
+ return _buttonFlags.IsFlagSet( DRAW_FOCUS_BOX );
+}
+
+void Button::DrawFocusBox( bool bEnable )
+{
+ _buttonFlags.SetFlag( DRAW_FOCUS_BOX, bEnable );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::NavigateTo()
+{
+ BaseClass::NavigateTo();
+
+ SetArmed( true );
+
+ if ( IsPC() )
+ {
+ RequestFocus( 0 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::NavigateFrom()
+{
+ BaseClass::NavigateFrom();
+
+ SetArmed( false );
+
+ OnKeyCodeReleased( KEY_XBUTTON_A );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Paint button on screen
+//-----------------------------------------------------------------------------
+void Button::Paint(void)
+{
+ if ( !ShouldPaint() )
+ return;
+
+ BaseClass::Paint();
+
+ if ( HasFocus() && IsEnabled() && IsDrawingFocusBox() )
+ {
+ int x0, y0, x1, y1;
+ int wide, tall;
+ GetSize(wide, tall);
+ x0 = 3, y0 = 3, x1 = wide - 4 , y1 = tall - 2;
+ DrawFocusBorder(x0, y0, x1, y1);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Perform graphical layout of button.
+//-----------------------------------------------------------------------------
+void Button::PerformLayout()
+{
+ // reset our border
+ SetBorder( GetBorder(_buttonFlags.IsFlagSet( DEPRESSED ), _buttonFlags.IsFlagSet( ARMED ), _buttonFlags.IsFlagSet( SELECTED ), HasFocus() ) );
+
+ // set our color
+ SetFgColor(GetButtonFgColor());
+ SetBgColor(GetButtonBgColor());
+
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get button foreground color
+// Output : Color
+//-----------------------------------------------------------------------------
+Color Button::GetButtonFgColor()
+{
+ if ( !_buttonFlags.IsFlagSet( BLINK ) )
+ {
+ if (_buttonFlags.IsFlagSet( DEPRESSED ))
+ return _depressedFgColor;
+ if (_buttonFlags.IsFlagSet( ARMED ))
+ return _armedFgColor;
+ if (_buttonFlags.IsFlagSet( SELECTED))
+ return _selectedFgColor;
+ return _defaultFgColor;
+ }
+
+ Color cBlendedColor;
+
+ if (_buttonFlags.IsFlagSet( DEPRESSED ))
+ cBlendedColor = _depressedFgColor;
+ else if (_buttonFlags.IsFlagSet( ARMED ))
+ cBlendedColor = _armedFgColor;
+ else if (_buttonFlags.IsFlagSet( SELECTED ))
+ cBlendedColor = _selectedFgColor;
+ else
+ cBlendedColor = _defaultFgColor;
+
+ float fBlink = ( sinf( system()->GetTimeMillis() * 0.01f ) + 1.0f ) * 0.5f;
+
+ if ( _buttonFlags.IsFlagSet( BLINK ) )
+ {
+ cBlendedColor[ 0 ] = (float)cBlendedColor[ 0 ] * fBlink + (float)_blinkFgColor[ 0 ] * ( 1.0f - fBlink );
+ cBlendedColor[ 1 ] = (float)cBlendedColor[ 1 ] * fBlink + (float)_blinkFgColor[ 1 ] * ( 1.0f - fBlink );
+ cBlendedColor[ 2 ] = (float)cBlendedColor[ 2 ] * fBlink + (float)_blinkFgColor[ 2 ] * ( 1.0f - fBlink );
+ cBlendedColor[ 3 ] = (float)cBlendedColor[ 3 ] * fBlink + (float)_blinkFgColor[ 3 ] * ( 1.0f - fBlink );
+ }
+
+ return cBlendedColor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get button background color
+//-----------------------------------------------------------------------------
+Color Button::GetButtonBgColor()
+{
+ if (_buttonFlags.IsFlagSet( DEPRESSED ))
+ return _depressedBgColor;
+ if (_buttonFlags.IsFlagSet( ARMED ))
+ return _armedBgColor;
+ if (_buttonFlags.IsFlagSet( SELECTED ))
+ return _selectedBgColor;
+ return _defaultBgColor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when key focus is received
+//-----------------------------------------------------------------------------
+void Button::OnSetFocus()
+{
+ InvalidateLayout(false);
+ BaseClass::OnSetFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Respond when focus is killed
+//-----------------------------------------------------------------------------
+void Button::OnKillFocus()
+{
+ InvalidateLayout(false);
+ BaseClass::OnKillFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ // get the borders we need
+ _defaultBorder = pScheme->GetBorder("ButtonBorder");
+ _depressedBorder = pScheme->GetBorder("ButtonDepressedBorder");
+ _keyFocusBorder = pScheme->GetBorder("ButtonKeyFocusBorder");
+
+ _defaultFgColor = GetSchemeColor("Button.TextColor", Color(255, 255, 255, 255), pScheme);
+ _defaultBgColor = GetSchemeColor("Button.BgColor", Color(0, 0, 0, 255), pScheme);
+
+ _armedFgColor = GetSchemeColor("Button.ArmedTextColor", _defaultFgColor, pScheme);
+ _armedBgColor = GetSchemeColor("Button.ArmedBgColor", _defaultBgColor, pScheme);
+
+ _selectedFgColor = GetSchemeColor("Button.SelectedTextColor", _selectedFgColor, pScheme);
+ _selectedBgColor = GetSchemeColor("Button.SelectedBgColor", _selectedBgColor, pScheme);
+
+ _depressedFgColor = GetSchemeColor("Button.DepressedTextColor", _defaultFgColor, pScheme);
+ _depressedBgColor = GetSchemeColor("Button.DepressedBgColor", _defaultBgColor, pScheme);
+ _keyboardFocusColor = GetSchemeColor("Button.FocusBorderColor", Color(0,0,0,255), pScheme);
+
+ _blinkFgColor = GetSchemeColor("Button.BlinkColor", Color(255, 155, 0, 255), pScheme);
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set default button colors.
+//-----------------------------------------------------------------------------
+void Button::SetDefaultColor(Color fgColor, Color bgColor)
+{
+ if (!(_defaultFgColor == fgColor && _defaultBgColor == bgColor))
+ {
+ _defaultFgColor = fgColor;
+ _defaultBgColor = bgColor;
+
+ InvalidateLayout(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set armed button colors
+//-----------------------------------------------------------------------------
+void Button::SetArmedColor(Color fgColor, Color bgColor)
+{
+ if (!(_armedFgColor == fgColor && _armedBgColor == bgColor))
+ {
+ _armedFgColor = fgColor;
+ _armedBgColor = bgColor;
+
+ InvalidateLayout(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set armed button colors
+//-----------------------------------------------------------------------------
+void Button::SetSelectedColor(Color fgColor, Color bgColor)
+{
+ if (!(_selectedFgColor == fgColor && _selectedBgColor == bgColor))
+ {
+ _selectedFgColor = fgColor;
+ _selectedBgColor = bgColor;
+
+ InvalidateLayout(false);
+ }
+}
+//-----------------------------------------------------------------------------
+// Purpose: Set depressed button colors
+//-----------------------------------------------------------------------------
+void Button::SetDepressedColor(Color fgColor, Color bgColor)
+{
+ if (!(_depressedFgColor == fgColor && _depressedBgColor == bgColor))
+ {
+ _depressedFgColor = fgColor;
+ _depressedBgColor = bgColor;
+
+ InvalidateLayout(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set blink button color
+//-----------------------------------------------------------------------------
+void Button::SetBlinkColor(Color fgColor)
+{
+ if (!(_blinkFgColor == fgColor))
+ {
+ _blinkFgColor = fgColor;
+
+ InvalidateLayout(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set default button border attributes.
+//-----------------------------------------------------------------------------
+void Button::SetDefaultBorder(IBorder *border)
+{
+ _defaultBorder = border;
+ InvalidateLayout(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set depressed button border attributes.
+//-----------------------------------------------------------------------------
+void Button::SetDepressedBorder(IBorder *border)
+{
+ _depressedBorder = border;
+ InvalidateLayout(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set key focus button border attributes.
+//-----------------------------------------------------------------------------
+void Button::SetKeyFocusBorder(IBorder *border)
+{
+ _keyFocusBorder = border;
+ InvalidateLayout(false);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Get button border attributes.
+//-----------------------------------------------------------------------------
+IBorder *Button::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
+{
+ if ( _buttonFlags.IsFlagSet( BUTTON_BORDER_ENABLED ) )
+ {
+ // raised buttons with no armed state
+ if (depressed)
+ return _depressedBorder;
+ if (keyfocus)
+ return _keyFocusBorder;
+ if (IsEnabled() && _buttonFlags.IsFlagSet( DEFAULT_BUTTON ))
+ return _keyFocusBorder;
+ return _defaultBorder;
+ }
+ else
+ {
+ // flat buttons that raise
+ if (depressed)
+ return _depressedBorder;
+ if (armed)
+ return _defaultBorder;
+ }
+
+ return _defaultBorder;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets this button to be the button that is accessed by default
+// when the user hits ENTER or SPACE
+//-----------------------------------------------------------------------------
+void Button::SetAsCurrentDefaultButton(int state)
+{
+ if ( _buttonFlags.IsFlagSet( DEFAULT_BUTTON ) != (bool)state )
+ {
+ _buttonFlags.SetFlag( DEFAULT_BUTTON, state );
+ if (state)
+ {
+ // post a message up notifying our nav group that we're now the default button
+ KeyValues *msg = new KeyValues( "CurrentDefaultButtonSet" );
+ msg->SetInt( "button", ToHandle() );
+ CallParentFunction( msg );
+ }
+
+ InvalidateLayout();
+ Repaint();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets this button to be the button that is accessed by default
+// when the user hits ENTER or SPACE
+//-----------------------------------------------------------------------------
+void Button::SetAsDefaultButton(int state)
+{
+ if ( _buttonFlags.IsFlagSet( DEFAULT_BUTTON ) != (bool)state )
+ {
+ _buttonFlags.SetFlag( DEFAULT_BUTTON, state );
+ if (state)
+ {
+ // post a message up notifying our nav group that we're now the default button
+ KeyValues *msg = new KeyValues( "DefaultButtonSet" );
+ msg->SetInt( "button", ToHandle() );
+ CallParentFunction( msg );
+ }
+
+ InvalidateLayout();
+ Repaint();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets rollover sound
+//-----------------------------------------------------------------------------
+void Button::SetArmedSound(const char *sound)
+{
+ if (sound)
+ {
+ m_sArmedSoundName = g_ButtonSoundNames.AddString(sound);
+ }
+ else
+ {
+ m_sArmedSoundName = UTL_INVAL_SYMBOL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::SetDepressedSound(const char *sound)
+{
+ if (sound)
+ {
+ m_sDepressedSoundName = g_ButtonSoundNames.AddString(sound);
+ }
+ else
+ {
+ m_sDepressedSoundName = UTL_INVAL_SYMBOL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::SetReleasedSound(const char *sound)
+{
+ if (sound)
+ {
+ m_sReleasedSoundName = g_ButtonSoundNames.AddString(sound);
+ }
+ else
+ {
+ m_sReleasedSoundName = UTL_INVAL_SYMBOL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set button to be mouse clickable or not.
+//-----------------------------------------------------------------------------
+void Button::SetMouseClickEnabled(MouseCode code,bool state)
+{
+ if(state)
+ {
+ //set bit to 1
+ _mouseClickMask|=1<<((int)(code+1));
+ }
+ else
+ {
+ //set bit to 0
+ _mouseClickMask&=~(1<<((int)(code+1)));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if button is mouse clickable
+//-----------------------------------------------------------------------------
+bool Button::IsMouseClickEnabled(MouseCode code)
+{
+ if(_mouseClickMask&(1<<((int)(code+1))))
+ {
+ return true;
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the command to send when the button is pressed
+//-----------------------------------------------------------------------------
+void Button::SetCommand( const char *command )
+{
+ SetCommand(new KeyValues("Command", "command", command));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the message to send when the button is pressed
+//-----------------------------------------------------------------------------
+void Button::SetCommand( KeyValues *message )
+{
+ // delete the old message
+ if (_actionMessage)
+ {
+ _actionMessage->deleteThis();
+ }
+
+ _actionMessage = message;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Peeks at the message to send when button is pressed
+// Input : -
+// Output : KeyValues
+//-----------------------------------------------------------------------------
+KeyValues *Button::GetCommand()
+{
+ return _actionMessage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Message targets that the button has been pressed
+//-----------------------------------------------------------------------------
+void Button::FireActionSignal()
+{
+ // message-based action signal
+ if (_actionMessage)
+ {
+ // see if it's a url
+ if (!stricmp(_actionMessage->GetName(), "command")
+ && !strnicmp(_actionMessage->GetString("command", ""), "url ", strlen("url "))
+ && strstr(_actionMessage->GetString("command", ""), "://"))
+ {
+ // it's a command to launch a url, run it
+ system()->ShellExecute("open", _actionMessage->GetString("command", " ") + 4);
+ }
+ PostActionSignal(_actionMessage->MakeCopy());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets info about the button
+//-----------------------------------------------------------------------------
+bool Button::RequestInfo(KeyValues *outputData)
+{
+ if (!stricmp(outputData->GetName(), "CanBeDefaultButton"))
+ {
+ outputData->SetInt("result", CanBeDefaultButton() ? 1 : 0);
+ return true;
+ }
+ else if (!stricmp(outputData->GetName(), "GetState"))
+ {
+ outputData->SetInt("state", IsSelected());
+ return true;
+ }
+ else if ( !stricmp( outputData->GetName(), "GetCommand" ))
+ {
+ if ( _actionMessage )
+ {
+ outputData->SetString( "command", _actionMessage->GetString( "command", "" ) );
+ }
+ else
+ {
+ outputData->SetString( "command", "" );
+ }
+ return true;
+ }
+
+
+ return BaseClass::RequestInfo(outputData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool Button::CanBeDefaultButton(void)
+{
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Get control settings for editing
+//-----------------------------------------------------------------------------
+void Button::GetSettings( KeyValues *outResourceData )
+{
+ BaseClass::GetSettings(outResourceData);
+
+ if (_actionMessage)
+ {
+ outResourceData->SetString("command", _actionMessage->GetString("command", ""));
+ }
+ outResourceData->SetInt("default", _buttonFlags.IsFlagSet( DEFAULT_BUTTON ) );
+ if ( m_bSelectionStateSaved )
+ {
+ outResourceData->SetInt( "selected", IsSelected() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings(inResourceData);
+
+ const char *cmd = inResourceData->GetString("command", "");
+ if (*cmd)
+ {
+ // add in the command
+ SetCommand(cmd);
+ }
+
+ // set default button state
+ int defaultButton = inResourceData->GetInt("default");
+ if (defaultButton && CanBeDefaultButton())
+ {
+ SetAsDefaultButton(true);
+ }
+
+ // saved selection state
+ int iSelected = inResourceData->GetInt( "selected", -1 );
+ if ( iSelected != -1 )
+ {
+ SetSelected( iSelected != 0 );
+ m_bSelectionStateSaved = true;
+ }
+
+ m_bStaySelectedOnClick = inResourceData->GetBool( "stayselectedonclick", false );
+
+ const char *sound = inResourceData->GetString("sound_armed", "");
+ if (*sound)
+ {
+ SetArmedSound(sound);
+ }
+ sound = inResourceData->GetString("sound_depressed", "");
+ if (*sound)
+ {
+ SetDepressedSound(sound);
+ }
+ sound = inResourceData->GetString("sound_released", "");
+ if (*sound)
+ {
+ SetReleasedSound(sound);
+ }
+
+ _activationType = (ActivationType_t)inResourceData->GetInt( "button_activation_type", ACTIVATE_ONRELEASED );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Describes editing details
+//-----------------------------------------------------------------------------
+const char *Button::GetDescription( void )
+{
+ static char buf[1024];
+ Q_snprintf(buf, sizeof(buf), "%s, string command, int default", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::OnSetState(int state)
+{
+ SetSelected((bool)state);
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::OnCursorEntered()
+{
+ if (IsEnabled() && !IsSelected() )
+ {
+ SetArmed( true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::OnCursorExited()
+{
+ if ( !_buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) && !IsSelected() )
+ {
+ SetArmed( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::OnMousePressed(MouseCode code)
+{
+ if (!IsEnabled())
+ return;
+
+ if (!IsMouseClickEnabled(code))
+ return;
+
+ if (_activationType == ACTIVATE_ONPRESSED)
+ {
+ if ( IsKeyBoardInputEnabled() )
+ {
+ RequestFocus();
+ }
+ DoClick();
+ return;
+ }
+
+ // play activation sound
+ if (m_sDepressedSoundName != UTL_INVAL_SYMBOL)
+ {
+ surface()->PlaySound(g_ButtonSoundNames.String(m_sDepressedSoundName));
+ }
+
+ if (IsUseCaptureMouseEnabled() && _activationType == ACTIVATE_ONPRESSEDANDRELEASED)
+ {
+ {
+ if ( IsKeyBoardInputEnabled() )
+ {
+ RequestFocus();
+ }
+ SetSelected(true);
+ Repaint();
+ }
+
+ // lock mouse input to going to this button
+ input()->SetMouseCapture(GetVPanel());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::OnMouseDoublePressed(MouseCode code)
+{
+ OnMousePressed(code);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::OnMouseReleased(MouseCode code)
+{
+ // ensure mouse capture gets released
+ if (IsUseCaptureMouseEnabled())
+ {
+ input()->SetMouseCapture(NULL);
+ }
+
+ if (_activationType == ACTIVATE_ONPRESSED)
+ return;
+
+ if (!IsMouseClickEnabled(code))
+ return;
+
+ if (!IsSelected() && _activationType == ACTIVATE_ONPRESSEDANDRELEASED)
+ return;
+
+ // it has to be both enabled and (mouse over the button or using a key) to fire
+ if ( IsEnabled() && ( GetVPanel() == input()->GetMouseOver() || _buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) ) )
+ {
+ DoClick();
+ }
+ else if ( !m_bStaySelectedOnClick )
+ {
+ SetSelected(false);
+ }
+
+ // make sure the button gets unselected
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::OnKeyCodePressed(KeyCode code)
+{
+ KeyCode localCode = GetBaseButtonCode( code );
+
+ if( ( localCode == KEY_XBUTTON_A ) && IsEnabled() )
+ {
+ SetArmed( true );
+ _buttonFlags.SetFlag( BUTTON_KEY_DOWN );
+ if( _activationType != ACTIVATE_ONRELEASED )
+ {
+ DoClick();
+ }
+ }
+ else if (code == KEY_SPACE || code == KEY_ENTER)
+ {
+ SetArmed(true);
+ _buttonFlags.SetFlag( BUTTON_KEY_DOWN );
+ OnMousePressed(MOUSE_LEFT);
+ if (IsUseCaptureMouseEnabled()) // undo the mouse capture since its a fake mouse click!
+ {
+ input()->SetMouseCapture(NULL);
+ }
+ }
+ else
+ {
+ _buttonFlags.ClearFlag( BUTTON_KEY_DOWN );
+ BaseClass::OnKeyCodePressed( code );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Button::OnKeyCodeReleased( KeyCode keycode )
+{
+ vgui::KeyCode code = GetBaseButtonCode( keycode );
+
+ if ( _buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) && ( code == KEY_XBUTTON_A || code == KEY_XBUTTON_START ) )
+ {
+ SetArmed( true );
+ if( _activationType != ACTIVATE_ONPRESSED )
+ {
+ DoClick();
+ }
+ }
+ else if (_buttonFlags.IsFlagSet( BUTTON_KEY_DOWN ) && (code == KEY_SPACE || code == KEY_ENTER))
+ {
+ SetArmed(true);
+ OnMouseReleased(MOUSE_LEFT);
+ }
+ else
+ {
+ BaseClass::OnKeyCodeReleased( keycode );
+ }
+ _buttonFlags.ClearFlag( BUTTON_KEY_DOWN );
+
+ if ( !( code == KEY_XSTICK1_UP || code == KEY_XSTICK1_DOWN || code == KEY_XSTICK1_LEFT || code == KEY_XSTICK1_RIGHT ||
+ code == KEY_XSTICK2_UP || code == KEY_XSTICK2_DOWN || code == KEY_XSTICK2_LEFT || code == KEY_XSTICK2_RIGHT ||
+ code == KEY_XBUTTON_UP || code == KEY_XBUTTON_DOWN || code == KEY_XBUTTON_LEFT || code == KEY_XBUTTON_RIGHT ||
+ keycode == KEY_UP|| keycode == KEY_DOWN || keycode == KEY_LEFT || keycode == KEY_RIGHT ) )
+ {
+ SetArmed( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override this to draw different focus border
+//-----------------------------------------------------------------------------
+void Button::DrawFocusBorder(int tx0, int ty0, int tx1, int ty1)
+{
+ surface()->DrawSetColor(_keyboardFocusColor);
+ DrawDashedLine(tx0, ty0, tx1, ty0+1, 1, 1); // top
+ DrawDashedLine(tx0, ty0, tx0+1, ty1, 1, 1); // left
+ DrawDashedLine(tx0, ty1-1, tx1, ty1, 1, 1); // bottom
+ DrawDashedLine(tx1-1, ty0, tx1, ty1, 1, 1); // right
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Size the object to its button and text. - only works from in ApplySchemeSettings or PerformLayout()
+//-----------------------------------------------------------------------------
+void Button::SizeToContents()
+{
+ int wide, tall;
+ GetContentSize(wide, tall);
+ SetSize(wide + Label::Content, tall + Label::Content);
+}
diff --git a/mp/src/vgui2/vgui_controls/CheckButton.cpp b/mp/src/vgui2/vgui_controls/CheckButton.cpp
new file mode 100644
index 00000000..ded20bd5
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/CheckButton.cpp
@@ -0,0 +1,205 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <vgui/ISurface.h>
+#include <vgui/IScheme.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/Image.h>
+#include <vgui_controls/CheckButton.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+void CheckImage::Paint()
+{
+ DrawSetTextFont(GetFont());
+
+ // draw background
+ if (_CheckButton->IsEnabled() && _CheckButton->IsCheckButtonCheckable() )
+ {
+ DrawSetTextColor(_bgColor);
+ }
+ else
+ {
+ DrawSetTextColor(_CheckButton->GetDisabledBgColor());
+ }
+ DrawPrintChar(0, 1, 'g');
+
+ // draw border box
+ DrawSetTextColor(_borderColor1);
+ DrawPrintChar(0, 1, 'e');
+ DrawSetTextColor(_borderColor2);
+ DrawPrintChar(0, 1, 'f');
+
+ // draw selected check
+ if (_CheckButton->IsSelected())
+ {
+ if ( !_CheckButton->IsEnabled() )
+ {
+ DrawSetTextColor( _CheckButton->GetDisabledFgColor() );
+ }
+ else
+ {
+ DrawSetTextColor(_checkColor);
+ }
+
+ DrawPrintChar(0, 2, 'b');
+ }
+}
+
+DECLARE_BUILD_FACTORY_DEFAULT_TEXT( CheckButton, CheckButton );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CheckButton::CheckButton(Panel *parent, const char *panelName, const char *text) : ToggleButton(parent, panelName, text)
+{
+ SetContentAlignment(a_west);
+ m_bCheckButtonCheckable = true;
+
+ // create the image
+ _checkBoxImage = new CheckImage(this);
+
+ SetTextImageIndex(1);
+ SetImageAtIndex(0, _checkBoxImage, CHECK_INSET);
+
+ _selectedFgColor = Color( 196, 181, 80, 255 );
+ _disabledFgColor = Color(130, 130, 130, 255);
+ _disabledBgColor = Color(62, 70, 55, 255);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CheckButton::~CheckButton()
+{
+ delete _checkBoxImage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CheckButton::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetDefaultColor( GetSchemeColor("CheckButton.TextColor", pScheme), GetBgColor() );
+ _checkBoxImage->_bgColor = GetSchemeColor("CheckButton.BgColor", Color(62, 70, 55, 255), pScheme);
+ _checkBoxImage->_borderColor1 = GetSchemeColor("CheckButton.Border1", Color(20, 20, 20, 255), pScheme);
+ _checkBoxImage->_borderColor2 = GetSchemeColor("CheckButton.Border2", Color(90, 90, 90, 255), pScheme);
+ _checkBoxImage->_checkColor = GetSchemeColor("CheckButton.Check", Color(20, 20, 20, 255), pScheme);
+ _selectedFgColor = GetSchemeColor("CheckButton.SelectedTextColor", GetSchemeColor("ControlText", pScheme), pScheme);
+ _disabledFgColor = GetSchemeColor("CheckButton.DisabledFgColor", Color(130, 130, 130, 255), pScheme);
+ _disabledBgColor = GetSchemeColor("CheckButton.DisabledBgColor", Color(62, 70, 55, 255), pScheme);
+
+ Color bgArmedColor = GetSchemeColor( "CheckButton.ArmedBgColor", Color(62, 70, 55, 255), pScheme);
+ SetArmedColor( GetFgColor(), bgArmedColor );
+
+ Color bgDepressedColor = GetSchemeColor( "CheckButton.DepressedBgColor", Color(62, 70, 55, 255), pScheme);
+ SetDepressedColor( GetFgColor(), bgDepressedColor );
+
+ _highlightFgColor = GetSchemeColor( "CheckButton.HighlightFgColor", Color(62, 70, 55, 255), pScheme);
+
+ SetContentAlignment(Label::a_west);
+
+ _checkBoxImage->SetFont( pScheme->GetFont("Marlett", IsProportional()) );
+ _checkBoxImage->ResizeImageToContent();
+ SetImageAtIndex(0, _checkBoxImage, CHECK_INSET);
+
+ // don't draw a background
+ SetPaintBackgroundEnabled(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+IBorder *CheckButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
+{
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check the button
+//-----------------------------------------------------------------------------
+void CheckButton::SetSelected(bool state )
+{
+ if (m_bCheckButtonCheckable)
+ {
+ // send a message saying we've been checked
+ KeyValues *msg = new KeyValues("CheckButtonChecked", "state", (int)state);
+ PostActionSignal(msg);
+
+ BaseClass::SetSelected(state);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets whether or not the state of the check can be changed
+//-----------------------------------------------------------------------------
+void CheckButton::SetCheckButtonCheckable(bool state)
+{
+ m_bCheckButtonCheckable = state;
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets a different foreground text color if we are selected
+//-----------------------------------------------------------------------------
+#ifdef _X360
+Color CheckButton::GetButtonFgColor()
+{
+ if (HasFocus())
+ {
+ return _selectedFgColor;
+ }
+
+ return BaseClass::GetButtonFgColor();
+}
+#else
+Color CheckButton::GetButtonFgColor()
+{
+ if ( IsArmed() )
+ {
+ return _highlightFgColor;
+ }
+
+ if (IsSelected())
+ {
+ return _selectedFgColor;
+ }
+
+ return BaseClass::GetButtonFgColor();
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CheckButton::OnCheckButtonChecked(Panel *panel)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CheckButton::SetHighlightColor(Color fgColor)
+{
+ if ( _highlightFgColor != fgColor )
+ {
+ _highlightFgColor = fgColor;
+
+ InvalidateLayout(false);
+ }
+}
+
diff --git a/mp/src/vgui2/vgui_controls/CheckButtonList.cpp b/mp/src/vgui2/vgui_controls/CheckButtonList.cpp
new file mode 100644
index 00000000..de47fd21
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/CheckButtonList.cpp
@@ -0,0 +1,212 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include <vgui_controls/CheckButtonList.h>
+#include <vgui_controls/CheckButton.h>
+#include <vgui_controls/ScrollBar.h>
+#include <KeyValues.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CheckButtonList::CheckButtonList(Panel *parent, const char *name) : BaseClass(parent, name)
+{
+ m_pScrollBar = new ScrollBar(this, NULL, true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CheckButtonList::~CheckButtonList()
+{
+ RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds a check button to the list
+//-----------------------------------------------------------------------------
+int CheckButtonList::AddItem(const char *itemText, bool startsSelected, KeyValues *userData)
+{
+ CheckItem_t newItem;
+ newItem.checkButton = new vgui::CheckButton(this, NULL, itemText);
+ newItem.checkButton->SetSilentMode( true );
+ newItem.checkButton->SetSelected(startsSelected);
+ newItem.checkButton->SetSilentMode( false );
+ newItem.checkButton->AddActionSignalTarget(this);
+ newItem.userData = userData;
+ InvalidateLayout();
+ return m_CheckItems.AddToTail(newItem);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: clears the list
+//-----------------------------------------------------------------------------
+void CheckButtonList::RemoveAll()
+{
+ for (int i = 0; i < m_CheckItems.Count(); i++)
+ {
+ m_CheckItems[i].checkButton->MarkForDeletion();
+ if (m_CheckItems[i].userData)
+ {
+ m_CheckItems[i].userData->deleteThis();
+ }
+ }
+
+ m_CheckItems.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the number of items in list that are checked
+//-----------------------------------------------------------------------------
+int CheckButtonList::GetCheckedItemCount()
+{
+ int count = 0;
+ for (int i = 0; i < m_CheckItems.Count(); i++)
+ {
+ if (m_CheckItems[i].checkButton->IsSelected())
+ {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: lays out buttons
+//-----------------------------------------------------------------------------
+void CheckButtonList::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ // get sizes
+ int x = 4, y = 4, wide = GetWide() - ((x * 2) + m_pScrollBar->GetWide()), tall = 22;
+
+ // set scrollbar
+ int totalHeight = y + (m_CheckItems.Count() * tall);
+ if (totalHeight > GetTall())
+ {
+ m_pScrollBar->SetRange(0, totalHeight + 1);
+ m_pScrollBar->SetRangeWindow(GetTall());
+ m_pScrollBar->SetVisible(true);
+ m_pScrollBar->SetBounds(GetWide() - 21, 0, 19, GetTall() - 2);
+ SetPaintBorderEnabled(true);
+ y -= m_pScrollBar->GetValue();
+ }
+ else
+ {
+ m_pScrollBar->SetVisible(false);
+ SetPaintBorderEnabled(false);
+ }
+
+ // position the items
+ for (int i = 0; i < m_CheckItems.Count(); i++)
+ {
+ CheckButton *btn = m_CheckItems[i].checkButton;
+ btn->SetBounds(x, y, wide, tall);
+ y += tall;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the border on the window
+//-----------------------------------------------------------------------------
+void CheckButtonList::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+ SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: iteration
+//-----------------------------------------------------------------------------
+bool CheckButtonList::IsItemIDValid(int itemID)
+{
+ return m_CheckItems.IsValidIndex(itemID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: iteration
+//-----------------------------------------------------------------------------
+int CheckButtonList::GetHighestItemID()
+{
+ return m_CheckItems.Count() - 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: iteration
+//-----------------------------------------------------------------------------
+KeyValues *CheckButtonList::GetItemData(int itemID)
+{
+ return m_CheckItems[itemID].userData;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+int CheckButtonList::GetItemCount()
+{
+ return m_CheckItems.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+bool CheckButtonList::IsItemChecked(int itemID)
+{
+ return m_CheckItems[itemID].checkButton->IsSelected();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the state of the check button
+//-----------------------------------------------------------------------------
+void CheckButtonList::SetItemCheckable(int itemID, bool state)
+{
+ m_CheckItems[itemID].checkButton->SetCheckButtonCheckable(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Forwards up check button selected message
+//-----------------------------------------------------------------------------
+void CheckButtonList::OnCheckButtonChecked( KeyValues *pParams )
+{
+ vgui::Panel *pPanel = (vgui::Panel *)pParams->GetPtr( "panel" );
+ int c = m_CheckItems.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ if ( pPanel == m_CheckItems[i].checkButton )
+ {
+ KeyValues *kv = new KeyValues( "CheckButtonChecked", "itemid", i );
+ kv->SetInt( "state", pParams->GetInt( "state" ) );
+ PostActionSignal( kv );
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: updates from scrollbar movement
+//-----------------------------------------------------------------------------
+void CheckButtonList::OnScrollBarSliderMoved()
+{
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Mouse wheeled
+//-----------------------------------------------------------------------------
+void CheckButtonList::OnMouseWheeled(int delta)
+{
+ int val = m_pScrollBar->GetValue();
+ val -= (delta * 15);
+ m_pScrollBar->SetValue(val);
+}
diff --git a/mp/src/vgui2/vgui_controls/CircularProgressBar.cpp b/mp/src/vgui2/vgui_controls/CircularProgressBar.cpp
new file mode 100644
index 00000000..47ae6d20
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/CircularProgressBar.cpp
@@ -0,0 +1,292 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+
+#include <vgui_controls/CircularProgressBar.h>
+#include <vgui_controls/Controls.h>
+
+#include <vgui/ILocalize.h>
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <KeyValues.h>
+
+#include "mathlib/mathlib.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( CircularProgressBar );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CircularProgressBar::CircularProgressBar(Panel *parent, const char *panelName) : ProgressBar(parent, panelName)
+{
+ m_iProgressDirection = CircularProgressBar::PROGRESS_CCW;
+
+ for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ )
+ {
+ m_nTextureId[i] = -1;
+ m_pszImageName[i] = NULL;
+ m_lenImageName[i] = 0;
+ }
+
+ m_iStartSegment = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CircularProgressBar::~CircularProgressBar()
+{
+ for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ )
+ {
+ if ( vgui::surface() && m_nTextureId[i] )
+ {
+ vgui::surface()->DestroyTextureID( m_nTextureId[i] );
+ m_nTextureId[i] = -1;
+ }
+
+ delete [] m_pszImageName[i];
+ m_lenImageName[i] = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CircularProgressBar::ApplySettings(KeyValues *inResourceData)
+{
+ for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ )
+ {
+ delete [] m_pszImageName[i];
+ m_pszImageName[i] = NULL;
+ m_lenImageName[i] = 0;
+ }
+
+ const char *imageName = inResourceData->GetString("fg_image", "");
+ if (*imageName)
+ {
+ SetFgImage( imageName );
+ }
+ imageName = inResourceData->GetString("bg_image", "");
+ if (*imageName)
+ {
+ SetBgImage( imageName );
+ }
+
+ BaseClass::ApplySettings( inResourceData );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CircularProgressBar::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetFgColor(GetSchemeColor("CircularProgressBar.FgColor", pScheme));
+ SetBgColor(GetSchemeColor("CircularProgressBar.BgColor", pScheme));
+ SetBorder(NULL);
+
+ for ( int i = 0; i < NUM_PROGRESS_TEXTURES; i++ )
+ {
+ if ( m_pszImageName[i] && strlen( m_pszImageName[i] ) > 0 )
+ {
+ if ( m_nTextureId[i] == -1 )
+ {
+ m_nTextureId[i] = surface()->CreateNewTextureID();
+ }
+
+ surface()->DrawSetTextureFile( m_nTextureId[i], m_pszImageName[i], true, false);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets an image by file name
+//-----------------------------------------------------------------------------
+void CircularProgressBar::SetImage(const char *imageName, progress_textures_t iPos)
+{
+ const char *pszDir = "vgui/";
+ int len = Q_strlen(imageName) + 1;
+ len += strlen(pszDir);
+
+ if ( m_pszImageName[iPos] && ( m_lenImageName[iPos] < len ) )
+ {
+ // If we already have a buffer, but it is too short, then free the buffer
+ delete [] m_pszImageName[iPos];
+ m_pszImageName[iPos] = NULL;
+ m_lenImageName[iPos] = 0;
+ }
+
+ if ( !m_pszImageName[iPos] )
+ {
+ m_pszImageName[iPos] = new char[ len ];
+ m_lenImageName[iPos] = len;
+ }
+
+ Q_snprintf( m_pszImageName[iPos], len, "%s%s", pszDir, imageName );
+ InvalidateLayout(false, true); // force applyschemesettings to run
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CircularProgressBar::PaintBackground()
+{
+ // If we don't have a Bg image, use the foreground
+ int iTextureID = m_nTextureId[PROGRESS_TEXTURE_BG] != -1 ? m_nTextureId[PROGRESS_TEXTURE_BG] : m_nTextureId[PROGRESS_TEXTURE_FG];
+ vgui::surface()->DrawSetTexture( iTextureID );
+ vgui::surface()->DrawSetColor( GetBgColor() );
+
+ int wide, tall;
+ GetSize(wide, tall);
+
+ vgui::surface()->DrawTexturedRect( 0, 0, wide, tall );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CircularProgressBar::Paint()
+{
+ float flProgress = GetProgress();
+ float flEndAngle;
+
+ if ( m_iProgressDirection == PROGRESS_CW )
+ {
+ flEndAngle = flProgress;
+ }
+ else
+ {
+ flEndAngle = ( 1.0 - flProgress );
+ }
+
+ DrawCircleSegment( GetFgColor(), flEndAngle, ( m_iProgressDirection == PROGRESS_CW ) );
+}
+
+typedef struct
+{
+ float minProgressRadians;
+
+ float vert1x;
+ float vert1y;
+ float vert2x;
+ float vert2y;
+
+ int swipe_dir_x;
+ int swipe_dir_y;
+} circular_progress_segment_t;
+
+namespace vgui
+{
+// This defines the properties of the 8 circle segments
+// in the circular progress bar.
+circular_progress_segment_t Segments[8] =
+{
+ { 0.0, 0.5, 0.0, 1.0, 0.0, 1, 0 },
+ { M_PI * 0.25, 1.0, 0.0, 1.0, 0.5, 0, 1 },
+ { M_PI * 0.5, 1.0, 0.5, 1.0, 1.0, 0, 1 },
+ { M_PI * 0.75, 1.0, 1.0, 0.5, 1.0, -1, 0 },
+ { M_PI, 0.5, 1.0, 0.0, 1.0, -1, 0 },
+ { M_PI * 1.25, 0.0, 1.0, 0.0, 0.5, 0, -1 },
+ { M_PI * 1.5, 0.0, 0.5, 0.0, 0.0, 0, -1 },
+ { M_PI * 1.75, 0.0, 0.0, 0.5, 0.0, 1, 0 },
+};
+
+};
+
+#define SEGMENT_ANGLE ( M_PI / 4 )
+
+// function to draw from A to B degrees, with a direction
+// we draw starting from the top ( 0 progress )
+void CircularProgressBar::DrawCircleSegment( Color c, float flEndProgress, bool bClockwise )
+{
+ if ( m_nTextureId[PROGRESS_TEXTURE_FG] == -1 )
+ return;
+
+ int wide, tall;
+ GetSize(wide, tall);
+
+ float flWide = (float)wide;
+ float flTall = (float)tall;
+
+ float flHalfWide = (float)wide / 2;
+ float flHalfTall = (float)tall / 2;
+
+ vgui::surface()->DrawSetTexture( m_nTextureId[PROGRESS_TEXTURE_FG] );
+ vgui::surface()->DrawSetColor( c );
+
+ // TODO - if we want to progress CCW, reverse a few things
+
+ float flEndProgressRadians = flEndProgress * M_PI * 2;
+
+ int cur_wedge = m_iStartSegment;
+ for ( int i=0;i<8;i++ )
+ {
+ if ( flEndProgressRadians > Segments[cur_wedge].minProgressRadians)
+ {
+ vgui::Vertex_t v[3];
+
+ // vert 0 is ( 0.5, 0.5 )
+ v[0].m_Position.Init( flHalfWide, flHalfTall );
+ v[0].m_TexCoord.Init( 0.5f, 0.5f );
+
+ float flInternalProgress = flEndProgressRadians - Segments[cur_wedge].minProgressRadians;
+
+ if ( flInternalProgress < SEGMENT_ANGLE )
+ {
+ // Calc how much of this slice we should be drawing
+
+ if ( i % 2 == 1 )
+ {
+ flInternalProgress = SEGMENT_ANGLE - flInternalProgress;
+ }
+
+ float flTan = tan(flInternalProgress);
+
+ float flDeltaX, flDeltaY;
+
+ if ( i % 2 == 1 )
+ {
+ flDeltaX = ( flHalfWide - flHalfTall * flTan ) * Segments[i].swipe_dir_x;
+ flDeltaY = ( flHalfTall - flHalfWide * flTan ) * Segments[i].swipe_dir_y;
+ }
+ else
+ {
+ flDeltaX = flHalfTall * flTan * Segments[i].swipe_dir_x;
+ flDeltaY = flHalfWide * flTan * Segments[i].swipe_dir_y;
+ }
+
+ v[2].m_Position.Init( Segments[i].vert1x * flWide + flDeltaX, Segments[i].vert1y * flTall + flDeltaY );
+ v[2].m_TexCoord.Init( Segments[i].vert1x + ( flDeltaX / flHalfWide ) * 0.5, Segments[i].vert1y + ( flDeltaY / flHalfTall ) * 0.5 );
+ }
+ else
+ {
+ // full segment, easy calculation
+ v[2].m_Position.Init( flHalfWide + flWide * ( Segments[i].vert2x - 0.5 ), flHalfTall + flTall * ( Segments[i].vert2y - 0.5 ) );
+ v[2].m_TexCoord.Init( Segments[i].vert2x, Segments[i].vert2y );
+ }
+
+ // vert 2 is ( Segments[i].vert1x, Segments[i].vert1y )
+ v[1].m_Position.Init( flHalfWide + flWide * ( Segments[i].vert1x - 0.5 ), flHalfTall + flTall * ( Segments[i].vert1y - 0.5 ) );
+ v[1].m_TexCoord.Init( Segments[i].vert1x, Segments[i].vert1y );
+
+ vgui::surface()->DrawTexturedPolygon( 3, v );
+ }
+
+ cur_wedge++;
+ if ( cur_wedge >= 8)
+ cur_wedge = 0;
+ }
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/ComboBox.cpp b/mp/src/vgui2/vgui_controls/ComboBox.cpp
new file mode 100644
index 00000000..4949bf19
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ComboBox.cpp
@@ -0,0 +1,1041 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#define PROTECTED_THINGS_DISABLE
+
+#include "vgui/Cursor.h"
+#include "vgui/IInput.h"
+#include "vgui/ILocalize.h"
+#include "vgui/IScheme.h"
+#include "vgui/ISurface.h"
+#include "vgui/IPanel.h"
+#include "KeyValues.h"
+
+#include "vgui_controls/ComboBox.h"
+#include "vgui_controls/Menu.h"
+#include "vgui_controls/MenuItem.h"
+#include "vgui_controls/TextImage.h"
+
+#include <ctype.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+namespace vgui
+{
+ComboBoxButton::ComboBoxButton(ComboBox *parent, const char *panelName, const char *text) : Button(parent, panelName, text)
+{
+ SetButtonActivationType(ACTIVATE_ONPRESSED);
+}
+
+void ComboBoxButton::ApplySchemeSettings(IScheme *pScheme)
+{
+ Button::ApplySchemeSettings(pScheme);
+
+ SetFont(pScheme->GetFont("Marlett", IsProportional()));
+ SetContentAlignment(Label::a_west);
+#ifdef OSX
+ SetTextInset(-3, 0);
+#else
+ SetTextInset(3, 0);
+#endif
+ SetDefaultBorder(pScheme->GetBorder("ScrollBarButtonBorder"));
+
+ // arrow changes color but the background doesnt.
+ SetDefaultColor(GetSchemeColor("ComboBoxButton.ArrowColor", pScheme), GetSchemeColor("ComboBoxButton.BgColor", pScheme));
+ SetArmedColor(GetSchemeColor("ComboBoxButton.ArmedArrowColor", pScheme), GetSchemeColor("ComboBoxButton.BgColor", pScheme));
+ SetDepressedColor(GetSchemeColor("ComboBoxButton.ArmedArrowColor", pScheme), GetSchemeColor("ComboBoxButton.BgColor", pScheme));
+ m_DisabledBgColor = GetSchemeColor("ComboBoxButton.DisabledBgColor", pScheme);
+}
+
+IBorder * ComboBoxButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
+{
+ return NULL;
+ // return Button::GetBorder(depressed, armed, selected, keyfocus);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Dim the arrow on the button when exiting the box
+// only if the menu is closed, so let the parent handle this.
+//-----------------------------------------------------------------------------
+void ComboBoxButton::OnCursorExited()
+{
+ // want the arrow to go grey when we exit the box if the menu is not open
+ CallParentFunction(new KeyValues("CursorExited"));
+}
+
+} // namespace vgui
+
+vgui::Panel *ComboBox_Factory()
+{
+ return new ComboBox( NULL, NULL, 5, true );
+}
+DECLARE_BUILD_FACTORY_CUSTOM( ComboBox, ComboBox_Factory );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+// Input : parent - parent class
+// panelName
+// numLines - number of lines in dropdown menu
+// allowEdit - whether combobox is editable or not
+//-----------------------------------------------------------------------------
+ComboBox::ComboBox(Panel *parent, const char *panelName, int numLines, bool allowEdit ) : TextEntry(parent, panelName)
+{
+ SetEditable(allowEdit);
+ SetHorizontalScrolling(false); // do not scroll, always Start at the beginning of the text.
+
+ // create the drop-down menu
+ m_pDropDown = new Menu(this, NULL);
+ m_pDropDown->AddActionSignalTarget(this);
+ m_pDropDown->SetTypeAheadMode( Menu::TYPE_AHEAD_MODE );
+
+ // button to Activate menu
+ m_pButton = new ComboBoxButton(this, "Button", "u");
+ m_pButton->SetCommand("ButtonClicked");
+ m_pButton->AddActionSignalTarget(this);
+
+ SetNumberOfEditLines(numLines);
+
+ m_bHighlight = false;
+ m_iDirection = Menu::DOWN;
+ m_iOpenOffsetY = 0;
+ m_bPreventTextChangeMessage = false;
+ m_szBorderOverride[0] = '\0';
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+ComboBox::~ComboBox()
+{
+ m_pDropDown->DeletePanel();
+ m_pButton->DeletePanel();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the number of items in the dropdown menu.
+// Input : numLines - number of items in dropdown menu
+//-----------------------------------------------------------------------------
+void ComboBox::SetNumberOfEditLines( int numLines )
+{
+ m_pDropDown->SetNumberOfVisibleItems( numLines );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add an item to the drop down
+// Input : char *itemText - name of dropdown menu item
+//-----------------------------------------------------------------------------
+int ComboBox::AddItem(const char *itemText, const KeyValues *userData)
+{
+ // when the menu item is selected it will send the custom message "SetText"
+ return m_pDropDown->AddMenuItem( itemText, new KeyValues("SetText", "text", itemText), this, userData );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Add an item to the drop down
+// Input : char *itemText - name of dropdown menu item
+//-----------------------------------------------------------------------------
+int ComboBox::AddItem(const wchar_t *itemText, const KeyValues *userData)
+{
+ // add the element to the menu
+ // when the menu item is selected it will send the custom message "SetText"
+ KeyValues *kv = new KeyValues("SetText");
+ kv->SetWString("text", itemText);
+ // get an ansi version for the menuitem name
+ char ansi[128];
+ g_pVGuiLocalize->ConvertUnicodeToANSI(itemText, ansi, sizeof(ansi));
+ return m_pDropDown->AddMenuItem(ansi, kv, this, userData);
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes a single item
+//-----------------------------------------------------------------------------
+void ComboBox::DeleteItem( int itemID )
+{
+ if ( !m_pDropDown->IsValidMenuID(itemID))
+ return;
+
+ m_pDropDown->DeleteItem( itemID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates a current item to the drop down
+// Input : char *itemText - name of dropdown menu item
+//-----------------------------------------------------------------------------
+bool ComboBox::UpdateItem(int itemID, const char *itemText, const KeyValues *userData)
+{
+ if ( !m_pDropDown->IsValidMenuID(itemID))
+ return false;
+
+ // when the menu item is selected it will send the custom message "SetText"
+ m_pDropDown->UpdateMenuItem(itemID, itemText, new KeyValues("SetText", "text", itemText), userData);
+ InvalidateLayout();
+ return true;
+}
+//-----------------------------------------------------------------------------
+// Purpose: Updates a current item to the drop down
+// Input : wchar_t *itemText - name of dropdown menu item
+//-----------------------------------------------------------------------------
+bool ComboBox::UpdateItem(int itemID, const wchar_t *itemText, const KeyValues *userData)
+{
+ if ( !m_pDropDown->IsValidMenuID(itemID))
+ return false;
+
+ // when the menu item is selected it will send the custom message "SetText"
+ KeyValues *kv = new KeyValues("SetText");
+ kv->SetWString("text", itemText);
+ m_pDropDown->UpdateMenuItem(itemID, itemText, kv, userData);
+ InvalidateLayout();
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates a current item to the drop down
+// Input : wchar_t *itemText - name of dropdown menu item
+//-----------------------------------------------------------------------------
+bool ComboBox::IsItemIDValid( int itemID )
+{
+ return m_pDropDown->IsValidMenuID(itemID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ComboBox::SetItemEnabled(const char *itemText, bool state)
+{
+ m_pDropDown->SetItemEnabled(itemText, state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ComboBox::SetItemEnabled(int itemID, bool state)
+{
+ m_pDropDown->SetItemEnabled(itemID, state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove all items from the drop down menu
+//-----------------------------------------------------------------------------
+void ComboBox::RemoveAll()
+{
+ m_pDropDown->DeleteAllItems();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ComboBox::GetItemCount()
+{
+ return m_pDropDown->GetItemCount();
+}
+
+int ComboBox::GetItemIDFromRow( int row )
+{
+ // valid from [0, GetItemCount)
+ return m_pDropDown->GetMenuID( row );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activate the item in the menu list, as if that menu item had been selected by the user
+// Input : itemID - itemID from AddItem in list of dropdown items
+//-----------------------------------------------------------------------------
+void ComboBox::ActivateItem(int itemID)
+{
+ m_pDropDown->ActivateItem(itemID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activate the item in the menu list, as if that menu item had been selected by the user
+// Input : itemID - itemID from AddItem in list of dropdown items
+//-----------------------------------------------------------------------------
+void ComboBox::ActivateItemByRow(int row)
+{
+ m_pDropDown->ActivateItemByRow(row);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activate the item in the menu list, without sending a TextChanged message
+// Input : row - row to activate
+//-----------------------------------------------------------------------------
+void ComboBox::SilentActivateItemByRow(int row)
+{
+ int itemID = GetItemIDFromRow( row );
+ if ( itemID >= 0 )
+ {
+ SilentActivateItem( itemID );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activate the item in the menu list, without sending a TextChanged message
+// Input : itemID - itemID from AddItem in list of dropdown items
+//-----------------------------------------------------------------------------
+void ComboBox::SilentActivateItem(int itemID)
+{
+ m_pDropDown->SilentActivateItem(itemID);
+
+ // Now manually call our set text, with a wrapper to ensure we don't send the Text Changed message
+ wchar_t name[ 256 ];
+ GetItemText( itemID, name, sizeof( name ) );
+
+ m_bPreventTextChangeMessage = true;
+ OnSetText( name );
+ m_bPreventTextChangeMessage = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allows a custom menu to be used with the combo box
+//-----------------------------------------------------------------------------
+void ComboBox::SetMenu( Menu *menu )
+{
+ if ( m_pDropDown )
+ {
+ m_pDropDown->MarkForDeletion();
+ }
+
+ m_pDropDown = menu;
+ if ( m_pDropDown )
+ {
+ m_pDropDown->SetParent( this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Layout the format of the combo box for drawing on screen
+//-----------------------------------------------------------------------------
+void ComboBox::PerformLayout()
+{
+ int wide, tall;
+ GetPaintSize(wide, tall);
+
+ BaseClass::PerformLayout();
+
+ HFont buttonFont = m_pButton->GetFont();
+ int fontTall = surface()->GetFontTall( buttonFont );
+
+ int buttonSize = min( tall, fontTall );
+
+ int buttonY = ( ( tall - 1 ) - buttonSize ) / 2;
+
+ // Some dropdown button icons in our games are wider than they are taller. We need to factor that in.
+ int button_wide, button_tall;
+ m_pButton->GetContentSize(button_wide, button_tall);
+ button_wide = max( buttonSize, button_wide );
+
+ m_pButton->SetBounds( wide - button_wide, buttonY, button_wide, buttonSize );
+ if ( IsEditable() )
+ {
+ SetCursor(dc_ibeam);
+ }
+ else
+ {
+ SetCursor(dc_arrow);
+ }
+
+ m_pButton->SetEnabled(IsEnabled());
+
+ DoMenuLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ComboBox::DoMenuLayout()
+{
+ m_pDropDown->PositionRelativeToPanel( this, m_iDirection, m_iOpenOffsetY );
+
+ // reset the width of the drop down menu to be the width of the combo box
+ m_pDropDown->SetFixedWidth(GetWide());
+ m_pDropDown->ForceCalculateWidth();
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sorts the items in the list
+//-----------------------------------------------------------------------------
+void ComboBox::SortItems( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: return the index of the last selected item
+//-----------------------------------------------------------------------------
+int ComboBox::GetActiveItem()
+{
+ return m_pDropDown->GetActiveItem();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues *ComboBox::GetActiveItemUserData()
+{
+ return m_pDropDown->GetItemUserData(GetActiveItem());
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues *ComboBox::GetItemUserData(int itemID)
+{
+ return m_pDropDown->GetItemUserData(itemID);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void ComboBox::GetItemText( int itemID, wchar_t *text, int bufLenInBytes )
+{
+ m_pDropDown->GetItemText( itemID, text, bufLenInBytes );
+}
+
+void ComboBox::GetItemText( int itemID, char *text, int bufLenInBytes )
+{
+ m_pDropDown->GetItemText( itemID, text, bufLenInBytes );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool ComboBox::IsDropdownVisible()
+{
+ return m_pDropDown->IsVisible();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *inResourceData -
+//-----------------------------------------------------------------------------
+void ComboBox::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetBorder( pScheme->GetBorder( m_szBorderOverride[0] ? m_szBorderOverride : "ComboBoxBorder" ) );
+}
+
+void ComboBox::ApplySettings( KeyValues *pInResourceData )
+{
+ BaseClass::ApplySettings( pInResourceData );
+
+ const char *pBorderOverride = pInResourceData->GetString( "border_override", NULL );
+ if ( pBorderOverride )
+ {
+ V_strncpy( m_szBorderOverride, pBorderOverride, sizeof( m_szBorderOverride ) );
+ }
+
+ KeyValues *pKVButton = pInResourceData->FindKey( "Button" );
+ if ( pKVButton && m_pButton )
+ {
+ m_pButton->ApplySettings( pKVButton );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the visiblity of the drop down menu button.
+//-----------------------------------------------------------------------------
+void ComboBox::SetDropdownButtonVisible(bool state)
+{
+ m_pButton->SetVisible(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: overloads TextEntry MousePressed
+//-----------------------------------------------------------------------------
+void ComboBox::OnMousePressed(MouseCode code)
+{
+ if ( !m_pDropDown )
+ return;
+
+ if ( !IsEnabled() )
+ return;
+
+ // make sure it's getting pressed over us (it may not be due to mouse capture)
+ if ( !IsCursorOver() )
+ {
+ HideMenu();
+ return;
+ }
+
+ if ( IsEditable() )
+ {
+ BaseClass::OnMousePressed(code);
+ HideMenu();
+ }
+ else
+ {
+ // clicking on a non-editable text box just activates the drop down menu
+ RequestFocus();
+ DoClick();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Double-click acts the same as a single-click
+//-----------------------------------------------------------------------------
+void ComboBox::OnMouseDoublePressed(MouseCode code)
+{
+ if (IsEditable())
+ {
+ BaseClass::OnMouseDoublePressed(code);
+ }
+ else
+ {
+ OnMousePressed(code);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when a command is received from the menu
+// Changes the label text to be that of the command
+// Input : char *command -
+//-----------------------------------------------------------------------------
+void ComboBox::OnCommand( const char *command )
+{
+ if (!stricmp(command, "ButtonClicked"))
+ {
+ // hide / show the menu underneath
+ DoClick();
+ }
+
+ Panel::OnCommand(command);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ComboBox::OnSetText(const wchar_t *newtext)
+{
+ // see if the combobox text has changed, and if so, post a message detailing the new text
+ const wchar_t *text = newtext;
+
+ // check if the new text is a localized string, if so undo it
+ if (*text == '#')
+ {
+ char cbuf[255];
+ g_pVGuiLocalize->ConvertUnicodeToANSI(text, cbuf, 255);
+
+ // try lookup in localization tables
+ StringIndex_t unlocalizedTextSymbol = g_pVGuiLocalize->FindIndex(cbuf + 1);
+
+ if (unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX)
+ {
+ // we have a new text value
+ text = g_pVGuiLocalize->GetValueByIndex(unlocalizedTextSymbol);
+ }
+ }
+
+ wchar_t wbuf[255];
+ GetText(wbuf, 254);
+
+ if ( wcscmp(wbuf, text) )
+ {
+ // text has changed
+ SetText(text);
+
+ // fire off that things have changed
+ if ( !m_bPreventTextChangeMessage )
+ {
+ PostActionSignal(new KeyValues("TextChanged", "text", text));
+ }
+ Repaint();
+ }
+
+ // close the box
+ HideMenu();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: hides the menu
+//-----------------------------------------------------------------------------
+void ComboBox::HideMenu(void)
+{
+ if ( !m_pDropDown )
+ return;
+
+ // hide the menu
+ m_pDropDown->SetVisible(false);
+ Repaint();
+ OnHideMenu(m_pDropDown);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: shows the menu
+//-----------------------------------------------------------------------------
+void ComboBox::ShowMenu(void)
+{
+ if ( !m_pDropDown )
+ return;
+
+ // hide the menu
+ m_pDropDown->SetVisible(false);
+ DoClick();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the window loses focus; hides the menu
+//-----------------------------------------------------------------------------
+void ComboBox::OnKillFocus()
+{
+ SelectNoText();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the menu is closed
+//-----------------------------------------------------------------------------
+void ComboBox::OnMenuClose()
+{
+ HideMenu();
+
+ if ( HasFocus() )
+ {
+ SelectAllText(false);
+ }
+ else if ( m_bHighlight )
+ {
+ m_bHighlight = false;
+ // we want the text to be highlighted when we request the focus
+// SelectAllOnFirstFocus(true);
+ RequestFocus();
+ }
+ // if cursor is in this box or the arrow box
+ else if ( IsCursorOver() )// make sure it's getting pressed over us (it may not be due to mouse capture)
+ {
+ SelectAllText(false);
+ OnCursorEntered();
+ // Get focus so the box will unhighlight if we click somewhere else.
+ RequestFocus();
+ }
+ else
+ {
+ m_pButton->SetArmed(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles hotkey accesses
+// FIXME: make this open different directions as necessary see menubutton.
+//-----------------------------------------------------------------------------
+void ComboBox::DoClick()
+{
+ // menu is already visible, hide the menu
+ if ( m_pDropDown->IsVisible() )
+ {
+ HideMenu();
+ return;
+ }
+
+ // do nothing if menu is not enabled
+ if ( !m_pDropDown->IsEnabled() )
+ {
+ return;
+ }
+ // force the menu to Think
+ m_pDropDown->PerformLayout();
+
+ // make sure we're at the top of the draw order (and therefore our children as well)
+ // RequestFocus();
+
+ // We want the item that is shown in the combo box to show as selected
+ int itemToSelect = -1;
+ int i;
+ wchar_t comboBoxContents[255];
+ GetText(comboBoxContents, 255);
+ for ( i = 0 ; i < m_pDropDown->GetItemCount() ; i++ )
+ {
+ wchar_t menuItemName[255];
+ int menuID = m_pDropDown->GetMenuID(i);
+ m_pDropDown->GetMenuItem(menuID)->GetText(menuItemName, 255);
+ if (!wcscmp(menuItemName, comboBoxContents))
+ {
+ itemToSelect = i;
+ break;
+ }
+ }
+ // if we found a match, highlight it on opening the menu
+ if ( itemToSelect >= 0 )
+ {
+ m_pDropDown->SetCurrentlyHighlightedItem( m_pDropDown->GetMenuID(itemToSelect) );
+ }
+
+ // reset the dropdown's position
+ DoMenuLayout();
+
+
+ // make sure we're at the top of the draw order (and therefore our children as well)
+ // this important to make sure the menu will be drawn in the foreground
+ MoveToFront();
+
+ // !KLUDGE! Force alpha to solid. Otherwise,
+ // we run into weird VGUI problems with pops
+ // and the stencil test
+ Color c = m_pDropDown->GetBgColor();
+ c[3] = 255;
+ m_pDropDown->SetBgColor( c );
+
+ // notify
+ OnShowMenu(m_pDropDown);
+
+ // show the menu
+ m_pDropDown->SetVisible(true);
+
+ // bring to focus
+ m_pDropDown->RequestFocus();
+
+ // no text is highlighted when the menu is opened
+ SelectNoText();
+
+ // highlight the arrow while menu is open
+ m_pButton->SetArmed(true);
+
+ Repaint();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Brighten the arrow on the button when entering the box
+//-----------------------------------------------------------------------------
+void ComboBox::OnCursorEntered()
+{
+ // want the arrow to go white when we enter the box
+ m_pButton->OnCursorEntered();
+ TextEntry::OnCursorEntered();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Dim the arrow on the button when exiting the box
+//-----------------------------------------------------------------------------
+void ComboBox::OnCursorExited()
+{
+ // want the arrow to go grey when we exit the box if the menu is not open
+ if ( !m_pDropDown->IsVisible() )
+ {
+ m_pButton->SetArmed(false);
+ TextEntry::OnCursorExited();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#ifdef _X360
+void ComboBox::OnMenuItemSelected()
+{
+ m_bHighlight = true;
+ // For editable cbs, fill in the text field from whatever is chosen from the dropdown...
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [pfreese] The text for the combo box should be updated regardless of its
+ // editable state, and in any case, the member variable below was never
+ // correctly initialized.
+ //=============================================================================
+
+ // if ( m_bAllowEdit )
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ {
+ int idx = GetActiveItem();
+ if ( idx >= 0 )
+ {
+ wchar_t name[ 256 ];
+ GetItemText( idx, name, sizeof( name ) );
+
+ OnSetText( name );
+ }
+ }
+
+ Repaint();
+
+ // go to the next control
+ if(!NavigateDown())
+ {
+ NavigateUp();
+ }
+}
+#else
+void ComboBox::OnMenuItemSelected()
+{
+ m_bHighlight = true;
+ // For editable cbs, fill in the text field from whatever is chosen from the dropdown...
+ //if ( m_bAllowEdit )
+ {
+ int idx = GetActiveItem();
+ if ( idx >= 0 )
+ {
+ wchar_t name[ 256 ];
+ GetItemText( idx, name, sizeof( name ) );
+
+ OnSetText( name );
+ }
+ }
+
+ Repaint();
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ComboBox::OnSizeChanged(int wide, int tall)
+{
+ BaseClass::OnSizeChanged( wide, tall);
+
+ // set the drawwidth.
+ int bwide, btall;
+ PerformLayout();
+ m_pButton->GetSize( bwide, btall);
+ SetDrawWidth( wide - bwide );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#ifdef _X360
+void ComboBox::OnSetFocus()
+{
+ BaseClass::OnSetFocus();
+
+ GotoTextEnd();
+ SelectAllText(true);
+}
+#else
+void ComboBox::OnSetFocus()
+{
+ BaseClass::OnSetFocus();
+
+ GotoTextEnd();
+ SelectAllText(false);
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#ifdef _X360
+void ComboBox::OnKeyCodePressed(KeyCode code)
+{
+ switch ( GetBaseButtonCode( code ) )
+ {
+ case KEY_XBUTTON_A:
+ DoClick();
+ break;
+ case KEY_XBUTTON_UP:
+ case KEY_XSTICK1_UP:
+ case KEY_XSTICK2_UP:
+ if(m_pDropDown->IsVisible())
+ {
+ MoveAlongMenuItemList(-1);
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed(code);
+ }
+ break;
+ case KEY_XBUTTON_DOWN:
+ case KEY_XSTICK1_DOWN:
+ case KEY_XSTICK2_DOWN:
+ if(m_pDropDown->IsVisible())
+ {
+ MoveAlongMenuItemList(1);
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed(code);
+ }
+ break;
+ default:
+ BaseClass::OnKeyCodePressed(code);
+ break;
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles up/down arrows
+//-----------------------------------------------------------------------------
+void ComboBox::OnKeyCodeTyped(KeyCode code)
+{
+ bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
+
+ if (alt)
+ {
+ switch (code)
+ {
+ case KEY_UP:
+ case KEY_DOWN:
+ {
+ DoClick();
+ break;
+ }
+ default:
+ {
+ BaseClass::OnKeyCodeTyped(code);
+ break;
+ }
+ }
+ }
+ else
+ {
+ switch (code)
+ {
+ case KEY_HOME:
+ case KEY_END:
+ case KEY_PAGEUP:
+ case KEY_PAGEDOWN:
+ case KEY_UP:
+ case KEY_DOWN:
+ {
+ int itemSelected = m_pDropDown->GetCurrentlyHighlightedItem();
+ m_pDropDown->OnKeyCodeTyped(code);
+ int itemToSelect = m_pDropDown->GetCurrentlyHighlightedItem();
+
+ if ( itemToSelect != itemSelected )
+ {
+ SelectMenuItem(itemToSelect);
+ }
+ break;
+ }
+
+ case KEY_ENTER:
+ {
+ int itemToSelect = m_pDropDown->GetCurrentlyHighlightedItem();
+ m_pDropDown->ActivateItem(itemToSelect);
+ break;
+ }
+
+ default:
+ {
+ BaseClass::OnKeyCodeTyped(code);
+ break;
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: handles key input
+//-----------------------------------------------------------------------------
+void ComboBox::OnKeyTyped(wchar_t unichar)
+{
+ if ( IsEditable() || unichar == '\t') // don't play with key presses in edit mode
+ {
+ BaseClass::OnKeyTyped( unichar );
+ return;
+ }
+
+ int itemSelected = m_pDropDown->GetCurrentlyHighlightedItem();
+ m_pDropDown->OnKeyTyped(unichar);
+ int itemToSelect = m_pDropDown->GetCurrentlyHighlightedItem();
+
+ if ( itemToSelect != itemSelected )
+ {
+ SelectMenuItem(itemToSelect);
+ }
+ else
+ {
+ BaseClass::OnKeyTyped( unichar );
+ }
+}
+
+void ComboBox::SelectMenuItem(int itemToSelect)
+{
+ // if we found this item, then we scroll up or down
+ if ( itemToSelect >= 0 && itemToSelect < m_pDropDown->GetItemCount() )
+ {
+ wchar_t menuItemName[255];
+
+ int menuID = m_pDropDown->GetMenuID(itemToSelect);
+ m_pDropDown->GetMenuItem(menuID)->GetText(menuItemName, 254);
+ OnSetText(menuItemName);
+ SelectAllText(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ComboBox::MoveAlongMenuItemList(int direction)
+{
+ // We want the item that is shown in the combo box to show as selected
+ int itemToSelect = -1;
+ wchar_t menuItemName[255];
+ int i;
+
+ wchar_t comboBoxContents[255];
+ GetText(comboBoxContents, 254);
+ for ( i = 0 ; i < m_pDropDown->GetItemCount() ; i++ )
+ {
+ int menuID = m_pDropDown->GetMenuID(i);
+ m_pDropDown->GetMenuItem(menuID)->GetText(menuItemName, 254);
+
+ if ( !wcscmp(menuItemName, comboBoxContents) )
+ {
+ itemToSelect = i;
+ break;
+ }
+ }
+
+ if ( itemToSelect >= 0 )
+ {
+ int newItem = itemToSelect + direction;
+ if ( newItem < 0 )
+ {
+ newItem = 0;
+ }
+ else if ( newItem >= m_pDropDown->GetItemCount() )
+ {
+ newItem = m_pDropDown->GetItemCount() - 1;
+ }
+ SelectMenuItem(newItem);
+ }
+
+}
+
+void ComboBox::MoveToFirstMenuItem()
+{
+ SelectMenuItem(0);
+}
+
+void ComboBox::MoveToLastMenuItem()
+{
+ SelectMenuItem(m_pDropDown->GetItemCount() - 1);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the direction from the menu button the menu should open
+//-----------------------------------------------------------------------------
+void ComboBox::SetOpenDirection(Menu::MenuDirection_e direction)
+{
+ m_iDirection = direction;
+}
+
+void ComboBox::SetFont( HFont font )
+{
+ BaseClass::SetFont( font );
+
+ m_pDropDown->SetFont( font );
+}
+
+
+void ComboBox::SetUseFallbackFont( bool bState, HFont hFallback )
+{
+ BaseClass::SetUseFallbackFont( bState, hFallback );
+ m_pDropDown->SetUseFallbackFont( bState, hFallback );
+}
diff --git a/mp/src/vgui2/vgui_controls/ControllerMap.cpp b/mp/src/vgui2/vgui_controls/ControllerMap.cpp
new file mode 100644
index 00000000..a0c5d8fb
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ControllerMap.cpp
@@ -0,0 +1,160 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vgui_controls/ControllerMap.h"
+#include "vgui/ISurface.h"
+#include "vgui/KeyCode.h"
+#include "KeyValues.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+struct keystring_t
+{
+ int code;
+ const char *name;
+};
+
+static const keystring_t s_ControllerButtons[] = { { KEY_XBUTTON_UP, "KEY_XBUTTON_UP" },
+ { KEY_XBUTTON_DOWN, "KEY_XBUTTON_DOWN" },
+ { KEY_XBUTTON_LEFT, "KEY_XBUTTON_LEFT" },
+ { KEY_XBUTTON_RIGHT, "KEY_XBUTTON_RIGHT" },
+ { KEY_XBUTTON_START, "KEY_XBUTTON_START" },
+ { KEY_XBUTTON_BACK, "KEY_XBUTTON_BACK" },
+ { KEY_XBUTTON_STICK1, "KEY_XBUTTON_STICK1" },
+ { KEY_XBUTTON_STICK2, "KEY_XBUTTON_STICK2" },
+ { KEY_XBUTTON_A, "KEY_XBUTTON_A" },
+ { KEY_XBUTTON_B, "KEY_XBUTTON_B" },
+ { KEY_XBUTTON_X, "KEY_XBUTTON_X" },
+ { KEY_XBUTTON_Y, "KEY_XBUTTON_Y" },
+ { KEY_XBUTTON_LEFT_SHOULDER, "KEY_XBUTTON_LEFT_SHOULDER" },
+ { KEY_XBUTTON_RIGHT_SHOULDER, "KEY_XBUTTON_RIGHT_SHOULDER" },
+ { KEY_XBUTTON_LTRIGGER, "KEY_XBUTTON_LTRIGGER" },
+ { KEY_XBUTTON_RTRIGGER, "KEY_XBUTTON_RTRIGGER" },
+ { KEY_XSTICK1_UP, "KEY_XSTICK1_UP" },
+ { KEY_XSTICK1_DOWN, "KEY_XSTICK1_DOWN" },
+ { KEY_XSTICK1_LEFT, "KEY_XSTICK1_LEFT" },
+ { KEY_XSTICK1_RIGHT, "KEY_XSTICK1_RIGHT" },
+ { KEY_XSTICK2_UP, "KEY_XSTICK2_UP" },
+ { KEY_XSTICK2_DOWN, "KEY_XSTICK2_DOWN" },
+ { KEY_XSTICK2_LEFT, "KEY_XSTICK2_LEFT" },
+ { KEY_XSTICK2_RIGHT, "KEY_XSTICK2_RIGHT" } };
+
+//-----------------------------------------------------------------------------
+// Purpose: for the UtlMap
+//-----------------------------------------------------------------------------
+bool lessFunc( const int &lhs, const int &rhs )
+{
+ return lhs < rhs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: converts a button name string to the equivalent keycode
+//-----------------------------------------------------------------------------
+int StringToButtonCode( const char *name )
+{
+ for ( int i = 0; i < ARRAYSIZE( s_ControllerButtons ); ++i )
+ {
+ if ( !Q_stricmp( s_ControllerButtons[i].name, name ) )
+ return s_ControllerButtons[i].code;
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: intercepts the keycode from its parent, and handles it according to
+// the button map. If the keycode isn't handled, it gets passed on to the parent.
+//-----------------------------------------------------------------------------
+void CControllerMap::OnKeyCodeTyped( vgui::KeyCode code )
+{
+ int idx = m_buttonMap.Find( code );
+ if ( idx != m_buttonMap.InvalidIndex() )
+ {
+ GetParent()->OnCommand( m_buttonMap[idx].cmd.String() );
+ }
+ else
+ {
+ // Disable input before forwarding the message
+ // so it doesn't feed back here again.
+ SetKeyBoardInputEnabled( false );
+ GetParent()->OnKeyCodeTyped( code );
+ SetKeyBoardInputEnabled( true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: constructor
+//-----------------------------------------------------------------------------
+CControllerMap::CControllerMap( vgui::Panel *parent, const char *name ) : BaseClass( parent, name )
+{
+ m_buttonMap.SetLessFunc( lessFunc );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets up the button/command bindings
+//-----------------------------------------------------------------------------
+void CControllerMap::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings( inResourceData );
+
+ // loop through all the data adding items to the menu
+ for (KeyValues *dat = inResourceData->GetFirstSubKey(); dat != NULL; dat = dat->GetNextKey())
+ {
+ if ( !Q_stricmp( dat->GetName(), "button" ) )
+ {
+ const char *buttonName = dat->GetString( "name", "" );
+ int keycode = StringToButtonCode( buttonName );
+ if ( keycode != -1 )
+ {
+ button_t b;
+ b.cmd = CUtlSymbol( dat->GetString( "command", "" ) );
+
+ // text and icon are optional - their existence means this button
+ // should be displayed in the footer panel.
+ const char *helpText = dat->GetString( "text", NULL );
+ if ( helpText )
+ {
+ b.text = CUtlSymbol( helpText );
+ b.icon = CUtlSymbol( dat->GetString( "icon", NULL ) );
+ }
+
+ m_buttonMap.Insert( keycode, b );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the help text for a binding, if it exists
+//-----------------------------------------------------------------------------
+const char *CControllerMap::GetBindingText( int idx )
+{
+ CUtlSymbol s = m_buttonMap[idx].text;
+ if ( s.IsValid() )
+ {
+ return s.String();
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the icon for a binding, if it exists
+//-----------------------------------------------------------------------------
+const char *CControllerMap::GetBindingIcon( int idx )
+{
+ CUtlSymbol s = m_buttonMap[idx].icon;
+ if ( s.IsValid() )
+ {
+ return s.String();
+ }
+ return NULL;
+}
+
+DECLARE_BUILD_FACTORY( CControllerMap );
+
diff --git a/mp/src/vgui2/vgui_controls/DirectorySelectDialog.cpp b/mp/src/vgui2/vgui_controls/DirectorySelectDialog.cpp
new file mode 100644
index 00000000..a7474fb1
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/DirectorySelectDialog.cpp
@@ -0,0 +1,594 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#define PROTECTED_THINGS_DISABLE
+
+#include <vgui_controls/Button.h>
+#include <vgui_controls/ComboBox.h>
+#include <vgui_controls/DirectorySelectDialog.h>
+#include <vgui_controls/TreeView.h>
+#include <vgui_controls/ImageList.h>
+#include <vgui_controls/MessageBox.h>
+#include <vgui/Cursor.h>
+#include <KeyValues.h>
+#include <vgui/IInput.h>
+#include <vgui/ISurface.h>
+#include <vgui/ISystem.h>
+#include <filesystem.h>
+
+#ifdef WIN32
+#include <direct.h>
+#include <stdio.h>
+#include <io.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DirectoryTreeView::DirectoryTreeView(DirectorySelectDialog *parent, const char *name) : TreeView(parent, name)
+{
+ m_pParent = parent;
+}
+
+void DirectoryTreeView::GenerateChildrenOfNode(int itemIndex)
+{
+ m_pParent->GenerateChildrenOfDirectoryNode(itemIndex);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Used to prompt the user to create a directory
+//-----------------------------------------------------------------------------
+class CreateDirectoryDialog : public Frame
+{
+ DECLARE_CLASS_SIMPLE(CreateDirectoryDialog, Frame);
+
+public:
+ CreateDirectoryDialog(Panel *parent, const char *defaultCreateDirName) : BaseClass(parent, NULL)
+ {
+ SetSize(320, 100);
+ SetSizeable(false);
+ SetTitle("Choose directory name", false);
+ MoveToCenterOfScreen();
+
+ m_pOKButton = new Button(this, "OKButton", "#vgui_ok");
+ m_pCancelButton = new Button(this, "OKButton", "#vgui_cancel");
+ m_pNameEntry = new TextEntry(this, "NameEntry");
+
+ m_pOKButton->SetCommand("OK");
+ m_pCancelButton->SetCommand("Close");
+ m_pNameEntry->SetText(defaultCreateDirName);
+ m_pNameEntry->RequestFocus();
+ m_pNameEntry->SelectAllText(true);
+
+ // If some other window was hogging the input focus, then we have to hog it or else we'll never get input.
+ m_PrevAppFocusPanel = vgui::input()->GetAppModalSurface();
+ if ( m_PrevAppFocusPanel )
+ vgui::input()->SetAppModalSurface( GetVPanel() );
+ }
+
+ ~CreateDirectoryDialog()
+ {
+ if ( m_PrevAppFocusPanel )
+ vgui::input()->SetAppModalSurface( m_PrevAppFocusPanel );
+ }
+
+ virtual void PerformLayout()
+ {
+ BaseClass::PerformLayout();
+
+ m_pNameEntry->SetBounds(24, 32, GetWide() - 48, 24);
+ m_pOKButton->SetBounds(GetWide() - 176, 64, 72, 24);
+ m_pCancelButton->SetBounds(GetWide() - 94, 64, 72, 24);
+ }
+
+ virtual void OnCommand(const char *command)
+ {
+ if (!stricmp(command, "OK"))
+ {
+ PostActionSignal(new KeyValues("CreateDirectory", "dir", GetControlString("NameEntry")));
+ Close();
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+ }
+
+ virtual void OnClose()
+ {
+ BaseClass::OnClose();
+ MarkForDeletion();
+ }
+
+private:
+ vgui::Button *m_pOKButton;
+ vgui::Button *m_pCancelButton;
+ vgui::TextEntry *m_pNameEntry;
+ vgui::VPANEL m_PrevAppFocusPanel;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+DirectorySelectDialog::DirectorySelectDialog(vgui::Panel *parent, const char *title) : Frame(parent, NULL)
+{
+ SetTitle(title, true);
+ SetSize(320, 360);
+ SetMinimumSize(300, 240);
+ m_szCurrentDir[0] = 0;
+ m_szDefaultCreateDirName[0] = 0;
+
+ m_pDirTree = new DirectoryTreeView(this, "DirTree");
+ m_pDriveCombo = new ComboBox(this, "DriveCombo", 6, false);
+ m_pCancelButton = new Button(this, "CancelButton", "#VGui_Cancel");
+ m_pSelectButton = new Button(this, "SelectButton", "#VGui_Select");
+ m_pCreateButton = new Button(this, "CreateButton", "#VGui_CreateFolder");
+ m_pCancelButton->SetCommand("Cancel");
+ m_pSelectButton->SetCommand("Select");
+ m_pCreateButton->SetCommand("Create");
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: lays out controls
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ // lay out all the controls
+ m_pDriveCombo->SetBounds(24, 30, GetWide() - 48, 24);
+ m_pDirTree->SetBounds(24, 64, GetWide() - 48, GetTall() - 128);
+
+ m_pCreateButton->SetBounds(24, GetTall() - 48, 104, 24);
+ m_pSelectButton->SetBounds(GetWide() - 172, GetTall() - 48, 72, 24);
+ m_pCancelButton->SetBounds(GetWide() - 96, GetTall() - 48, 72, 24);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: lays out controls
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::ApplySchemeSettings(IScheme *pScheme)
+{
+ ImageList *imageList = new ImageList(false);
+ imageList->AddImage(scheme()->GetImage("Resource/icon_folder", false));
+ imageList->AddImage(scheme()->GetImage("Resource/icon_folder_selected", false));
+ m_pDirTree->SetImageList(imageList, true);
+
+ BaseClass::ApplySchemeSettings(pScheme);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the start string forward until we hit a slash and return the
+// the first character past the trailing slash
+//-----------------------------------------------------------------------------
+inline const char *MoveToNextSubDir( const char *pStart, int *nCount )
+{
+ int nMoved = 0;
+
+ // Move past pre-pended slash
+ if ( pStart[nMoved] == '\\' )
+ {
+ nMoved++;
+ }
+
+ // Move past the current block of text until we've hit the next path seperator (or end)
+ while ( pStart[nMoved] != '\\' && pStart[nMoved] != '\0' )
+ {
+ nMoved++;
+ }
+
+ // Move past trailing slash
+ if ( pStart[nMoved] == '\\' )
+ {
+ nMoved++;
+ }
+
+ // Give back a count if they've supplied a pointer
+ if ( nCount != NULL )
+ {
+ *nCount = nMoved;
+ }
+
+ // The beginning of the next string, past slash
+ return (pStart+nMoved);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Walk through our directory structure given a path as our guide, while expanding
+// and populating the nodes of the tree view to match
+// Input : *path - path (with drive letter) to show
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::ExpandTreeToPath( const char *lpszPath, bool bSelectFinalDirectory /*= true*/ )
+{
+ // Make sure our slashes are correct!
+ char workPath[MAX_PATH];
+ Q_strncpy( workPath, lpszPath, sizeof(workPath) );
+ Q_FixSlashes( workPath );
+
+ // Set us to the work drive
+ SetStartDirectory( workPath );
+
+ // Check that the path is valid
+ if ( workPath[0] == '\0' || DoesDirectoryHaveSubdirectories( m_szCurrentDrive, "" ) == false )
+ {
+ // Failing, start in C:
+ SetStartDirectory( "C:\\" );
+ }
+
+ // Start at the root of our tree
+ int nItemIndex = m_pDirTree->GetRootItemIndex();
+
+ // Move past the drive letter to the first subdir
+ int nPathPos = 0;
+ const char *lpszSubDirName = MoveToNextSubDir( workPath, &nPathPos );
+ const char *lpszLastSubDirName = NULL;
+ int nPathIncr = 0;
+ char subDirName[MAX_PATH];
+
+ // While there are subdirectory names present, expand and populate the tree with their subdirectories
+ while ( lpszSubDirName[0] != '\0' )
+ {
+ // Move our string pointer forward while keeping where our last subdir started off
+ lpszLastSubDirName = lpszSubDirName;
+ lpszSubDirName = MoveToNextSubDir( lpszSubDirName, &nPathIncr );
+
+ // Get the span between the last subdir and the new one
+ Q_StrLeft( lpszLastSubDirName, nPathIncr, subDirName, sizeof(subDirName) );
+ Q_StripTrailingSlash( subDirName );
+
+ // Increment where we are in the string for use later
+ nPathPos += nPathIncr;
+
+ // Run through the list and expand to our currently selected directory
+ for ( int i = 0; i < m_pDirTree->GetNumChildren( nItemIndex ); i++ )
+ {
+ // Get the child and data for it
+ int nChild = m_pDirTree->GetChild( nItemIndex, i );
+ KeyValues *pValues = m_pDirTree->GetItemData( nChild );
+
+ // See if this matches
+ if ( Q_stricmp( pValues->GetString( "Text" ), subDirName ) == 0 )
+ {
+ // This is the new root item
+ nItemIndex = nChild;
+
+ // Get the full path (starting from the drive letter) up to our current subdir
+ Q_strncpy( subDirName, workPath, nPathPos );
+ Q_AppendSlash( subDirName, sizeof(subDirName) );
+
+ // Expand the tree node and populate its subdirs for our next iteration
+ ExpandTreeNode( subDirName, nItemIndex );
+ break;
+ }
+ }
+ }
+
+ // Select our last directory if we've been asked to (and it's valid)
+ if ( bSelectFinalDirectory && m_pDirTree->IsItemIDValid( nItemIndex ) )
+ {
+ // If we don't call this once before selecting an item, the tree will not be properly expanded
+ // before it calculates how to show the selected item in the view
+ PerformLayout();
+
+ // Select that item
+ m_pDirTree->AddSelectedItem( nItemIndex, true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets where it should start searching
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::SetStartDirectory(const char *path)
+{
+ strncpy(m_szCurrentDir, path, sizeof(m_szCurrentDir));
+ strncpy(m_szCurrentDrive, path, sizeof(m_szCurrentDrive));
+ m_szCurrentDrive[sizeof(m_szCurrentDrive) - 1] = 0;
+ char *firstSlash = strstr(m_szCurrentDrive, "\\");
+ if (firstSlash)
+ {
+ firstSlash[1] = 0;
+ }
+
+ BuildDirTree();
+ BuildDriveChoices();
+
+ // update state of create directory button
+ int selectedIndex = m_pDirTree->GetFirstSelectedItem();
+ if (m_pDirTree->IsItemIDValid(selectedIndex))
+ {
+ m_pCreateButton->SetEnabled(true);
+ }
+ else
+ {
+ m_pCreateButton->SetEnabled(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets what name should show up by default in the create directory dialog
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::SetDefaultCreateDirectoryName(const char *defaultCreateDirName)
+{
+ strncpy(m_szDefaultCreateDirName, defaultCreateDirName, sizeof(m_szDefaultCreateDirName));
+ m_szDefaultCreateDirName[sizeof(m_szDefaultCreateDirName) - 1] = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: opens the dialog
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::DoModal()
+{
+ input()->SetAppModalSurface(GetVPanel());
+ BaseClass::Activate();
+ MoveToCenterOfScreen();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Builds drive choices
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::BuildDriveChoices()
+{
+ m_pDriveCombo->DeleteAllItems();
+
+ char drives[256] = { 0 };
+ int len = system()->GetAvailableDrives(drives, sizeof(drives));
+ char *pBuf = drives;
+ KeyValues *kv = new KeyValues("drive");
+ for (int i = 0; i < len / 4; i++)
+ {
+ kv->SetString("drive", pBuf);
+ int itemID = m_pDriveCombo->AddItem(pBuf, kv);
+ if (!stricmp(pBuf, m_szCurrentDrive))
+ {
+ m_pDriveCombo->ActivateItem(itemID);
+ }
+
+ pBuf += 4;
+ }
+ kv->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Builds the base tree directory
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::BuildDirTree()
+{
+ // clear current tree
+ m_pDirTree->RemoveAll();
+
+ // add in a root
+ int rootIndex = m_pDirTree->AddItem(new KeyValues("root", "Text", m_szCurrentDrive), -1);
+
+ // build first level of the tree
+ ExpandTreeNode(m_szCurrentDrive, rootIndex);
+
+ // start the root expanded
+ m_pDirTree->ExpandItem(rootIndex, true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: expands a path
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::ExpandTreeNode(const char *path, int parentNodeIndex)
+{
+ // set the small wait cursor
+ surface()->SetCursor(dc_waitarrow);
+
+ // get all the subfolders of the current drive
+ char searchString[512];
+ sprintf(searchString, "%s*.*", path);
+
+ FileFindHandle_t h;
+ const char *pFileName = g_pFullFileSystem->FindFirstEx( searchString, NULL, &h );
+ for ( ; pFileName; pFileName = g_pFullFileSystem->FindNext( h ) )
+ {
+ if ( !Q_stricmp( pFileName, ".." ) || !Q_stricmp( pFileName, "." ) )
+ continue;
+
+ KeyValues *kv = new KeyValues("item");
+ kv->SetString("Text", pFileName);
+ // set the folder image
+ kv->SetInt("Image", 1);
+ kv->SetInt("SelectedImage", 1);
+ kv->SetInt("Expand", DoesDirectoryHaveSubdirectories(path, pFileName));
+ m_pDirTree->AddItem(kv, parentNodeIndex);
+ }
+ g_pFullFileSystem->FindClose( h );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool DirectorySelectDialog::DoesDirectoryHaveSubdirectories(const char *path, const char *dir)
+{
+ char searchString[512];
+ sprintf(searchString, "%s%s\\*.*", path, dir);
+
+ FileFindHandle_t h;
+ const char *pFileName = g_pFullFileSystem->FindFirstEx( searchString, NULL, &h );
+ for ( ; pFileName; pFileName = g_pFullFileSystem->FindNext( h ) )
+ {
+ char szFullPath[ MAX_PATH ];
+ Q_snprintf( szFullPath, sizeof(szFullPath), "%s\\%s", path, pFileName );
+ Q_FixSlashes( szFullPath );
+ if ( g_pFullFileSystem->IsDirectory( szFullPath ) )
+ {
+ g_pFullFileSystem->FindClose( h );
+ return true;
+ }
+ }
+ g_pFullFileSystem->FindClose( h );
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generates the children for the specified node
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::GenerateChildrenOfDirectoryNode(int nodeIndex)
+{
+ // generate path
+ char path[512];
+ GenerateFullPathForNode(nodeIndex, path, sizeof(path));
+
+ // expand out
+ ExpandTreeNode(path, nodeIndex);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: creates the full path for a node
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::GenerateFullPathForNode(int nodeIndex, char *path, int pathBufferSize)
+{
+ // get all the nodes
+ CUtlLinkedList<int, int> nodes;
+ nodes.AddToTail(nodeIndex);
+ int parentIndex = nodeIndex;
+ while (1)
+ {
+ parentIndex = m_pDirTree->GetItemParent(parentIndex);
+ if (parentIndex == -1)
+ break;
+ nodes.AddToHead(parentIndex);
+ }
+
+ // walk the nodes, adding to the path
+ path[0] = 0;
+ bool bFirst = true;
+ FOR_EACH_LL( nodes, i )
+ {
+ KeyValues *kv = m_pDirTree->GetItemData( nodes[i] );
+ strcat(path, kv->GetString("Text"));
+
+ if (!bFirst)
+ {
+ strcat(path, "\\");
+ }
+ bFirst = false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles combo box changes
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::OnTextChanged()
+{
+ KeyValues *kv = m_pDriveCombo->GetActiveItemUserData();
+ if (!kv)
+ return;
+ const char *newDrive = kv->GetString("drive");
+ if (stricmp(newDrive, m_szCurrentDrive))
+ {
+ // drive changed, reset
+ SetStartDirectory(newDrive);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: creates a directory
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::OnCreateDirectory(const char *dir)
+{
+ int selectedIndex = m_pDirTree->GetFirstSelectedItem();
+ if (m_pDirTree->IsItemIDValid(selectedIndex))
+ {
+ char fullPath[512];
+ GenerateFullPathForNode(selectedIndex, fullPath, sizeof(fullPath));
+
+ // create the new directory underneath
+ strcat(fullPath, dir);
+ if (_mkdir(fullPath) == 0)
+ {
+ // add new path to tree view
+ KeyValues *kv = new KeyValues("item");
+ kv->SetString("Text", dir);
+ // set the folder image
+ kv->SetInt("Image", 1);
+ kv->SetInt("SelectedImage", 1);
+ int itemID = m_pDirTree->AddItem(kv, selectedIndex);
+
+ // select the item
+ m_pDirTree->AddSelectedItem( itemID, true );
+ }
+ else
+ {
+ // print error message
+ MessageBox *box = new MessageBox("#vgui_CreateDirectoryFail_Title", "#vgui_CreateDirectoryFail_Info");
+ box->DoModal(this);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: dialog closes
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::OnClose()
+{
+ BaseClass::OnClose();
+ MarkForDeletion();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: handles button commands
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::OnCommand(const char *command)
+{
+ if (!stricmp(command, "Cancel"))
+ {
+ Close();
+ }
+ else if (!stricmp(command, "Select"))
+ {
+ // path selected
+ int selectedIndex = m_pDirTree->GetFirstSelectedItem();
+ if (m_pDirTree->IsItemIDValid(selectedIndex))
+ {
+ char fullPath[512];
+ GenerateFullPathForNode(selectedIndex, fullPath, sizeof(fullPath));
+ PostActionSignal(new KeyValues("DirectorySelected", "dir", fullPath));
+ Close();
+ }
+ }
+ else if (!stricmp(command, "Create"))
+ {
+ int selectedIndex = m_pDirTree->GetFirstSelectedItem();
+ if (m_pDirTree->IsItemIDValid(selectedIndex))
+ {
+ CreateDirectoryDialog *dlg = new CreateDirectoryDialog(this, m_szDefaultCreateDirName);
+ dlg->AddActionSignalTarget(this);
+ dlg->Activate();
+ }
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the text in the combo
+//-----------------------------------------------------------------------------
+void DirectorySelectDialog::OnTreeViewItemSelected()
+{
+ int selectedIndex = m_pDirTree->GetFirstSelectedItem();
+ if (!m_pDirTree->IsItemIDValid(selectedIndex))
+ {
+ m_pCreateButton->SetEnabled(false);
+ return;
+ }
+ m_pCreateButton->SetEnabled(true);
+
+ // build the string
+ char fullPath[512];
+ GenerateFullPathForNode(selectedIndex, fullPath, sizeof(fullPath));
+
+ int itemID = m_pDriveCombo->GetActiveItem();
+ m_pDriveCombo->UpdateItem(itemID, fullPath, NULL);
+ m_pDriveCombo->SetText(fullPath);
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/Divider.cpp b/mp/src/vgui2/vgui_controls/Divider.cpp
new file mode 100644
index 00000000..26b843e7
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/Divider.cpp
@@ -0,0 +1,41 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <vgui/IScheme.h>
+
+#include <vgui_controls/Divider.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( Divider );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+Divider::Divider(Panel *parent, const char *name) : Panel(parent, name)
+{
+ SetSize(128, 2);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+Divider::~Divider()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets up the border as the line to draw as a divider
+//-----------------------------------------------------------------------------
+void Divider::ApplySchemeSettings(IScheme *pScheme)
+{
+ SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
+ BaseClass::ApplySchemeSettings(pScheme);
+}
diff --git a/mp/src/vgui2/vgui_controls/EditablePanel.cpp b/mp/src/vgui2/vgui_controls/EditablePanel.cpp
new file mode 100644
index 00000000..1980db6c
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/EditablePanel.cpp
@@ -0,0 +1,1069 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+
+#include <vgui/IPanel.h>
+#include <vgui/ISurface.h>
+#include <vgui/ISystem.h>
+#include <vgui/ILocalize.h>
+#include <KeyValues.h>
+#include "vgui/IVGui.h"
+
+#include <vgui_controls/BuildGroup.h>
+#include <vgui_controls/BuildModeDialog.h>
+#include <vgui_controls/EditablePanel.h>
+
+// these includes are all for the virtual contruction factory Dialog::CreateControlByName()
+#include <vgui_controls/Button.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/CheckButton.h>
+#include <vgui_controls/ComboBox.h>
+#include <vgui_controls/Menu.h>
+#include <vgui_controls/MenuItem.h>
+#include <vgui_controls/MessageBox.h>
+#include <vgui_controls/ProgressBar.h>
+#include <vgui_controls/RadioButton.h>
+#include <vgui_controls/ScrollBar.h>
+#include <vgui_controls/ToggleButton.h>
+#include <vgui_controls/ImagePanel.h>
+#include <vgui_controls/AnimatingImagePanel.h>
+#include <vgui_controls/Divider.h>
+#include <vgui_controls/URLLabel.h>
+#include <vgui_controls/RichText.h>
+#include <vgui_controls/BitmapImagePanel.h>
+
+#include "filesystem.h"
+#include "fmtstr.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( EditablePanel );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+#pragma warning( disable : 4355 )
+
+EditablePanel::EditablePanel(Panel *parent, const char *panelName) : Panel(parent, panelName), m_NavGroup(this)
+{
+ _buildGroup = new BuildGroup(this, this);
+ m_pszConfigName = NULL;
+ m_iConfigID = 0;
+ m_pDialogVariables = NULL;
+ m_bShouldSkipAutoResize = false;
+
+ // add ourselves to the build group
+ SetBuildGroup(GetBuildGroup());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+EditablePanel::EditablePanel(Panel *parent, const char *panelName, HScheme hScheme) : Panel(parent, panelName, hScheme), m_NavGroup(this)
+{
+ _buildGroup = new BuildGroup(this, this);
+ m_pszConfigName = NULL;
+ m_iConfigID = 0;
+ m_pDialogVariables = NULL;
+ m_bShouldSkipAutoResize = false;
+
+ // add ourselves to the build group
+ SetBuildGroup(GetBuildGroup());
+}
+
+#pragma warning( default : 4355 )
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+EditablePanel::~EditablePanel()
+{
+ delete [] m_pszConfigName;
+ delete _buildGroup;
+
+ if (m_pDialogVariables)
+ {
+ m_pDialogVariables->deleteThis();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when a child is added to the panel.
+//-----------------------------------------------------------------------------
+void EditablePanel::OnChildAdded(VPANEL child)
+{
+ BaseClass::OnChildAdded(child);
+
+ // add only if we're in the same module
+ Panel *panel = ipanel()->GetPanel(child, GetModuleName());
+ if (panel)
+ {
+ panel->SetBuildGroup(_buildGroup);
+ panel->AddActionSignalTarget(this);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void EditablePanel::OnKeyCodePressed( KeyCode code )
+{
+ static ConVarRef vgui_nav_lock_default_button( "vgui_nav_lock_default_button" );
+ if ( !vgui_nav_lock_default_button.IsValid() || vgui_nav_lock_default_button.GetInt() == 0 )
+ {
+ ButtonCode_t nButtonCode = GetBaseButtonCode( code );
+
+ // check for a default button
+ VPANEL panel = GetFocusNavGroup().GetCurrentDefaultButton();
+ if ( panel && !IsConsoleStylePanel() )
+ {
+ switch ( nButtonCode )
+ {
+ case KEY_XBUTTON_UP:
+ case KEY_XSTICK1_UP:
+ case KEY_XSTICK2_UP:
+ case KEY_UP:
+ case KEY_XBUTTON_DOWN:
+ case KEY_XSTICK1_DOWN:
+ case KEY_XSTICK2_DOWN:
+ case KEY_DOWN:
+ case KEY_XBUTTON_LEFT:
+ case KEY_XSTICK1_LEFT:
+ case KEY_XSTICK2_LEFT:
+ case KEY_LEFT:
+ case KEY_XBUTTON_RIGHT:
+ case KEY_XSTICK1_RIGHT:
+ case KEY_XSTICK2_RIGHT:
+ case KEY_RIGHT:
+ case KEY_XBUTTON_B:
+ // Navigating menus
+ vgui_nav_lock_default_button.SetValue( 1 );
+ PostMessage( panel, new KeyValues( "KeyCodePressed", "code", code ) );
+ return;
+
+ case KEY_XBUTTON_A:
+ case KEY_ENTER:
+ if ( ipanel()->IsVisible( panel ) && ipanel()->IsEnabled( panel ) )
+ {
+ // Activate the button
+ PostMessage( panel, new KeyValues( "Hotkey" ) );
+ return;
+ }
+ }
+ }
+ }
+
+ if ( !m_PassUnhandledInput )
+ return;
+
+ // Nothing to do with the button
+ BaseClass::OnKeyCodePressed( code );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Callback for when the panel size has been changed
+//-----------------------------------------------------------------------------
+void EditablePanel::OnSizeChanged(int wide, int tall)
+{
+ BaseClass::OnSizeChanged(wide, tall);
+ InvalidateLayout();
+
+ for (int i = 0; i < GetChildCount(); i++)
+ {
+ // perform auto-layout on the child panel
+ Panel *child = GetChild(i);
+ if ( !child )
+ continue;
+
+ int x, y, w, h;
+ child->GetBounds( x, y, w, h );
+
+ int px, py;
+ child->GetPinOffset( px, py );
+
+ int ox, oy;
+ child->GetResizeOffset( ox, oy );
+
+ int ex;
+ int ey;
+
+ AutoResize_e resize = child->GetAutoResize();
+ bool bResizeHoriz = ( resize == AUTORESIZE_RIGHT || resize == AUTORESIZE_DOWNANDRIGHT );
+ bool bResizeVert = ( resize == AUTORESIZE_DOWN || resize == AUTORESIZE_DOWNANDRIGHT );
+
+ // The correct version of this code would say:
+ // if ( resize != AUTORESIZE_NO )
+ // but we're very close to shipping and this causes artifacts in other vgui panels that now
+ // depend on this bug. So, I've added m_bShouldSkipAutoResize, which defaults to false but can
+ // be set using "skip_autoresize" in a .res file
+ if ( !m_bShouldSkipAutoResize )
+ {
+ PinCorner_e pinCorner = child->GetPinCorner();
+ if ( pinCorner == PIN_TOPRIGHT || pinCorner == PIN_BOTTOMRIGHT )
+ {
+ // move along with the right edge
+ ex = wide + px;
+ x = bResizeHoriz ? ox : ex - w;
+ }
+ else
+ {
+ x = px;
+ ex = bResizeHoriz ? wide + ox : px + w;
+ }
+
+ if ( pinCorner == PIN_BOTTOMLEFT || pinCorner == PIN_BOTTOMRIGHT )
+ {
+ // move along with the right edge
+ ey = tall + py;
+ y = bResizeVert ? oy : ey - h;
+ }
+ else
+ {
+ y = py;
+ ey = bResizeVert ? tall + oy : py + h;
+ }
+
+ // Clamp..
+ if ( ex < x )
+ {
+ ex = x;
+ }
+ if ( ey < y )
+ {
+ ey = y;
+ }
+
+ child->SetBounds( x, y, ex - x, ey - y );
+ child->InvalidateLayout();
+ }
+ }
+ Repaint();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void EditablePanel::OnCurrentDefaultButtonSet( VPANEL defaultButton )
+{
+ m_NavGroup.SetCurrentDefaultButton( defaultButton, false );
+
+ // forward the message up
+ if (GetVParent())
+ {
+ KeyValues *msg = new KeyValues("CurrentDefaultButtonSet");
+ msg->SetInt("button", ivgui()->PanelToHandle( defaultButton ) );
+ PostMessage(GetVParent(), msg);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void EditablePanel::OnDefaultButtonSet( VPANEL defaultButton )
+{
+ Panel *panel = ipanel()->GetPanel( defaultButton, GetModuleName() );
+
+ m_NavGroup.SetDefaultButton(panel);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void EditablePanel::OnFindDefaultButton()
+{
+ if (m_NavGroup.GetDefaultButton())
+ {
+ m_NavGroup.SetCurrentDefaultButton(m_NavGroup.GetDefaultButton());
+ }
+ else
+ {
+ if (GetVParent())
+ {
+ PostMessage(GetVParent(), new KeyValues("FindDefaultButton"));
+ }
+ }
+}
+
+struct leaf_t
+{
+ short x, y, wide, tall;
+ unsigned char split; // 0 no split; 1 x-axis, 2 y-axis
+ bool filled; // true if this is already filled
+ short splitpos; // place of split
+
+ leaf_t *left;
+ leaf_t *right;
+};
+
+leaf_t g_Leaves[256];
+int g_iNextLeaf;
+
+inline leaf_t *AllocLeaf()
+{
+ Assert(g_iNextLeaf < 255);
+
+ return &g_Leaves[g_iNextLeaf++];
+}
+
+void AddSolidToTree(leaf_t *leaf, int x, int y, int wide, int tall)
+{
+ // clip to this leaf
+ if (x < leaf->x)
+ {
+ wide -= (leaf->x - x);
+ if (wide < 1)
+ return;
+ x = leaf->x;
+ }
+ if (y < leaf->y)
+ {
+ tall -= (leaf->y - y);
+ if (tall < 1)
+ return;
+ y = leaf->y;
+ }
+ if (x + wide > leaf->x + leaf->wide)
+ {
+ wide -= ((x + wide) - (leaf->x + leaf->wide));
+ if (wide < 1)
+ return;
+ }
+ if (y + tall > leaf->y + leaf->tall)
+ {
+ tall -= ((y + tall) - (leaf->y + leaf->tall));
+ if (tall < 1)
+ return;
+ }
+
+ // the rect should now be completely within the leaf
+ if (leaf->split == 1)
+ {
+ // see if it is to the left or the right of the split
+ if (x < leaf->splitpos)
+ {
+ // it's to the left
+ AddSolidToTree(leaf->left, x, y, wide, tall);
+ }
+ else if (x + wide > leaf->splitpos)
+ {
+ // it's to the right
+ AddSolidToTree(leaf->right, x, y, wide, tall);
+ }
+ }
+ else if (leaf->split == 2)
+ {
+ // check y
+ // see if it is to the left (above) or the right (below) of the split
+ if (y < leaf->splitpos)
+ {
+ // it's above
+ AddSolidToTree(leaf->left, x, y, wide, tall);
+ }
+ else if (y + tall > leaf->splitpos)
+ {
+ // it's below
+ AddSolidToTree(leaf->right, x, y, wide, tall);
+ }
+ }
+ else
+ {
+ // this leaf is unsplit, make the first split against the first edge we find
+ if (x > leaf->x)
+ {
+ // split the left side of the rect
+ leaf->split = 1;
+ leaf->splitpos = (short)x;
+
+ // create 2 new leaves
+ leaf_t *left = AllocLeaf();
+ leaf_t *right = AllocLeaf();
+ memset(left, 0, sizeof(leaf_t));
+ memset(right, 0, sizeof(leaf_t));
+ leaf->left = left;
+ leaf->right = right;
+
+ left->x = leaf->x;
+ left->y = leaf->y;
+ left->wide = (short)(leaf->splitpos - leaf->x);
+ left->tall = leaf->tall;
+
+ right->x = leaf->splitpos;
+ right->y = leaf->y;
+ right->wide = (short)(leaf->wide - left->wide);
+ right->tall = leaf->tall;
+
+ // split the right leaf by the current rect
+ AddSolidToTree(leaf->right, x, y, wide, tall);
+ }
+ else if (y > leaf->y)
+ {
+ // split the top edge
+ leaf->split = 2;
+ leaf->splitpos = (short)y;
+
+ // create 2 new leaves (facing to the east)
+ leaf_t *left = AllocLeaf();
+ leaf_t *right = AllocLeaf();
+ memset(left, 0, sizeof(leaf_t));
+ memset(right, 0, sizeof(leaf_t));
+ leaf->left = left;
+ leaf->right = right;
+
+ left->x = leaf->x;
+ left->y = leaf->y;
+ left->wide = leaf->wide;
+ left->tall = (short)(y - leaf->y);
+
+ right->x = leaf->x;
+ right->y = leaf->splitpos;
+ right->wide = leaf->wide;
+ right->tall = (short)(leaf->tall + leaf->y - right->y);
+
+ // split the right leaf by the current rect
+ AddSolidToTree(leaf->right, x, y, wide, tall);
+ }
+ else if (x + wide < leaf->x + leaf->wide)
+ {
+ // split the right edge
+ leaf->split = 1;
+ leaf->splitpos = (short)(x + wide);
+
+ // create 2 new leaves
+ leaf_t *left = AllocLeaf();
+ leaf_t *right = AllocLeaf();
+ memset(left, 0, sizeof(leaf_t));
+ memset(right, 0, sizeof(leaf_t));
+ leaf->left = left;
+ leaf->right = right;
+
+ left->x = leaf->x;
+ left->y = leaf->y;
+ left->wide = (short)(leaf->splitpos - leaf->x);
+ left->tall = leaf->tall;
+
+ right->x = leaf->splitpos;
+ right->y = leaf->y;
+ right->wide = (short)(leaf->wide - left->wide);
+ right->tall = leaf->tall;
+
+ // split the left leaf by the current rect
+ AddSolidToTree(leaf->left, x, y, wide, tall);
+ }
+ else if (y + tall < leaf->y + leaf->tall)
+ {
+ // split the bottom edge
+ leaf->split = 2;
+ leaf->splitpos = (short)(y + tall);
+
+ // create 2 new leaves (facing to the east)
+ leaf_t *left = AllocLeaf();
+ leaf_t *right = AllocLeaf();
+ memset(left, 0, sizeof(leaf_t));
+ memset(right, 0, sizeof(leaf_t));
+ leaf->left = left;
+ leaf->right = right;
+
+ left->x = leaf->x;
+ left->y = leaf->y;
+ left->wide = leaf->wide;
+ left->tall = (short)(leaf->splitpos - leaf->y);
+
+ right->x = leaf->x;
+ right->y = leaf->splitpos;
+ right->wide = leaf->wide;
+ right->tall = (short)(leaf->tall - left->tall);
+
+ // split the left leaf by the current rect
+ AddSolidToTree(leaf->left, x, y, wide, tall);
+ }
+ else
+ {
+ // this is the exact same rect! don't draw this leaf
+ leaf->filled = true;
+ return;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fills the panel background, clipping if possible
+//-----------------------------------------------------------------------------
+void EditablePanel::PaintBackground()
+{
+ BaseClass::PaintBackground();
+ return;
+
+/*
+ test code, using a screenspace bsp tree to reduce overdraw in vgui
+ not yet fully functional
+
+// test: fill background with obnoxious color to show holes
+// surface()->DrawSetColor(Color(255, 0, 0, 255));
+// surface()->DrawFilledRect(0, 0, GetWide(), GetTall());
+// return;
+
+ // reset the leaf memory
+ g_iNextLeaf = 0;
+
+ leaf_t *headNode = AllocLeaf();
+ memset(headNode, 0, sizeof(leaf_t));
+
+ headNode->wide = (short)GetWide();
+ headNode->tall = (short)GetTall();
+
+ // split the leaf by the first child
+ for (int i = 0; i < GetChildCount(); i++)
+ {
+ Panel *child = GetChild(i);
+ if (child->IsOpaque())
+ {
+ int x, y, wide, tall;
+ child->GetBounds(x, y, wide, tall);
+
+ // ignore small children
+ if (wide + tall < 100)
+ continue;
+
+ AddSolidToTree(headNode, x, y, wide, tall);
+ }
+ }
+
+ // walk the built tree, painting the background
+ Color col = GetBgColor();
+ surface()->DrawSetColor(col);
+ for (i = 0; i < g_iNextLeaf; i++)
+ {
+ leaf_t *leaf = g_Leaves + i;
+ if (leaf->splitpos || leaf->filled)
+ continue;
+ surface()->DrawFilledRect(leaf->x, leaf->y, leaf->x + leaf->wide, leaf->y + leaf->tall);
+ }
+*/
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activates the build mode dialog for editing panels.
+//-----------------------------------------------------------------------------
+void EditablePanel::ActivateBuildMode()
+{
+ _buildGroup->SetEnabled(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads panel settings from a resource file.
+//-----------------------------------------------------------------------------
+void EditablePanel::LoadControlSettings(const char *resourceName, const char *pathID, KeyValues *pKeyValues, KeyValues *pConditions)
+{
+#if defined( DBGFLAG_ASSERT ) && !defined(OSX) && !defined(LINUX)
+ extern IFileSystem *g_pFullFileSystem;
+ // Since nobody wants to fix this assert, I'm making it a Msg instead:
+ // editablepanel.cpp (535) : Resource file "resource\DebugOptionsPanel.res" not found on disk!
+ // AssertMsg( g_pFullFileSystem->FileExists( resourceName ), CFmtStr( "Resource file \"%s\" not found on disk!", resourceName ).Access() );
+ if ( !g_pFullFileSystem->FileExists( resourceName ) )
+ {
+ Msg( "Resource file \"%s\" not found on disk!", resourceName );
+ }
+#endif
+ _buildGroup->LoadControlSettings(resourceName, pathID, pKeyValues, pConditions);
+ ForceSubPanelsToUpdateWithNewDialogVariables();
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: registers a file in the list of control settings, so the vgui dialog can choose between them to edit
+//-----------------------------------------------------------------------------
+void EditablePanel::RegisterControlSettingsFile(const char *resourceName, const char *pathID)
+{
+ _buildGroup->RegisterControlSettingsFile(resourceName, pathID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the name of this dialog so it can be saved in the user config area
+//-----------------------------------------------------------------------------
+void EditablePanel::LoadUserConfig(const char *configName, int dialogID)
+{
+ KeyValues *data = system()->GetUserConfigFileData(configName, dialogID);
+
+ delete [] m_pszConfigName;
+ int len = Q_strlen(configName) + 1;
+ m_pszConfigName = new char[ len ];
+ Q_strncpy(m_pszConfigName, configName, len );
+ m_iConfigID = dialogID;
+
+ // apply our user config settings (this will recurse through our children)
+ if (data)
+ {
+ ApplyUserConfigSettings(data);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: saves all the settings to the document
+//-----------------------------------------------------------------------------
+void EditablePanel::SaveUserConfig()
+{
+ if (m_pszConfigName)
+ {
+ KeyValues *data = system()->GetUserConfigFileData(m_pszConfigName, m_iConfigID);
+
+ // get our user config settings (this will recurse through our children)
+ if (data)
+ {
+ GetUserConfigSettings(data);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: combines both of the above, LoadControlSettings & LoadUserConfig
+//-----------------------------------------------------------------------------
+void EditablePanel::LoadControlSettingsAndUserConfig(const char *dialogResourceName, int dialogID)
+{
+ LoadControlSettings(dialogResourceName);
+ LoadUserConfig(dialogResourceName, dialogID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: applies the user config settings to all the children
+//-----------------------------------------------------------------------------
+void EditablePanel::ApplyUserConfigSettings(KeyValues *userConfig)
+{
+ for (int i = 0; i < GetChildCount(); i++)
+ {
+ Panel *child = GetChild(i);
+ if (child->HasUserConfigSettings())
+ {
+ const char *name = child->GetName();
+ if (name && *name)
+ {
+ child->ApplyUserConfigSettings(userConfig->FindKey(name, true));
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets all the children's user config settings
+//-----------------------------------------------------------------------------
+void EditablePanel::GetUserConfigSettings(KeyValues *userConfig)
+{
+ for (int i = 0; i < GetChildCount(); i++)
+ {
+ Panel *child = GetChild(i);
+ if (child->HasUserConfigSettings())
+ {
+ const char *name = child->GetName();
+ if (name && *name)
+ {
+ child->GetUserConfigSettings(userConfig->FindKey(name, true));
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Save user config settings
+//-----------------------------------------------------------------------------
+void EditablePanel::OnClose()
+{
+ SaveUserConfig();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle information requests
+//-----------------------------------------------------------------------------
+bool EditablePanel::RequestInfo(KeyValues *data)
+{
+ if (!stricmp(data->GetName(), "BuildDialog"))
+ {
+ // a build dialog is being requested, give it one
+ // a bit hacky, but this is a case where vgui.dll needs to reach out
+ data->SetPtr("PanelPtr", new BuildModeDialog( (BuildGroup *)data->GetPtr("BuildGroupPtr")));
+ return true;
+ }
+ else if (!stricmp(data->GetName(), "ControlFactory"))
+ {
+ Panel *newPanel = CreateControlByName(data->GetString("ControlName"));
+ if (newPanel)
+ {
+ data->SetPtr("PanelPtr", newPanel);
+ return true;
+ }
+ }
+ return BaseClass::RequestInfo(data);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the buildgroup that this panel is part of.
+// Input :
+// Output : BuildGroup
+//-----------------------------------------------------------------------------
+BuildGroup *EditablePanel::GetBuildGroup()
+{
+ return _buildGroup;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return a pointer to the nav group
+// Output : FocusNavGroup
+//-----------------------------------------------------------------------------
+FocusNavGroup &EditablePanel::GetFocusNavGroup()
+{
+ return m_NavGroup;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool EditablePanel::RequestFocusNext(VPANEL panel)
+{
+ bool bRet = m_NavGroup.RequestFocusNext(panel);
+ if ( IsPC() && !bRet && IsConsoleStylePanel() )
+ {
+ NavigateDown();
+ }
+ return bRet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool EditablePanel::RequestFocusPrev(VPANEL panel)
+{
+ bool bRet = m_NavGroup.RequestFocusPrev(panel);
+ if ( IsPC() && !bRet && IsConsoleStylePanel() )
+ {
+ NavigateUp();
+ }
+ return bRet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Delegates focus to a sub panel
+// Input : direction - the direction in which focus travelled to arrive at this panel; forward = 1, back = -1
+//-----------------------------------------------------------------------------
+void EditablePanel::RequestFocus(int direction)
+{
+ // we must be a sub panel for this to be called
+ // delegate focus
+ if (direction == 1)
+ {
+ RequestFocusNext(NULL);
+ }
+ else if (direction == -1)
+ {
+ RequestFocusPrev(NULL);
+ }
+ else
+ {
+ BaseClass::RequestFocus();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Pass the focus down onto the last used panel
+//-----------------------------------------------------------------------------
+void EditablePanel::OnSetFocus()
+{
+ Panel *focus = m_NavGroup.GetCurrentFocus();
+ if (focus && focus != this)
+ {
+ focus->RequestFocus();
+ }
+ else
+ {
+ focus = m_NavGroup.GetDefaultPanel();
+ if (focus)
+ {
+ focus->RequestFocus();
+ focus->OnSetFocus();
+ }
+ }
+
+ BaseClass::OnSetFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the resource file is loaded to set up the panel state
+// Input : *inResourceData -
+//-----------------------------------------------------------------------------
+void EditablePanel::ApplySettings(KeyValues *inResourceData)
+{
+ BaseClass::ApplySettings(inResourceData);
+
+ _buildGroup->ApplySettings(inResourceData);
+
+ m_bShouldSkipAutoResize = inResourceData->GetBool( "skip_autoresize", false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Update focus info for navigation
+//-----------------------------------------------------------------------------
+void EditablePanel::OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel)
+{
+ if (!ipanel()->IsPopup(subFocus))
+ {
+ defaultPanel = m_NavGroup.SetCurrentFocus(subFocus, defaultPanel);
+ }
+ BaseClass::OnRequestFocus(GetVPanel(), defaultPanel);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the panel that currently has keyfocus
+//-----------------------------------------------------------------------------
+VPANEL EditablePanel::GetCurrentKeyFocus()
+{
+ Panel *focus = m_NavGroup.GetCurrentFocus();
+ if (focus == this)
+ return NULL;
+
+ if (focus)
+ {
+ if (focus->IsPopup())
+ return BaseClass::GetCurrentKeyFocus();
+
+ // chain down the editpanel hierarchy
+ VPANEL subFocus = focus->GetCurrentKeyFocus();
+ if (subFocus)
+ return subFocus;
+
+ // hit a leaf panel, return that
+ return focus->GetVPanel();
+ }
+ return BaseClass::GetCurrentKeyFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the panel with the specified hotkey
+//-----------------------------------------------------------------------------
+Panel *EditablePanel::HasHotkey(wchar_t key)
+{
+ if( !IsVisible() || !IsEnabled()) // not visible, so can't respond to a hot key
+ {
+ return NULL;
+ }
+
+ for (int i = 0; i < GetChildCount(); i++)
+ {
+ Panel *hot = GetChild(i)->HasHotkey(key);
+ if (hot && hot->IsVisible() && hot->IsEnabled())
+ {
+ return hot;
+ }
+ }
+
+ return NULL;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shortcut function to setting enabled state of control
+//-----------------------------------------------------------------------------
+void EditablePanel::SetControlEnabled(const char *controlName, bool enabled)
+{
+ Panel *control = FindChildByName(controlName);
+ if (control)
+ {
+ control->SetEnabled(enabled);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shortcut function to setting visibility state of control
+//-----------------------------------------------------------------------------
+void EditablePanel::SetControlVisible(const char *controlName, bool visible)
+{
+ Panel *control = FindChildByName(controlName);
+ if (control)
+ {
+ control->SetVisible(visible);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shortcut function to set data in child controls
+//-----------------------------------------------------------------------------
+void EditablePanel::SetControlString(const char *controlName, const char *string)
+{
+ Panel *control = FindChildByName(controlName);
+ if (control)
+ {
+ if (string[0] == '#')
+ {
+ const wchar_t *wszText = g_pVGuiLocalize->Find(string);
+ if (wszText)
+ {
+ PostMessage(control, new KeyValues("SetText", "text", wszText));
+ }
+ }
+ else
+ {
+ PostMessage(control, new KeyValues("SetText", "text", string));
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shortcut function to set data in child controls
+//-----------------------------------------------------------------------------
+void EditablePanel::SetControlString(const char *controlName, const wchar_t *string)
+{
+ Panel *control = FindChildByName(controlName);
+ if (control)
+ {
+ PostMessage(control, new KeyValues("SetText", "text", string));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shortcut function to set data in child controls
+//-----------------------------------------------------------------------------
+void EditablePanel::SetControlInt(const char *controlName, int state)
+{
+ Panel *control = FindChildByName(controlName);
+ if (control)
+ {
+ PostMessage(control, new KeyValues("SetState", "state", state));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shortcut function to get data in child controls
+//-----------------------------------------------------------------------------
+int EditablePanel::GetControlInt(const char *controlName, int defaultState)
+{
+ Panel *control = FindChildByName(controlName);
+ if (control)
+ {
+ KeyValues *data = new KeyValues("GetState");
+ if (control->RequestInfo(data))
+ {
+ int state = data->GetInt("state", defaultState);
+ data->deleteThis();
+ return state;
+ }
+ }
+ return defaultState;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shortcut function to get data in child controls
+//-----------------------------------------------------------------------------
+const char *EditablePanel::GetControlString(const char *controlName, const char *defaultString)
+{
+ static char buf[512];
+ GetControlString(controlName, buf, sizeof(buf) - 1, defaultString);
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shortcut function to get data in child controls
+//-----------------------------------------------------------------------------
+void EditablePanel::GetControlString(const char *controlName, char *buf, int bufSize, const char *defaultString)
+{
+ Panel *control = FindChildByName(controlName);
+ KeyValues *data = new KeyValues("GetText");
+ if (control && control->RequestInfo(data))
+ {
+ Q_strncpy(buf, data->GetString("text", defaultString), bufSize);
+ }
+ else
+ {
+ // no value found, copy in default text
+ Q_strncpy(buf, defaultString, bufSize);
+ }
+
+ // ensure null termination of string
+ buf[bufSize - 1] = 0;
+
+ // free
+ data->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: localization variables (used in constructing UI strings)
+//-----------------------------------------------------------------------------
+void EditablePanel::SetDialogVariable(const char *varName, const char *value)
+{
+ GetDialogVariables()->SetString(varName, value);
+ ForceSubPanelsToUpdateWithNewDialogVariables();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: localization variables (used in constructing UI strings)
+//-----------------------------------------------------------------------------
+void EditablePanel::SetDialogVariable(const char *varName, const wchar_t *value)
+{
+ GetDialogVariables()->SetWString(varName, value);
+ ForceSubPanelsToUpdateWithNewDialogVariables();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: localization variables (used in constructing UI strings)
+//-----------------------------------------------------------------------------
+void EditablePanel::SetDialogVariable(const char *varName, int value)
+{
+ GetDialogVariables()->SetInt(varName, value);
+ ForceSubPanelsToUpdateWithNewDialogVariables();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: localization variables (used in constructing UI strings)
+//-----------------------------------------------------------------------------
+void EditablePanel::SetDialogVariable(const char *varName, float value)
+{
+ GetDialogVariables()->SetFloat(varName, value);
+ ForceSubPanelsToUpdateWithNewDialogVariables();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: redraws child panels with new localization vars
+//-----------------------------------------------------------------------------
+void EditablePanel::ForceSubPanelsToUpdateWithNewDialogVariables()
+{
+ if (m_pDialogVariables)
+ {
+ ipanel()->SendMessage(GetVPanel(), m_pDialogVariables, GetVPanel());
+ for (int i = 0; i < ipanel()->GetChildCount(GetVPanel()); i++)
+ {
+ ipanel()->SendMessage(ipanel()->GetChild(GetVPanel(), i), m_pDialogVariables, GetVPanel());
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: lazy creation of localization vars object
+//-----------------------------------------------------------------------------
+KeyValues *EditablePanel::GetDialogVariables()
+{
+ if (m_pDialogVariables)
+ return m_pDialogVariables;
+
+ m_pDialogVariables = new KeyValues("DialogVariables");
+ return m_pDialogVariables;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Virtual factory for control creation
+//-----------------------------------------------------------------------------
+Panel *EditablePanel::CreateControlByName(const char *controlName)
+{
+ Panel *fromFactory = CBuildFactoryHelper::InstancePanel( controlName );
+ if ( fromFactory )
+ {
+ return fromFactory;
+ }
+
+ return NULL;
+}
diff --git a/mp/src/vgui2/vgui_controls/ExpandButton.cpp b/mp/src/vgui2/vgui_controls/ExpandButton.cpp
new file mode 100644
index 00000000..5c891acc
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ExpandButton.cpp
@@ -0,0 +1,114 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <vgui/ISurface.h>
+#include <vgui/IScheme.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/Image.h>
+#include <vgui_controls/ExpandButton.h>
+#include <vgui_controls/TextImage.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( ExpandButton );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+ExpandButton::ExpandButton( Panel *parent, const char *panelName ) : ToggleButton( parent, panelName, "" )
+{
+ m_bExpandable = true;
+ m_hFont = INVALID_FONT;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+ExpandButton::~ExpandButton()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ExpandButton::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ m_Color = GetSchemeColor( "ExpandButton.Color", pScheme );
+ m_hFont = pScheme->GetFont("Marlett", IsProportional() );
+
+ // don't draw a background
+ SetPaintBackgroundEnabled(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+IBorder *ExpandButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
+{
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Expand the button
+//-----------------------------------------------------------------------------
+void ExpandButton::SetSelected(bool state)
+{
+ if ( m_bExpandable && ( state != IsSelected() ) )
+ {
+ // send a message saying we've been checked
+ KeyValues *msg = new KeyValues("Expanded", "state", (int)state);
+ PostActionSignal(msg);
+
+ BaseClass::SetSelected(state);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: sets whether or not the state of the check can be changed
+//-----------------------------------------------------------------------------
+void ExpandButton::SetExpandable(bool state)
+{
+ m_bExpandable = state;
+ Repaint();
+}
+
+
+void ExpandButton::Paint()
+{
+ surface()->DrawSetTextFont( m_hFont );
+
+ wchar_t code = IsSelected( ) ? L'6' : L'4';
+ wchar_t pString[2] = { code, 0 };
+
+ // draw selected check
+ int tw, th, w, h;
+ GetSize( w, h );
+ surface()->GetTextSize( m_hFont, pString, tw, th );
+ surface()->DrawSetTextColor( m_Color );
+ surface()->DrawSetTextPos( ( w - tw ) / 2, ( h - th ) / 2 );
+ surface()->DrawUnicodeChar( code );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ExpandButton::OnExpanded(Panel *panel)
+{
+}
diff --git a/mp/src/vgui2/vgui_controls/FileOpenDialog.cpp b/mp/src/vgui2/vgui_controls/FileOpenDialog.cpp
new file mode 100644
index 00000000..8a1af699
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/FileOpenDialog.cpp
@@ -0,0 +1,1701 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Implementation of vgui generic open file dialog
+//
+// $NoKeywords: $
+//===========================================================================//
+
+
+#define PROTECTED_THINGS_DISABLE
+
+#if !defined( _X360 ) && defined( WIN32 )
+#include "winlite.h"
+#include <shellapi.h>
+#elif defined( POSIX )
+#include <stdlib.h>
+#define _stat stat
+#define _wcsnicmp wcsncmp
+#elif defined( _X360 )
+#else
+#error
+#endif
+
+#undef GetCurrentDirectory
+#include "filesystem.h"
+#include <sys/stat.h>
+
+#include "tier1/utldict.h"
+#include "tier1/utlstring.h"
+
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <vgui/ISystem.h>
+#include <KeyValues.h>
+#include <vgui/IVGui.h>
+#include <vgui/ILocalize.h>
+#include <vgui/IInput.h>
+
+#include <vgui_controls/FileOpenDialog.h>
+
+#include <vgui_controls/Button.h>
+#include <vgui_controls/ComboBox.h>
+#include <vgui_controls/ImagePanel.h>
+#include <vgui_controls/InputDialog.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/ListPanel.h>
+#include <vgui_controls/TextEntry.h>
+#include <vgui_controls/ImageList.h>
+#include <vgui_controls/MenuItem.h>
+#include <vgui_controls/Tooltip.h>
+
+#if defined( _X360 )
+#include "xbox/xbox_win32stubs.h"
+#undef GetCurrentDirectory
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+static int s_nLastSortColumn = 0;
+
+static int ListFileNameSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ NOTE_UNUSED( pPanel );
+
+ bool dir1 = item1.kv->GetInt("directory") == 1;
+ bool dir2 = item2.kv->GetInt("directory") == 1;
+
+ // if they're both not directories of files, return if dir1 is a directory (before files)
+ if (dir1 != dir2)
+ {
+ return dir1 ? -1 : 1;
+ }
+
+ const char *string1 = item1.kv->GetString("text");
+ const char *string2 = item2.kv->GetString("text");
+
+ // YWB: Mimic windows behavior where filenames starting with numbers are sorted based on numeric part
+ int num1 = Q_atoi( string1 );
+ int num2 = Q_atoi( string2 );
+
+ if ( num1 != 0 &&
+ num2 != 0 )
+ {
+ if ( num1 < num2 )
+ return -1;
+ else if ( num1 > num2 )
+ return 1;
+ }
+
+ // Push numbers before everything else
+ if ( num1 != 0 )
+ {
+ return -1;
+ }
+
+ // Push numbers before everything else
+ if ( num2 != 0 )
+ {
+ return 1;
+ }
+
+ return Q_stricmp( string1, string2 );
+}
+
+static int ListBaseStringSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName )
+{
+ bool dir1 = item1.kv->GetInt("directory") == 1;
+ bool dir2 = item2.kv->GetInt("directory") == 1;
+
+ // if they're both not directories of files, return if dir1 is a directory (before files)
+ if (dir1 != dir2)
+ {
+ return -1;
+ }
+
+ const char *string1 = item1.kv->GetString(fieldName);
+ const char *string2 = item2.kv->GetString(fieldName);
+ int cval = Q_stricmp(string1, string2);
+ if ( cval == 0 )
+ {
+ // Use filename to break ties
+ return ListFileNameSortFunc( pPanel, item1, item2 );
+ }
+
+ return cval;
+}
+
+static int ListBaseIntegerSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName )
+{
+ bool dir1 = item1.kv->GetInt("directory") == 1;
+ bool dir2 = item2.kv->GetInt("directory") == 1;
+
+ // if they're both not directories of files, return if dir1 is a directory (before files)
+ if (dir1 != dir2)
+ {
+ return -1;
+ }
+
+ int i1 = item1.kv->GetInt(fieldName);
+ int i2 = item2.kv->GetInt(fieldName);
+ if ( i1 == i2 )
+ {
+ // Use filename to break ties
+ return ListFileNameSortFunc( pPanel, item1, item2 );
+ }
+
+ return ( i1 < i2 ) ? -1 : 1;
+}
+
+static int ListBaseInteger64SortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *lowfield, char const *highfield )
+{
+ bool dir1 = item1.kv->GetInt("directory") == 1;
+ bool dir2 = item2.kv->GetInt("directory") == 1;
+
+ // if they're both not directories of files, return if dir1 is a directory (before files)
+ if (dir1 != dir2)
+ {
+ return dir1 ? -1 : 1;
+ }
+
+ uint32 l1 = item1.kv->GetInt(lowfield);
+ uint32 h1 = item1.kv->GetInt(highfield);
+ uint32 l2 = item2.kv->GetInt(lowfield);
+ uint32 h2 = item2.kv->GetInt(highfield);
+ uint64 i1 = (uint64)( (uint64)l1 | ( (uint64)h1 << 32 ) );
+ uint64 i2 = (uint64)( (uint64)l2 | ( (uint64)h2 << 32 ) );
+
+ if ( i1 == i2 )
+ {
+ // Use filename to break ties
+ return ListFileNameSortFunc( pPanel, item1, item2 );
+ }
+
+ return ( i1 < i2 ) ? -1 : 1;
+}
+
+
+static int ListFileSizeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ return ListBaseIntegerSortFunc( pPanel, item1, item2, "filesizeint" );
+}
+
+static int ListFileModifiedSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ // NOTE: Backward order to get most recent files first
+ return ListBaseInteger64SortFunc( pPanel, item2, item1, "modifiedint_low", "modifiedint_high" );
+}
+static int ListFileCreatedSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ // NOTE: Backward order to get most recent files first
+ return ListBaseInteger64SortFunc( pPanel, item2, item1, "createdint_low", "createdint_high" );
+}
+static int ListFileAttributesSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ return ListBaseStringSortFunc( pPanel, item1, item2, "attributes" );
+}
+static int ListFileTypeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ return ListBaseStringSortFunc( pPanel, item1, item2, "type" );
+}
+
+
+
+namespace vgui
+{
+
+class FileCompletionMenu : public Menu
+{
+public:
+ FileCompletionMenu(Panel *parent, const char *panelName) : Menu(parent, panelName)
+ {
+ }
+
+ // override it so it doesn't request focus
+ virtual void SetVisible(bool state)
+ {
+ Panel::SetVisible(state);
+ }
+
+};
+
+
+//-----------------------------------------------------------------------------
+// File completion edit text entry
+//-----------------------------------------------------------------------------
+class FileCompletionEdit : public TextEntry
+{
+ DECLARE_CLASS_SIMPLE( FileCompletionEdit, TextEntry );
+
+public:
+ FileCompletionEdit(Panel *parent);
+ ~FileCompletionEdit();
+
+ int AddItem(const char *itemText, KeyValues *userData);
+ int AddItem(const wchar_t *itemText, KeyValues *userData);
+ void DeleteAllItems();
+ int GetItemCount();
+ int GetItemIDFromRow(int row);
+ int GetRowFromItemID(int itemID);
+ virtual void PerformLayout();
+ void OnSetText(const wchar_t *newtext);
+ virtual void OnKillFocus();
+ void HideMenu(void);
+ void ShowMenu(void);
+ virtual void OnKeyCodeTyped(KeyCode code);
+ MESSAGE_FUNC_INT( OnMenuItemHighlight, "MenuItemHighlight", itemID );
+
+private:
+ FileCompletionMenu *m_pDropDown;
+};
+
+
+
+FileCompletionEdit::FileCompletionEdit(Panel *parent) : TextEntry(parent, NULL)
+{
+ m_pDropDown = new FileCompletionMenu(this, NULL);
+ m_pDropDown->AddActionSignalTarget(this);
+}
+
+FileCompletionEdit::~FileCompletionEdit()
+{
+ delete m_pDropDown;
+}
+
+int FileCompletionEdit::AddItem(const char *itemText, KeyValues *userData)
+{
+ // when the menu item is selected it will send the custom message "SetText"
+ return m_pDropDown->AddMenuItem(itemText, new KeyValues("SetText", "text", itemText), this, userData);
+}
+int FileCompletionEdit::AddItem(const wchar_t *itemText, KeyValues *userData)
+{
+ // add the element to the menu
+ // when the menu item is selected it will send the custom message "SetText"
+ KeyValues *kv = new KeyValues("SetText");
+ kv->SetWString("text", itemText);
+
+ // get an ansi version for the menuitem name
+ char ansi[128];
+ g_pVGuiLocalize->ConvertUnicodeToANSI(itemText, ansi, sizeof(ansi));
+ return m_pDropDown->AddMenuItem(ansi, kv, this, userData);
+}
+
+void FileCompletionEdit::DeleteAllItems()
+{
+ m_pDropDown->DeleteAllItems();
+}
+
+int FileCompletionEdit::GetItemCount()
+{
+ return m_pDropDown->GetItemCount();
+}
+
+int FileCompletionEdit::GetItemIDFromRow(int row)
+{
+ // valid from [0, GetItemCount)
+ return m_pDropDown->GetMenuID(row);
+}
+
+int FileCompletionEdit::GetRowFromItemID(int itemID)
+{
+ int i;
+ for (i=0;i<GetItemCount();i++)
+ {
+ if (m_pDropDown->GetMenuID(i) == itemID)
+ return i;
+ }
+ return -1;
+}
+
+void FileCompletionEdit::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ m_pDropDown->PositionRelativeToPanel( this, Menu::DOWN, 0 );
+
+ // reset the width of the drop down menu to be the width of this edit box
+ m_pDropDown->SetFixedWidth(GetWide());
+ m_pDropDown->ForceCalculateWidth();
+}
+
+void FileCompletionEdit::OnSetText(const wchar_t *newtext)
+{
+ // see if the combobox text has changed, and if so, post a message detailing the new text
+ wchar_t wbuf[255];
+ GetText( wbuf, 254 );
+
+ if ( wcscmp(wbuf, newtext) )
+ {
+ // text has changed
+ SetText(newtext);
+
+ // fire off that things have changed
+ PostActionSignal(new KeyValues("TextChanged", "text", newtext));
+ Repaint();
+ }
+}
+
+void FileCompletionEdit::OnKillFocus()
+{
+ HideMenu();
+ BaseClass::OnKillFocus();
+}
+
+void FileCompletionEdit::HideMenu(void)
+{
+ // hide the menu
+ m_pDropDown->SetVisible(false);
+}
+
+void FileCompletionEdit::ShowMenu(void)
+{
+ // reset the dropdown's position
+ m_pDropDown->InvalidateLayout();
+
+ // make sure we're at the top of the draw order (and therefore our children as well)
+ // this important to make sure the menu will be drawn in the foreground
+ MoveToFront();
+
+ // reset the drop down
+ m_pDropDown->ClearCurrentlyHighlightedItem();
+
+ // limit it to only 6
+ if (m_pDropDown->GetItemCount() > 6)
+ {
+ m_pDropDown->SetNumberOfVisibleItems(6);
+ }
+ else
+ {
+ m_pDropDown->SetNumberOfVisibleItems(m_pDropDown->GetItemCount());
+ }
+ // show the menu
+ m_pDropDown->SetVisible(true);
+
+ Repaint();
+}
+
+void FileCompletionEdit::OnKeyCodeTyped(KeyCode code)
+{
+ if ( code == KEY_DOWN )
+ {
+ if (m_pDropDown->GetItemCount() > 0)
+ {
+ int menuID = m_pDropDown->GetCurrentlyHighlightedItem();
+ int row = -1;
+ if ( menuID == -1 )
+ {
+ row = m_pDropDown->GetItemCount() - 1;
+ }
+ else
+ {
+ row = GetRowFromItemID(menuID);
+ }
+ row++;
+ if (row == m_pDropDown->GetItemCount())
+ {
+ row = 0;
+ }
+ menuID = GetItemIDFromRow(row);
+ m_pDropDown->SetCurrentlyHighlightedItem(menuID);
+ return;
+ }
+ }
+ else if ( code == KEY_UP )
+ {
+ if (m_pDropDown->GetItemCount() > 0)
+ {
+ int menuID = m_pDropDown->GetCurrentlyHighlightedItem();
+ int row = -1;
+ if ( menuID == -1 )
+ {
+ row = 0;
+ }
+ else
+ {
+ row = GetRowFromItemID(menuID);
+ }
+ row--;
+ if ( row < 0 )
+ {
+ row = m_pDropDown->GetItemCount() - 1;
+ }
+ menuID = GetItemIDFromRow(row);
+ m_pDropDown->SetCurrentlyHighlightedItem(menuID);
+ return;
+ }
+ }
+ else if ( code == KEY_ESCAPE )
+ {
+ if ( m_pDropDown->IsVisible() )
+ {
+ HideMenu();
+ return;
+ }
+ }
+ BaseClass::OnKeyCodeTyped(code);
+ return;
+}
+
+void FileCompletionEdit::OnMenuItemHighlight( int itemID )
+{
+ char wbuf[80];
+ if ( m_pDropDown->IsValidMenuID(itemID) )
+ {
+ m_pDropDown->GetMenuItem(itemID)->GetText(wbuf, 80);
+ }
+ else
+ {
+ wbuf[0] = 0;
+ }
+ SetText(wbuf);
+ RequestFocus();
+ GotoTextEnd();
+}
+
+
+} // namespace vgui
+
+
+//-----------------------------------------------------------------------------
+// Dictionary of start dir contexts
+//-----------------------------------------------------------------------------
+static CUtlDict< CUtlString, unsigned short > s_StartDirContexts;
+
+struct ColumnInfo_t
+{
+ char const *columnName;
+ char const *columnText;
+ int startingWidth;
+ int minWidth;
+ int maxWidth;
+ int flags;
+ SortFunc *pfnSort;
+ Label::Alignment alignment;
+};
+
+static ColumnInfo_t g_ColInfo[] =
+{
+ { "text", "#FileOpenDialog_Col_Name", 175, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileNameSortFunc , Label::a_west },
+ { "filesize", "#FileOpenDialog_Col_Size", 100, 20, 10000, 0, &ListFileSizeSortFunc , Label::a_east },
+ { "type", "#FileOpenDialog_Col_Type", 150, 20, 10000, 0, &ListFileTypeSortFunc , Label::a_west },
+ { "modified", "#FileOpenDialog_Col_DateModified", 125, 20, 10000, 0, &ListFileModifiedSortFunc , Label::a_west },
+// { "created", "#FileOpenDialog_Col_DateCreated", 125, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileCreatedSortFunc , Label::a_west },
+ { "attributes", "#FileOpenDialog_Col_Attributes", 50, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileAttributesSortFunc , Label::a_west },
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+FileOpenDialog::FileOpenDialog(Panel *parent, const char *title, bool bOpenOnly, KeyValues* pContextKeyValues ) :
+ Frame( parent, "FileOpenDialog" )
+{
+ m_DialogType = bOpenOnly ? FOD_OPEN : FOD_SAVE;
+ Init( title, pContextKeyValues );
+}
+
+
+FileOpenDialog::FileOpenDialog( Panel *parent, const char *title, FileOpenDialogType_t type, KeyValues *pContextKeyValues ) :
+ Frame( parent, "FileOpenDialog" )
+{
+ m_DialogType = type;
+ Init( title, pContextKeyValues );
+}
+
+void FileOpenDialog::Init( const char *title, KeyValues *pContextKeyValues )
+{
+ m_bFileSelected = false;
+ SetTitle(title, true);
+ SetMinimizeButtonVisible(false);
+
+#ifdef POSIX
+ Q_strncpy(m_szLastPath, "/", sizeof( m_szLastPath ) );
+#else
+ Q_strncpy(m_szLastPath, "c:\\", sizeof( m_szLastPath ) );
+#endif
+
+ m_pContextKeyValues = pContextKeyValues;
+
+ // Get the list of available drives and put them in a menu here.
+ // Start with the directory we are in.
+ m_pFullPathEdit = new ComboBox(this, "FullPathEdit", 6, false);
+ m_pFullPathEdit->GetTooltip()->SetTooltipFormatToSingleLine();
+
+ // list panel
+ m_pFileList = new ListPanel(this, "FileList");
+ for ( int i = 0; i < ARRAYSIZE( g_ColInfo ); ++i )
+ {
+ const ColumnInfo_t& info = g_ColInfo[ i ];
+
+ m_pFileList->AddColumnHeader( i, info.columnName, info.columnText, info.startingWidth, info.minWidth, info.maxWidth, info.flags );
+ m_pFileList->SetSortFunc( i, info.pfnSort );
+ m_pFileList->SetColumnTextAlignment( i, info.alignment );
+ }
+
+ m_pFileList->SetSortColumn( s_nLastSortColumn );
+ m_pFileList->SetMultiselectEnabled( false );
+
+ // file name edit box
+ m_pFileNameEdit = new FileCompletionEdit(this);
+ m_pFileNameEdit->AddActionSignalTarget(this);
+
+ m_pFileTypeCombo = new ComboBox( this, "FileTypeCombo", 6, false );
+
+ switch ( m_DialogType )
+ {
+ case FOD_OPEN:
+ m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this );
+ break;
+ case FOD_SAVE:
+ m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Save", this );
+ break;
+ case FOD_SELECT_DIRECTORY:
+ m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Select", this );
+ m_pFileTypeCombo->SetVisible( false );
+ break;
+ }
+
+ m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this );
+ m_pFolderUpButton = new Button( this, "FolderUpButton", "", this );
+ m_pFolderUpButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_Up" );
+ m_pNewFolderButton = new Button( this, "NewFolderButton", "", this );
+ m_pNewFolderButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_NewFolder" );
+ m_pOpenInExplorerButton = new Button( this, "OpenInExplorerButton", "", this );
+
+#if defined ( OSX )
+ m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInFinderButton" );
+#elif defined ( POSIX )
+ m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInDesktopManagerButton" );
+#else // Assume Windows / Explorer
+ m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInExplorerButton" );
+#endif
+
+ Label *lookIn = new Label( this, "LookInLabel", "#FileOpenDialog_Look_in" );
+ Label *fileName = new Label( this, "FileNameLabel",
+ ( m_DialogType != FOD_SELECT_DIRECTORY ) ? "#FileOpenDialog_File_name" : "#FileOpenDialog_Directory_Name" );
+
+ m_pFolderIcon = new ImagePanel(NULL, "FolderIcon");
+
+ // set up the control's initial positions
+ SetSize( 600, 260 );
+
+ int nFileEditLeftSide = ( m_DialogType != FOD_SELECT_DIRECTORY ) ? 84 : 100;
+ int nFileNameWidth = ( m_DialogType != FOD_SELECT_DIRECTORY ) ? 72 : 82;
+
+ m_pFullPathEdit->SetBounds(67, 32, 310, 24);
+ m_pFolderUpButton->SetBounds(362, 32, 24, 24);
+ m_pNewFolderButton->SetBounds(392, 32, 24, 24);
+ m_pOpenInExplorerButton->SetBounds(332, 32, 24, 24);
+ m_pFileList->SetBounds(10, 60, 406, 130);
+ m_pFileNameEdit->SetBounds( nFileEditLeftSide, 194, 238, 24);
+ m_pFileTypeCombo->SetBounds( nFileEditLeftSide, 224, 238, 24);
+ m_pOpenButton->SetBounds(336, 194, 74, 24);
+ m_pCancelButton->SetBounds(336, 224, 74, 24);
+ lookIn->SetBounds(10, 32, 55, 24);
+ fileName->SetBounds(10, 194, nFileNameWidth, 24);
+
+ // set autolayout parameters
+ m_pFullPathEdit->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_RIGHT, 67, 32, -100, 0 );
+ m_pFileNameEdit->SetAutoResize( Panel::PIN_BOTTOMLEFT, Panel::AUTORESIZE_RIGHT, nFileEditLeftSide, -42, -104, 0 );
+ m_pFileTypeCombo->SetAutoResize( Panel::PIN_BOTTOMLEFT, Panel::AUTORESIZE_RIGHT, nFileEditLeftSide, -12, -104, 0 );
+ m_pFileList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 10, 60, -10, -70 );
+
+ m_pFolderUpButton->SetPinCorner( Panel::PIN_TOPRIGHT, -40, 32 );
+ m_pNewFolderButton->SetPinCorner( Panel::PIN_TOPRIGHT, -10, 32 );
+ m_pOpenInExplorerButton->SetPinCorner( Panel::PIN_TOPRIGHT, -70, 32 );
+ m_pOpenButton->SetPinCorner( Panel::PIN_BOTTOMRIGHT, -16, -42 );
+ m_pCancelButton->SetPinCorner( Panel::PIN_BOTTOMRIGHT, -16, -12 );
+ lookIn->SetPinCorner( Panel::PIN_TOPLEFT, 10, 32 );
+ fileName->SetPinCorner( Panel::PIN_BOTTOMLEFT, 10, -42 );
+
+ // label settings
+ lookIn->SetContentAlignment(Label::a_west);
+ fileName->SetContentAlignment(Label::a_west);
+
+ lookIn->SetAssociatedControl(m_pFullPathEdit);
+ fileName->SetAssociatedControl(m_pFileNameEdit);
+
+ if ( m_DialogType != FOD_SELECT_DIRECTORY )
+ {
+ Label *fileType = new Label(this, "FileTypeLabel", "#FileOpenDialog_File_type");
+ fileType->SetBounds(10, 224, 72, 24);
+ fileType->SetPinCorner( Panel::PIN_BOTTOMLEFT, 10, -12 );
+ fileType->SetContentAlignment(Label::a_west);
+ fileType->SetAssociatedControl( m_pFileTypeCombo );
+ }
+
+ // set tab positions
+ GetFocusNavGroup().SetDefaultButton(m_pOpenButton);
+
+ m_pFileNameEdit->SetTabPosition(1);
+ m_pFileTypeCombo->SetTabPosition(2);
+ m_pOpenButton->SetTabPosition(3);
+ m_pCancelButton->SetTabPosition(4);
+ m_pFullPathEdit->SetTabPosition(5);
+ m_pFileList->SetTabPosition(6);
+
+ m_pOpenButton->SetCommand( ( m_DialogType != FOD_SELECT_DIRECTORY ) ? new KeyValues( "OnOpen" ) : new KeyValues( "SelectFolder" ) );
+ m_pCancelButton->SetCommand( "CloseModal" );
+ m_pFolderUpButton->SetCommand( new KeyValues( "OnFolderUp" ) );
+ m_pNewFolderButton->SetCommand( new KeyValues( "OnNewFolder" ) );
+ m_pOpenInExplorerButton->SetCommand( new KeyValues( "OpenInExplorer" ) );
+
+ SetSize( 600, 384 );
+
+ m_nStartDirContext = s_StartDirContexts.InvalidIndex();
+
+ // Set our starting path to the current directory
+ char pLocalPath[255];
+ g_pFullFileSystem->GetCurrentDirectory( pLocalPath , 255 );
+ if ( !pLocalPath[0] || ( IsOSX() && V_strlen(pLocalPath) <= 2 ) )
+ {
+ const char *pszHomeDir = getenv( "HOME" );
+ V_strcpy_safe( pLocalPath, pszHomeDir );
+ }
+
+ SetStartDirectory( pLocalPath );
+
+ // Because these call through virtual functions, we can't issue them in the constructor, so we post a message to ourselves instead!!
+ PostMessage( GetVPanel(), new KeyValues( "PopulateFileList" ) );
+ PostMessage( GetVPanel(), new KeyValues( "PopulateDriveList" ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+FileOpenDialog::~FileOpenDialog()
+{
+ s_nLastSortColumn = m_pFileList->GetSortColumn();
+ if ( m_pContextKeyValues )
+ {
+ m_pContextKeyValues->deleteThis();
+ m_pContextKeyValues = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply scheme settings
+//-----------------------------------------------------------------------------
+void FileOpenDialog::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+ m_pFolderIcon->SetImage(scheme()->GetImage("resource/icon_folder", false));
+ m_pFolderUpButton->AddImage(scheme()->GetImage("resource/icon_folderup", false), -3);
+ m_pNewFolderButton->AddImage( scheme()->GetImage("resource/icon_newfolder", false), -3 );
+ m_pOpenInExplorerButton->AddImage( scheme()->GetImage("resource/icon_play_once", false), -3 );
+
+ ImageList *imageList = new ImageList(false);
+ imageList->AddImage(scheme()->GetImage("resource/icon_file", false));
+ imageList->AddImage(scheme()->GetImage("resource/icon_folder", false));
+ imageList->AddImage(scheme()->GetImage("resource/icon_folder_selected", false));
+
+ m_pFileList->SetImageList(imageList, true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Prevent default button ('select') from getting triggered
+// when selecting directories. Instead, open the directory
+//-----------------------------------------------------------------------------
+void FileOpenDialog::OnKeyCodeTyped(KeyCode code)
+{
+ if ( m_DialogType == FOD_SELECT_DIRECTORY && code == KEY_ENTER )
+ {
+ OnOpen();
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( code );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void FileOpenDialog::PopulateDriveList()
+{
+ char fullpath[MAX_PATH * 4];
+ char subDirPath[MAX_PATH * 4];
+ GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH);
+ Q_strncpy(subDirPath, fullpath, sizeof( subDirPath ) );
+
+ m_pFullPathEdit->DeleteAllItems();
+
+#ifdef WIN32
+ // populate the drive list
+ char buf[512];
+ int len = system()->GetAvailableDrives(buf, 512);
+ char *pBuf = buf;
+ for (int i=0; i < len / 4; i++)
+ {
+ m_pFullPathEdit->AddItem(pBuf, NULL);
+
+ // is this our drive - add all subdirectories
+ if (!_strnicmp(pBuf, fullpath, 2))
+ {
+ int indent = 0;
+ char *pData = fullpath;
+ while (*pData)
+ {
+ if ( *pData == CORRECT_PATH_SEPARATOR )
+ {
+ if (indent > 0)
+ {
+ memset(subDirPath, ' ', indent);
+ memcpy(subDirPath+indent, fullpath, pData-fullpath);
+ subDirPath[indent+pData-fullpath] = 0;
+
+ m_pFullPathEdit->AddItem(subDirPath, NULL);
+ }
+ indent += 2;
+ }
+ pData++;
+ }
+ }
+ pBuf += 4;
+ }
+#else
+ m_pFullPathEdit->AddItem("/", NULL);
+
+ char *pData = fullpath;
+ int indent = 0;
+ while (*pData)
+ {
+ if (*pData == '/' && ( pData[1] != '\0' ) )
+ {
+ if (indent > 0)
+ {
+ memset(subDirPath, ' ', indent);
+ memcpy(subDirPath+indent, fullpath, pData-fullpath);
+ subDirPath[indent+pData-fullpath] = 0;
+
+ m_pFullPathEdit->AddItem(subDirPath, NULL);
+ }
+ indent += 2;
+ }
+ pData++;
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Delete self on close
+//-----------------------------------------------------------------------------
+void FileOpenDialog::OnClose()
+{
+ s_nLastSortColumn = m_pFileList->GetSortColumn();
+ if ( !m_bFileSelected )
+ {
+ KeyValues *pKeyValues = new KeyValues( "FileSelectionCancelled" );
+ PostActionSignal( pKeyValues );
+ m_bFileSelected = true;
+ }
+
+ m_pFileNameEdit->SetText("");
+ m_pFileNameEdit->HideMenu();
+
+ if ( vgui::input()->GetAppModalSurface() == GetVPanel() )
+ {
+ input()->SetAppModalSurface(NULL);
+ }
+
+ BaseClass::OnClose();
+}
+
+void FileOpenDialog::OnFolderUp()
+{
+ MoveUpFolder();
+ OnOpen();
+}
+
+void FileOpenDialog::OnInputCompleted( KeyValues *data )
+{
+ if ( m_hInputDialog.Get() )
+ {
+ delete m_hInputDialog.Get();
+ }
+
+ input()->SetAppModalSurface( m_SaveModal );
+ m_SaveModal = 0;
+
+ NewFolder( data->GetString( "text" ) );
+ OnOpen();
+}
+
+void FileOpenDialog::OnInputCanceled()
+{
+ input()->SetAppModalSurface( m_SaveModal );
+ m_SaveModal = 0;
+}
+
+void FileOpenDialog::OnNewFolder()
+{
+ if ( m_hInputDialog.Get() )
+ delete m_hInputDialog.Get();
+
+ m_hInputDialog = new InputDialog( this, "#FileOpenDialog_NewFolder_InputTitle", "#FileOpenDialog_NewFolderPrompt", "#FileOpenDialog_NewFolder_DefaultName" );
+ if ( m_hInputDialog.Get() )
+ {
+ m_SaveModal = input()->GetAppModalSurface();
+
+ KeyValues *pContextKeyValues = new KeyValues( "NewFolder" );
+ m_hInputDialog->SetSmallCaption( true );
+ m_hInputDialog->SetMultiline( false );
+ m_hInputDialog->DoModal( pContextKeyValues );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Opens the current file/folder in explorer
+//-----------------------------------------------------------------------------
+void FileOpenDialog::OnOpenInExplorer()
+{
+ char pCurrentDirectory[MAX_PATH];
+ GetCurrentDirectory( pCurrentDirectory, sizeof(pCurrentDirectory) );
+#if !defined( _X360 ) && defined( WIN32 )
+ ShellExecute( NULL, NULL, pCurrentDirectory, NULL, NULL, SW_SHOWNORMAL );
+#elif defined( OSX )
+ char szCmd[ MAX_PATH * 2];
+ Q_snprintf( szCmd, sizeof(szCmd), "/usr/bin/open \"%s\"", pCurrentDirectory );
+ ::system( szCmd );
+#elif defined( LINUX )
+ char szCmd[ MAX_PATH * 2 ];
+ Q_snprintf( szCmd, sizeof(szCmd), "xdg-open \"%s\" &", pCurrentDirectory );
+ ::system( szCmd );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle for button commands
+//-----------------------------------------------------------------------------
+void FileOpenDialog::OnCommand(const char *command)
+{
+ if (!stricmp(command, "Cancel"))
+ {
+ Close();
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the start directory context (and resets the start directory in the process)
+//-----------------------------------------------------------------------------
+void FileOpenDialog::SetStartDirectoryContext( const char *pStartDirContext, const char *pDefaultDir )
+{
+ bool bUseCurrentDirectory = true;
+ if ( pStartDirContext )
+ {
+ m_nStartDirContext = s_StartDirContexts.Find( pStartDirContext );
+ if ( m_nStartDirContext == s_StartDirContexts.InvalidIndex() )
+ {
+ m_nStartDirContext = s_StartDirContexts.Insert( pStartDirContext, pDefaultDir );
+ bUseCurrentDirectory = ( pDefaultDir == NULL );
+ }
+ else
+ {
+ bUseCurrentDirectory = false;
+ }
+ }
+ else
+ {
+ m_nStartDirContext = s_StartDirContexts.InvalidIndex();
+ }
+
+ if ( !bUseCurrentDirectory )
+ {
+ SetStartDirectory( s_StartDirContexts[m_nStartDirContext].Get() );
+ }
+ else
+ {
+ // Set our starting path to the current directory
+ char pLocalPath[255];
+ g_pFullFileSystem->GetCurrentDirectory( pLocalPath, 255 );
+ SetStartDirectory( pLocalPath );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the starting directory of the file search.
+//-----------------------------------------------------------------------------
+void FileOpenDialog::SetStartDirectory( const char *dir )
+{
+ m_pFullPathEdit->SetText(dir);
+
+ // ensure it's validity
+ ValidatePath();
+
+ // Store this in the start directory list
+ if ( m_nStartDirContext != s_StartDirContexts.InvalidIndex() )
+ {
+ char pDirBuf[MAX_PATH];
+ GetCurrentDirectory( pDirBuf, sizeof(pDirBuf) );
+ s_StartDirContexts[ m_nStartDirContext ] = pDirBuf;
+ }
+
+ PopulateDriveList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Add filters for the drop down combo box
+//-----------------------------------------------------------------------------
+void FileOpenDialog::AddFilter( const char *filter, const char *filterName, bool bActive, const char *pFilterInfo )
+{
+ KeyValues *kv = new KeyValues("item");
+ kv->SetString( "filter", filter );
+ kv->SetString( "filterinfo", pFilterInfo );
+ int itemID = m_pFileTypeCombo->AddItem(filterName, kv);
+ if ( bActive )
+ {
+ m_pFileTypeCombo->ActivateItem(itemID);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activate the dialog
+//-----------------------------------------------------------------------------
+void FileOpenDialog::DoModal( bool bUnused )
+{
+ m_bFileSelected = false;
+ m_pFileNameEdit->RequestFocus();
+ BaseClass::DoModal();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the directory this is currently in
+//-----------------------------------------------------------------------------
+void FileOpenDialog::GetCurrentDirectory(char *buf, int bufSize)
+{
+ // get the text from the text entry
+ m_pFullPathEdit->GetText(buf, bufSize);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the last selected file name
+//-----------------------------------------------------------------------------
+void FileOpenDialog::GetSelectedFileName(char *buf, int bufSize)
+{
+ m_pFileNameEdit->GetText(buf, bufSize);
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a new folder
+//-----------------------------------------------------------------------------
+void FileOpenDialog::NewFolder( char const *folderName )
+{
+ char pCurrentDirectory[MAX_PATH];
+ GetCurrentDirectory( pCurrentDirectory, sizeof(pCurrentDirectory) );
+
+ char pFullPath[MAX_PATH];
+ char pNewFolderName[MAX_PATH];
+ Q_strncpy( pNewFolderName, folderName, sizeof(pNewFolderName) );
+ int i = 2;
+ do
+ {
+ Q_MakeAbsolutePath( pFullPath, sizeof(pFullPath), pNewFolderName, pCurrentDirectory );
+ if ( !g_pFullFileSystem->FileExists( pFullPath, NULL ) &&
+ !g_pFullFileSystem->IsDirectory( pFullPath, NULL ) )
+ {
+ g_pFullFileSystem->CreateDirHierarchy( pFullPath, NULL );
+ m_pFileNameEdit->SetText( pNewFolderName );
+ return;
+ }
+
+ Q_snprintf( pNewFolderName, sizeof(pNewFolderName), "%s%d", folderName, i );
+ ++i;
+ } while ( i <= 999 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the directory structure up
+//-----------------------------------------------------------------------------
+void FileOpenDialog::MoveUpFolder()
+{
+ char fullpath[MAX_PATH * 4];
+ GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH);
+
+ Q_StripLastDir( fullpath, sizeof( fullpath ) );
+ // append a trailing slash
+ Q_AppendSlash( fullpath, sizeof( fullpath ) );
+
+ SetStartDirectory(fullpath);
+ PopulateFileList();
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Validate that the current path is valid
+//-----------------------------------------------------------------------------
+void FileOpenDialog::ValidatePath()
+{
+ char fullpath[MAX_PATH * 4];
+ GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH);
+ Q_RemoveDotSlashes( fullpath );
+
+ // when statting a directory on Windows, you want to include
+ // the terminal slash exactly when you are statting a root
+ // directory. PKMN.
+#ifdef _WIN32
+ if ( Q_strlen( fullpath ) != 3 )
+ {
+ Q_StripTrailingSlash( fullpath );
+ }
+#endif
+ // cleanup the path, we format tabs into the list to make it pretty in the UI
+ Q_StripPrecedingAndTrailingWhitespace( fullpath );
+
+ struct _stat buf;
+ if ( ( 0 == _stat( fullpath, &buf ) ) &&
+ ( 0 != ( buf.st_mode & S_IFDIR ) ) )
+ {
+ Q_AppendSlash( fullpath, sizeof( fullpath ) );
+ Q_strncpy(m_szLastPath, fullpath, sizeof(m_szLastPath));
+ }
+ else
+ {
+ // failed to load file, use the previously successful path
+ }
+
+ m_pFullPathEdit->SetText(m_szLastPath);
+ m_pFullPathEdit->GetTooltip()->SetText(m_szLastPath);
+}
+
+#ifdef WIN32
+const char *GetAttributesAsString( DWORD dwAttributes )
+{
+ static char out[ 256 ];
+ out[ 0 ] = 0;
+ if ( dwAttributes & FILE_ATTRIBUTE_ARCHIVE )
+ {
+ Q_strncat( out, "A", sizeof( out ), COPY_ALL_CHARACTERS );
+ }
+ if ( dwAttributes & FILE_ATTRIBUTE_COMPRESSED )
+ {
+ Q_strncat( out, "C", sizeof( out ), COPY_ALL_CHARACTERS );
+ }
+ if ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY )
+ {
+ Q_strncat( out, "D", sizeof( out ), COPY_ALL_CHARACTERS );
+ }
+ if ( dwAttributes & FILE_ATTRIBUTE_HIDDEN )
+ {
+ Q_strncat( out, "H", sizeof( out ), COPY_ALL_CHARACTERS );
+ }
+ if ( dwAttributes & FILE_ATTRIBUTE_READONLY )
+ {
+ Q_strncat( out, "R", sizeof( out ), COPY_ALL_CHARACTERS );
+ }
+ if ( dwAttributes & FILE_ATTRIBUTE_SYSTEM )
+ {
+ Q_strncat( out, "S", sizeof( out ), COPY_ALL_CHARACTERS );
+ }
+ if ( dwAttributes & FILE_ATTRIBUTE_TEMPORARY )
+ {
+ Q_strncat( out, "T", sizeof( out ), COPY_ALL_CHARACTERS );
+ }
+ return out;
+}
+
+const char *GetFileTimetamp( FILETIME ft )
+{
+ SYSTEMTIME local;
+ FILETIME localFileTime;
+ FileTimeToLocalFileTime( &ft, &localFileTime );
+ FileTimeToSystemTime( &localFileTime, &local );
+
+ static char out[ 256 ];
+
+ bool am = true;
+ WORD hour = local.wHour;
+ if ( hour >= 12 )
+ {
+ am = false;
+ // 12:42 pm displays as 12:42 pm
+ // 13:42 pm displays as 1:42 pm
+ if ( hour > 12 )
+ {
+ hour -= 12;
+ }
+ }
+ Q_snprintf( out, sizeof( out ), "%d/%02d/%04d %d:%02d %s",
+ local.wMonth,
+ local.wDay,
+ local.wYear,
+ hour,
+ local.wMinute,
+ am ? "AM" : "PM" // TODO: Localize this?
+ );
+ return out;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Fill the filelist with the names of all the files in the current directory
+//-----------------------------------------------------------------------------
+#define MAX_FILTER_LENGTH 255
+void FileOpenDialog::PopulateFileList()
+{
+ // clear the current list
+ m_pFileList->DeleteAllItems();
+
+ FileFindHandle_t findHandle;
+ char pszFileModified[64];
+
+ // get the current directory
+ char currentDir[MAX_PATH * 4];
+ char dir[MAX_PATH * 4];
+ char filterList[MAX_FILTER_LENGTH+1];
+ GetCurrentDirectory(currentDir, sizeof(dir));
+
+ KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData();
+ if (combokv)
+ {
+ Q_strncpy(filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH);
+ }
+ else
+ {
+ // add wildcard for search
+ Q_strncpy(filterList, "*\0", MAX_FILTER_LENGTH);
+ }
+
+
+ char *filterPtr = filterList;
+ KeyValues *kv = new KeyValues("item");
+
+ if ( m_DialogType != FOD_SELECT_DIRECTORY )
+ {
+ while ((filterPtr != NULL) && (*filterPtr != 0))
+ {
+ // parse the next filter in the list.
+ char curFilter[MAX_FILTER_LENGTH];
+ curFilter[0] = 0;
+ int i = 0;
+ while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' ')))
+ {
+ ++filterPtr;
+ }
+ while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' '))
+ {
+ curFilter[i++] = *(filterPtr++);
+ }
+ curFilter[i] = 0;
+
+ if (curFilter[0] == 0)
+ {
+ break;
+ }
+
+ Q_snprintf( dir, MAX_PATH*4, "%s%s", currentDir, curFilter );
+
+ // Open the directory and walk it, loading files
+ const char *pszFileName = g_pFullFileSystem->FindFirst( dir, &findHandle );
+ while ( pszFileName )
+ {
+ if ( !g_pFullFileSystem->FindIsDirectory( findHandle )
+ || !IsOSX()
+ || ( IsOSX() && g_pFullFileSystem->FindIsDirectory( findHandle ) && Q_stristr( pszFileName, ".app" ) ) )
+ {
+ char pFullPath[MAX_PATH];
+ Q_snprintf( pFullPath, MAX_PATH, "%s%s", currentDir, pszFileName );
+
+ // add the file to the list
+ kv->SetString( "text", pszFileName );
+
+ kv->SetInt( "image", 1 );
+
+ IImage *image = surface()->GetIconImageForFullPath( pFullPath );
+
+ if ( image )
+ {
+ kv->SetPtr( "iconImage", (void *)image );
+ }
+
+ kv->SetInt("imageSelected", 1);
+ kv->SetInt("directory", 0);
+
+ kv->SetString( "filesize", Q_pretifymem( g_pFullFileSystem->Size( pFullPath ), 0, true ) );
+ Q_FixSlashes( pFullPath );
+ wchar_t fileType[ 80 ];
+ g_pFullFileSystem->GetFileTypeForFullPath( pFullPath, fileType, sizeof( fileType ) );
+ kv->SetWString( "type", fileType );
+
+ kv->SetString( "attributes", g_pFullFileSystem->IsFileWritable( pFullPath )? "" : "R" );
+
+ long fileModified = g_pFullFileSystem->GetFileTime( pFullPath );
+ g_pFullFileSystem->FileTimeToString( pszFileModified, sizeof( pszFileModified ), fileModified );
+ kv->SetString( "modified", pszFileModified );
+
+// kv->SetString( "created", GetFileTimetamp( findData.ftCreationTime ) );
+
+ m_pFileList->AddItem(kv, 0, false, false);
+ }
+
+ pszFileName = g_pFullFileSystem->FindNext( findHandle );
+ }
+ g_pFullFileSystem->FindClose( findHandle );
+ }
+ }
+
+ // find all the directories
+ GetCurrentDirectory( dir, sizeof(dir) );
+ Q_strncat(dir, "*", sizeof( dir ), COPY_ALL_CHARACTERS);
+
+ const char *pszFileName = g_pFullFileSystem->FindFirst( dir, &findHandle );
+ while ( pszFileName )
+ {
+ if ( pszFileName[0] != '.' && g_pFullFileSystem->FindIsDirectory( findHandle )
+ && ( !IsOSX() || ( IsOSX() && !Q_stristr( pszFileName, ".app" ) ) ) )
+ {
+ char pFullPath[MAX_PATH];
+ Q_snprintf( pFullPath, MAX_PATH, "%s%s", currentDir, pszFileName );
+
+ kv->SetString("text", pszFileName );
+ kv->SetPtr( "iconImage", (void *)NULL );
+ kv->SetInt("image", 2);
+ kv->SetInt("imageSelected", 3);
+ kv->SetInt("directory", 1);
+
+ kv->SetString( "filesize", "" );
+ kv->SetString( "type", "#FileOpenDialog_FileType_Folder" );
+
+ kv->SetString( "attributes", g_pFullFileSystem->IsFileWritable( pFullPath )? "" : "R" );
+
+ long fileModified = g_pFullFileSystem->GetFileTime( pFullPath );
+ g_pFullFileSystem->FileTimeToString( pszFileModified, sizeof( pszFileModified ), fileModified );
+ kv->SetString( "modified", pszFileModified );
+
+// kv->SetString( "created", GetFileTimetamp( findData.ftCreationTime ) );
+
+ m_pFileList->AddItem( kv, 0, false, false );
+ }
+
+ pszFileName = g_pFullFileSystem->FindNext( findHandle );
+ }
+ g_pFullFileSystem->FindClose( findHandle );
+
+ kv->deleteThis();
+ m_pFileList->SortList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Does the specified extension match something in the filter list?
+//-----------------------------------------------------------------------------
+bool FileOpenDialog::ExtensionMatchesFilter( const char *pExt )
+{
+ KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData();
+ if ( !combokv )
+ return true;
+
+ char filterList[MAX_FILTER_LENGTH+1];
+ Q_strncpy( filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH );
+
+ char *filterPtr = filterList;
+ while ((filterPtr != NULL) && (*filterPtr != 0))
+ {
+ // parse the next filter in the list.
+ char curFilter[MAX_FILTER_LENGTH];
+ curFilter[0] = 0;
+ int i = 0;
+ while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' ')))
+ {
+ ++filterPtr;
+ }
+ while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' '))
+ {
+ curFilter[i++] = *(filterPtr++);
+ }
+ curFilter[i] = 0;
+
+ if (curFilter[0] == 0)
+ break;
+
+ if ( !Q_stricmp( curFilter, "*" ) || !Q_stricmp( curFilter, "*.*" ) )
+ return true;
+
+ // FIXME: This isn't exactly right, but tough cookies;
+ // it assumes the first two characters of the filter are *.
+ Assert( curFilter[0] == '*' && curFilter[1] == '.' );
+ if ( !Q_stricmp( &curFilter[2], pExt ) )
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Choose the first non *.* filter in the filter list
+//-----------------------------------------------------------------------------
+void FileOpenDialog::ChooseExtension( char *pExt, int nBufLen )
+{
+ pExt[0] = 0;
+
+ KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData();
+ if ( !combokv )
+ return;
+
+ char filterList[MAX_FILTER_LENGTH+1];
+ Q_strncpy( filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH );
+
+ char *filterPtr = filterList;
+ while ((filterPtr != NULL) && (*filterPtr != 0))
+ {
+ // parse the next filter in the list.
+ char curFilter[MAX_FILTER_LENGTH];
+ curFilter[0] = 0;
+ int i = 0;
+ while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' ')))
+ {
+ ++filterPtr;
+ }
+ while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' '))
+ {
+ curFilter[i++] = *(filterPtr++);
+ }
+ curFilter[i] = 0;
+
+ if (curFilter[0] == 0)
+ break;
+
+ if ( !Q_stricmp( curFilter, "*" ) || !Q_stricmp( curFilter, "*.*" ) )
+ continue;
+
+ // FIXME: This isn't exactly right, but tough cookies;
+ // it assumes the first two characters of the filter are *.
+ Assert( curFilter[0] == '*' && curFilter[1] == '.' );
+ Q_strncpy( pExt, &curFilter[1], nBufLen );
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Saves the file to the start dir context
+//-----------------------------------------------------------------------------
+void FileOpenDialog::SaveFileToStartDirContext( const char *pFullPath )
+{
+ if ( m_nStartDirContext == s_StartDirContexts.InvalidIndex() )
+ return;
+
+ char pPath[MAX_PATH];
+ pPath[0] = 0;
+ Q_ExtractFilePath( pFullPath, pPath, sizeof(pPath) );
+ s_StartDirContexts[ m_nStartDirContext ] = pPath;
+}
+
+
+//-----------------------------------------------------------------------------
+// Posts a file selected message
+//-----------------------------------------------------------------------------
+void FileOpenDialog::PostFileSelectedMessage( const char *pFileName )
+{
+ m_bFileSelected = true;
+
+ // open the file!
+ KeyValues *pKeyValues = new KeyValues( "FileSelected", "fullpath", pFileName );
+ KeyValues *pFilterKeys = m_pFileTypeCombo->GetActiveItemUserData();
+ const char *pFilterInfo = pFilterKeys ? pFilterKeys->GetString( "filterinfo", NULL ) : NULL;
+ if ( pFilterInfo )
+ {
+ pKeyValues->SetString( "filterinfo", pFilterInfo );
+ }
+ if ( m_pContextKeyValues )
+ {
+ pKeyValues->AddSubKey( m_pContextKeyValues );
+ m_pContextKeyValues = NULL;
+ }
+ PostActionSignal( pKeyValues );
+ CloseModal();
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects the current folder
+//-----------------------------------------------------------------------------
+void FileOpenDialog::OnSelectFolder()
+{
+ ValidatePath();
+
+ // construct a file path
+ char pFileName[MAX_PATH];
+ GetSelectedFileName( pFileName, sizeof( pFileName ) );
+
+ Q_StripTrailingSlash( pFileName );
+
+ if ( !stricmp(pFileName, "..") )
+ {
+ MoveUpFolder();
+
+ // clear the name text
+ m_pFileNameEdit->SetText("");
+ return;
+ }
+
+ if ( !stricmp(pFileName, ".") )
+ {
+ // clear the name text
+ m_pFileNameEdit->SetText("");
+ return;
+ }
+
+ // Compute the full path
+ char pFullPath[MAX_PATH * 4];
+ if ( !Q_IsAbsolutePath( pFileName ) )
+ {
+ GetCurrentDirectory(pFullPath, sizeof(pFullPath) - MAX_PATH);
+ strcat( pFullPath, pFileName );
+ if ( !pFileName[0] )
+ {
+ Q_StripTrailingSlash( pFullPath );
+ }
+ }
+ else
+ {
+ Q_strncpy( pFullPath, pFileName, sizeof(pFullPath) );
+ }
+
+ if ( g_pFullFileSystem->FileExists( pFullPath ) )
+ {
+ // open the file!
+ SaveFileToStartDirContext( pFullPath );
+ PostFileSelectedMessage( pFullPath );
+ return;
+ }
+
+ PopulateDriveList();
+ PopulateFileList();
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle the open button being pressed
+// checks on what has changed and acts accordingly
+//-----------------------------------------------------------------------------
+void FileOpenDialog::OnOpen()
+{
+ ValidatePath();
+
+ // construct a file path
+ char pFileName[MAX_PATH];
+ GetSelectedFileName( pFileName, sizeof( pFileName ) );
+
+ int nLen = Q_strlen( pFileName );
+ bool bSpecifiedDirectory = ( pFileName[nLen-1] == '/' || pFileName[nLen-1] == '\\' ) && (!IsOSX() || ( IsOSX() && !Q_stristr( pFileName, ".app" ) ) );
+ Q_StripTrailingSlash( pFileName );
+
+ if ( !stricmp(pFileName, "..") )
+ {
+ MoveUpFolder();
+
+ // clear the name text
+ m_pFileNameEdit->SetText("");
+ return;
+ }
+
+ if ( !stricmp(pFileName, ".") )
+ {
+ // clear the name text
+ m_pFileNameEdit->SetText("");
+ return;
+ }
+
+ // Compute the full path
+ char pFullPath[MAX_PATH * 4];
+ if ( !Q_IsAbsolutePath( pFileName ) )
+ {
+ GetCurrentDirectory(pFullPath, sizeof(pFullPath) - MAX_PATH);
+ Q_AppendSlash( pFullPath, sizeof( pFullPath ) );
+ strcat(pFullPath, pFileName);
+ if ( !pFileName[0] )
+ {
+ Q_StripTrailingSlash( pFullPath );
+ }
+ }
+ else
+ {
+ Q_strncpy( pFullPath, pFileName, sizeof(pFullPath) );
+ }
+
+ Q_StripTrailingSlash( pFullPath );
+
+ // when statting a directory on Windows, you want to include
+ // the terminal slash exactly when you are statting a root
+ // directory. PKMN.
+#ifdef _WIN32
+ if ( Q_strlen( pFullPath ) == 2 )
+ {
+ Q_AppendSlash( pFullPath, Q_ARRAYSIZE( pFullPath ) );
+ }
+#endif
+
+
+ // If the name specified is a directory, then change directory
+ if ( g_pFullFileSystem->IsDirectory( pFullPath, NULL ) &&
+ ( !IsOSX() || ( IsOSX() && !Q_stristr( pFullPath, ".app" ) ) ) )
+ {
+ // it's a directory; change to the specified directory
+ if ( !bSpecifiedDirectory )
+ {
+ Q_AppendSlash( pFullPath, Q_ARRAYSIZE( pFullPath ) );
+ }
+ SetStartDirectory( pFullPath );
+
+ // clear the name text
+ m_pFileNameEdit->SetText("");
+ m_pFileNameEdit->HideMenu();
+
+ PopulateDriveList();
+ PopulateFileList();
+ InvalidateLayout();
+ return;
+ }
+ else if ( bSpecifiedDirectory )
+ {
+ PopulateDriveList();
+ PopulateFileList();
+ InvalidateLayout();
+ return;
+ }
+
+ // Append suffix of the first filter that isn't *.*
+ char extension[512];
+ Q_ExtractFileExtension( pFullPath, extension, sizeof(extension) );
+ if ( !ExtensionMatchesFilter( extension ) )
+ {
+ ChooseExtension( extension, sizeof(extension) );
+ Q_SetExtension( pFullPath, extension, sizeof(pFullPath) );
+ }
+
+ if ( g_pFullFileSystem->FileExists( pFullPath ) )
+ {
+ // open the file!
+ SaveFileToStartDirContext( pFullPath );
+ PostFileSelectedMessage( pFullPath );
+ return;
+ }
+
+ // file not found
+ if ( ( m_DialogType == FOD_SAVE ) && pFileName[0] )
+ {
+ // open the file!
+ SaveFileToStartDirContext( pFullPath );
+ PostFileSelectedMessage( pFullPath );
+ return;
+ }
+
+ PopulateDriveList();
+ PopulateFileList();
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: using the file edit box as a prefix, create a menu of all possible files
+//-----------------------------------------------------------------------------
+void FileOpenDialog::PopulateFileNameCompletion()
+{
+ char buf[80];
+ m_pFileNameEdit->GetText(buf, 80);
+ wchar_t wbuf[80];
+ m_pFileNameEdit->GetText(wbuf, 80);
+ int bufLen = wcslen(wbuf);
+
+ // delete all items before we check if there's even a string
+ m_pFileNameEdit->DeleteAllItems();
+
+ // no string at all - don't show even bother showing it
+ if (bufLen == 0)
+ {
+ m_pFileNameEdit->HideMenu();
+ return;
+ }
+
+ // what files use current string as a prefix?
+ int nCount = m_pFileList->GetItemCount();
+ int i;
+ for ( i = 0 ; i < nCount ; i++ )
+ {
+ KeyValues *kv = m_pFileList->GetItem(m_pFileList->GetItemIDFromRow(i));
+ const wchar_t *wszString = kv->GetWString("text");
+ if ( !_wcsnicmp(wbuf, wszString, bufLen) )
+ {
+ m_pFileNameEdit->AddItem(wszString, NULL);
+ }
+ }
+
+ // if there are any items - show the menu
+ if ( m_pFileNameEdit->GetItemCount() > 0 )
+ {
+ m_pFileNameEdit->ShowMenu();
+ }
+ else
+ {
+ m_pFileNameEdit->HideMenu();
+ }
+
+ m_pFileNameEdit->InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle an item in the list being selected
+//-----------------------------------------------------------------------------
+void FileOpenDialog::OnItemSelected()
+{
+ // make sure only one item is selected
+ if (m_pFileList->GetSelectedItemsCount() != 1)
+ {
+ m_pFileNameEdit->SetText("");
+ }
+ else
+ {
+ // put the file name into the text edit box
+ KeyValues *data = m_pFileList->GetItem(m_pFileList->GetSelectedItem(0));
+ m_pFileNameEdit->SetText(data->GetString("text"));
+ }
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle an item in the Drive combo box being selected
+//-----------------------------------------------------------------------------
+void FileOpenDialog::OnTextChanged(KeyValues *kv)
+{
+ Panel *pPanel = (Panel *) kv->GetPtr("panel", NULL);
+
+ // first check which control had its text changed!
+ if (pPanel == m_pFullPathEdit)
+ {
+ m_pFileNameEdit->HideMenu();
+ m_pFileNameEdit->SetText("");
+ OnOpen();
+ }
+ else if (pPanel == m_pFileNameEdit)
+ {
+ PopulateFileNameCompletion();
+ }
+ else if (pPanel == m_pFileTypeCombo)
+ {
+ m_pFileNameEdit->HideMenu();
+ PopulateFileList();
+ }
+}
diff --git a/mp/src/vgui2/vgui_controls/FileOpenStateMachine.cpp b/mp/src/vgui2/vgui_controls/FileOpenStateMachine.cpp
new file mode 100644
index 00000000..5572c753
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/FileOpenStateMachine.cpp
@@ -0,0 +1,497 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// This is a helper class designed to help with the chains of modal dialogs
+// encountered when trying to open or save a particular file
+//
+//=============================================================================
+
+#include "vgui_controls/FileOpenStateMachine.h"
+#include "tier1/KeyValues.h"
+#include "vgui_controls/FileOpenDialog.h"
+#include "vgui_controls/MessageBox.h"
+#include "vgui_controls/perforcefilelistframe.h"
+#include "vgui_controls/savedocumentquery.h"
+#include "filesystem.h"
+#include "p4lib/ip4.h"
+#include "tier2/tier2.h"
+#include "tier0/icommandline.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+FileOpenStateMachine::FileOpenStateMachine( vgui::Panel *pParent, IFileOpenStateMachineClient *pClient ) : BaseClass( pParent, "FileOpenStateMachine" )
+{
+ m_pClient = pClient;
+ m_CompletionState = SUCCESSFUL;
+ m_CurrentState = STATE_NONE;
+ m_pContextKeyValues = NULL;
+ SetVisible( false );
+}
+
+FileOpenStateMachine::~FileOpenStateMachine()
+{
+ CleanUpContextKeyValues();
+}
+
+
+//-----------------------------------------------------------------------------
+// Cleans up keyvalues
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::CleanUpContextKeyValues()
+{
+ if ( m_pContextKeyValues )
+ {
+ m_pContextKeyValues->deleteThis();
+ m_pContextKeyValues = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the state machine completion state
+//-----------------------------------------------------------------------------
+FileOpenStateMachine::CompletionState_t FileOpenStateMachine::GetCompletionState()
+{
+ return m_CompletionState;
+}
+
+
+//-----------------------------------------------------------------------------
+// Utility to set the completion state
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::SetCompletionState( FileOpenStateMachine::CompletionState_t state )
+{
+ m_CompletionState = state;
+ if ( m_CompletionState == IN_PROGRESS )
+ return;
+
+ m_CurrentState = STATE_NONE;
+
+ KeyValues *kv = new KeyValues( "FileStateMachineFinished" );
+ kv->SetInt( "completionState", m_CompletionState );
+ kv->SetInt( "wroteFile", m_bWroteFile );
+ kv->SetString( "fullPath", m_FileName.Get() );
+ kv->SetString( "fileType", m_bIsOpeningFile ? m_OpenFileType.Get() : m_SaveFileType.Get() );
+ if ( m_pContextKeyValues )
+ {
+ kv->AddSubKey( m_pContextKeyValues );
+ m_pContextKeyValues = NULL;
+ }
+ PostActionSignal( kv );
+}
+
+
+//-----------------------------------------------------------------------------
+// Called by the message box in OverwriteFileDialog
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::OnOverwriteFile( )
+{
+ CheckOutDialog( );
+}
+
+void FileOpenStateMachine::OnCancelOverwriteFile( )
+{
+ SetCompletionState( FILE_NOT_OVERWRITTEN );
+}
+
+
+//-----------------------------------------------------------------------------
+// Shows the overwrite existing file dialog
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::OverwriteFileDialog( )
+{
+ if ( !g_pFullFileSystem->FileExists( m_FileName ) )
+ {
+ CheckOutDialog( );
+ return;
+ }
+
+ m_CurrentState = STATE_SHOWING_OVERWRITE_DIALOG;
+
+ char pBuf[1024];
+ Q_snprintf( pBuf, sizeof(pBuf), "File already exists. Overwrite it?\n\n\"%s\"\n", m_FileName.Get() );
+ vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Overwrite Existing File?", pBuf, GetParent() );
+ pMessageBox->AddActionSignalTarget( this );
+ pMessageBox->SetOKButtonVisible( true );
+ pMessageBox->SetOKButtonText( "Yes" );
+ pMessageBox->SetCancelButtonVisible( true );
+ pMessageBox->SetCancelButtonText( "No" );
+ pMessageBox->SetCloseButtonVisible( false );
+ pMessageBox->SetCommand( new KeyValues( "OverwriteFile" ) );
+ pMessageBox->SetCancelCommand( new KeyValues( "CancelOverwriteFile" ) );
+ pMessageBox->DoModal();
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to open a particular file in perforce, and deal with all the lovely dialogs
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::OnFileSelectionCancelled()
+{
+ if ( m_CurrentState == STATE_SHOWING_SAVE_DIALOG )
+ {
+ SetCompletionState( FILE_SAVE_NAME_NOT_SPECIFIED );
+ return;
+ }
+
+ if ( m_CurrentState == STATE_SHOWING_OPEN_DIALOG )
+ {
+ SetCompletionState( FILE_OPEN_NAME_NOT_SPECIFIED );
+ return;
+ }
+
+ Assert(0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to open a particular file in perforce, and deal with all the lovely dialogs
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::OnFileSelected( KeyValues *pKeyValues )
+{
+ if ( m_CurrentState == STATE_SHOWING_SAVE_DIALOG )
+ {
+ m_FileName = pKeyValues->GetString( "fullpath" );
+ const char *pFilterInfo = pKeyValues->GetString( "filterinfo" );
+ if ( pFilterInfo )
+ {
+ m_SaveFileType = pFilterInfo;
+ }
+ OverwriteFileDialog();
+ return;
+ }
+
+ if ( m_CurrentState == STATE_SHOWING_OPEN_DIALOG )
+ {
+ m_FileName = pKeyValues->GetString( "fullpath" );
+ const char *pFilterInfo = pKeyValues->GetString( "filterinfo" );
+ if ( pFilterInfo )
+ {
+ m_OpenFileType = pFilterInfo;
+ }
+ ReadFile( );
+ return;
+ }
+
+ Assert(0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes the file out
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::WriteFile()
+{
+ m_CurrentState = STATE_WRITING_FILE;
+ if ( !m_pClient->OnWriteFileToDisk( m_FileName, m_SaveFileType, m_pContextKeyValues ) )
+ {
+ SetCompletionState( ERROR_WRITING_FILE );
+ return;
+ }
+
+ m_bWroteFile = true;
+ if ( m_bShowPerforceDialogs )
+ {
+ m_CurrentState = STATE_SHOWING_PERFORCE_ADD_DIALOG;
+ ShowPerforceQuery( GetParent(), m_FileName, this, NULL, PERFORCE_ACTION_FILE_ADD );
+ return;
+ }
+
+ if ( !m_bIsOpeningFile )
+ {
+ SetCompletionState( SUCCESSFUL );
+ return;
+ }
+
+ OpenFileDialog();
+}
+
+
+//-----------------------------------------------------------------------------
+// Called by the message box in MakeFileWriteableDialog
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::OnMakeFileWriteable( )
+{
+ if ( !g_pFullFileSystem->SetFileWritable( m_FileName, true ) )
+ {
+ SetCompletionState( ERROR_MAKING_FILE_WRITEABLE );
+ return;
+ }
+
+ WriteFile();
+}
+
+void FileOpenStateMachine::OnCancelMakeFileWriteable( )
+{
+ SetCompletionState( FILE_NOT_MADE_WRITEABLE );
+}
+
+
+//-----------------------------------------------------------------------------
+// Shows the make file writeable dialog
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::MakeFileWriteableDialog( )
+{
+ // If the file is writeable, write it!
+ if ( !g_pFullFileSystem->FileExists( m_FileName ) || g_pFullFileSystem->IsFileWritable( m_FileName ) )
+ {
+ WriteFile();
+ return;
+ }
+
+ // If it's in perforce, and not checked out, then we must abort.
+ bool bIsInPerforce = p4->IsFileInPerforce( m_FileName );
+ bool bIsOpened = ( p4->GetFileState( m_FileName ) != P4FILE_UNOPENED );
+ if ( bIsInPerforce && !bIsOpened )
+ {
+ SetCompletionState( FILE_NOT_CHECKED_OUT );
+ return;
+ }
+
+ m_CurrentState = STATE_SHOWING_MAKE_FILE_WRITEABLE_DIALOG;
+
+ char pBuf[1024];
+ Q_snprintf( pBuf, sizeof(pBuf), "Encountered read-only file. Should it be made writeable?\n\n\"%s\"\n", m_FileName.Get() );
+ vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Make File Writeable?", pBuf, GetParent() );
+ pMessageBox->AddActionSignalTarget( this );
+ pMessageBox->SetOKButtonVisible( true );
+ pMessageBox->SetOKButtonText( "Yes" );
+ pMessageBox->SetCancelButtonVisible( true );
+ pMessageBox->SetCancelButtonText( "No" );
+ pMessageBox->SetCloseButtonVisible( false );
+ pMessageBox->SetCommand( new KeyValues( "MakeFileWriteable" ) );
+ pMessageBox->SetCancelCommand( new KeyValues( "CancelMakeFileWriteable" ) );
+ pMessageBox->DoModal();
+}
+
+
+//-----------------------------------------------------------------------------
+// Called when ShowPerforceQuery completes
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::OnPerforceQueryCompleted( KeyValues *pKeyValues )
+{
+ if ( m_CurrentState == STATE_SHOWING_CHECK_OUT_DIALOG )
+ {
+ if ( pKeyValues->GetInt( "operationPerformed" ) == 0 )
+ {
+ SetCompletionState( FILE_NOT_CHECKED_OUT );
+ return;
+ }
+
+ MakeFileWriteableDialog();
+ return;
+ }
+
+ if ( m_CurrentState == STATE_SHOWING_PERFORCE_ADD_DIALOG )
+ {
+ if ( !m_bIsOpeningFile )
+ {
+ SetCompletionState( SUCCESSFUL );
+ return;
+ }
+
+ OpenFileDialog();
+ return;
+ }
+
+ Assert(0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to open a particular file in perforce, and deal with all the lovely dialogs
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::CheckOutDialog( )
+{
+ if ( m_bShowPerforceDialogs )
+ {
+ m_CurrentState = STATE_SHOWING_CHECK_OUT_DIALOG;
+ ShowPerforceQuery( GetParent(), m_FileName, this, NULL, PERFORCE_ACTION_FILE_EDIT );
+ return;
+ }
+
+ WriteFile();
+}
+
+
+//-----------------------------------------------------------------------------
+// These 3 messages come from the savedocumentquery dialog
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::OnSaveFile()
+{
+ if ( !m_FileName[0] || !Q_IsAbsolutePath( m_FileName ) )
+ {
+ m_CurrentState = STATE_SHOWING_SAVE_DIALOG;
+
+ FileOpenDialog *pDialog = new FileOpenDialog( GetParent(), "Save As", false );
+ m_pClient->SetupFileOpenDialog( pDialog, false, m_SaveFileType, m_pContextKeyValues );
+ pDialog->SetDeleteSelfOnClose( true );
+ pDialog->AddActionSignalTarget( this );
+ pDialog->DoModal( );
+ return;
+ }
+
+ CheckOutDialog( );
+}
+
+void FileOpenStateMachine::OnMarkNotDirty()
+{
+ if ( !m_bIsOpeningFile )
+ {
+ SetCompletionState( SUCCESSFUL );
+ return;
+ }
+
+ // Jump right to opening the file
+ OpenFileDialog( );
+}
+
+void FileOpenStateMachine::OnCancelSaveDocument()
+{
+ SetCompletionState( FILE_SAVE_CANCELLED );
+}
+
+
+//-----------------------------------------------------------------------------
+// Show the save document query dialog
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::ShowSaveQuery( )
+{
+ m_CurrentState = STATE_SHOWING_SAVE_DIRTY_FILE_DIALOG;
+ ShowSaveDocumentQuery( GetParent(), m_FileName, m_SaveFileType, 0, this, NULL );
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to save a specified file, and deal with all the lovely dialogs
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::SaveFile( KeyValues *pContextKeyValues, const char *pFileName, const char *pFileType, int nFlags )
+{
+ CleanUpContextKeyValues();
+ SetCompletionState( IN_PROGRESS );
+ m_pContextKeyValues = pContextKeyValues;
+ m_FileName = pFileName;
+ m_SaveFileType = pFileType;
+ m_OpenFileType = NULL;
+ m_OpenFileName = NULL;
+
+ // Clear the P4 dialog flag for SDK users and licensees without Perforce
+ if ( g_pFullFileSystem->IsSteam() || CommandLine()->FindParm( "-nop4" ) )
+ {
+ nFlags &= ~FOSM_SHOW_PERFORCE_DIALOGS;
+ }
+
+ m_bShowPerforceDialogs = ( nFlags & FOSM_SHOW_PERFORCE_DIALOGS ) != 0;
+ m_bShowSaveQuery = ( nFlags & FOSM_SHOW_SAVE_QUERY ) != 0;
+ m_bIsOpeningFile = false;
+ m_bWroteFile = false;
+
+ if ( m_bShowSaveQuery )
+ {
+ ShowSaveQuery();
+ return;
+ }
+
+ OnSaveFile();
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads the file in
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::ReadFile()
+{
+ m_CurrentState = STATE_READING_FILE;
+ if ( !m_pClient->OnReadFileFromDisk( m_FileName, m_OpenFileType, m_pContextKeyValues ) )
+ {
+ SetCompletionState( ERROR_READING_FILE );
+ return;
+ }
+
+ SetCompletionState( SUCCESSFUL );
+}
+
+
+//-----------------------------------------------------------------------------
+// Shows the open file dialog
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::OpenFileDialog( )
+{
+ m_CurrentState = STATE_SHOWING_OPEN_DIALOG;
+
+ if ( m_OpenFileName.IsEmpty() )
+ {
+ FileOpenDialog *pDialog = new FileOpenDialog( GetParent(), "Open", true );
+ m_pClient->SetupFileOpenDialog( pDialog, true, m_OpenFileType, m_pContextKeyValues );
+ pDialog->SetDeleteSelfOnClose( true );
+ pDialog->AddActionSignalTarget( this );
+ pDialog->DoModal( );
+ }
+ else
+ {
+ m_FileName = m_OpenFileName;
+ ReadFile();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Opens a file, saves an existing one if necessary
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::OpenFile( const char *pOpenFileType, KeyValues *pContextKeyValues, const char *pSaveFileName, const char *pSaveFileType, int nFlags )
+{
+ CleanUpContextKeyValues();
+ SetCompletionState( IN_PROGRESS );
+ m_pContextKeyValues = pContextKeyValues;
+ m_FileName = pSaveFileName;
+ m_SaveFileType = pSaveFileType;
+ m_OpenFileType = pOpenFileType;
+ m_OpenFileName = NULL;
+ m_bShowPerforceDialogs = ( nFlags & FOSM_SHOW_PERFORCE_DIALOGS ) != 0;
+ m_bShowSaveQuery = ( nFlags & FOSM_SHOW_SAVE_QUERY ) != 0;
+ m_bIsOpeningFile = true;
+ m_bWroteFile = false;
+
+ if ( m_bShowSaveQuery )
+ {
+ ShowSaveQuery();
+ return;
+ }
+
+ OpenFileDialog();
+}
+
+
+//-----------------------------------------------------------------------------
+// Version of OpenFile that skips browsing for a particular file to open
+//-----------------------------------------------------------------------------
+void FileOpenStateMachine::OpenFile( const char *pOpenFileName, const char *pOpenFileType, KeyValues *pContextKeyValues, const char *pSaveFileName, const char *pSaveFileType, int nFlags )
+{
+ CleanUpContextKeyValues();
+ SetCompletionState( IN_PROGRESS );
+ m_pContextKeyValues = pContextKeyValues;
+ m_FileName = pSaveFileName;
+ m_SaveFileType = pSaveFileType;
+ m_OpenFileType = pOpenFileType;
+ m_bShowPerforceDialogs = ( nFlags & FOSM_SHOW_PERFORCE_DIALOGS ) != 0;
+ m_bShowSaveQuery = ( nFlags & FOSM_SHOW_SAVE_QUERY ) != 0;
+ m_bIsOpeningFile = true;
+ m_bWroteFile = false;
+ m_OpenFileName = pOpenFileName;
+ if ( m_bShowSaveQuery )
+ {
+ ShowSaveQuery();
+ return;
+ }
+
+ OpenFileDialog();
+}
+
+
diff --git a/mp/src/vgui2/vgui_controls/FocusNavGroup.cpp b/mp/src/vgui2/vgui_controls/FocusNavGroup.cpp
new file mode 100644
index 00000000..10fc88ad
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/FocusNavGroup.cpp
@@ -0,0 +1,433 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <assert.h>
+
+#include <vgui/ISurface.h>
+#include <vgui/IVGui.h>
+#include <vgui/IPanel.h>
+#include <vgui/VGUI.h>
+#include <KeyValues.h>
+#include <tier0/dbg.h>
+
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/FocusNavGroup.h>
+#include <vgui_controls/Panel.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+// Input : *panel - parent panel
+//-----------------------------------------------------------------------------
+FocusNavGroup::FocusNavGroup(Panel *panel) : _mainPanel(panel)
+{
+ _currentFocus = NULL;
+ _topLevelFocus = false;
+ _defaultButton = NULL;
+ _currentDefaultButton = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+FocusNavGroup::~FocusNavGroup()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the focus to the previous panel in the tab order
+// Input : *panel - panel currently with focus
+//-----------------------------------------------------------------------------
+bool FocusNavGroup::RequestFocusPrev(VPANEL panel)
+{
+ if(panel==0)
+ return false;
+
+ _currentFocus = NULL;
+ int newPosition = 9999999;
+ if (panel)
+ {
+ newPosition = ipanel()->GetTabPosition(panel);
+ }
+
+ bool bFound = false;
+ bool bRepeat = true;
+ Panel *best = NULL;
+ while (1)
+ {
+ newPosition--;
+ if (newPosition > 0)
+ {
+ int bestPosition = 0;
+
+ // look for the next tab position
+ for (int i = 0; i < _mainPanel->GetChildCount(); i++)
+ {
+ Panel *child = _mainPanel->GetChild(i);
+ if (child && child->IsVisible() && child->IsEnabled() && child->GetTabPosition())
+ {
+ int tabPosition = child->GetTabPosition();
+ if (tabPosition == newPosition)
+ {
+ // we've found the right tab
+ best = child;
+ bestPosition = newPosition;
+
+ // don't loop anymore since we've found the correct panel
+ break;
+ }
+ else if (tabPosition < newPosition && tabPosition > bestPosition)
+ {
+ // record the match since this is the closest so far
+ bestPosition = tabPosition;
+ best = child;
+ }
+ }
+ }
+
+ if (!bRepeat)
+ break;
+
+ if (best)
+ break;
+ }
+ else
+ {
+ // reset new position for next loop
+ newPosition = 9999999;
+ }
+
+ // haven't found an item
+
+ if (!_topLevelFocus)
+ {
+ // check to see if we should push the focus request up
+ if (_mainPanel->GetVParent() && _mainPanel->GetVParent() != surface()->GetEmbeddedPanel())
+ {
+ // we're not a top level panel, so forward up the request instead of looping
+ if (ipanel()->RequestFocusPrev(_mainPanel->GetVParent(), _mainPanel->GetVPanel()))
+ {
+ bFound = true;
+ SetCurrentDefaultButton(NULL);
+ break;
+ }
+ }
+ }
+
+ // not found an item, loop back
+ newPosition = 9999999;
+ bRepeat = false;
+ }
+
+ if (best)
+ {
+ _currentFocus = best->GetVPanel();
+ best->RequestFocus(-1);
+ bFound = true;
+
+ if (!CanButtonBeDefault(best->GetVPanel()))
+ {
+ if (_defaultButton)
+ {
+ SetCurrentDefaultButton(_defaultButton);
+ }
+ else
+ {
+ SetCurrentDefaultButton(NULL);
+
+ // we need to ask the parent to set its default button
+ if (_mainPanel->GetVParent())
+ {
+ ivgui()->PostMessage(_mainPanel->GetVParent(), new KeyValues("FindDefaultButton"), NULL);
+ }
+ }
+ }
+ else
+ {
+ SetCurrentDefaultButton(best->GetVPanel());
+ }
+ }
+ return bFound;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the focus to the previous panel in the tab order
+// Input : *panel - panel currently with focus
+//-----------------------------------------------------------------------------
+bool FocusNavGroup::RequestFocusNext(VPANEL panel)
+{
+ // basic recursion guard, in case user has set up a bad focus hierarchy
+ static int stack_depth = 0;
+ stack_depth++;
+
+ _currentFocus = NULL;
+ int newPosition = 0;
+ if (panel)
+ {
+ newPosition = ipanel()->GetTabPosition(panel);
+ }
+
+ bool bFound = false;
+ bool bRepeat = true;
+ Panel *best = NULL;
+ while (1)
+ {
+ newPosition++;
+ int bestPosition = 999999;
+
+ // look for the next tab position
+ for (int i = 0; i < _mainPanel->GetChildCount(); i++)
+ {
+ Panel *child = _mainPanel->GetChild(i);
+ if ( !child )
+ continue;
+
+ if (child && child->IsVisible() && child->IsEnabled() && child->GetTabPosition())
+ {
+ int tabPosition = child->GetTabPosition();
+ if (tabPosition == newPosition)
+ {
+ // we've found the right tab
+ best = child;
+ bestPosition = newPosition;
+
+ // don't loop anymore since we've found the correct panel
+ break;
+ }
+ else if (tabPosition > newPosition && tabPosition < bestPosition)
+ {
+ // record the match since this is the closest so far
+ bestPosition = tabPosition;
+ best = child;
+ }
+ }
+ }
+
+ if (!bRepeat)
+ break;
+
+ if (best)
+ break;
+
+ // haven't found an item
+
+ // check to see if we should push the focus request up
+ if (!_topLevelFocus)
+ {
+ if (_mainPanel->GetVParent() && _mainPanel->GetVParent() != surface()->GetEmbeddedPanel())
+ {
+ // we're not a top level panel, so forward up the request instead of looping
+ if (stack_depth < 15)
+ {
+ if (ipanel()->RequestFocusNext(_mainPanel->GetVParent(), _mainPanel->GetVPanel()))
+ {
+ bFound = true;
+ SetCurrentDefaultButton(NULL);
+ break;
+ }
+
+ // if we find one then we break, otherwise we loop
+ }
+ }
+ }
+
+ // loop back
+ newPosition = 0;
+ bRepeat = false;
+ }
+
+ if (best)
+ {
+ _currentFocus = best->GetVPanel();
+ best->RequestFocus(1);
+ bFound = true;
+
+ if (!CanButtonBeDefault(best->GetVPanel()))
+ {
+ if (_defaultButton)
+ {
+ SetCurrentDefaultButton(_defaultButton);
+ }
+ else
+ {
+ SetCurrentDefaultButton(NULL);
+
+ // we need to ask the parent to set its default button
+ if (_mainPanel->GetVParent())
+ {
+ ivgui()->PostMessage(_mainPanel->GetVParent(), new KeyValues("FindDefaultButton"), NULL);
+ }
+ }
+ }
+ else
+ {
+ SetCurrentDefaultButton(best->GetVPanel());
+ }
+ }
+
+ stack_depth--;
+ return bFound;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the panel that owns this FocusNavGroup to be the root in the focus traversal heirarchy
+//-----------------------------------------------------------------------------
+void FocusNavGroup::SetFocusTopLevel(bool state)
+{
+ _topLevelFocus = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets panel which receives input when ENTER is hit
+//-----------------------------------------------------------------------------
+void FocusNavGroup::SetDefaultButton(Panel *panel)
+{
+ VPANEL vpanel = panel ? panel->GetVPanel() : NULL;
+ if ( vpanel == _defaultButton.Get() )
+ return;
+
+// Assert(CanButtonBeDefault(vpanel));
+
+ _defaultButton = vpanel;
+ SetCurrentDefaultButton(_defaultButton);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets panel which receives input when ENTER is hit
+//-----------------------------------------------------------------------------
+void FocusNavGroup::SetCurrentDefaultButton(VPANEL panel, bool sendCurrentDefaultButtonMessage)
+{
+ if (panel == _currentDefaultButton.Get())
+ return;
+
+ if ( sendCurrentDefaultButtonMessage && _currentDefaultButton.Get() != 0)
+ {
+ ivgui()->PostMessage(_currentDefaultButton, new KeyValues("SetAsCurrentDefaultButton", "state", 0), NULL);
+ }
+
+ _currentDefaultButton = panel;
+
+ if ( sendCurrentDefaultButtonMessage && _currentDefaultButton.Get() != 0)
+ {
+ ivgui()->PostMessage(_currentDefaultButton, new KeyValues("SetAsCurrentDefaultButton", "state", 1), NULL);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets panel which receives input when ENTER is hit
+//-----------------------------------------------------------------------------
+VPANEL FocusNavGroup::GetCurrentDefaultButton()
+{
+ return _currentDefaultButton;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets panel which receives input when ENTER is hit
+//-----------------------------------------------------------------------------
+VPANEL FocusNavGroup::GetDefaultButton()
+{
+ return _defaultButton;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: finds the panel which is activated by the specified key
+// Input : code - the keycode of the hotkey
+// Output : Panel * - NULL if no panel found
+//-----------------------------------------------------------------------------
+Panel *FocusNavGroup::FindPanelByHotkey(wchar_t key)
+{
+ for (int i = 0; i < _mainPanel->GetChildCount(); i++)
+ {
+ Panel *child = _mainPanel->GetChild(i);
+ if ( !child )
+ continue;
+
+ Panel *hot = child->HasHotkey(key);
+ if (hot && hot->IsVisible() && hot->IsEnabled())
+ {
+ return hot;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Panel *FocusNavGroup::GetDefaultPanel()
+{
+ for (int i = 0; i < _mainPanel->GetChildCount(); i++)
+ {
+ Panel *child = _mainPanel->GetChild(i);
+ if ( !child )
+ continue;
+
+ if (child->GetTabPosition() == 1)
+ {
+ return child;
+ }
+ }
+
+ return NULL; // no specific panel set
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Panel *FocusNavGroup::GetCurrentFocus()
+{
+ return _currentFocus ? ipanel()->GetPanel(_currentFocus, vgui::GetControlsModuleName()) : NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the current focus
+//-----------------------------------------------------------------------------
+VPANEL FocusNavGroup::SetCurrentFocus(VPANEL focus, VPANEL defaultPanel)
+{
+ _currentFocus = focus;
+
+ // if we haven't found a default panel yet, let's see if we know of one
+ if (defaultPanel == 0)
+ {
+ // can this focus itself by the default
+ if (CanButtonBeDefault(focus))
+ {
+ defaultPanel = focus;
+ }
+ else if (_defaultButton) // do we know of a default button
+ {
+ defaultPanel = _defaultButton;
+ }
+ }
+
+ SetCurrentDefaultButton(defaultPanel);
+ return defaultPanel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if the specified panel can be the default
+//-----------------------------------------------------------------------------
+bool FocusNavGroup::CanButtonBeDefault(VPANEL panel)
+{
+ if( panel == 0 )
+ return false;
+
+ KeyValues *data = new KeyValues("CanBeDefaultButton");
+
+ bool bResult = false;
+ if (ipanel()->RequestInfo(panel, data))
+ {
+ bResult = (data->GetInt("result") == 1);
+ }
+ data->deleteThis();
+ return bResult;
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/Frame.cpp b/mp/src/vgui2/vgui_controls/Frame.cpp
new file mode 100644
index 00000000..3d4718ad
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/Frame.cpp
@@ -0,0 +1,2396 @@
+//========= Copyright 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 );
+ }
+
+ virtual void ApplySchemeSettings(IScheme *pScheme)
+ {
+ Button::ApplySchemeSettings(pScheme);
+
+ _enabledFgColor = GetSchemeColor("FrameTitleButton.FgColor", pScheme);
+ _enabledBgColor = GetSchemeColor("FrameTitleButton.BgColor", pScheme);
+
+ _disabledFgColor = GetSchemeColor("FrameTitleButton.DisabledFgColor", pScheme);
+ _disabledBgColor = GetSchemeColor("FrameTitleButton.DisabledBgColor", pScheme);
+
+ _brightBorder = pScheme->GetBorder("TitleButtonBorder");
+ _depressedBorder = pScheme->GetBorder("TitleButtonDepressedBorder");
+ _disabledBorder = pScheme->GetBorder("TitleButtonDisabledBorder");
+
+ SetDisabledLook(_disabledLook);
+ }
+
+ virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
+ {
+ if (_disabledLook)
+ {
+ return _disabledBorder;
+ }
+
+ if (depressed)
+ {
+ return _depressedBorder;
+ }
+
+ return _brightBorder;
+ }
+
+ virtual void SetDisabledLook(bool state)
+ {
+ _disabledLook = state;
+ if (!_disabledLook)
+ {
+ SetDefaultColor(_enabledFgColor, _enabledBgColor);
+ SetArmedColor(_enabledFgColor, _enabledBgColor);
+ SetDepressedColor(_enabledFgColor, _enabledBgColor);
+ }
+ else
+ {
+ // setup disabled colors
+ SetDefaultColor(_disabledFgColor, _disabledBgColor);
+ SetArmedColor(_disabledFgColor, _disabledBgColor);
+ SetDepressedColor(_disabledFgColor, _disabledBgColor);
+ }
+ }
+
+ virtual void PerformLayout()
+ {
+ Button::PerformLayout();
+ Repaint();
+ }
+
+ // Don't request focus.
+ // This will keep items in the listpanel selected.
+ virtual void OnMousePressed(MouseCode code)
+ {
+ if (!IsEnabled())
+ return;
+
+ if (!IsMouseClickEnabled(code))
+ return;
+
+ if (IsUseCaptureMouseEnabled())
+ {
+ {
+ SetSelected(true);
+ Repaint();
+ }
+
+ // lock mouse input to going to this button
+ input()->SetMouseCapture(GetVPanel());
+ }
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: icon button
+//-----------------------------------------------------------------------------
+class FrameSystemButton : public MenuButton
+{
+ DECLARE_CLASS_SIMPLE( FrameSystemButton, MenuButton );
+
+private:
+ IImage *_enabled, *_disabled;
+ Color _enCol, _disCol;
+ bool _respond;
+ CUtlString m_EnabledImage;
+ CUtlString m_DisabledImage;
+
+public:
+ FrameSystemButton(Panel *parent, const char *panelName) : MenuButton(parent, panelName, "")
+ {
+ _disabled = _enabled = NULL;
+ _respond = true;
+ SetEnabled(false);
+ // This menu will open if we use the left or right mouse button
+ SetMouseClickEnabled( MOUSE_RIGHT, true );
+ SetBlockDragChaining( true );
+ }
+
+ void SetImages( const char *pEnabledImage, const char *pDisabledImage = NULL )
+ {
+ m_EnabledImage = pEnabledImage;
+ m_DisabledImage = pDisabledImage ? pDisabledImage : pEnabledImage;
+ }
+
+ void GetImageSize( int &w, int &h )
+ {
+ w = h = 0;
+
+ int tw = 0, th = 0;
+ if ( _enabled )
+ {
+ _enabled->GetSize( w, h );
+ }
+ if ( _disabled )
+ {
+ _disabled->GetSize( tw, th );
+ }
+ if ( tw > w )
+ {
+ w = tw;
+ }
+ if ( th > h )
+ {
+ h = th;
+ }
+ }
+
+ virtual void ApplySchemeSettings(IScheme *pScheme)
+ {
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ _enCol = GetSchemeColor("FrameSystemButton.FgColor", pScheme);
+ _disCol = GetSchemeColor("FrameSystemButton.BgColor", pScheme);
+
+ const char *pEnabledImage = m_EnabledImage.Length() ? m_EnabledImage.Get() :
+ pScheme->GetResourceString( "FrameSystemButton.Icon" );
+ const char *pDisabledImage = m_DisabledImage.Length() ? m_DisabledImage.Get() :
+ pScheme->GetResourceString( "FrameSystemButton.DisabledIcon" );
+ _enabled = scheme()->GetImage( pEnabledImage, false);
+ _disabled = scheme()->GetImage( pDisabledImage, false);
+
+ SetTextInset(0, 0);
+
+ // get our iconic image
+ SetEnabled(IsEnabled());
+ }
+
+ virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
+ {
+ return NULL;
+ }
+
+ virtual void SetEnabled(bool state)
+ {
+ Button::SetEnabled(state);
+
+ if (IsEnabled())
+ {
+ if ( _enabled )
+ {
+ SetImageAtIndex(0, _enabled, 0);
+ }
+ SetBgColor(_enCol);
+ SetDefaultColor(_enCol, _enCol);
+ SetArmedColor(_enCol, _enCol);
+ SetDepressedColor(_enCol, _enCol);
+ }
+ else
+ {
+ if ( _disabled )
+ {
+ SetImageAtIndex(0, _disabled, 0);
+ }
+ SetBgColor(_disCol);
+ SetDefaultColor(_disCol, _disCol);
+ SetArmedColor(_disCol, _disCol);
+ SetDepressedColor(_disCol, _disCol);
+ }
+ }
+
+ void SetResponsive(bool state)
+ {
+ _respond = state;
+ }
+
+ virtual void OnMousePressed(MouseCode code)
+ {
+ // button may look enabled but not be responsive
+ if (!_respond)
+ return;
+
+ BaseClass::OnMousePressed(code);
+ }
+
+ virtual void OnMouseDoublePressed(MouseCode code)
+ {
+ // button may look enabled but not be responsive
+ if (!_respond)
+ return;
+
+ // only close if left is double pressed
+ if (code == MOUSE_LEFT)
+ {
+ // double click on the icon closes the window
+ // But only if the menu contains a 'close' item
+ vgui::Menu *pMenu = GetMenu();
+ if ( pMenu && pMenu->FindChildByName("Close") )
+ {
+ PostMessage(GetVParent(), new KeyValues("CloseFrameButtonPressed"));
+ }
+ }
+ }
+
+};
+
+} // namespace vgui
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+Frame::Frame(Panel *parent, const char *panelName, bool showTaskbarIcon /*=true*/, bool bPopup /*=true*/ ) : EditablePanel(parent, panelName)
+{
+ // frames start invisible, to avoid having window flicker in on taskbar
+ SetVisible(false);
+ if ( bPopup )
+ {
+ MakePopup(showTaskbarIcon);
+ }
+
+ m_hPreviousModal = 0;
+
+ _title=null;
+ _moveable=true;
+ _sizeable=true;
+ m_bHasFocus=false;
+ _flashWindow=false;
+ _drawTitleBar = true;
+ m_bPreviouslyVisible = false;
+ m_bFadingOut = false;
+ m_bDisableFadeEffect = false;
+ m_flTransitionEffectTime = 0.0f;
+ m_flFocusTransitionEffectTime = 0.0f;
+ m_bDeleteSelfOnClose = false;
+ m_iClientInsetX = 5;
+ m_iClientInsetY = 5;
+ m_iClientInsetXOverridden = false;
+ m_iTitleTextInsetX = 28;
+ m_bClipToParent = false;
+ m_bSmallCaption = false;
+ m_bChainKeysToParent = false;
+ m_bPrimed = false;
+ m_hCustomTitleFont = INVALID_FONT;
+
+ SetTitle("#Frame_Untitled", parent ? false : true);
+
+ // add ourselves to the build group
+ SetBuildGroup(GetBuildGroup());
+
+ SetMinimumSize(128,66);
+
+ GetFocusNavGroup().SetFocusTopLevel(true);
+
+#if !defined( _X360 )
+ _sysMenu = NULL;
+
+ // add dragging grips
+ _topGrip = new GripPanel(this, "frame_topGrip", 0, -1);
+ _bottomGrip = new GripPanel(this, "frame_bottomGrip", 0, 1);
+ _leftGrip = new GripPanel(this, "frame_leftGrip", -1, 0);
+ _rightGrip = new GripPanel(this, "frame_rightGrip", 1, 0);
+ _topLeftGrip = new GripPanel(this, "frame_tlGrip", -1, -1);
+ _topRightGrip = new GripPanel(this, "frame_trGrip", 1, -1);
+ _bottomLeftGrip = new GripPanel(this, "frame_blGrip", -1, 1);
+ _bottomRightGrip = new GripPanel(this, "frame_brGrip", 1, 1);
+ _captionGrip = new CaptionGripPanel(this, "frame_caption" );
+ _captionGrip->SetCursor(dc_arrow);
+
+ _minimizeButton = new FrameButton(this, "frame_minimize","0");
+ _minimizeButton->AddActionSignalTarget(this);
+ _minimizeButton->SetCommand(new KeyValues("Minimize"));
+
+ _maximizeButton = new FrameButton(this, "frame_maximize", "1");
+ //!! no maximize handler implemented yet, so leave maximize button disabled
+ SetMaximizeButtonVisible(false);
+
+ char str[] = { 0x6F, 0 };
+ _minimizeToSysTrayButton = new FrameButton(this, "frame_mintosystray", str);
+ _minimizeToSysTrayButton->SetCommand("MinimizeToSysTray");
+ SetMinimizeToSysTrayButtonVisible(false);
+
+ _closeButton = new FrameButton(this, "frame_close", "r");
+ _closeButton->AddActionSignalTarget(this);
+ _closeButton->SetCommand(new KeyValues("CloseFrameButtonPressed"));
+
+ if (!surface()->SupportsFeature(ISurface::FRAME_MINIMIZE_MAXIMIZE))
+ {
+ SetMinimizeButtonVisible(false);
+ SetMaximizeButtonVisible(false);
+ }
+
+ if (parent)
+ {
+ // vgui doesn't support subwindow minimization
+ SetMinimizeButtonVisible(false);
+ SetMaximizeButtonVisible(false);
+ }
+
+ _menuButton = new FrameSystemButton(this, "frame_menu");
+ _menuButton->SetMenu(GetSysMenu());
+#endif
+
+ SetupResizeCursors();
+
+ REGISTER_COLOR_AS_OVERRIDABLE( m_InFocusBgColor, "infocus_bgcolor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( m_OutOfFocusBgColor, "outoffocus_bgcolor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _titleBarBgColor, "titlebarbgcolor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _titleBarDisabledBgColor, "titlebardisabledbgcolor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _titleBarFgColor, "titlebarfgcolor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _titleBarDisabledFgColor, "titlebardisabledfgcolor_override" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+Frame::~Frame()
+{
+ if ( input()->GetAppModalSurface() == GetVPanel() )
+ {
+ vgui::input()->ReleaseAppModalSurface();
+ if ( m_hPreviousModal != 0 )
+ {
+ vgui::input()->SetAppModalSurface( m_hPreviousModal );
+ m_hPreviousModal = 0;
+ }
+ }
+
+#if !defined( _X360 )
+ delete _topGrip;
+ delete _bottomGrip;
+ delete _leftGrip;
+ delete _rightGrip;
+ delete _topLeftGrip;
+ delete _topRightGrip;
+ delete _bottomLeftGrip;
+ delete _bottomRightGrip;
+ delete _captionGrip;
+ delete _minimizeButton;
+ delete _maximizeButton;
+ delete _closeButton;
+ delete _menuButton;
+ delete _minimizeToSysTrayButton;
+#endif
+ delete _title;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Setup the grips on the edges of the panel to resize it.
+//-----------------------------------------------------------------------------
+void Frame::SetupResizeCursors()
+{
+#if !defined( _X360 )
+ if (IsSizeable())
+ {
+ _topGrip->SetCursor(dc_sizens);
+ _bottomGrip->SetCursor(dc_sizens);
+ _leftGrip->SetCursor(dc_sizewe);
+ _rightGrip->SetCursor(dc_sizewe);
+ _topLeftGrip->SetCursor(dc_sizenwse);
+ _topRightGrip->SetCursor(dc_sizenesw);
+ _bottomLeftGrip->SetCursor(dc_sizenesw);
+ _bottomRightGrip->SetCursor(dc_sizenwse);
+
+ _bottomRightGrip->SetPaintEnabled(true);
+ _bottomRightGrip->SetPaintBackgroundEnabled(true);
+ }
+ else
+ {
+ // not resizable, so just use the default cursor
+ _topGrip->SetCursor(dc_arrow);
+ _bottomGrip->SetCursor(dc_arrow);
+ _leftGrip->SetCursor(dc_arrow);
+ _rightGrip->SetCursor(dc_arrow);
+ _topLeftGrip->SetCursor(dc_arrow);
+ _topRightGrip->SetCursor(dc_arrow);
+ _bottomLeftGrip->SetCursor(dc_arrow);
+ _bottomRightGrip->SetCursor(dc_arrow);
+
+ _bottomRightGrip->SetPaintEnabled(false);
+ _bottomRightGrip->SetPaintBackgroundEnabled(false);
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Bring the frame to the front and requests focus, ensures it's not minimized
+//-----------------------------------------------------------------------------
+void Frame::Activate()
+{
+ MoveToFront();
+ if ( IsKeyBoardInputEnabled() )
+ {
+ RequestFocus();
+ }
+ SetVisible(true);
+ SetEnabled(true);
+ if (m_bFadingOut)
+ {
+ // we were fading out, make sure to fade back in
+ m_bFadingOut = false;
+ m_bPreviouslyVisible = false;
+ }
+
+ surface()->SetMinimized(GetVPanel(), false);
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up, cleans up modal dialogs
+//-----------------------------------------------------------------------------
+void Frame::DoModal( )
+{
+ // move to the middle of the screen
+ MoveToCenterOfScreen();
+ InvalidateLayout();
+ Activate();
+ m_hPreviousModal = vgui::input()->GetAppModalSurface();
+ vgui::input()->SetAppModalSurface( GetVPanel() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Closes a modal dialog
+//-----------------------------------------------------------------------------
+void Frame::CloseModal()
+{
+ vgui::input()->ReleaseAppModalSurface();
+ if ( m_hPreviousModal != 0 )
+ {
+ vgui::input()->SetAppModalSurface( m_hPreviousModal );
+ m_hPreviousModal = 0;
+ }
+ PostMessage( this, new KeyValues("Close") );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: activates the dialog
+// if dialog is not currently visible it starts it minimized and flashing in the taskbar
+//-----------------------------------------------------------------------------
+void Frame::ActivateMinimized()
+{
+ if ( ( IsVisible() && !IsMinimized() ) || !surface()->SupportsFeature( ISurface::FRAME_MINIMIZE_MAXIMIZE ) )
+ {
+ Activate();
+ }
+ else
+ {
+ ipanel()->MoveToBack(GetVPanel());
+ surface()->SetMinimized(GetVPanel(), true);
+ SetVisible(true);
+ SetEnabled(true);
+ if (m_bFadingOut)
+ {
+ // we were fading out, make sure to fade back in
+ m_bFadingOut = false;
+ m_bPreviouslyVisible = false;
+ }
+ FlashWindow();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the dialog is currently minimized
+//-----------------------------------------------------------------------------
+bool Frame::IsMinimized()
+{
+ return surface()->IsMinimized(GetVPanel());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Center the dialog on the screen
+//-----------------------------------------------------------------------------
+void Frame::MoveToCenterOfScreen()
+{
+ int wx, wy, ww, wt;
+ surface()->GetWorkspaceBounds(wx, wy, ww, wt);
+ SetPos((ww - GetWide()) / 2, (wt - GetTall()) / 2);
+}
+
+
+void Frame::LayoutProportional( FrameButton *bt )
+{
+ float scale = 1.0;
+
+ if( IsProportional() )
+ {
+ int screenW, screenH;
+ surface()->GetScreenSize( screenW, screenH );
+
+ int proW,proH;
+ surface()->GetProportionalBase( proW, proH );
+
+ scale = ( (float)( screenH ) / (float)( proH ) );
+ }
+
+ bt->SetSize( (int)( FrameButton::GetButtonSide( this ) * scale ), (int)( FrameButton::GetButtonSide( this ) * scale ) );
+ bt->SetTextInset( (int)( ceil( 2 * scale ) ), (int) ( ceil(1 * scale ) ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: per-frame thinking, used for transition effects
+// only gets called if the Frame is visible
+//-----------------------------------------------------------------------------
+void Frame::OnThink()
+{
+ BaseClass::OnThink();
+
+ // check for transition effects
+ if (IsVisible() && m_flTransitionEffectTime > 0 && ( !m_bDisableFadeEffect ))
+ {
+ if (m_bFadingOut)
+ {
+ // we're fading out, see if we're done so we can fully hide the window
+ if (GetAlpha() < ( IsX360() ? 64 : 1 ))
+ {
+ FinishClose();
+ }
+ }
+ else if (!m_bPreviouslyVisible)
+ {
+ // need to fade-in
+ m_bPreviouslyVisible = true;
+
+ // fade in
+ if (IsX360())
+ {
+ SetAlpha(64);
+ }
+ else
+ {
+ SetAlpha(0);
+ }
+ GetAnimationController()->RunAnimationCommand(this, "alpha", 255.0f, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);
+ }
+ }
+
+ // check for focus changes
+ bool hasFocus = false;
+
+ if (input())
+ {
+ VPANEL focus = input()->GetFocus();
+ if (focus && ipanel()->HasParent(focus, GetVPanel()))
+ {
+ if ( input()->GetAppModalSurface() == 0 ||
+ input()->GetAppModalSurface() == GetVPanel() )
+ {
+ hasFocus = true;
+ }
+ }
+ }
+ if (hasFocus != m_bHasFocus)
+ {
+ // Because vgui focus is message based, and focus gets reset to NULL when a focused panel is deleted, we defer the flashing/transition
+ // animation for an extra frame in case something is deleted, a message is sent, and then we become the focused panel again on the
+ // next frame
+ if ( !m_bPrimed )
+ {
+ m_bPrimed = true;
+ return;
+ }
+ m_bPrimed = false;
+ m_bHasFocus = hasFocus;
+ OnFrameFocusChanged(m_bHasFocus);
+ }
+ else
+ {
+ m_bPrimed = false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the frame focus changes
+//-----------------------------------------------------------------------------
+void Frame::OnFrameFocusChanged(bool bHasFocus)
+{
+#if !defined( _X360 )
+ // enable/disable the frame buttons
+ _minimizeButton->SetDisabledLook(!bHasFocus);
+ _maximizeButton->SetDisabledLook(!bHasFocus);
+ _closeButton->SetDisabledLook(!bHasFocus);
+ _minimizeToSysTrayButton->SetDisabledLook(!bHasFocus);
+ _menuButton->SetEnabled(bHasFocus);
+ _minimizeButton->InvalidateLayout();
+ _maximizeButton->InvalidateLayout();
+ _minimizeToSysTrayButton->InvalidateLayout();
+ _closeButton->InvalidateLayout();
+ _menuButton->InvalidateLayout();
+#endif
+
+ if (bHasFocus)
+ {
+ _title->SetColor(_titleBarFgColor);
+ }
+ else
+ {
+ _title->SetColor(_titleBarDisabledFgColor);
+ }
+
+ // set our background color
+ if (bHasFocus)
+ {
+ if (m_flFocusTransitionEffectTime && ( !m_bDisableFadeEffect ))
+ {
+ GetAnimationController()->RunAnimationCommand(this, "BgColor", m_InFocusBgColor, 0.0f, m_bDisableFadeEffect ? 0.0f : m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);
+ }
+ else
+ {
+ SetBgColor(m_InFocusBgColor);
+ }
+ }
+ else
+ {
+ if (m_flFocusTransitionEffectTime && ( !m_bDisableFadeEffect ))
+ {
+ GetAnimationController()->RunAnimationCommand(this, "BgColor", m_OutOfFocusBgColor, 0.0f, m_bDisableFadeEffect ? 0.0f : m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);
+ }
+ else
+ {
+ SetBgColor(m_OutOfFocusBgColor);
+ }
+ }
+
+ // Stop flashing when we get focus
+ if (bHasFocus && _flashWindow)
+ {
+ FlashWindowStop();
+ }
+}
+
+int Frame::GetDraggerSize()
+{
+ const int DRAGGER_SIZE = 5;
+ if ( m_bSmallCaption )
+ {
+ return 3;
+ }
+
+ return DRAGGER_SIZE;
+}
+
+int Frame::GetCornerSize()
+{
+ const int CORNER_SIZE = 8;
+ if ( m_bSmallCaption )
+ {
+ return 6;
+ }
+
+ return CORNER_SIZE;
+}
+
+int Frame::GetBottomRightSize()
+{
+ const int BOTTOMRIGHTSIZE = 18;
+ if ( m_bSmallCaption )
+ {
+ return 12;
+ }
+
+ return BOTTOMRIGHTSIZE;
+}
+
+int Frame::GetCaptionHeight()
+{
+ const int CAPTIONHEIGHT = 23;
+ if ( m_bSmallCaption )
+ {
+ return 12;
+ }
+ return CAPTIONHEIGHT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recalculate the position of all items
+//-----------------------------------------------------------------------------
+void Frame::PerformLayout()
+{
+ // chain back
+ BaseClass::PerformLayout();
+
+ // move everything into place
+ int wide, tall;
+ GetSize(wide, tall);
+
+#if !defined( _X360 )
+ int DRAGGER_SIZE = GetDraggerSize();
+ int CORNER_SIZE = GetCornerSize();
+ int CORNER_SIZE2 = CORNER_SIZE * 2;
+ int BOTTOMRIGHTSIZE = GetBottomRightSize();
+
+ _topGrip->SetBounds(CORNER_SIZE, 0, wide - CORNER_SIZE2, DRAGGER_SIZE);
+ _leftGrip->SetBounds(0, CORNER_SIZE, DRAGGER_SIZE, tall - CORNER_SIZE2);
+ _topLeftGrip->SetBounds(0, 0, CORNER_SIZE, CORNER_SIZE);
+ _topRightGrip->SetBounds(wide - CORNER_SIZE, 0, CORNER_SIZE, CORNER_SIZE);
+ _bottomLeftGrip->SetBounds(0, tall - CORNER_SIZE, CORNER_SIZE, CORNER_SIZE);
+
+ // make the bottom-right grip larger
+ _bottomGrip->SetBounds(CORNER_SIZE, tall - DRAGGER_SIZE, wide - (CORNER_SIZE + BOTTOMRIGHTSIZE), DRAGGER_SIZE);
+ _rightGrip->SetBounds(wide - DRAGGER_SIZE, CORNER_SIZE, DRAGGER_SIZE, tall - (CORNER_SIZE + BOTTOMRIGHTSIZE));
+
+ _bottomRightGrip->SetBounds(wide - BOTTOMRIGHTSIZE, tall - BOTTOMRIGHTSIZE, BOTTOMRIGHTSIZE, BOTTOMRIGHTSIZE);
+
+ _captionGrip->SetSize(wide-10,GetCaptionHeight());
+
+ _topGrip->MoveToFront();
+ _bottomGrip->MoveToFront();
+ _leftGrip->MoveToFront();
+ _rightGrip->MoveToFront();
+ _topLeftGrip->MoveToFront();
+ _topRightGrip->MoveToFront();
+ _bottomLeftGrip->MoveToFront();
+ _bottomRightGrip->MoveToFront();
+
+ _maximizeButton->MoveToFront();
+ _menuButton->MoveToFront();
+ _minimizeButton->MoveToFront();
+ _minimizeToSysTrayButton->MoveToFront();
+ _menuButton->SetBounds(5+2, 5+3, GetCaptionHeight()-5, GetCaptionHeight()-5);
+#endif
+
+ float scale = 1;
+ if (IsProportional())
+ {
+ int screenW, screenH;
+ surface()->GetScreenSize( screenW, screenH );
+
+ int proW,proH;
+ surface()->GetProportionalBase( proW, proH );
+
+ scale = ( (float)( screenH ) / (float)( proH ) );
+ }
+
+#if !defined( _X360 )
+ int offset_start = (int)( 20 * scale );
+ int offset = offset_start;
+
+ int top_border_offset = (int) ( ( 5+3 ) * scale );
+ if ( m_bSmallCaption )
+ {
+ top_border_offset = (int) ( ( 3 ) * scale );
+ }
+
+ int side_border_offset = (int) ( 5 * scale );
+ // push the buttons against the east side
+ if (_closeButton->IsVisible())
+ {
+ _closeButton->SetPos((wide-side_border_offset)-offset,top_border_offset);
+ offset += offset_start;
+ LayoutProportional( _closeButton );
+
+ }
+ if (_minimizeToSysTrayButton->IsVisible())
+ {
+ _minimizeToSysTrayButton->SetPos((wide-side_border_offset)-offset,top_border_offset);
+ offset += offset_start;
+ LayoutProportional( _minimizeToSysTrayButton );
+ }
+ if (_maximizeButton->IsVisible())
+ {
+ _maximizeButton->SetPos((wide-side_border_offset)-offset,top_border_offset);
+ offset += offset_start;
+ LayoutProportional( _maximizeButton );
+ }
+ if (_minimizeButton->IsVisible())
+ {
+ _minimizeButton->SetPos((wide-side_border_offset)-offset,top_border_offset);
+ offset += offset_start;
+ LayoutProportional( _minimizeButton );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the text in the title bar.
+//-----------------------------------------------------------------------------
+void Frame::SetTitle(const char *title, bool surfaceTitle)
+{
+ if (!_title)
+ {
+ _title = new TextImage( "" );
+ }
+
+ Assert(title);
+ _title->SetText(title);
+
+ // see if the combobox text has changed, and if so, post a message detailing the new text
+ const char *newTitle = title;
+
+ // check if the new text is a localized string, if so undo it
+ wchar_t unicodeText[128];
+ unicodeText[0] = 0;
+ if (*newTitle == '#')
+ {
+ // try lookup in localization tables
+ StringIndex_t unlocalizedTextSymbol = g_pVGuiLocalize->FindIndex(newTitle + 1);
+ if (unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX)
+ {
+ // we have a new text value
+ wcsncpy( unicodeText, g_pVGuiLocalize->GetValueByIndex(unlocalizedTextSymbol), sizeof( unicodeText) / sizeof(wchar_t) );
+ }
+ }
+ else
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode( newTitle, unicodeText, sizeof(unicodeText) );
+ }
+
+ if (surfaceTitle)
+ {
+ surface()->SetTitle(GetVPanel(), unicodeText);
+ }
+
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the unicode text in the title bar
+//-----------------------------------------------------------------------------
+void Frame::SetTitle(const wchar_t *title, bool surfaceTitle)
+{
+ if (!_title)
+ {
+ _title = new TextImage( "" );
+ }
+ _title->SetText(title);
+ if (surfaceTitle)
+ {
+ surface()->SetTitle(GetVPanel(), title);
+ }
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the text in the title bar.
+//-----------------------------------------------------------------------------
+void Frame::InternalSetTitle(const char *title)
+{
+ SetTitle(title, true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the movability of the panel
+//-----------------------------------------------------------------------------
+void Frame::SetMoveable(bool state)
+{
+ _moveable=state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the resizability of the panel
+//-----------------------------------------------------------------------------
+void Frame::SetSizeable(bool state)
+{
+ _sizeable=state;
+
+ SetupResizeCursors();
+}
+
+// When moving via caption, don't let any part of window go outside parent's bounds
+void Frame::SetClipToParent( bool state )
+{
+ m_bClipToParent = state;
+}
+
+bool Frame::GetClipToParent() const
+{
+ return m_bClipToParent;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check the movability of the panel
+//-----------------------------------------------------------------------------
+bool Frame::IsMoveable()
+{
+ return _moveable;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check the resizability of the panel
+//-----------------------------------------------------------------------------
+bool Frame::IsSizeable()
+{
+ return _sizeable;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the size of the panel inside the frame edges.
+//-----------------------------------------------------------------------------
+void Frame::GetClientArea(int &x, int &y, int &wide, int &tall)
+{
+ x = m_iClientInsetX;
+
+ GetSize(wide, tall);
+
+ if (_drawTitleBar)
+ {
+ int captionTall = surface()->GetFontTall(_title->GetFont());
+
+ int border = m_bSmallCaption ? CAPTION_TITLE_BORDER_SMALL : CAPTION_TITLE_BORDER;
+ int yinset = m_bSmallCaption ? 0 : m_iClientInsetY;
+
+ yinset += m_iTitleTextInsetYOverride;
+
+ y = yinset + captionTall + border + 1;
+ tall = (tall - yinset) - y;
+ }
+
+ if ( m_bSmallCaption )
+ {
+ tall -= 5;
+ }
+
+ wide = (wide - m_iClientInsetX) - x;
+}
+
+//
+//-----------------------------------------------------------------------------
+// Purpose: applies user configuration settings
+//-----------------------------------------------------------------------------
+void Frame::ApplyUserConfigSettings(KeyValues *userConfig)
+{
+ // calculate defaults
+ int wx, wy, ww, wt;
+ vgui::surface()->GetWorkspaceBounds(wx, wy, ww, wt);
+
+ int x, y, wide, tall;
+ GetBounds(x, y, wide, tall);
+ bool bNoSettings = false;
+ if (_moveable)
+ {
+ // check to see if anything is set
+ if (!userConfig->FindKey("xpos", false))
+ {
+ bNoSettings = true;
+ }
+
+ // get the user config position
+ // default to where we're currently at
+ x = userConfig->GetInt("xpos", x);
+ y = userConfig->GetInt("ypos", y);
+ }
+ if (_sizeable)
+ {
+ wide = userConfig->GetInt("wide", wide);
+ tall = userConfig->GetInt("tall", tall);
+
+ // Make sure it's no larger than the workspace
+ if ( wide > ww )
+ {
+ wide = ww;
+ }
+ if ( tall > wt )
+ {
+ tall = wt;
+ }
+ }
+
+ // see if the dialog has a place on the screen it wants to start
+ if (bNoSettings && GetDefaultScreenPosition(x, y, wide, tall))
+ {
+ bNoSettings = false;
+ }
+
+ // make sure it conforms to the minimum size of the dialog
+ int minWide, minTall;
+ GetMinimumSize(minWide, minTall);
+ if (wide < minWide)
+ {
+ wide = minWide;
+ }
+ if (tall < minTall)
+ {
+ tall = minTall;
+ }
+
+ // make sure it's on the screen
+ if (x + wide > ww)
+ {
+ x = wx + ww - wide;
+ }
+ if (y + tall > wt)
+ {
+ y = wy + wt - tall;
+ }
+
+ if (x < wx)
+ {
+ x = wx;
+ }
+ if (y < wy)
+ {
+ y = wy;
+ }
+
+ SetBounds(x, y, wide, tall);
+
+ if (bNoSettings)
+ {
+ // since nothing was set, default our position to the middle of the screen
+ MoveToCenterOfScreen();
+ }
+
+ BaseClass::ApplyUserConfigSettings(userConfig);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns user config settings for this control
+//-----------------------------------------------------------------------------
+void Frame::GetUserConfigSettings(KeyValues *userConfig)
+{
+ if (_moveable)
+ {
+ int x, y;
+ GetPos(x, y);
+ userConfig->SetInt("xpos", x);
+ userConfig->SetInt("ypos", y);
+ }
+ if (_sizeable)
+ {
+ int w, t;
+ GetSize(w, t);
+ userConfig->SetInt("wide", w);
+ userConfig->SetInt("tall", t);
+ }
+
+ BaseClass::GetUserConfigSettings(userConfig);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: optimization, return true if this control has any user config settings
+//-----------------------------------------------------------------------------
+bool Frame::HasUserConfigSettings()
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the default position and size on the screen to appear the first time (defaults to centered)
+//-----------------------------------------------------------------------------
+bool Frame::GetDefaultScreenPosition(int &x, int &y, int &wide, int &tall)
+{
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: draws title bar
+//-----------------------------------------------------------------------------
+void Frame::PaintBackground()
+{
+ // take the panel with focus and check up tree for this panel
+ // if you find it, than some child of you has the focus, so
+ // you should be focused
+ Color titleColor = _titleBarDisabledBgColor;
+ if (m_bHasFocus)
+ {
+ titleColor = _titleBarBgColor;
+ }
+
+ BaseClass::PaintBackground();
+
+ if (_drawTitleBar)
+ {
+ int wide = GetWide();
+ int tall = surface()->GetFontTall(_title->GetFont());
+
+ // caption
+ surface()->DrawSetColor(titleColor);
+ int inset = m_bSmallCaption ? 3 : 5;
+ int captionHeight = m_bSmallCaption ? 14: 28;
+
+ surface()->DrawFilledRect(inset, inset, wide - inset, captionHeight );
+
+ if (_title)
+ {
+ int nTitleX = m_iTitleTextInsetXOverride ? m_iTitleTextInsetXOverride : m_iTitleTextInsetX;
+ int nTitleWidth = wide - 72;
+#if !defined( _X360 )
+ if ( _menuButton && _menuButton->IsVisible() )
+ {
+ int mw, mh;
+ _menuButton->GetImageSize( mw, mh );
+ nTitleX += mw;
+ nTitleWidth -= mw;
+ }
+#endif
+ int nTitleY;
+ if ( m_iTitleTextInsetYOverride )
+ {
+ nTitleY = m_iTitleTextInsetYOverride;
+ }
+ else
+ {
+ nTitleY = m_bSmallCaption ? 2 : 9;
+ }
+ _title->SetPos( nTitleX, nTitleY );
+ _title->SetSize( nTitleWidth, tall);
+ _title->Paint();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Frame::ApplySchemeSettings(IScheme *pScheme)
+{
+ // always chain back
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetOverridableColor( &_titleBarFgColor, GetSchemeColor("FrameTitleBar.TextColor", pScheme) );
+ SetOverridableColor( &_titleBarBgColor, GetSchemeColor("FrameTitleBar.BgColor", pScheme) );
+ SetOverridableColor( &_titleBarDisabledFgColor, GetSchemeColor("FrameTitleBar.DisabledTextColor", pScheme) );
+ SetOverridableColor( &_titleBarDisabledBgColor, GetSchemeColor("FrameTitleBar.DisabledBgColor", pScheme) );
+
+ const char *font = NULL;
+ if ( m_bSmallCaption )
+ {
+ font = pScheme->GetResourceString("FrameTitleBar.SmallFont");
+ }
+ else
+ {
+ font = pScheme->GetResourceString("FrameTitleBar.Font");
+ }
+
+ HFont titlefont;
+ if ( m_hCustomTitleFont )
+ {
+ titlefont = m_hCustomTitleFont;
+ }
+ else
+ {
+ titlefont = pScheme->GetFont((font && *font) ? font : "Default", IsProportional());
+ }
+
+ _title->SetFont( titlefont );
+ _title->ResizeImageToContent();
+
+#if !defined( _X360 )
+ HFont marfont = (HFont)0;
+ if ( m_bSmallCaption )
+ {
+ marfont = pScheme->GetFont( "MarlettSmall", IsProportional() );
+ }
+ else
+ {
+ marfont = pScheme->GetFont( "Marlett", IsProportional() );
+ }
+
+ _minimizeButton->SetFont(marfont);
+ _maximizeButton->SetFont(marfont);
+ _minimizeToSysTrayButton->SetFont(marfont);
+ _closeButton->SetFont(marfont);
+#endif
+
+ m_flTransitionEffectTime = atof(pScheme->GetResourceString("Frame.TransitionEffectTime"));
+ m_flFocusTransitionEffectTime = atof(pScheme->GetResourceString("Frame.FocusTransitionEffectTime"));
+
+ SetOverridableColor( &m_InFocusBgColor, pScheme->GetColor("Frame.BgColor", GetBgColor()) );
+ SetOverridableColor( &m_OutOfFocusBgColor, pScheme->GetColor("Frame.OutOfFocusBgColor", m_InFocusBgColor) );
+
+ const char *resourceString = pScheme->GetResourceString("Frame.ClientInsetX");
+ if ( resourceString )
+ {
+ m_iClientInsetX = atoi(resourceString);
+ }
+ resourceString = pScheme->GetResourceString("Frame.ClientInsetY");
+ if ( resourceString )
+ {
+ m_iClientInsetY = atoi(resourceString);
+ }
+ resourceString = pScheme->GetResourceString("Frame.TitleTextInsetX");
+ if ( resourceString )
+ {
+ m_iTitleTextInsetX = atoi(resourceString);
+ }
+
+ SetBgColor(m_InFocusBgColor);
+ SetBorder(pScheme->GetBorder("FrameBorder"));
+
+ OnFrameFocusChanged( m_bHasFocus );
+}
+
+// Disables the fade-in/out-effect even if configured in the scheme settings
+void Frame::DisableFadeEffect( void )
+{
+ m_flFocusTransitionEffectTime = 0.f;
+ m_flTransitionEffectTime = 0.f;
+}
+
+void Frame::SetFadeEffectDisableOverride( bool disabled )
+{
+ m_bDisableFadeEffect = disabled;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply settings loaded from a resource file
+//-----------------------------------------------------------------------------
+void Frame::ApplySettings(KeyValues *inResourceData)
+{
+ // Don't change the frame's visibility, remove that setting from the config data
+ inResourceData->SetInt("visible", -1);
+ BaseClass::ApplySettings(inResourceData);
+
+ SetCloseButtonVisible( inResourceData->GetBool( "setclosebuttonvisible", true ) );
+
+ if( !inResourceData->GetInt("settitlebarvisible", 1 ) ) // if "title" is "0" then don't draw the title bar
+ {
+ SetTitleBarVisible( false );
+ }
+
+ // set the title
+ const char *title = inResourceData->GetString("title", "");
+ if (title && *title)
+ {
+ SetTitle(title, true);
+ }
+
+ const char *titlefont = inResourceData->GetString("title_font", "");
+ if ( titlefont && titlefont[0] )
+ {
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+ if ( pScheme )
+ {
+ m_hCustomTitleFont = pScheme->GetFont( titlefont );
+ }
+ }
+
+ KeyValues *pKV = inResourceData->FindKey( "clientinsetx_override", false );
+ if ( pKV )
+ {
+ m_iClientInsetX = pKV->GetInt();
+ m_iClientInsetXOverridden = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply settings loaded from a resource file
+//-----------------------------------------------------------------------------
+void Frame::GetSettings(KeyValues *outResourceData)
+{
+ BaseClass::GetSettings(outResourceData);
+ outResourceData->SetInt("settitlebarvisible", _drawTitleBar );
+
+ if (_title)
+ {
+ char buf[256];
+ _title->GetUnlocalizedText( buf, 255 );
+ if (buf[0])
+ {
+ outResourceData->SetString("title", buf);
+ }
+ }
+
+ if ( m_iClientInsetXOverridden )
+ {
+ outResourceData->SetInt( "clientinsetx_override", m_iClientInsetX );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns a description of the settings possible for a frame
+//-----------------------------------------------------------------------------
+const char *Frame::GetDescription()
+{
+ static char buf[512];
+ Q_snprintf(buf, sizeof(buf), "%s, string title", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Go invisible when a close message is recieved.
+//-----------------------------------------------------------------------------
+void Frame::OnClose()
+{
+ // if we're modal, release that before we hide the window else the wrong window will get focus
+ if (input()->GetAppModalSurface() == GetVPanel())
+ {
+ input()->ReleaseAppModalSurface();
+ if ( m_hPreviousModal != 0 )
+ {
+ vgui::input()->SetAppModalSurface( m_hPreviousModal );
+ m_hPreviousModal = 0;
+ }
+ }
+
+ BaseClass::OnClose();
+
+ if (m_flTransitionEffectTime && !m_bDisableFadeEffect)
+ {
+ // begin the hide transition effect
+ GetAnimationController()->RunAnimationCommand(this, "alpha", 0.0f, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);
+ m_bFadingOut = true;
+ // move us to the back of the draw order (so that fading out over the top of other dialogs doesn't look wierd)
+ surface()->MovePopupToBack(GetVPanel());
+ }
+ else
+ {
+ // hide us immediately
+ FinishClose();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Close button in frame pressed
+//-----------------------------------------------------------------------------
+void Frame::OnCloseFrameButtonPressed()
+{
+ OnCommand("Close");
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Command handling
+//-----------------------------------------------------------------------------
+void Frame::OnCommand(const char *command)
+{
+ if (!stricmp(command, "Close"))
+ {
+ Close();
+ }
+ else if (!stricmp(command, "CloseModal"))
+ {
+ CloseModal();
+ }
+ else if (!stricmp(command, "Minimize"))
+ {
+ OnMinimize();
+ }
+ else if (!stricmp(command, "MinimizeToSysTray"))
+ {
+ OnMinimizeToSysTray();
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the system menu
+//-----------------------------------------------------------------------------
+Menu *Frame::GetSysMenu()
+{
+#if !defined( _X360 )
+ if (!_sysMenu)
+ {
+ _sysMenu = new Menu(this, NULL);
+ _sysMenu->SetVisible(false);
+ _sysMenu->AddActionSignalTarget(this);
+
+ _sysMenu->AddMenuItem("Minimize", "#SysMenu_Minimize", "Minimize", this);
+ _sysMenu->AddMenuItem("Maximize", "#SysMenu_Maximize", "Maximize", this);
+ _sysMenu->AddMenuItem("Close", "#SysMenu_Close", "Close", this);
+
+ // check for enabling/disabling menu items
+ // this might have to be done at other times as well.
+ Panel *menuItem = _sysMenu->FindChildByName("Minimize");
+ if (menuItem)
+ {
+ menuItem->SetEnabled(_minimizeButton->IsVisible());
+ }
+ menuItem = _sysMenu->FindChildByName("Maximize");
+ if (menuItem)
+ {
+ menuItem->SetEnabled(_maximizeButton->IsVisible());
+ }
+ menuItem = _sysMenu->FindChildByName("Close");
+ if (menuItem)
+ {
+ menuItem->SetEnabled(_closeButton->IsVisible());
+ }
+ }
+
+ return _sysMenu;
+#else
+ return NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the system menu
+//-----------------------------------------------------------------------------
+void Frame::SetSysMenu(Menu *menu)
+{
+#if !defined( _X360 )
+ if (menu == _sysMenu)
+ return;
+
+ _sysMenu->MarkForDeletion();
+ _sysMenu = menu;
+
+ _menuButton->SetMenu(_sysMenu);
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Set the system menu images
+//-----------------------------------------------------------------------------
+void Frame::SetImages( const char *pEnabledImage, const char *pDisabledImage )
+{
+#if !defined( _X360 )
+ _menuButton->SetImages( pEnabledImage, pDisabledImage );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Close the window
+//-----------------------------------------------------------------------------
+void Frame::Close()
+{
+ OnClose();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finishes closing the dialog
+//-----------------------------------------------------------------------------
+void Frame::FinishClose()
+{
+ SetVisible(false);
+ m_bPreviouslyVisible = false;
+ m_bFadingOut = false;
+
+ OnFinishedClose();
+
+ if (m_bDeleteSelfOnClose)
+ {
+ // Must be last because if vgui is not running then this will call delete this!!!
+ MarkForDeletion();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Frame::OnFinishedClose()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Minimize the window on the taskbar.
+//-----------------------------------------------------------------------------
+void Frame::OnMinimize()
+{
+ surface()->SetMinimized(GetVPanel(), true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Does nothing by default
+//-----------------------------------------------------------------------------
+void Frame::OnMinimizeToSysTray()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Respond to mouse presses
+//-----------------------------------------------------------------------------
+void Frame::OnMousePressed(MouseCode code)
+{
+ if (!IsBuildGroupEnabled())
+ {
+ // if a child doesn't have focus, get it for ourselves
+ VPANEL focus = input()->GetFocus();
+ if (!focus || !ipanel()->HasParent(focus, GetVPanel()))
+ {
+ RequestFocus();
+ }
+ }
+
+ BaseClass::OnMousePressed(code);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle visibility of the system menu button
+//-----------------------------------------------------------------------------
+void Frame::SetMenuButtonVisible(bool state)
+{
+#if !defined( _X360 )
+ _menuButton->SetVisible(state);
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle respond of the system menu button
+// it will look enabled or disabled in response to the title bar
+// but may not activate.
+//-----------------------------------------------------------------------------
+void Frame::SetMenuButtonResponsive(bool state)
+{
+#if !defined( _X360 )
+ _menuButton->SetResponsive(state);
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle visibility of the minimize button
+//-----------------------------------------------------------------------------
+void Frame::SetMinimizeButtonVisible(bool state)
+{
+#if !defined( _X360 )
+ _minimizeButton->SetVisible(state);
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle visibility of the maximize button
+//-----------------------------------------------------------------------------
+void Frame::SetMaximizeButtonVisible(bool state)
+{
+#if !defined( _X360 )
+ _maximizeButton->SetVisible(state);
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggles visibility of the minimize-to-systray icon (defaults to false)
+//-----------------------------------------------------------------------------
+void Frame::SetMinimizeToSysTrayButtonVisible(bool state)
+{
+#if !defined( _X360 )
+ _minimizeToSysTrayButton->SetVisible(state);
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle visibility of the close button
+//-----------------------------------------------------------------------------
+void Frame::SetCloseButtonVisible(bool state)
+{
+#if !defined( _X360 )
+ _closeButton->SetVisible(state);
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: soaks up any remaining messages
+//-----------------------------------------------------------------------------
+void Frame::OnKeyCodeReleased(KeyCode code)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: soaks up any remaining messages
+//-----------------------------------------------------------------------------
+void Frame::OnKeyFocusTicked()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggles window flash state on a timer
+//-----------------------------------------------------------------------------
+void Frame::InternalFlashWindow()
+{
+ if (_flashWindow)
+ {
+ // toggle icon flashing
+ _nextFlashState = true;
+ surface()->FlashWindow(GetVPanel(), _nextFlashState);
+ _nextFlashState = !_nextFlashState;
+
+ PostMessage(this, new KeyValues("FlashWindow"), 1.8f);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds the child to the focus nav group
+//-----------------------------------------------------------------------------
+void Frame::OnChildAdded(VPANEL child)
+{
+ BaseClass::OnChildAdded(child);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Flash the window system tray button until the frame gets focus
+//-----------------------------------------------------------------------------
+void Frame::FlashWindow()
+{
+ _flashWindow = true;
+ _nextFlashState = true;
+
+ InternalFlashWindow();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stops any window flashing
+//-----------------------------------------------------------------------------
+void Frame::FlashWindowStop()
+{
+ surface()->FlashWindow(GetVPanel(), false);
+ _flashWindow = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: load the control settings - should be done after all the children are added to the dialog
+//-----------------------------------------------------------------------------
+void Frame::LoadControlSettings( const char *dialogResourceName, const char *pathID, KeyValues *pPreloadedKeyValues, KeyValues *pConditions )
+{
+ BaseClass::LoadControlSettings( dialogResourceName, pathID, pPreloadedKeyValues, pConditions );
+
+ // set the focus on the default control
+ Panel *defaultFocus = GetFocusNavGroup().GetDefaultPanel();
+ if (defaultFocus)
+ {
+ defaultFocus->RequestFocus();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks for ctrl+shift+b hits to enter build mode
+// Activates any hotkeys / default buttons
+// Swallows any unhandled input
+//-----------------------------------------------------------------------------
+void Frame::OnKeyCodeTyped(KeyCode code)
+{
+ bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+ bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+ bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
+
+ if ( IsX360() )
+ {
+ vgui::Panel *pMap = FindChildByName( "ControllerMap" );
+ if ( pMap && pMap->IsKeyBoardInputEnabled() )
+ {
+ pMap->OnKeyCodeTyped( code );
+ return;
+ }
+ }
+
+ if ( ctrl && shift && alt && code == KEY_B)
+ {
+ // enable build mode
+ ActivateBuildMode();
+ }
+ else if (ctrl && shift && alt && code == KEY_R)
+ {
+ // reload the scheme
+ VPANEL top = surface()->GetEmbeddedPanel();
+ if (top)
+ {
+ // reload the data file
+ scheme()->ReloadSchemes();
+
+ Panel *panel = ipanel()->GetPanel(top, GetModuleName());
+ if (panel)
+ {
+ // make the top-level panel reload it's scheme, it will chain down to all the child panels
+ panel->InvalidateLayout(false, true);
+ }
+ }
+ }
+ else if (alt && code == KEY_F4)
+ {
+ // user has hit the close
+ PostMessage(this, new KeyValues("CloseFrameButtonPressed"));
+ }
+ else if (code == KEY_ENTER)
+ {
+ // check for a default button
+ VPANEL panel = GetFocusNavGroup().GetCurrentDefaultButton();
+ if (panel && ipanel()->IsVisible( panel ) && ipanel()->IsEnabled( panel ))
+ {
+ // Activate the button
+ PostMessage(panel, new KeyValues("Hotkey"));
+ }
+ }
+ else if ( code == KEY_ESCAPE &&
+ surface()->SupportsFeature(ISurface::ESCAPE_KEY) &&
+ input()->GetAppModalSurface() == GetVPanel() )
+ {
+ // ESC cancels, unless we're in the engine - in the engine ESC flips between the UI and the game
+ CloseModal();
+ }
+ // Usually don't chain back as Frames are the end of the line for key presses, unless
+ // m_bChainKeysToParent is set
+ else if ( m_bChainKeysToParent )
+ {
+ BaseClass::OnKeyCodeTyped( code );
+ }
+ else
+ {
+ input()->OnKeyCodeUnhandled( (int)code );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If true, then OnKeyCodeTyped messages continue up past the Frame
+// Input : state -
+//-----------------------------------------------------------------------------
+void Frame::SetChainKeysToParent( bool state )
+{
+ m_bChainKeysToParent = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If true, then OnKeyCodeTyped messages continue up past the Frame
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Frame::CanChainKeysToParent() const
+{
+ return m_bChainKeysToParent;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks for ctrl+shift+b hits to enter build mode
+// Activates any hotkeys / default buttons
+// Swallows any unhandled input
+//-----------------------------------------------------------------------------
+void Frame::OnKeyTyped(wchar_t unichar)
+{
+ Panel *panel = GetFocusNavGroup().FindPanelByHotkey(unichar);
+ if (panel)
+ {
+ // tell the panel to Activate
+ PostMessage(panel, new KeyValues("Hotkey"));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets all title bar controls
+//-----------------------------------------------------------------------------
+void Frame::SetTitleBarVisible( bool state )
+{
+ _drawTitleBar = state;
+ SetMenuButtonVisible(state);
+ SetMinimizeButtonVisible(state);
+ SetMaximizeButtonVisible(state);
+ SetCloseButtonVisible(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the frame to delete itself on close
+//-----------------------------------------------------------------------------
+void Frame::SetDeleteSelfOnClose( bool state )
+{
+ m_bDeleteSelfOnClose = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: updates localized text
+//-----------------------------------------------------------------------------
+void Frame::OnDialogVariablesChanged( KeyValues *dialogVariables )
+{
+ StringIndex_t index = _title->GetUnlocalizedTextSymbol();
+ if (index != INVALID_LOCALIZE_STRING_INDEX)
+ {
+ // reconstruct the string from the variables
+ wchar_t buf[1024];
+ g_pVGuiLocalize->ConstructString(buf, sizeof(buf), index, dialogVariables);
+ SetTitle(buf, true);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles staying on screen when the screen size changes
+//-----------------------------------------------------------------------------
+void Frame::OnScreenSizeChanged(int iOldWide, int iOldTall)
+{
+ BaseClass::OnScreenSizeChanged(iOldWide, iOldTall);
+
+ if (IsProportional())
+ return;
+
+ // make sure we're completely on screen
+ int iNewWide, iNewTall;
+ surface()->GetScreenSize(iNewWide, iNewTall);
+
+ int x, y, wide, tall;
+ GetBounds(x, y, wide, tall);
+
+ // make sure the bottom-right corner is on the screen first
+ if (x + wide > iNewWide)
+ {
+ x = iNewWide - wide;
+ }
+ if (y + tall > iNewTall)
+ {
+ y = iNewTall - tall;
+ }
+
+ // make sure the top-left is visible
+ x = max( 0, x );
+ y = max( 0, y );
+
+ // apply
+ SetPos(x, y);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: For supporting thin caption bars
+// Input : state -
+//-----------------------------------------------------------------------------
+void Frame::SetSmallCaption( bool state )
+{
+ m_bSmallCaption = state;
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Frame::IsSmallCaption() const
+{
+ return m_bSmallCaption;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Static method to place a frame under the cursor
+//-----------------------------------------------------------------------------
+void Frame::PlaceUnderCursor( )
+{
+ // get cursor position, this is local to this text edit window
+ int cursorX, cursorY;
+ input()->GetCursorPos( cursorX, cursorY );
+
+ // relayout the menu immediately so that we know it's size
+ InvalidateLayout(true);
+ int w, h;
+ GetSize( w, h );
+
+ // work out where the cursor is and therefore the best place to put the frame
+ int sw, sh;
+ surface()->GetScreenSize( sw, sh );
+
+ // Try to center it first
+ int x, y;
+ x = cursorX - ( w / 2 );
+ y = cursorY - ( h / 2 );
+
+ // Clamp to various sides
+ if ( x + w > sw )
+ {
+ x = sw - w;
+ }
+ if ( y + h > sh )
+ {
+ y = sh - h;
+ }
+ if ( x < 0 )
+ {
+ x = 0;
+ }
+ if ( y < 0 )
+ {
+ y = 0;
+ }
+
+ SetPos( x, y );
+}
diff --git a/mp/src/vgui2/vgui_controls/GraphPanel.cpp b/mp/src/vgui2/vgui_controls/GraphPanel.cpp
new file mode 100644
index 00000000..1fd883ce
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/GraphPanel.cpp
@@ -0,0 +1,308 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include <math.h>
+
+#include <vgui_controls/GraphPanel.h>
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <vgui/IVGui.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( GraphPanel );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+GraphPanel::GraphPanel(Panel *parent, const char *name) : BaseClass(parent, name)
+{
+ m_flDomainSize = 100.0f;
+ m_flLowRange = 0.0f;
+ m_flHighRange = 1.0f;
+ m_bUseDynamicRange = true;
+ m_flMinDomainSize = 0.0f;
+ m_flMaxDomainSize = 0.0f;
+ m_bMaxDomainSizeSet = false;
+
+ // rendering, need to pull these from scheme/res file
+ m_iGraphBarWidth = 2;
+ m_iGraphBarGapWidth = 2;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: domain settings (x-axis settings)
+//-----------------------------------------------------------------------------
+void GraphPanel::SetDisplayDomainSize(float size)
+{
+ m_flDomainSize = size;
+
+ // set the max domain size if it hasn't been set yet
+ if (!m_bMaxDomainSizeSet)
+ {
+ SetMaxDomainSize(size);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the smallest domain that will be displayed
+//-----------------------------------------------------------------------------
+void GraphPanel::SetMinDomainSize(float size)
+{
+ m_flMinDomainSize = size;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the samples to keep
+//-----------------------------------------------------------------------------
+void GraphPanel::SetMaxDomainSize(float size)
+{
+ m_flMaxDomainSize = size;
+ m_bMaxDomainSizeSet = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: range settings (y-axis settings)
+//-----------------------------------------------------------------------------
+void GraphPanel::SetUseFixedRange(float lowRange, float highRange)
+{
+ m_bUseDynamicRange = false;
+ m_flLowRange = lowRange;
+ m_flHighRange = highRange;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the graph to dynamically determine the range
+//-----------------------------------------------------------------------------
+void GraphPanel::SetUseDynamicRange(float *rangeList, int numRanges)
+{
+ m_bUseDynamicRange = true;
+ m_RangeList.CopyArray(rangeList, numRanges);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the currently displayed range
+//-----------------------------------------------------------------------------
+void GraphPanel::GetDisplayedRange(float &lowRange, float &highRange)
+{
+ lowRange = m_flLowRange;
+ highRange = m_flHighRange;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds an item to the end of the list
+//-----------------------------------------------------------------------------
+void GraphPanel::AddItem(float sampleEnd, float sampleValue)
+{
+ if (m_Samples.Count() && m_Samples[m_Samples.Tail()].value == sampleValue)
+ {
+ // collapse identical samples
+ m_Samples[m_Samples.Tail()].sampleEnd = sampleEnd;
+ }
+ else
+ {
+ // add to the end of the samples list
+ Sample_t item;
+ item.value = sampleValue;
+ item.sampleEnd = sampleEnd;
+ m_Samples.AddToTail(item);
+ }
+
+ // see if this frees up any samples past the end
+ if (m_bMaxDomainSizeSet)
+ {
+ float freePoint = sampleEnd - m_flMaxDomainSize;
+ while (m_Samples[m_Samples.Head()].sampleEnd < freePoint)
+ {
+ m_Samples.Remove(m_Samples.Head());
+ }
+ }
+
+/*
+ // see the max number of samples necessary to display this information reasonably precisely
+ static const int MAX_LIKELY_GRAPH_WIDTH = 800;
+ int maxSamplesNeeded = 2 * MAX_LIKELY_GRAPH_WIDTH / (m_iGraphBarWidth + m_iGraphBarGapWidth);
+ if (m_Samples.Count() > 2)
+ {
+ // see if we can collapse some items
+ float highestSample = m_Samples[m_Samples.Tail()].sampleEnd;
+
+ // iterate the items
+ // always keep the head around so we have something to go against
+ int sampleIndex = m_Samples.Next(m_Samples.Head());
+ int nextSampleIndex = m_Samples.Next(sampleIndex);
+
+ while (m_Samples.IsInList(nextSampleIndex))
+ {
+ // calculate what sampling precision is actually needed to display this data
+ float distanceFromEnd = highestSample - m_Samples[sampleIndex].sampleEnd;
+
+// if (distanceFromEnd < m_flDomainSize)
+// break;
+
+ //!! this calculation is very incorrect
+ float minNeededSampleSize = distanceFromEnd / (m_flMinDomainSize * maxSamplesNeeded);
+ float sampleSize = m_Samples[nextSampleIndex].sampleEnd - m_Samples[sampleIndex].sampleEnd;
+
+ if (sampleSize < minNeededSampleSize)
+ {
+ // collapse the item into the next index
+ m_Samples[nextSampleIndex].value = 0.5f * (m_Samples[nextSampleIndex].value + m_Samples[sampleIndex].value);
+
+ // remove the item from the list
+ m_Samples.Remove(sampleIndex);
+
+ // move to the next item
+ sampleIndex = nextSampleIndex;
+ nextSampleIndex = m_Samples.Next(sampleIndex);
+ }
+ else
+ {
+ // this item didn't need collapsing, so assume the next item won't
+ break;
+ }
+ }
+ }
+*/
+
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns number of items that can be displayed
+//-----------------------------------------------------------------------------
+int GraphPanel::GetVisibleItemCount()
+{
+ return GetWide() / (m_iGraphBarWidth + m_iGraphBarGapWidth);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: lays out the graph
+//-----------------------------------------------------------------------------
+void GraphPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: draws the graph
+//-----------------------------------------------------------------------------
+void GraphPanel::Paint()
+{
+ if (!m_Samples.Count())
+ return;
+
+ // walk from right to left drawing the resampled data
+ int sampleIndex = m_Samples.Tail();
+ int x = GetWide() - (m_iGraphBarWidth + m_iGraphBarGapWidth);
+
+ // calculate how big each sample should be
+ float sampleSize = m_flDomainSize / GetVisibleItemCount();
+
+ // calculate where in the domain we start resampling
+ float resampleStart = m_Samples[sampleIndex].sampleEnd - sampleSize;
+ // always resample from a sample point that is a multiple of the sampleSize
+ resampleStart -= (float)fmod(resampleStart, sampleSize);
+
+ // bar size multiplier
+ float barSizeMultiplier = GetTall() / (m_flHighRange - m_flLowRange);
+
+ // set render color
+ surface()->DrawSetColor(GetFgColor());
+
+ // recalculate the sample range for dynamic resizing
+ float flMinValue = m_Samples[m_Samples.Head()].value;
+ float flMaxValue = m_Samples[m_Samples.Head()].value;
+
+ // iterate the bars to draw
+ while (x > 0 && m_Samples.IsInList(sampleIndex))
+ {
+ // move back the drawing point
+ x -= (m_iGraphBarWidth + m_iGraphBarGapWidth);
+
+ // collect the samples
+ float value = 0.0f;
+ float maxValue = 0.0f;
+ int samplesTouched = 0;
+ int prevSampleIndex = m_Samples.Previous(sampleIndex);
+ while (m_Samples.IsInList(prevSampleIndex))
+ {
+ // take the value
+ value += m_Samples[sampleIndex].value;
+ samplesTouched++;
+
+ // do some work to calculate the sample range
+ if (m_Samples[sampleIndex].value < flMinValue)
+ {
+ flMinValue = m_Samples[sampleIndex].value;
+ }
+ if (m_Samples[sampleIndex].value > flMaxValue)
+ {
+ flMaxValue = m_Samples[sampleIndex].value;
+ }
+ if (m_Samples[sampleIndex].value > maxValue)
+ {
+ maxValue = m_Samples[sampleIndex].value;
+ }
+
+ if (resampleStart < m_Samples[prevSampleIndex].sampleEnd)
+ {
+ // we're out of the sampling range, we need to move on to the next sample
+ sampleIndex = prevSampleIndex;
+ prevSampleIndex = m_Samples.Previous(sampleIndex);
+ }
+ else
+ {
+ // we're done with this resample
+ // move back the resample start
+ resampleStart -= sampleSize;
+ // draw the current item
+ break;
+ }
+ }
+
+ // draw the item
+ // show the max value in the sample, not the average
+ int size = (int)(maxValue * barSizeMultiplier);
+// int size = (int)((value * barSizeMultiplier) / samplesTouched);
+ surface()->DrawFilledRect(x, GetTall() - size, x + m_iGraphBarWidth, GetTall());
+ }
+
+ // calculate our final range (for use next frame)
+ if (m_bUseDynamicRange)
+ {
+ flMinValue = 0;
+
+ // find the range that fits
+ for (int i = 0; i < m_RangeList.Count(); i++)
+ {
+ if (m_RangeList[i] > flMaxValue)
+ {
+ flMaxValue = m_RangeList[i];
+ break;
+ }
+ }
+
+ m_flLowRange = flMinValue;
+ m_flHighRange = flMaxValue;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets up colors
+//-----------------------------------------------------------------------------
+void GraphPanel::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetFgColor(GetSchemeColor("GraphPanel.FgColor", pScheme));
+ SetBgColor(GetSchemeColor("GraphPanel.BgColor", pScheme));
+ SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
+}
diff --git a/mp/src/vgui2/vgui_controls/HTML.cpp b/mp/src/vgui2/vgui_controls/HTML.cpp
new file mode 100644
index 00000000..d7f68f10
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/HTML.cpp
@@ -0,0 +1,2278 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+// This class is a message box that has two buttons, ok and cancel instead of
+// just the ok button of a message box. We use a message box class for the ok button
+// and implement another button here.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vgui_controls/pch_vgui_controls.h"
+#include <vgui_controls/EditablePanel.h>
+#include <vgui_controls/Menu.h>
+#include <vgui_controls/MessageBox.h>
+
+#include <tier0/memdbgoff.h>
+#include <tier0/valve_minmax_off.h>
+#include "htmlmessages.pb.h"
+#include <tier0/valve_minmax_on.h>
+#include <tier0/memdbgon.h>
+
+#include "html/ipainthtml.h"
+#include "html/ihtmlchrome.h"
+#include "html/ichromehtmlwrapper.h"
+#include "html/htmlprotobuf.h"
+
+#include "filesystem.h"
+#include "../vgui2/src/vgui_key_translation.h"
+
+#undef PostMessage
+#undef MessageBox
+
+#include "OfflineMode.h"
+
+// memdbgon must be the last include file in a .cpp file
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+// helper to send IPC messages to the CEF thread
+#define DISPATCH_MESSAGE( eCmd ) \
+ if (surface()->AccessChromeHTMLController()) \
+ { \
+ cmd.Body().set_browser_handle( m_iBrowser );\
+ HTMLCommandBuffer_t *pBuf = surface()->AccessChromeHTMLController()->GetFreeCommandBuffer( eCmd, m_iBrowser ); \
+ cmd.SerializeCrossProc( &pBuf->m_Buffer ); \
+ if ( m_iBrowser == -1 ) { m_vecPendingMessages.AddToTail( pBuf ); } \
+ else \
+ { \
+ surface()->AccessChromeHTMLController()->PushCommand( pBuf ); \
+ surface()->AccessChromeHTMLController()->WakeThread(); \
+ }\
+ } \
+
+const int k_nMaxCustomCursors = 2; // the max number of custom cursors we keep cached PER html control
+
+//-----------------------------------------------------------------------------
+// Purpose: A simple passthrough panel to render the border onto the HTML widget
+//-----------------------------------------------------------------------------
+class HTMLInterior : public Panel
+{
+ DECLARE_CLASS_SIMPLE( HTMLInterior, Panel );
+public:
+ HTMLInterior( HTML *parent ) : BaseClass( parent, "HTMLInterior" )
+ {
+ m_pHTML = parent;
+ SetPaintBackgroundEnabled( false );
+ SetKeyBoardInputEnabled( false );
+ SetMouseInputEnabled( false );
+ }
+
+private:
+ HTML *m_pHTML;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: a vgui container for popup menus displayed by a control, only 1 menu for any control can be visible at a time
+//-----------------------------------------------------------------------------
+class HTMLComboBoxHost : public vgui::EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( HTMLComboBoxHost, EditablePanel );
+public:
+ HTMLComboBoxHost( HTML *parent, const char *panelName ) : EditablePanel( parent, panelName )
+ {
+ m_pParent = parent;
+ MakePopup(false);
+ }
+ ~HTMLComboBoxHost() {}
+
+ virtual void PaintBackground();
+
+ virtual void OnMousePressed(MouseCode code);
+ virtual void OnMouseReleased(MouseCode code);
+ virtual void OnCursorMoved(int x,int y);
+ virtual void OnMouseDoublePressed(MouseCode code);
+ virtual void OnKeyTyped(wchar_t unichar);
+ virtual void OnKeyCodeTyped(KeyCode code);
+ virtual void OnKeyCodeReleased(KeyCode code);
+ virtual void OnMouseWheeled(int delta);
+
+ virtual void OnKillFocus()
+ {
+ if ( vgui::input()->GetFocus() != m_pParent->GetVPanel() ) // if its not our parent trying to steal focus
+ {
+ BaseClass::OnKillFocus();
+ if ( m_pParent )
+ m_pParent->HidePopup();
+ }
+ }
+
+ virtual void PerformLayout()
+ {
+ // no op the perform layout as we just render the html controls popup texture into it
+ // we don't want the menu logic trying to play with its size
+ }
+
+
+private:
+ HTML *m_pParent;
+ CUtlVector<HTMLCommandBuffer_t *> m_vecPendingMessages;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: container class for any external popup windows the browser requests
+//-----------------------------------------------------------------------------
+class HTMLPopup : public vgui::Frame
+{
+ DECLARE_CLASS_SIMPLE( HTMLPopup, vgui::Frame );
+ class PopupHTML : public vgui::HTML
+ {
+ DECLARE_CLASS_SIMPLE( PopupHTML, vgui::HTML );
+ public:
+ PopupHTML( Frame *parent, const char *pchName, bool allowJavaScript , bool bPopupWindow ) : HTML( parent, pchName, allowJavaScript, bPopupWindow ) { m_pParent = parent; }
+
+ virtual void OnSetHTMLTitle( const char *pchTitle )
+ {
+ BaseClass::OnSetHTMLTitle( pchTitle );
+ m_pParent->SetTitle( pchTitle, true );
+ }
+
+ private:
+ Frame *m_pParent;
+ };
+public:
+ HTMLPopup( Panel *parent, const char *pchURL, const char *pchTitle ) : Frame( NULL, "HtmlPopup", true )
+ {
+ m_pHTML = new PopupHTML( this, "htmlpopupchild", true, true );
+ m_pHTML->OpenURL( pchURL, NULL, false );
+ SetTitle( pchTitle, true );
+ }
+
+ ~HTMLPopup()
+ {
+ }
+
+ enum
+ {
+ vert_inset = 40,
+ horiz_inset = 6
+ };
+
+ void PerformLayout()
+ {
+ BaseClass::PerformLayout();
+ int wide, tall;
+ GetSize( wide, tall );
+ m_pHTML->SetPos( horiz_inset, vert_inset );
+ m_pHTML->SetSize( wide - horiz_inset*2, tall - vert_inset*2 );
+ }
+
+ void SetBounds( int x, int y, int wide, int tall )
+ {
+ BaseClass::SetBounds( x, y, wide + horiz_inset*2, tall + vert_inset*2 );
+ }
+
+ MESSAGE_FUNC( OnCloseWindow, "OnCloseWindow" )
+ {
+ Close();
+ }
+private:
+ PopupHTML *m_pHTML;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+HTML::HTML(Panel *parent, const char *name, bool allowJavaScript, bool bPopupWindow) : Panel(parent, name)
+{
+ m_iHTMLTextureID = 0;
+ m_iComboBoxTextureID = 0;
+ m_bCanGoBack = false;
+ m_bCanGoForward = false;
+ m_bInFind = false;
+ m_bRequestingDragURL = false;
+ m_bRequestingCopyLink = false;
+ m_flZoom = 100.0f;
+ m_iBrowser = -1;
+ m_bNeedsFullTextureUpload = false;
+
+ m_pInteriorPanel = new HTMLInterior( this );
+ SetPostChildPaintEnabled( true );
+ if (surface() && surface()->AccessChromeHTMLController())
+ {
+ surface()->AccessChromeHTMLController()->CreateBrowser( this, bPopupWindow, surface()->GetWebkitHTMLUserAgentString() );
+ }
+ else
+ {
+ Warning("Unable to access ChromeHTMLController");
+ }
+ m_iScrollBorderX=m_iScrollBorderY=0;
+ m_bScrollBarEnabled = true;
+ m_bContextMenuEnabled = true;
+ m_bNewWindowsOnly = false;
+ m_iMouseX = m_iMouseY = 0;
+ m_iDragStartX = m_iDragStartY = 0;
+ m_nViewSourceAllowedIndex = -1;
+ m_iWideLastHTMLSize = m_iTalLastHTMLSize = 0;
+
+ _hbar = new ScrollBar(this, "HorizScrollBar", false);
+ _hbar->SetVisible(false);
+ _hbar->AddActionSignalTarget(this);
+
+ _vbar = new ScrollBar(this, "VertScrollBar", true);
+ _vbar->SetVisible(false);
+ _vbar->AddActionSignalTarget(this);
+
+ m_pFindBar = new HTML::CHTMLFindBar( this );
+ m_pFindBar->SetZPos( 2 );
+ m_pFindBar->SetVisible( false );
+
+ m_pComboBoxHost = new HTMLComboBoxHost( this, "ComboBoxHost" );
+ m_pComboBoxHost->SetPaintBackgroundEnabled( true );
+ m_pComboBoxHost->SetVisible( false );
+
+ m_pContextMenu = new Menu( this, "contextmenu" );
+ m_pContextMenu->AddMenuItem( "#vgui_HTMLBack", new KeyValues( "Command", "command", "back" ), this );
+ m_pContextMenu->AddMenuItem( "#vgui_HTMLForward", new KeyValues( "Command", "command", "forward" ), this );
+ m_pContextMenu->AddMenuItem( "#vgui_HTMLReload", new KeyValues( "Command", "command", "reload" ), this );
+ m_pContextMenu->AddMenuItem( "#vgui_HTMLStop", new KeyValues( "Command", "command", "stop" ), this );
+ m_pContextMenu->AddSeparator();
+ m_pContextMenu->AddMenuItem( "#vgui_HTMLCopyUrl", new KeyValues( "Command", "command", "copyurl" ), this );
+ m_iCopyLinkMenuItemID = m_pContextMenu->AddMenuItem( "#vgui_HTMLCopyLink", new KeyValues( "Command", "command", "copylink" ), this );
+ m_pContextMenu->AddMenuItem( "#TextEntry_Copy", new KeyValues( "Command", "command", "copy" ), this );
+ m_pContextMenu->AddMenuItem( "#TextEntry_Paste", new KeyValues( "Command", "command", "paste" ), this );
+ m_pContextMenu->AddSeparator();
+ m_nViewSourceAllowedIndex = m_pContextMenu->AddMenuItem( "#vgui_HTMLViewSource", new KeyValues( "Command", "command", "viewsource" ), this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+HTML::~HTML()
+{
+ m_pContextMenu->MarkForDeletion();
+
+ if (surface()->AccessChromeHTMLController())
+ {
+ surface()->AccessChromeHTMLController()->RemoveBrowser( this );
+ }
+
+ FOR_EACH_VEC( m_vecHCursor, i )
+ {
+ // BR FIXME!
+// surface()->DeleteCursor( m_vecHCursor[i].m_Cursor );
+ }
+ m_vecHCursor.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle message to change our cursor
+//-----------------------------------------------------------------------------
+void HTML::OnSetCursorVGUI( int cursor )
+{
+ SetCursor( (HCursor)cursor );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets up colors/fonts/borders
+//-----------------------------------------------------------------------------
+void HTML::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+ BrowserResize();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: overrides panel class, paints a texture of the HTML window as a background
+//-----------------------------------------------------------------------------
+void HTML::Paint()
+{
+ //VPROF_BUDGET( "HTML::Paint()", VPROF_BUDGETGROUP_OTHER_VGUI );
+ BaseClass::Paint();
+
+ if ( m_iHTMLTextureID != 0 )
+ {
+ surface()->DrawSetTexture( m_iHTMLTextureID );
+ int tw = 0, tt = 0;
+ surface()->DrawSetColor( Color( 255, 255, 255, 255 ) );
+ GetSize( tw, tt );
+ surface()->DrawTexturedRect( 0, 0, tw, tt );
+ }
+
+ // If we have scrollbars, we need to draw the bg color under them, since the browser
+ // bitmap is a checkerboard under them, and they are transparent in the in-game client
+ if ( m_iScrollBorderX > 0 || m_iScrollBorderY > 0 )
+ {
+ int w, h;
+ GetSize( w, h );
+ IBorder *border = GetBorder();
+ int left = 0, top = 0, right = 0, bottom = 0;
+ if ( border )
+ {
+ border->GetInset( left, top, right, bottom );
+ }
+ surface()->DrawSetColor( GetBgColor() );
+ if ( m_iScrollBorderX )
+ {
+ surface()->DrawFilledRect( w-m_iScrollBorderX - right, top, w, h - bottom );
+ }
+ if ( m_iScrollBorderY )
+ {
+ surface()->DrawFilledRect( left, h-m_iScrollBorderY - bottom, w-m_iScrollBorderX - right, h );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: paint the combo box texture if we have one
+//-----------------------------------------------------------------------------
+void HTML::PaintComboBox()
+{
+ BaseClass::Paint();
+ if ( m_iComboBoxTextureID != 0 )
+ {
+ surface()->DrawSetTexture( m_iComboBoxTextureID );
+ surface()->DrawSetColor( Color( 255, 255, 255, 255 ) );
+ int tw = m_allocedComboBoxWidth;
+ int tt = m_allocedComboBoxHeight;
+ surface()->DrawTexturedRect( 0, 0, tw, tt );
+ }
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: overrides panel class, paints a texture of the HTML window as a background
+//-----------------------------------------------------------------------------
+void HTMLComboBoxHost::PaintBackground()
+{
+ BaseClass::PaintBackground();
+
+ m_pParent->PaintComboBox();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: causes a repaint when the layout changes
+//-----------------------------------------------------------------------------
+void HTML::PerformLayout()
+{
+ BaseClass::PerformLayout();
+ Repaint();
+ int vbarInset = _vbar->IsVisible() ? _vbar->GetWide() : 0;
+ int maxw = GetWide() - vbarInset;
+ m_pInteriorPanel->SetBounds( 0, 0, maxw, GetTall() );
+
+ IScheme *pClientScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "ClientScheme" ) );
+
+ int iSearchInsetY = 5;
+ int iSearchInsetX = 5;
+ int iSearchTall = 24;
+ int iSearchWide = 150;
+ const char *resourceString = pClientScheme->GetResourceString( "HTML.SearchInsetY");
+ if ( resourceString )
+ {
+ iSearchInsetY = atoi(resourceString);
+ }
+ resourceString = pClientScheme->GetResourceString( "HTML.SearchInsetX");
+ if ( resourceString )
+ {
+ iSearchInsetX = atoi(resourceString);
+ }
+ resourceString = pClientScheme->GetResourceString( "HTML.SearchTall");
+ if ( resourceString )
+ {
+ iSearchTall = atoi(resourceString);
+ }
+ resourceString = pClientScheme->GetResourceString( "HTML.SearchWide");
+ if ( resourceString )
+ {
+ iSearchWide = atoi(resourceString);
+ }
+
+ m_pFindBar->SetBounds( GetWide() - iSearchWide - iSearchInsetX - vbarInset, m_pFindBar->BIsHidden() ? -1*iSearchTall-5: iSearchInsetY, iSearchWide, iSearchTall );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: updates the underlying HTML surface widgets position
+//-----------------------------------------------------------------------------
+void HTML::OnMove()
+{
+ BaseClass::OnMove();
+
+ // tell cef where we are on the screen so plugins can correctly render
+ int nPanelAbsX, nPanelAbsY;
+ ipanel()->GetAbsPos( GetVPanel(), nPanelAbsX, nPanelAbsY );
+ CHTMLProtoBufMsg<CMsgBrowserPosition> cmd( eHTMLCommands_BrowserPosition );
+ cmd.Body().set_x( nPanelAbsX );
+ cmd.Body().set_y( nPanelAbsY );
+ DISPATCH_MESSAGE( eHTMLCommands_BrowserPosition );
+
+ if ( m_pComboBoxHost && m_pComboBoxHost->IsVisible() )
+ {
+ m_pComboBoxHost->SetVisible( false );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: calculates the need for and position of both horizontal and vertical scroll bars
+//-----------------------------------------------------------------------------
+void HTML::CalcScrollBars(int w, int h)
+{
+ bool bScrollbarVisible = _vbar->IsVisible();
+
+ if ( m_bScrollBarEnabled )
+ {
+ for ( int i = 0; i < 2; i++ )
+ {
+ int scrollx, scrolly, scrollwide, scrolltall;
+ bool bVisible = false;
+ if ( i==0 )
+ {
+ scrollx = m_scrollHorizontal.m_nX;
+ scrolly = m_scrollHorizontal.m_nY;
+ scrollwide = m_scrollHorizontal.m_nWide;
+ scrolltall = m_scrollHorizontal.m_nTall;
+ bVisible = m_scrollHorizontal.m_bVisible;
+
+ // scrollbar positioning tweaks - should be moved into a resource file
+ scrollwide += 14;
+ scrolltall += 5;
+ }
+ else
+ {
+ scrollx = m_scrollVertical.m_nX;
+ scrolly = m_scrollVertical.m_nY;
+ scrollwide = m_scrollVertical.m_nWide;
+ scrolltall = m_scrollVertical.m_nTall;
+ bVisible = m_scrollVertical.m_bVisible;
+
+ // scrollbar positioning tweaks - should be moved into a resource file
+ //scrollx -= 3;
+ if ( m_scrollHorizontal.m_bVisible )
+ scrolltall += 16;
+ else
+ scrolltall -= 2;
+
+ scrollwide += 5;
+ }
+
+ if ( bVisible && scrollwide && scrolltall )
+ {
+ int panelWide, panelTall;
+ GetSize( panelWide, panelTall );
+
+ ScrollBar *bar = _vbar;
+ if ( i == 0 )
+ bar = _hbar;
+
+ if (!bar->IsVisible())
+ {
+ bar->SetVisible(true);
+ // displayable area has changed, need to force an update
+ PostMessage(this, new KeyValues("OnSliderMoved"), 0.02f);
+ }
+
+ int rangeWindow = panelTall - scrollwide;
+ if ( i==0 )
+ rangeWindow = panelWide - scrolltall;
+ int range = m_scrollVertical.m_nMax + m_scrollVertical.m_nTall;
+ if ( i == 0 )
+ range = m_scrollHorizontal.m_nMax + m_scrollVertical.m_nWide;
+ int curValue = m_scrollVertical.m_nScroll;
+ if ( i == 0 )
+ curValue = m_scrollHorizontal.m_nScroll;
+
+ bar->SetEnabled(false);
+ bar->SetRangeWindow( rangeWindow );
+ bar->SetRange( 0, range ); // we want the range [0.. (img_h - h)], but the scrollbar actually returns [0..(range-rangeWindow)] so make sure -h gets deducted from the max range value
+ bar->SetButtonPressedScrollValue( 5 );
+ if ( curValue > ( bar->GetValue() + 5 ) || curValue < (bar->GetValue() - 5 ) )
+ bar->SetValue( curValue );
+
+ if ( i == 0 )
+ {
+ bar->SetPos( 0, h - scrolltall - 1 );
+ bar->SetWide( scrollwide );
+ bar->SetTall( scrolltall );
+ }
+ else
+ {
+ bar->SetPos( w - scrollwide, 0 );
+ bar->SetTall( scrolltall );
+ bar->SetWide( scrollwide );
+ }
+
+ if ( i == 0 )
+ m_iScrollBorderY=scrolltall;
+ else
+ m_iScrollBorderX=scrollwide;
+ }
+ else
+ {
+ if ( i == 0 )
+ {
+ m_iScrollBorderY=0;
+ _hbar->SetVisible( false );
+ }
+ else
+ {
+ m_iScrollBorderX=0;
+ _vbar->SetVisible( false );
+
+ }
+ }
+ }
+ }
+ else
+ {
+ m_iScrollBorderX = 0;
+ m_iScrollBorderY=0;
+ _vbar->SetVisible(false);
+ _hbar->SetVisible(false);
+ }
+
+ if ( bScrollbarVisible != _vbar->IsVisible() )
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: opens the URL, will accept any URL that IE accepts
+//-----------------------------------------------------------------------------
+void HTML::OpenURL(const char *URL, const char *postData, bool force)
+{
+ PostURL( URL, postData, force );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: opens the URL, will accept any URL that IE accepts
+//-----------------------------------------------------------------------------
+void HTML::PostURL(const char *URL, const char *pchPostData, bool force)
+{
+ if ( m_iBrowser < 0 )
+ {
+ m_sPendingURLLoad = URL;
+ m_sPendingPostData = pchPostData;
+ return;
+ }
+
+ if ( IsSteamInOfflineMode() && !force )
+ {
+ const char *baseDir = getenv("HTML_OFFLINE_DIR");
+ if ( baseDir )
+ {
+ // get the app we need to run
+ char htmlLocation[_MAX_PATH];
+ char otherName[128];
+ char fileLocation[_MAX_PATH];
+
+ if ( ! g_pFullFileSystem->FileExists( baseDir ) )
+ {
+ Q_snprintf( otherName, sizeof(otherName), "%senglish.html", OFFLINE_FILE );
+ baseDir = otherName;
+ }
+ g_pFullFileSystem->GetLocalCopy( baseDir ); // put this file on disk for IE to load
+
+ g_pFullFileSystem->GetLocalPath( baseDir, fileLocation, sizeof(fileLocation) );
+ Q_snprintf(htmlLocation, sizeof(htmlLocation), "file://%s", fileLocation);
+
+ CHTMLProtoBufMsg<CMsgPostURL> cmd( eHTMLCommands_PostURL );
+ cmd.Body().set_url( htmlLocation );
+ DISPATCH_MESSAGE( eHTMLCommands_PostURL );
+
+ }
+ else
+ {
+ CHTMLProtoBufMsg<CMsgPostURL> cmd( eHTMLCommands_PostURL );
+ cmd.Body().set_url( URL );
+ DISPATCH_MESSAGE( eHTMLCommands_PostURL );
+ }
+ }
+ else
+ {
+ if ( pchPostData && Q_strlen(pchPostData) > 0 )
+ {
+ CHTMLProtoBufMsg<CMsgPostURL> cmd( eHTMLCommands_PostURL );
+ cmd.Body().set_url( URL );
+ cmd.Body().set_post( pchPostData );
+ DISPATCH_MESSAGE( eHTMLCommands_PostURL );
+
+ }
+ else
+ {
+ CHTMLProtoBufMsg<CMsgPostURL> cmd( eHTMLCommands_PostURL );
+ cmd.Body().set_url( URL );
+ DISPATCH_MESSAGE( eHTMLCommands_PostURL );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: opens the URL, will accept any URL that IE accepts
+//-----------------------------------------------------------------------------
+bool HTML::StopLoading()
+{
+ CHTMLProtoBufMsg<CMsgStopLoad> cmd( eHTMLCommands_StopLoad );
+ DISPATCH_MESSAGE( eHTMLCommands_StopLoad );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: refreshes the current page
+//-----------------------------------------------------------------------------
+bool HTML::Refresh()
+{
+ CHTMLProtoBufMsg<CMsgReload> cmd( eHTMLCommands_Reload );
+ DISPATCH_MESSAGE( eHTMLCommands_Reload );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Tells the browser control to go back
+//-----------------------------------------------------------------------------
+void HTML::GoBack()
+{
+ CHTMLProtoBufMsg<CMsgGoBack> cmd( eHTMLCommands_GoBack );
+ DISPATCH_MESSAGE( eHTMLCommands_GoBack );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Tells the browser control to go forward
+//-----------------------------------------------------------------------------
+void HTML::GoForward()
+{
+ CHTMLProtoBufMsg<CMsgGoForward> cmd( eHTMLCommands_GoForward );
+ DISPATCH_MESSAGE( eHTMLCommands_GoForward );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks if the browser can go back further
+//-----------------------------------------------------------------------------
+bool HTML::BCanGoBack()
+{
+ return m_bCanGoBack;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks if the browser can go forward further
+//-----------------------------------------------------------------------------
+bool HTML::BCanGoFoward()
+{
+ return m_bCanGoForward;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: handle resizing
+//-----------------------------------------------------------------------------
+void HTML::OnSizeChanged(int wide,int tall)
+{
+ BaseClass::OnSizeChanged(wide,tall);
+ UpdateSizeAndScrollBars();
+ UpdateCachedHTMLValues();
+#ifdef WIN32
+ // under windows we get stuck in the windows message loop pushing out WM_WINDOWPOSCHANGED without returning in the windproc loop
+ // so we need to manually pump the html dispatching of messages here
+ if ( surface() && surface()->AccessChromeHTMLController() )
+ {
+ surface()->AccessChromeHTMLController()->RunFrame();
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Run javascript in the page
+//-----------------------------------------------------------------------------
+void HTML::RunJavascript( const char *pchScript )
+{
+ CHTMLProtoBufMsg<CMsgExecuteJavaScript> cmd( eHTMLCommands_ExecuteJavaScript );
+ cmd.Body().set_script( pchScript );
+ DISPATCH_MESSAGE( eHTMLCommands_ExecuteJavaScript );
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: helper to convert UI mouse codes to CEF ones
+//-----------------------------------------------------------------------------
+int ConvertMouseCodeToCEFCode( MouseCode code )
+{
+ switch( code )
+ {
+ case MOUSE_LEFT:
+ return IInputEventHTML::eButtonLeft;
+ break;
+ case MOUSE_RIGHT:
+ return IInputEventHTML::eButtonRight;
+ break;
+ case MOUSE_MIDDLE:
+ return IInputEventHTML::eButtonMiddle;
+ break;
+ default:
+ return IInputEventHTML::eButtonLeft;
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: passes mouse clicks to the control
+//-----------------------------------------------------------------------------
+void HTML::OnMousePressed(MouseCode code)
+{
+ m_sDragURL = NULL;
+
+ // mouse4 = back button
+ if ( code == MOUSE_4 )
+ {
+ PostActionSignal( new KeyValues( "HTMLBackRequested" ) );
+ return;
+ }
+ if ( code == MOUSE_5 )
+ {
+ PostActionSignal( new KeyValues( "HTMLForwardRequested" ) );
+ return;
+ }
+
+
+ if ( code == MOUSE_RIGHT && m_bContextMenuEnabled )
+ {
+ GetLinkAtPosition( m_iMouseX, m_iMouseY );
+ Menu::PlaceContextMenu( this, m_pContextMenu );
+ return;
+ }
+
+ // ask for the focus to come to this window
+ RequestFocus();
+
+ // now tell the browser about the click
+ // ignore right clicks if context menu has been disabled
+ if ( code != MOUSE_RIGHT )
+ {
+ CHTMLProtoBufMsg<CMsgMouseDown> cmd( eHTMLCommands_MouseDown );
+ cmd.Body().set_mouse_button( ConvertMouseCodeToCEFCode( code ) );
+ DISPATCH_MESSAGE( eHTMLCommands_MouseDown );
+ }
+
+ if ( code == MOUSE_LEFT )
+ {
+ input()->GetCursorPos( m_iDragStartX, m_iDragStartY );
+ int htmlx, htmly;
+ ipanel()->GetAbsPos( GetVPanel(), htmlx, htmly );
+
+ GetLinkAtPosition( m_iDragStartX - htmlx, m_iDragStartY - htmly );
+
+ m_bRequestingDragURL = true;
+ // make sure we get notified when the mouse gets released
+ if ( !m_sDragURL.IsEmpty() )
+ {
+ input()->SetMouseCapture( GetVPanel() );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: passes mouse up events
+//-----------------------------------------------------------------------------
+void HTML::OnMouseReleased(MouseCode code)
+{
+ if ( code == MOUSE_LEFT )
+ {
+ input()->SetMouseCapture( NULL );
+ input()->SetCursorOveride( 0 );
+
+ if ( !m_sDragURL.IsEmpty() && input()->GetMouseOver() != GetVPanel() && input()->GetMouseOver() != NULL )
+ {
+ // post the text as a drag drop to the target panel
+ KeyValuesAD kv( "DragDrop" );
+ if ( ipanel()->RequestInfo( input()->GetMouseOver(), kv )
+ && kv->GetPtr( "AcceptPanel" ) != NULL )
+ {
+ VPANEL vpanel = (VPANEL)kv->GetPtr( "AcceptPanel" );
+ ivgui()->PostMessage( vpanel, new KeyValues( "DragDrop", "text", m_sDragURL.Get() ), GetVPanel() );
+ }
+ }
+ m_sDragURL = NULL;
+ }
+
+ CHTMLProtoBufMsg<CMsgMouseUp> cmd( eHTMLCommands_MouseUp );
+ cmd.Body().set_mouse_button( ConvertMouseCodeToCEFCode( code ) );
+ DISPATCH_MESSAGE( eHTMLCommands_MouseUp );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: keeps track of where the cursor is
+//-----------------------------------------------------------------------------
+void HTML::OnCursorMoved(int x,int y)
+{
+ // Only do this when we are over the current panel
+ if ( vgui::input()->GetMouseOver() == GetVPanel() )
+ {
+ m_iMouseX = x;
+ m_iMouseY = y;
+
+ CHTMLProtoBufMsg<CMsgMouseMove> cmd( eHTMLCommands_MouseMove );
+ cmd.Body().set_x( m_iMouseX );
+ cmd.Body().set_y( m_iMouseY );
+ DISPATCH_MESSAGE( eHTMLCommands_MouseMove );
+ }
+ else if ( !m_sDragURL.IsEmpty() )
+ {
+ if ( input()->GetMouseOver() == NULL )
+ {
+ // we're not over any vgui window, switch to the OS implementation of drag/drop
+ // BR FIXME
+// surface()->StartDragDropText( m_sDragURL );
+ m_sDragURL = NULL;
+ }
+ }
+
+ if ( !m_sDragURL.IsEmpty() && !input()->GetCursorOveride() )
+ {
+ // if we've dragged far enough (in global coordinates), set to use the drag cursor
+ int gx, gy;
+ input()->GetCursorPos( gx, gy );
+ if ( abs(m_iDragStartX-gx) + abs(m_iDragStartY-gy) > 3 )
+ {
+// input()->SetCursorOveride( dc_alias );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: passes double click events to the browser
+//-----------------------------------------------------------------------------
+void HTML::OnMouseDoublePressed(MouseCode code)
+{
+ CHTMLProtoBufMsg<CMsgMouseDblClick> cmd( eHTMLCommands_MouseDblClick );
+ cmd.Body().set_mouse_button( ConvertMouseCodeToCEFCode( code ) );
+ DISPATCH_MESSAGE( eHTMLCommands_MouseDblClick );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: passes key presses to the browser (we don't current do this)
+//-----------------------------------------------------------------------------
+void HTML::OnKeyTyped(wchar_t unichar)
+{
+ CHTMLProtoBufMsg<CMsgKeyChar> cmd( eHTMLCommands_KeyChar );
+ cmd.Body().set_unichar( unichar );
+ DISPATCH_MESSAGE( eHTMLCommands_KeyChar );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: pop up the find dialog
+//-----------------------------------------------------------------------------
+void HTML::ShowFindDialog()
+{
+ IScheme *pClientScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "ClientScheme" ) );
+ if ( !pClientScheme )
+ return;
+
+ m_pFindBar->SetVisible( true );
+ m_pFindBar->RequestFocus();
+ m_pFindBar->SetText( "" );
+ m_pFindBar->HideCountLabel();
+ m_pFindBar->SetHidden( false );
+ int x = 0, y = 0, h = 0, w = 0;
+ m_pFindBar->GetBounds( x, y, w, h );
+ m_pFindBar->SetPos( x, -1*h );
+ int iSearchInsetY = 0;
+ const char *resourceString = pClientScheme->GetResourceString( "HTML.SearchInsetY");
+ if ( resourceString )
+ {
+ iSearchInsetY = atoi(resourceString);
+ }
+ float flAnimationTime = 0.0f;
+ resourceString = pClientScheme->GetResourceString( "HTML.SearchAnimationTime");
+ if ( resourceString )
+ {
+ flAnimationTime = atof(resourceString);
+ }
+
+ GetAnimationController()->RunAnimationCommand( m_pFindBar, "ypos", iSearchInsetY, 0.0f, flAnimationTime, AnimationController::INTERPOLATOR_LINEAR );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: hide the find dialog
+//-----------------------------------------------------------------------------
+void HTML::HideFindDialog()
+{
+ IScheme *pClientScheme = vgui::scheme()->GetIScheme( vgui::scheme()->GetScheme( "ClientScheme" ) );
+ if ( !pClientScheme )
+ return;
+
+ int x = 0, y = 0, h = 0, w = 0;
+ m_pFindBar->GetBounds( x, y, w, h );
+ float flAnimationTime = 0.0f;
+ const char *resourceString = pClientScheme->GetResourceString( "HTML.SearchAnimationTime");
+ if ( resourceString )
+ {
+ flAnimationTime = atof(resourceString);
+ }
+
+ GetAnimationController()->RunAnimationCommand( m_pFindBar, "ypos", -1*h-5, 0.0f, flAnimationTime, AnimationController::INTERPOLATOR_LINEAR );
+ m_pFindBar->SetHidden( true );
+ StopFind();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: is the find dialog visible?
+//-----------------------------------------------------------------------------
+bool HTML::FindDialogVisible()
+{
+ return m_pFindBar->IsVisible() && !m_pFindBar->BIsHidden();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: return the bitmask of any modifier keys that are currently down
+//-----------------------------------------------------------------------------
+int GetKeyModifiers()
+{
+ // Any time a key is pressed reset modifier list as well
+ int nModifierCodes = 0;
+ if( vgui::input()->IsKeyDown( KEY_LCONTROL ) || vgui::input()->IsKeyDown( KEY_RCONTROL ) )
+ nModifierCodes |= IInputEventHTML::CrtlDown;
+
+ if( vgui::input()->IsKeyDown( KEY_LALT ) || vgui::input()->IsKeyDown( KEY_RALT ) )
+ nModifierCodes |= IInputEventHTML::AltDown;
+
+ if( vgui::input()->IsKeyDown( KEY_LSHIFT ) || vgui::input()->IsKeyDown( KEY_RSHIFT ) )
+ nModifierCodes |= IInputEventHTML::ShiftDown;
+
+#ifdef OSX
+ // for now pipe through the cmd-key to be like the control key so we get copy/paste
+ if( vgui::input()->IsKeyDown( KEY_LWIN ) || vgui::input()->IsKeyDown( KEY_RWIN ) )
+ nModifierCodes |= IInputEventHTML::CrtlDown;
+#endif
+
+ return nModifierCodes;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: passes key presses to the browser
+//-----------------------------------------------------------------------------
+void HTML::OnKeyCodeTyped(KeyCode code)
+{
+ switch( code )
+ {
+ case KEY_PAGEDOWN:
+ {
+ int val = _vbar->GetValue();
+ val += 200;
+ _vbar->SetValue(val);
+ break;
+ }
+ case KEY_PAGEUP:
+ {
+ int val = _vbar->GetValue();
+ val -= 200;
+ _vbar->SetValue(val);
+ break;
+ }
+ case KEY_F5:
+ {
+ Refresh();
+ break;
+ }
+ case KEY_F:
+ {
+ if ( (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) )
+ || ( IsOSX() && ( input()->IsKeyDown(KEY_LWIN) || input()->IsKeyDown(KEY_RWIN) ) ) )
+ {
+ if ( !FindDialogVisible() )
+ {
+ ShowFindDialog();
+ }
+ else
+ {
+ HideFindDialog();
+ }
+ break;
+ }
+ }
+ case KEY_ESCAPE:
+ {
+ if ( FindDialogVisible() )
+ {
+ HideFindDialog();
+ break;
+ }
+ }
+ case KEY_TAB:
+ {
+ if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) )
+ {
+ // pass control-tab to parent (through baseclass)
+ BaseClass::OnKeyTyped( code );
+ return;
+ }
+ break;
+ }
+ }
+
+ CHTMLProtoBufMsg<CMsgKeyDown> cmd( eHTMLCommands_KeyDown );
+ cmd.Body().set_keycode( KeyCode_VGUIToVirtualKey(code) );
+ cmd.Body().set_modifiers( GetKeyModifiers() );
+ DISPATCH_MESSAGE( eHTMLCommands_KeyDown );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void HTML::OnKeyCodeReleased(KeyCode code)
+{
+ CHTMLProtoBufMsg<CMsgKeyDown> cmd( eHTMLCommands_KeyUp );
+ cmd.Body().set_keycode( KeyCode_VGUIToVirtualKey(code) );
+ cmd.Body().set_modifiers( GetKeyModifiers() );
+ DISPATCH_MESSAGE( eHTMLCommands_KeyUp );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: scrolls the vertical scroll bar on a web page
+//-----------------------------------------------------------------------------
+void HTML::OnMouseWheeled(int delta)
+{
+ if (_vbar && ( ( m_pComboBoxHost && !m_pComboBoxHost->IsVisible() ) ) )
+ {
+ int val = _vbar->GetValue();
+ val -= (delta * 100.0/3.0 ); // 100 for every 3 lines matches chromes code
+ _vbar->SetValue(val);
+ }
+
+ CHTMLProtoBufMsg<CMsgMouseWheel> cmd( eHTMLCommands_MouseWheel );
+ cmd.Body().set_delta( delta );
+ DISPATCH_MESSAGE( eHTMLCommands_MouseWheel );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Inserts a custom URL handler
+//-----------------------------------------------------------------------------
+void HTML::AddCustomURLHandler(const char *customProtocolName, vgui::Panel *target)
+{
+ int index = m_CustomURLHandlers.AddToTail();
+ m_CustomURLHandlers[index].hPanel = target;
+ Q_strncpy(m_CustomURLHandlers[index].url, customProtocolName, sizeof(m_CustomURLHandlers[index].url));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: shared code for sizing the HTML surface window
+//-----------------------------------------------------------------------------
+void HTML::BrowserResize()
+{
+ int w,h;
+ GetSize( w, h );
+ int right = 0, bottom = 0;
+ // TODO::STYLE
+ /*
+ IAppearance *pAppearance = GetAppearance();
+ int left = 0, top = 0;
+ if ( pAppearance )
+ {
+ pAppearance->GetInset( left, top, right, bottom );
+ }
+ */
+
+ if ( m_iWideLastHTMLSize != ( w - m_iScrollBorderX - right ) || m_iTalLastHTMLSize != ( h - m_iScrollBorderY - bottom ) )
+ {
+ m_iWideLastHTMLSize = w - m_iScrollBorderX - right;
+ m_iTalLastHTMLSize = h - m_iScrollBorderY - bottom;
+ if ( m_iTalLastHTMLSize <= 0 )
+ {
+ SetTall( 64 );
+ m_iTalLastHTMLSize = 64 - bottom;
+ }
+
+ {
+ CHTMLProtoBufMsg<CMsgBrowserSize> cmd( eHTMLCommands_BrowserSize );
+ cmd.Body().set_width( m_iWideLastHTMLSize );
+ cmd.Body().set_height( m_iTalLastHTMLSize );
+ DISPATCH_MESSAGE( eHTMLCommands_BrowserSize );
+ }
+
+
+ // webkit forgets the scroll offset when you resize (it saves the scroll in a DC and a resize throws away the DC)
+ // so just tell it after the resize
+ int scrollV = _vbar->GetValue();
+ int scrollH = _hbar->GetValue();
+
+ {
+ CHTMLProtoBufMsg<CMsgSetHorizontalScroll> cmd( eHTMLCommands_SetHorizontalScroll );
+ cmd.Body().set_scroll( scrollH );
+ DISPATCH_MESSAGE( eHTMLCommands_SetHorizontalScroll );
+ }
+ {
+ CHTMLProtoBufMsg<CMsgSetVerticalScroll> cmd( eHTMLCommands_SetVerticalScroll );
+ cmd.Body().set_scroll( scrollV );
+ DISPATCH_MESSAGE( eHTMLCommands_SetVerticalScroll );
+ }
+ }
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: when a slider moves causes the IE images to re-render itself
+//-----------------------------------------------------------------------------
+void HTML::OnSliderMoved()
+{
+ if(_hbar->IsVisible())
+ {
+ int scrollX =_hbar->GetValue();
+ CHTMLProtoBufMsg<CMsgSetHorizontalScroll> cmd( eHTMLCommands_SetHorizontalScroll );
+ cmd.Body().set_scroll( scrollX );
+ DISPATCH_MESSAGE( eHTMLCommands_SetHorizontalScroll );
+ }
+
+ if(_vbar->IsVisible())
+ {
+ int scrollY=_vbar->GetValue();
+ CHTMLProtoBufMsg<CMsgSetVerticalScroll> cmd( eHTMLCommands_SetVerticalScroll );
+ cmd.Body().set_scroll( scrollY );
+ DISPATCH_MESSAGE( eHTMLCommands_SetVerticalScroll );
+ }
+
+ // post a message that the slider has moved
+ PostActionSignal( new KeyValues( "HTMLSliderMoved" ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+bool HTML::IsScrolledToBottom()
+{
+ if ( !_vbar->IsVisible() )
+ return true;
+
+ return m_scrollVertical.m_nScroll >= m_scrollVertical.m_nMax;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+bool HTML::IsScrollbarVisible()
+{
+ return _vbar->IsVisible();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void HTML::SetScrollbarsEnabled(bool state)
+{
+ m_bScrollBarEnabled = state;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void HTML::SetContextMenuEnabled(bool state)
+{
+ m_bContextMenuEnabled = state;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void HTML::SetViewSourceEnabled(bool state)
+{
+ m_pContextMenu->SetItemVisible( m_nViewSourceAllowedIndex, state );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void HTML::NewWindowsOnly( bool state )
+{
+ m_bNewWindowsOnly = state;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: called when our children have finished painting
+//-----------------------------------------------------------------------------
+void HTML::PostChildPaint()
+{
+ BaseClass::PostChildPaint();
+ // TODO::STYLE
+ //m_pInteriorPanel->SetPaintAppearanceEnabled( true ); // turn painting back on so the IE hwnd can render this border
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a custom header to all requests
+//-----------------------------------------------------------------------------
+void HTML::AddHeader( const char *pchHeader, const char *pchValue )
+{
+ CHTMLProtoBufMsg<CMsgAddHeader> cmd( eHTMLCommands_AddHeader );
+ cmd.Body().set_key( pchHeader );
+ cmd.Body().set_value( pchValue );
+ DISPATCH_MESSAGE( eHTMLCommands_AddHeader );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void HTML::OnSetFocus()
+{
+ BaseClass::OnSetFocus();
+ CHTMLProtoBufMsg<CMsgSetFocus> cmd( eHTMLCommands_SetFocus );
+ cmd.Body().set_focus( true );
+ DISPATCH_MESSAGE( eHTMLCommands_SetFocus );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void HTML::OnKillFocus()
+{
+ if ( vgui::input()->GetFocus() != m_pComboBoxHost->GetVPanel() ) // if its not the menu stealing our focus
+ BaseClass::OnKillFocus();
+
+ // Don't clear the actual html focus if a context menu is what took focus
+ if ( m_pContextMenu->HasFocus() )
+ return;
+
+ if ( m_pComboBoxHost->HasFocus() )
+ return;
+
+ CHTMLProtoBufMsg<CMsgSetFocus> cmd( eHTMLCommands_SetFocus );
+ cmd.Body().set_focus( false );
+ DISPATCH_MESSAGE( eHTMLCommands_SetFocus );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: webkit is telling us to use this cursor type
+//-----------------------------------------------------------------------------
+void HTML::OnCommand( const char *pchCommand )
+{
+ if ( !Q_stricmp( pchCommand, "back" ) )
+ {
+ PostActionSignal( new KeyValues( "HTMLBackRequested" ) );
+ }
+ else if ( !Q_stricmp( pchCommand, "forward" ) )
+ {
+ PostActionSignal( new KeyValues( "HTMLForwardRequested" ) );
+ }
+ else if ( !Q_stricmp( pchCommand, "reload" ) )
+ {
+ Refresh();
+ }
+ else if ( !Q_stricmp( pchCommand, "stop" ) )
+ {
+ StopLoading();
+ }
+ else if ( !Q_stricmp( pchCommand, "viewsource" ) )
+ {
+ CHTMLProtoBufMsg<CMsgViewSource> cmd( eHTMLCommands_ViewSource );
+ DISPATCH_MESSAGE( eHTMLCommands_ViewSource );
+ }
+ else if ( !Q_stricmp( pchCommand, "copy" ) )
+ {
+ CHTMLProtoBufMsg<CMsgCopy> cmd( eHTMLCommands_Copy );
+ DISPATCH_MESSAGE( eHTMLCommands_Copy );
+ }
+ else if ( !Q_stricmp( pchCommand, "paste" ) )
+ {
+ CHTMLProtoBufMsg<CMsgPaste> cmd( eHTMLCommands_Paste );
+ DISPATCH_MESSAGE( eHTMLCommands_Paste );
+ }
+ else if ( !Q_stricmp( pchCommand, "copyurl" ) )
+ {
+ system()->SetClipboardText( m_sCurrentURL, m_sCurrentURL.Length() );
+ }
+ else if ( !Q_stricmp( pchCommand, "copylink" ) )
+ {
+ int x, y;
+ m_pContextMenu->GetPos( x, y );
+ int htmlx, htmly;
+ ipanel()->GetAbsPos( GetVPanel(), htmlx, htmly );
+
+ m_bRequestingCopyLink = true;
+ GetLinkAtPosition( x - htmlx, y - htmly );
+ }
+ else
+ BaseClass::OnCommand( pchCommand );
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: the control wants us to ask the user what file to load
+//-----------------------------------------------------------------------------
+void HTML::OnFileSelected( const char *pchSelectedFile )
+{
+ CHTMLProtoBufMsg<CMsgFileLoadDialogResponse> cmd( eHTMLCommands_FileLoadDialogResponse );
+ cmd.Body().add_files( pchSelectedFile );
+ DISPATCH_MESSAGE( eHTMLCommands_FileLoadDialogResponse );
+ m_hFileOpenDialog->Close();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the user dismissed the file dialog with no selection
+//-----------------------------------------------------------------------------
+void HTML::OnFileSelectionCancelled()
+{
+ CHTMLProtoBufMsg<CMsgFileLoadDialogResponse> cmd( eHTMLCommands_FileLoadDialogResponse );
+ DISPATCH_MESSAGE( eHTMLCommands_FileLoadDialogResponse );
+ m_hFileOpenDialog->Close();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: find any text on the html page with this sub string
+//-----------------------------------------------------------------------------
+void HTML::Find( const char *pchSubStr )
+{
+ m_bInFind = false;
+ if ( m_sLastSearchString == pchSubStr ) // same string as last time, lets fine next
+ m_bInFind = true;
+
+ m_sLastSearchString = pchSubStr;
+
+ CHTMLProtoBufMsg<CMsgFind> cmd( eHTMLCommands_Find );
+ cmd.Body().set_find( pchSubStr );
+ cmd.Body().set_infind( m_bInFind );
+ DISPATCH_MESSAGE( eHTMLCommands_Find );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: find any text on the html page with this sub string
+//-----------------------------------------------------------------------------
+void HTML::FindPrevious()
+{
+ CHTMLProtoBufMsg<CMsgFind> cmd( eHTMLCommands_Find );
+ cmd.Body().set_find( m_sLastSearchString );
+ cmd.Body().set_infind( m_bInFind );
+ cmd.Body().set_reverse( true );
+ DISPATCH_MESSAGE( eHTMLCommands_Find );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: find any text on the html page with this sub string
+//-----------------------------------------------------------------------------
+void HTML::FindNext()
+{
+ Find( m_sLastSearchString );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: stop an outstanding find request
+//-----------------------------------------------------------------------------
+void HTML::StopFind( )
+{
+ CHTMLProtoBufMsg<CMsgStopFind> cmd( eHTMLCommands_StopFind );
+ DISPATCH_MESSAGE( eHTMLCommands_StopFind );
+
+ m_bInFind = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: input handler
+//-----------------------------------------------------------------------------
+void HTML::OnEditNewLine( Panel *pPanel )
+{
+ OnTextChanged( pPanel );
+}
+
+
+//-----------------------h------------------------------------------------------
+// Purpose: input handler
+//-----------------------------------------------------------------------------
+void HTML::OnTextChanged( Panel *pPanel )
+{
+ char rgchText[2048];
+ m_pFindBar->GetText( rgchText, sizeof( rgchText ) );
+ Find( rgchText );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: passes mouse clicks to the control
+//-----------------------------------------------------------------------------
+void HTMLComboBoxHost::OnMousePressed(MouseCode code)
+{
+ m_pParent->OnMousePressed(code);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: passes mouse up events
+//-----------------------------------------------------------------------------
+void HTMLComboBoxHost::OnMouseReleased(MouseCode code)
+{
+ m_pParent->OnMouseReleased(code);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: keeps track of where the cursor is
+//-----------------------------------------------------------------------------
+void HTMLComboBoxHost::OnCursorMoved(int x,int y)
+{
+ // Only do this when we are over the current panel
+ if ( vgui::input()->GetMouseOver() == GetVPanel() )
+ {
+ int m_iBrowser = m_pParent->BrowserGetIndex();
+ CHTMLProtoBufMsg<CMsgMouseMove> cmd( eHTMLCommands_MouseMove );
+ cmd.Body().set_x( x );
+ cmd.Body().set_y( y );
+ DISPATCH_MESSAGE( eHTMLCommands_MouseMove );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: passes double click events to the browser
+//-----------------------------------------------------------------------------
+void HTMLComboBoxHost::OnMouseDoublePressed(MouseCode code)
+{
+ m_pParent->OnMouseDoublePressed(code);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: passes key presses to the browser (we don't current do this)
+//-----------------------------------------------------------------------------
+void HTMLComboBoxHost::OnKeyTyped(wchar_t unichar)
+{
+ m_pParent->OnKeyTyped(unichar);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: passes key presses to the browser
+//-----------------------------------------------------------------------------
+void HTMLComboBoxHost::OnKeyCodeTyped(KeyCode code)
+{
+ m_pParent->OnKeyCodeTyped(code);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void HTMLComboBoxHost::OnKeyCodeReleased(KeyCode code)
+{
+ m_pParent->OnKeyCodeReleased(code);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: scrolls the vertical scroll bar on a web page
+//-----------------------------------------------------------------------------
+void HTMLComboBoxHost::OnMouseWheeled(int delta)
+{
+ m_pParent->OnMouseWheeled( delta );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: helper class for the find bar
+//-----------------------------------------------------------------------------
+HTML::CHTMLFindBar::CHTMLFindBar( HTML *parent ) : EditablePanel( parent, "FindBar" )
+{
+ m_pParent = parent;
+ m_bHidden = false;
+ m_pFindBar = new TextEntry( this, "FindEntry" );
+ m_pFindBar->AddActionSignalTarget( parent );
+ m_pFindBar->SendNewLine( true );
+ m_pFindCountLabel = new Label( this, "FindCount", "" );
+ m_pFindCountLabel->SetVisible( false );
+ LoadControlSettings( "resource/layout/htmlfindbar.layout" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: button input into the find bar
+//-----------------------------------------------------------------------------
+void HTML::CHTMLFindBar::OnCommand( const char *pchCmd )
+{
+ if ( !Q_stricmp( pchCmd, "close" ) )
+ {
+ m_pParent->HideFindDialog();
+ }
+ else if ( !Q_stricmp( pchCmd, "previous" ) )
+ {
+ m_pParent->FindPrevious();
+ }
+ else if ( !Q_stricmp( pchCmd, "next" ) )
+ {
+ m_pParent->FindNext();
+ }
+ else
+ BaseClass::OnCommand( pchCmd );
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: TEMPORARY WORKAROUND FOR VS2005 INTERFACE ISSUES
+//-----------------------------------------------------------------------------
+#define TMP_HTML_MSG_FUNC( eHTMLCommand, bodyType, commandFunc ) \
+ case eHTMLCommand: \
+ { \
+ CHTMLProtoBufMsg< bodyType > cmd( pCmd->m_eCmd ); \
+ if ( cmd.BDeserializeCrossProc( &pCmd->m_Buffer ) ) \
+ commandFunc( &cmd.BodyConst() ); \
+ } \
+ break; \
+
+void HTML::_DeserializeAndDispatch( HTMLCommandBuffer_t *pCmd )
+{
+ switch ( pCmd->m_eCmd )
+ {
+ default:
+ break;
+ TMP_HTML_MSG_FUNC( eHTMLCommands_BrowserReady, CMsgBrowserReady, BrowserReady );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_NeedsPaint, CMsgNeedsPaint, BrowserNeedsPaint );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_StartRequest, CMsgStartRequest, BrowserStartRequest );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_URLChanged, CMsgURLChanged, BrowserURLChanged );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_FinishedRequest, CMsgFinishedRequest, BrowserFinishedRequest );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_ShowPopup, CMsgShowPopup, BrowserShowPopup );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_HidePopup, CMsgHidePopup, BrowserHidePopup );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_OpenNewTab, CMsgOpenNewTab, BrowserOpenNewTab );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_PopupHTMLWindow, CMsgPopupHTMLWindow, BrowserPopupHTMLWindow );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_SetHTMLTitle, CMsgSetHTMLTitle, BrowserSetHTMLTitle );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_LoadingResource, CMsgLoadingResource, BrowserLoadingResource );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_StatusText, CMsgStatusText, BrowserStatusText );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_SetCursor, CMsgSetCursor, BrowserSetCursor );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_FileLoadDialog, CMsgFileLoadDialog, BrowserFileLoadDialog );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_ShowToolTip, CMsgShowToolTip, BrowserShowToolTip );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_UpdateToolTip, CMsgUpdateToolTip, BrowserUpdateToolTip );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_HideToolTip, CMsgHideToolTip, BrowserHideToolTip );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_SearchResults, CMsgSearchResults, BrowserSearchResults );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_Close, CMsgClose, BrowserClose );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_GetZoomResponse, CMsgGetZoomResponse, BrowserGetZoomResponse );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_HorizontalScrollBarSizeResponse, CMsgHorizontalScrollBarSizeResponse, BrowserHorizontalScrollBarSizeResponse );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_VerticalScrollBarSizeResponse, CMsgVerticalScrollBarSizeResponse, BrowserVerticalScrollBarSizeResponse );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_LinkAtPositionResponse, CMsgLinkAtPositionResponse, BrowserLinkAtPositionResponse );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_ZoomToElementAtPositionResponse, CMsgZoomToElementAtPositionResponse, BrowserZoomToElementAtPositionResponse );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_JSAlert, CMsgJSAlert, BrowserJSAlert );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_JSConfirm, CMsgJSConfirm, BrowserJSConfirm );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_OpenSteamURL, CMsgOpenSteamURL, BrowserOpenSteamURL );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_CanGoBackandForward, CMsgCanGoBackAndForward, BrowserCanGoBackandForward );
+ TMP_HTML_MSG_FUNC( eHTMLCommands_SizePopup, CMsgSizePopup, BrowserSizePopup );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: browser has been constructed on the cef thread, lets use it
+//-----------------------------------------------------------------------------
+void HTML::BrowserReady( const CMsgBrowserReady *pCmd )
+{
+ const char *pchTitle = g_pVGuiLocalize->FindAsUTF8( "#cef_error_title" );
+ const char *pchHeader = g_pVGuiLocalize->FindAsUTF8( "#cef_error_header" );
+ const char *pchDetailCacheMiss = g_pVGuiLocalize->FindAsUTF8( "#cef_cachemiss" );
+ const char *pchDetailBadUURL = g_pVGuiLocalize->FindAsUTF8( "#cef_badurl" );
+ const char *pchDetailConnectionProblem = g_pVGuiLocalize->FindAsUTF8( "#cef_connectionproblem" );
+ const char *pchDetailProxyProblem = g_pVGuiLocalize->FindAsUTF8( "#cef_proxyconnectionproblem" );
+ const char *pchDetailUnknown = g_pVGuiLocalize->FindAsUTF8( "#cef_unknown" );
+
+ // tell it utf8 loc strings to use
+ CHTMLProtoBufMsg<CMsgBrowserErrorStrings> cmd( eHTMLCommands_BrowserErrorStrings );
+ cmd.Body().set_title( pchTitle );
+ cmd.Body().set_header( pchHeader );
+ cmd.Body().set_cache_miss( pchDetailCacheMiss );
+ cmd.Body().set_bad_url( pchDetailBadUURL );
+ cmd.Body().set_connection_problem( pchDetailConnectionProblem );
+ cmd.Body().set_proxy_problem( pchDetailProxyProblem );
+ cmd.Body().set_unknown( pchDetailUnknown );
+ DISPATCH_MESSAGE( eHTMLCommands_BrowserErrorStrings );
+
+ if ( !m_sPendingURLLoad.IsEmpty() )
+ {
+ PostURL( m_sPendingURLLoad, m_sPendingPostData, false );
+ m_sPendingURLLoad.Clear();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: we have a new texture to update
+//-----------------------------------------------------------------------------
+void HTML::BrowserNeedsPaint( const CMsgNeedsPaint *pCmd )
+{
+ int tw = 0, tt = 0;
+ if ( m_iHTMLTextureID != 0 )
+ {
+ tw = m_allocedTextureWidth;
+ tt = m_allocedTextureHeight;
+ }
+
+ if ( m_iHTMLTextureID != 0 && ( ( _vbar->IsVisible() && pCmd->scrolly() > 0 && abs( (int)pCmd->scrolly() - m_scrollVertical.m_nScroll) > 5 ) || ( _hbar->IsVisible() && pCmd->scrollx() > 0 && abs( (int)pCmd->scrollx() - m_scrollHorizontal.m_nScroll ) > 5 ) ) )
+ {
+ // this isn't an update from a scroll position we expect, ignore it and ask for a refresh of our update pos2
+ CHTMLProtoBufMsg<CMsgNeedsPaintResponse> cmd( eHTMLCommands_NeedsPaintResponse );
+ cmd.Body().set_textureid( pCmd->textureid() );
+ DISPATCH_MESSAGE( eHTMLCommands_NeedsPaintResponse );
+ m_bNeedsFullTextureUpload = true;
+ return;
+ }
+
+ // update the vgui texture
+ if ( m_bNeedsFullTextureUpload || m_iHTMLTextureID == 0 || tw != (int)pCmd->wide() || tt != (int)pCmd->tall() )
+ {
+ m_bNeedsFullTextureUpload = false;
+ if ( m_iHTMLTextureID != 0 )
+ surface()->DeleteTextureByID( m_iHTMLTextureID );
+
+ // if the dimensions changed we also need to re-create the texture ID to support the overlay properly (it won't resize a texture on the fly, this is the only control that needs
+ // to so lets have a tiny bit more code here to support that)
+ m_iHTMLTextureID = surface()->CreateNewTextureID( true );
+ surface()->DrawSetTextureRGBAEx( m_iHTMLTextureID, (const unsigned char *)pCmd->rgba(), pCmd->wide(), pCmd->tall(), IMAGE_FORMAT_BGRA8888 );// BR FIXME - this call seems to shift by some number of pixels?
+ m_allocedTextureWidth = pCmd->wide();
+ m_allocedTextureHeight = pCmd->tall();
+ }
+ else if ( (int)pCmd->updatewide() > 0 && (int)pCmd->updatetall() > 0 )
+ {
+ // same size texture, just bits changing in it, lets twiddle
+ surface()->DrawUpdateRegionTextureRGBA( m_iHTMLTextureID, pCmd->updatex(), pCmd->updatey(), (const unsigned char *)pCmd->rgba(), pCmd->updatewide(), pCmd->updatetall(), IMAGE_FORMAT_BGRA8888 );
+ }
+ else
+ {
+ surface()->DrawSetTextureRGBAEx( m_iHTMLTextureID, (const unsigned char *)pCmd->rgba(), pCmd->wide(), pCmd->tall(), IMAGE_FORMAT_BGRA8888 );
+ }
+
+ if ( m_pComboBoxHost->IsVisible() )
+ {
+ // update the combo box texture also
+ if ( m_iComboBoxTextureID != 0 )
+ {
+ tw = m_allocedComboBoxWidth;
+ tt = m_allocedComboBoxHeight;
+ }
+
+ if ( m_iComboBoxTextureID == 0 || tw != (int)pCmd->combobox_wide() || tt != (int)pCmd->combobox_tall() )
+ {
+ if ( m_iComboBoxTextureID != 0 )
+ surface()->DeleteTextureByID( m_iComboBoxTextureID );
+
+ // if the dimensions changed we also need to re-create the texture ID to support the overlay properly (it won't resize a texture on the fly, this is the only control that needs
+ // to so lets have a tiny bit more code here to support that)
+ m_iComboBoxTextureID = surface()->CreateNewTextureID( true );
+ surface()->DrawSetTextureRGBAEx( m_iComboBoxTextureID, (const unsigned char *)pCmd->combobox_rgba(), pCmd->combobox_wide(), pCmd->combobox_tall(), IMAGE_FORMAT_BGRA8888 );
+ m_allocedComboBoxWidth = (int)pCmd->combobox_wide();
+ m_allocedComboBoxHeight = (int)pCmd->combobox_tall();
+ }
+ else
+ {
+ // same size texture, just bits changing in it, lets twiddle
+ surface()->DrawUpdateRegionTextureRGBA( m_iComboBoxTextureID, 0, 0, (const unsigned char *)pCmd->combobox_rgba(), pCmd->combobox_wide(), pCmd->combobox_tall(), IMAGE_FORMAT_BGRA8888 );
+ }
+ }
+
+ // need a paint next time
+ Repaint();
+
+ CHTMLProtoBufMsg<CMsgNeedsPaintResponse> cmd( eHTMLCommands_NeedsPaintResponse );
+ cmd.Body().set_textureid( pCmd->textureid() );
+ DISPATCH_MESSAGE( eHTMLCommands_NeedsPaintResponse );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser wants to start loading this url, do we let it?
+//-----------------------------------------------------------------------------
+bool HTML::OnStartRequest( const char *url, const char *target, const char *pchPostData, bool bIsRedirect )
+{
+ if ( !url || !Q_stricmp( url, "about:blank") )
+ return true ; // this is just webkit loading a new frames contents inside an existing page
+
+ HideFindDialog();
+ // see if we have a custom handler for this
+ bool bURLHandled = false;
+ for (int i = 0; i < m_CustomURLHandlers.Count(); i++)
+ {
+ if (!Q_strnicmp(m_CustomURLHandlers[i].url,url, Q_strlen(m_CustomURLHandlers[i].url)))
+ {
+ // we have a custom handler
+ Panel *targetPanel = m_CustomURLHandlers[i].hPanel;
+ if (targetPanel)
+ {
+ PostMessage(targetPanel, new KeyValues("CustomURL", "url", m_CustomURLHandlers[i].url ) );
+ }
+
+ bURLHandled = true;
+ }
+ }
+
+ if (bURLHandled)
+ return false;
+
+ if ( m_bNewWindowsOnly && bIsRedirect )
+ {
+ if ( target && ( !Q_stricmp( target, "_blank" ) || !Q_stricmp( target, "_new" ) ) ) // only allow NEW windows (_blank ones)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if ( target && !Q_strlen( target ) )
+ {
+ m_sCurrentURL = url;
+
+ KeyValues *pMessage = new KeyValues( "OnURLChanged" );
+ pMessage->SetString( "url", url );
+ pMessage->SetString( "postdata", pchPostData );
+ pMessage->SetInt( "isredirect", bIsRedirect ? 1 : 0 );
+
+ PostActionSignal( pMessage );
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: callback from cef thread, load a url please
+//-----------------------------------------------------------------------------
+void HTML::BrowserStartRequest( const CMsgStartRequest *pCmd )
+{
+ bool bRes = OnStartRequest( pCmd->url().c_str(), pCmd->target().c_str(), pCmd->postdata().c_str(), pCmd->bisredirect() );
+
+ CHTMLProtoBufMsg<CMsgStartRequestResponse> cmd( eHTMLCommands_StartRequestResponse );
+ cmd.Body().set_ballow( bRes );
+ DISPATCH_MESSAGE( eHTMLCommands_StartRequestResponse );
+
+ UpdateCachedHTMLValues();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser went to a new url
+//-----------------------------------------------------------------------------
+void HTML::BrowserURLChanged( const CMsgURLChanged *pCmd )
+{
+ m_sCurrentURL = pCmd->url().c_str();
+
+ KeyValues *pMessage = new KeyValues( "OnURLChanged" );
+ pMessage->SetString( "url", pCmd->url().c_str() );
+ pMessage->SetString( "postdata", pCmd->postdata().c_str() );
+ pMessage->SetInt( "isredirect", pCmd->bisredirect() ? 1 : 0 );
+
+ PostActionSignal( pMessage );
+
+ OnURLChanged( m_sCurrentURL, pCmd->postdata().c_str(), pCmd->bisredirect() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: finished loading this page
+//-----------------------------------------------------------------------------
+void HTML::BrowserFinishedRequest( const CMsgFinishedRequest *pCmd )
+{
+ PostActionSignal( new KeyValues( "OnFinishRequest", "url", pCmd->url().c_str() ) );
+ if ( pCmd->pagetitle().length() )
+ PostActionSignal( new KeyValues( "PageTitleChange", "title", pCmd->pagetitle().c_str() ) );
+ KeyValues *pKVSecure = new KeyValues( "SecurityStatus" );
+ pKVSecure->SetString( "url", pCmd->url().c_str() );
+ pKVSecure->SetInt( "secure", pCmd->security_info().bissecure() );
+ pKVSecure->SetInt( "certerror", pCmd->security_info().bhascerterror() );
+ pKVSecure->SetInt( "isevcert", pCmd->security_info().bisevcert() );
+ pKVSecure->SetString( "certname", pCmd->security_info().certname().c_str() );
+ PostActionSignal( pKVSecure );
+
+ CUtlMap < CUtlString, CUtlString > mapHeaders;
+ SetDefLessFunc( mapHeaders );
+ for ( int i = 0; i < pCmd->headers_size(); i++ )
+ {
+ const CHTMLHeader &header = pCmd->headers(i);
+ mapHeaders.Insert( header.key().c_str(), header.value().c_str() );
+ }
+
+ OnFinishRequest( pCmd->url().c_str(), pCmd->pagetitle().c_str(), mapHeaders );
+
+ UpdateCachedHTMLValues();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: show a popup dialog
+//-----------------------------------------------------------------------------
+void HTML::BrowserShowPopup( const CMsgShowPopup *pCmd )
+{
+ m_pComboBoxHost->SetVisible( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: hide the popup
+//-----------------------------------------------------------------------------
+void HTML::HidePopup()
+{
+ m_pComboBoxHost->SetVisible( false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser wants us to hide a popup
+//-----------------------------------------------------------------------------
+void HTML::BrowserHidePopup( const CMsgHidePopup *pCmd )
+{
+ HidePopup();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser wants us to position a popup
+//-----------------------------------------------------------------------------
+void HTML::BrowserSizePopup( const CMsgSizePopup *pCmd )
+{
+ int nAbsX, nAbsY;
+ ipanel()->GetAbsPos( GetVPanel(), nAbsX, nAbsY );
+ m_pComboBoxHost->SetBounds( pCmd->x() + 1 + nAbsX, pCmd->y() + nAbsY, pCmd->wide(), pCmd->tall() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser wants to open a new tab
+//-----------------------------------------------------------------------------
+void HTML::BrowserOpenNewTab( const CMsgOpenNewTab *pCmd )
+{
+ (pCmd);
+ // Not suppored by default, if a child class overrides us and knows how to handle tabs, then it can do this.
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: display a new html window
+//-----------------------------------------------------------------------------
+void HTML::BrowserPopupHTMLWindow( const CMsgPopupHTMLWindow *pCmd )
+{
+ HTMLPopup *p = new HTMLPopup( this, pCmd->url().c_str(), "" );
+ int wide = pCmd->wide();
+ int tall = pCmd->tall();
+ if ( wide == 0 || tall == 0 )
+ {
+ wide = MAX( 640, GetWide() );
+ tall = MAX( 480, GetTall() );
+ }
+
+ p->SetBounds( pCmd->x(), pCmd->y(), wide, tall );
+ p->SetDeleteSelfOnClose( true );
+ if ( pCmd->x() == 0 || pCmd->y() == 0 )
+ p->MoveToCenterOfScreen();
+ p->Activate();
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us the page title
+//-----------------------------------------------------------------------------
+void HTML::BrowserSetHTMLTitle( const CMsgSetHTMLTitle *pCmd )
+{
+ PostMessage( GetParent(), new KeyValues( "OnSetHTMLTitle", "title", pCmd->title().c_str() ) );
+ OnSetHTMLTitle( pCmd->title().c_str() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: still loading stuff for this page
+//-----------------------------------------------------------------------------
+void HTML::BrowserLoadingResource( const CMsgLoadingResource *pCmd )
+{
+ UpdateCachedHTMLValues();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: status bar details
+//-----------------------------------------------------------------------------
+void HTML::BrowserStatusText( const CMsgStatusText *pCmd )
+{
+ PostActionSignal( new KeyValues( "OnSetStatusText", "status", pCmd->text().c_str() ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us to use this cursor
+//-----------------------------------------------------------------------------
+void HTML::BrowserSetCursor( const CMsgSetCursor *pCmd )
+{
+ // Mouse cursor value in CMsgSetCursor is set to one of EMouseCursor,
+ // by CChromePainter::OnSetCursor in html_chrome.cpp
+ // Code below relies on start of EMouseCursor being exactly same as vgui::CursorCode
+
+ vgui::CursorCode cursor;
+ uint32 msgCursor = pCmd->cursor();
+
+ if ( msgCursor >= (uint32)(dc_last) )
+ {
+ cursor = dc_arrow;
+ }
+ else
+ {
+ cursor = (CursorCode)msgCursor;
+ }
+
+ SetCursor( cursor );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling to show the file loading dialog
+//-----------------------------------------------------------------------------
+void HTML::BrowserFileLoadDialog( const CMsgFileLoadDialog *pCmd )
+{
+ /*
+ // try and use the OS-specific file dialog first
+ char rgchFileName[MAX_UNICODE_PATH_IN_UTF8];
+ if ( surface()->OpenOSFileOpenDialog( pCmd->title().c_str(), pCmd->initialfile().c_str(), NULL, rgchFileName, sizeof(rgchFileName) ) )
+ {
+ CHTMLProtoBufMsg<CMsgFileLoadDialogResponse> cmd( eHTMLCommands_FileLoadDialogResponse );
+ cmd.Body().add_files( rgchFileName );
+ DISPATCH_MESSAGE( eHTMLCommands_FileLoadDialogResponse );
+ }
+ else*/
+ {
+ // couldn't access an OS-specific dialog, use the internal one
+ if ( m_hFileOpenDialog.Get() )
+ {
+ delete m_hFileOpenDialog.Get();
+ m_hFileOpenDialog = NULL;
+ }
+ m_hFileOpenDialog = new FileOpenDialog( this, pCmd->title().c_str(), true );
+ m_hFileOpenDialog->SetStartDirectory( pCmd->initialfile().c_str() );
+ m_hFileOpenDialog->AddActionSignalTarget( this );
+ m_hFileOpenDialog->SetAutoDelete( true );
+ m_hFileOpenDialog->DoModal(false);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser asking to show a tooltip
+//-----------------------------------------------------------------------------
+void HTML::BrowserShowToolTip( const CMsgShowToolTip *pCmd )
+{
+ /*
+ BR FIXME
+ Tooltip *tip = GetTooltip();
+ tip->SetText( pCmd->text().c_str() );
+ tip->SetTooltipFormatToMultiLine();
+ tip->SetTooltipDelayMS( 250 );
+ tip->SetMaxToolTipWidth( MAX( 200, GetWide()/2 ) );
+ tip->ShowTooltip( this );
+ */
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us to update tool tip text
+//-----------------------------------------------------------------------------
+void HTML::BrowserUpdateToolTip( const CMsgUpdateToolTip *pCmd )
+{
+// GetTooltip()->SetText( pCmd->text().c_str() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling that it is done with the tip
+//-----------------------------------------------------------------------------
+void HTML::BrowserHideToolTip( const CMsgHideToolTip *pCmd )
+{
+// GetTooltip()->HideTooltip();
+// DeleteToolTip();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: callback when performing a search
+//-----------------------------------------------------------------------------
+void HTML::BrowserSearchResults( const CMsgSearchResults *pCmd )
+{
+ if ( pCmd->results() == 0 )
+ m_pFindBar->HideCountLabel();
+ else
+ m_pFindBar->ShowCountLabel();
+
+ if ( pCmd->results() > 0 )
+ m_pFindBar->SetDialogVariable( "findcount", (int)pCmd->results() );
+ if ( pCmd->activematch() > 0 )
+ m_pFindBar->SetDialogVariable( "findactive", (int)pCmd->activematch() );
+ m_pFindBar->InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us it had a close requested
+//-----------------------------------------------------------------------------
+void HTML::BrowserClose( const CMsgClose *pCmd )
+{
+ PostActionSignal( new KeyValues( "OnCloseWindow" ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us the size of the horizontal scrollbars
+//-----------------------------------------------------------------------------
+void HTML::BrowserHorizontalScrollBarSizeResponse( const CMsgHorizontalScrollBarSizeResponse *pCmd )
+{
+ ScrollData_t scrollHorizontal;
+ scrollHorizontal.m_nX = pCmd->x();
+ scrollHorizontal.m_nY = pCmd->y();
+ scrollHorizontal.m_nWide = pCmd->wide();
+ scrollHorizontal.m_nTall = pCmd->tall();
+ scrollHorizontal.m_nScroll = pCmd->scroll();
+ scrollHorizontal.m_nMax = pCmd->scroll_max();
+ scrollHorizontal.m_bVisible = ( m_scrollHorizontal.m_nTall > 0 );
+ scrollHorizontal.m_flZoom = pCmd->zoom();
+
+ if ( scrollHorizontal != m_scrollHorizontal )
+ {
+ m_scrollHorizontal = scrollHorizontal;
+ UpdateSizeAndScrollBars();
+ m_bNeedsFullTextureUpload = true;
+ }
+ else
+ m_scrollHorizontal = scrollHorizontal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us the size of the vertical scrollbars
+//-----------------------------------------------------------------------------
+void HTML::BrowserVerticalScrollBarSizeResponse( const CMsgVerticalScrollBarSizeResponse *pCmd )
+{
+ ScrollData_t scrollVertical;
+ scrollVertical.m_nX = pCmd->x();
+ scrollVertical.m_nY = pCmd->y();
+ scrollVertical.m_nWide = pCmd->wide();
+ scrollVertical.m_nTall = pCmd->tall();
+ scrollVertical.m_nScroll = pCmd->scroll();
+ scrollVertical.m_nMax = pCmd->scroll_max();
+ scrollVertical.m_bVisible = ( m_scrollVertical.m_nTall > 0 );
+ scrollVertical.m_flZoom = pCmd->zoom();
+
+ if ( scrollVertical != m_scrollVertical )
+ {
+ m_scrollVertical = scrollVertical;
+ UpdateSizeAndScrollBars();
+ m_bNeedsFullTextureUpload = true;
+ }
+ else
+ m_scrollVertical = scrollVertical;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us the current page zoom
+//-----------------------------------------------------------------------------
+void HTML::BrowserGetZoomResponse( const CMsgGetZoomResponse *pCmd )
+{
+ m_flZoom = pCmd->zoom();
+ if ( m_flZoom == 0.0f )
+ m_flZoom = 100.0f;
+ m_flZoom /= 100; // scale zoom to 1.0 being 100%, webkit gives us 100 for normal scale
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us what is at this location on the page
+//-----------------------------------------------------------------------------
+void HTML::BrowserLinkAtPositionResponse( const CMsgLinkAtPositionResponse *pCmd )
+{
+ m_LinkAtPos.m_sURL = pCmd->url().c_str();
+ m_LinkAtPos.m_nX = pCmd->x();
+ m_LinkAtPos.m_nY = pCmd->y();
+
+ m_pContextMenu->SetItemVisible( m_iCopyLinkMenuItemID, !m_LinkAtPos.m_sURL.IsEmpty() ? true : false );
+ if ( m_bRequestingDragURL )
+ {
+ m_bRequestingDragURL = false;
+ m_sDragURL = m_LinkAtPos.m_sURL;
+ // make sure we get notified when the mouse gets released
+ if ( !m_sDragURL.IsEmpty() )
+ {
+ input()->SetMouseCapture( GetVPanel() );
+ }
+ }
+
+ if ( m_bRequestingCopyLink )
+ {
+ m_bRequestingCopyLink = false;
+ if ( !m_LinkAtPos.m_sURL.IsEmpty() )
+ system()->SetClipboardText( m_LinkAtPos.m_sURL, m_LinkAtPos.m_sURL.Length() );
+ else
+ system()->SetClipboardText( "", 1 );
+ }
+
+ OnLinkAtPosition( m_LinkAtPos.m_sURL );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us that a zoom to element is done
+//-----------------------------------------------------------------------------
+void HTML::BrowserZoomToElementAtPositionResponse( const CMsgZoomToElementAtPositionResponse *pCmd )
+{
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us to pop a javascript alert dialog
+//-----------------------------------------------------------------------------
+void HTML::BrowserJSAlert( const CMsgJSAlert *pCmd )
+{
+ MessageBox *pDlg = new MessageBox( m_sCurrentURL, (const char *)pCmd->message().c_str(), this );
+ pDlg->AddActionSignalTarget( this );
+ pDlg->SetCommand( new KeyValues( "DismissJSDialog", "result", false ) );
+ pDlg->DoModal();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us to pop a js confirm dialog
+//-----------------------------------------------------------------------------
+void HTML::BrowserJSConfirm( const CMsgJSConfirm *pCmd )
+{
+ QueryBox *pDlg = new QueryBox( m_sCurrentURL, (const char *)pCmd->message().c_str(), this );
+ pDlg->AddActionSignalTarget( this );
+ pDlg->SetOKCommand( new KeyValues( "DismissJSDialog", "result", true ) );
+ pDlg->SetCancelCommand( new KeyValues( "DismissJSDialog", "result", false ) );
+ pDlg->DoModal();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: got an answer from the dialog, tell cef
+//-----------------------------------------------------------------------------
+void HTML::DismissJSDialog( int bResult )
+{
+ CHTMLProtoBufMsg<CMsgJSDialogResponse> cmd( eHTMLCommands_JSDialogResponse );
+ cmd.Body().set_result( bResult==1 );
+ DISPATCH_MESSAGE( eHTMLCommands_JSDialogResponse );
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us the state of back and forward buttons
+//-----------------------------------------------------------------------------
+void HTML::BrowserCanGoBackandForward( const CMsgCanGoBackAndForward *pCmd )
+{
+ m_bCanGoBack = pCmd->bgoback();
+ m_bCanGoForward = pCmd->bgoforward();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: browser telling us a steam url was asked to be loaded
+//-----------------------------------------------------------------------------
+void HTML::BrowserOpenSteamURL( const CMsgOpenSteamURL *pCmd )
+{
+ vgui::ivgui()->PostMessage( surface()->GetEmbeddedPanel(),
+ new KeyValues( "OpenSteamDialog", "cmd", pCmd->url().c_str() ), NULL, 0.3f );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: update the value of the cached variables we keep
+//-----------------------------------------------------------------------------
+void HTML::UpdateCachedHTMLValues()
+{
+ // request scroll bar sizes
+ {
+ CHTMLProtoBufMsg<CMsgVerticalScrollBarSize> cmd( eHTMLCommands_VerticalScrollBarSize );
+ DISPATCH_MESSAGE( eHTMLCommands_VerticalScrollBarSize );
+ }
+ {
+ CHTMLProtoBufMsg<CMsgHorizontalScrollBarSize> cmd( eHTMLCommands_HorizontalScrollBarSize );
+ DISPATCH_MESSAGE( eHTMLCommands_HorizontalScrollBarSize );
+ }
+ {
+ CHTMLProtoBufMsg<CMsgGetZoom> cmd( eHTMLCommands_GetZoom );
+ DISPATCH_MESSAGE( eHTMLCommands_GetZoom );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: ask the browser for what is at this x,y
+//-----------------------------------------------------------------------------
+void HTML::GetLinkAtPosition( int x, int y )
+{
+ CHTMLProtoBufMsg<CMsgLinkAtPosition> cmd( eHTMLCommands_LinkAtPosition );
+ cmd.Body().set_x( x );
+ cmd.Body().set_y( y );
+ DISPATCH_MESSAGE( eHTMLCommands_LinkAtPosition );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: send any queued html messages we have
+//-----------------------------------------------------------------------------
+void HTML::SendPendingHTMLMessages()
+{
+ FOR_EACH_VEC( m_vecPendingMessages, i )
+ {
+ m_vecPendingMessages[i]->m_iBrowser = m_iBrowser;
+ surface()->AccessChromeHTMLController()->PushCommand( m_vecPendingMessages[i] );
+ surface()->AccessChromeHTMLController()->WakeThread();
+ }
+ m_vecPendingMessages.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: update the size of the browser itself and scrollbars it shows
+//-----------------------------------------------------------------------------
+void HTML::UpdateSizeAndScrollBars()
+{
+ // Tell IE
+ BrowserResize();
+
+ // Do this after we tell IE!
+ int w,h;
+ GetSize( w, h );
+ CalcScrollBars(w,h);
+
+ InvalidateLayout();
+}
+
+
diff --git a/mp/src/vgui2/vgui_controls/Image.cpp b/mp/src/vgui2/vgui_controls/Image.cpp
new file mode 100644
index 00000000..b7ab8564
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/Image.cpp
@@ -0,0 +1,282 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <Color.h>
+#include <vgui/IPanel.h>
+#include <vgui/ISurface.h>
+
+#include <vgui_controls/Image.h>
+#include <vgui_controls/Controls.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Conctructor. Start with default position and default color.
+//-----------------------------------------------------------------------------
+Image::Image()
+{
+ SetPos(0,0);
+ SetSize(0,0);
+ SetColor(Color(255,255,255,255));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+Image::~Image()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the position of the image, you need to reset this every time you
+// call Paint()
+//-----------------------------------------------------------------------------
+void Image::SetPos(int x,int y)
+{
+ _pos[0]=x;
+ _pos[1]=y;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the position of the image
+//-----------------------------------------------------------------------------
+void Image::GetPos(int& x,int& y)
+{
+ x=_pos[0];
+ y=_pos[1];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the size of the image
+//-----------------------------------------------------------------------------
+void Image::GetSize(int &wide, int &tall)
+{
+ wide = _size[0];
+ tall = _size[1];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the size of the image contents (by default the set size)
+//-----------------------------------------------------------------------------
+void Image::GetContentSize(int &wide, int &tall)
+{
+ GetSize(wide, tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the size of the image
+//-----------------------------------------------------------------------------
+void Image::SetSize(int wide, int tall)
+{
+ _size[0]=wide;
+ _size[1]=tall;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the draw color using a Color struct.
+//-----------------------------------------------------------------------------
+void Image::DrawSetColor(Color col)
+{
+ surface()->DrawSetColor(col[0], col[1], col[2], col[3]);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the draw color using RGBA ints
+//-----------------------------------------------------------------------------
+void Image::DrawSetColor(int r,int g,int b,int a)
+{
+ surface()->DrawSetColor(r,g,b,a);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw a filled rectangle
+//-----------------------------------------------------------------------------
+void Image::DrawFilledRect(int x0,int y0,int x1,int y1)
+{
+ x0+=_pos[0];
+ y0+=_pos[1];
+ x1+=_pos[0];
+ y1+=_pos[1];
+ surface()->DrawFilledRect(x0,y0,x1,y1);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw an outlined rectangle
+//-----------------------------------------------------------------------------
+void Image::DrawOutlinedRect(int x0,int y0,int x1,int y1)
+{
+ x0+=_pos[0];
+ y0+=_pos[1];
+ x1+=_pos[0];
+ y1+=_pos[1];
+ surface()->DrawOutlinedRect(x0,y0,x1,y1);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw a line between two points
+//-----------------------------------------------------------------------------
+void Image::DrawLine(int x0,int y0,int x1,int y1)
+{
+ x0+=_pos[0];
+ y0+=_pos[1];
+ x1+=_pos[0];
+ y1+=_pos[1];
+ surface()->DrawLine(x0,y0,x1,y1);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw a line between a list of 'numPoints' points
+//-----------------------------------------------------------------------------
+void Image::DrawPolyLine(int *px, int *py, int numPoints)
+{
+ // update the positions to be relative to this panel
+ for(int i=0;i<numPoints;i++)
+ {
+ px[i] += _pos[0];
+ py[i] += _pos[1];
+ }
+
+ surface()->DrawPolyLine(px, py, numPoints);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the font
+//-----------------------------------------------------------------------------
+void Image::DrawSetTextFont(HFont font)
+{
+ surface()->DrawSetTextFont(font);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the text color using a color struct
+//-----------------------------------------------------------------------------
+void Image::DrawSetTextColor(Color sc)
+{
+ surface()->DrawSetTextColor(sc[0], sc[1], sc[2], sc[3]);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the text color useing RGBA ints
+//-----------------------------------------------------------------------------
+void Image::DrawSetTextColor(int r,int g,int b,int a)
+{
+ surface()->DrawSetTextColor(r,g,b,a);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the text position
+//-----------------------------------------------------------------------------
+void Image::DrawSetTextPos(int x,int y)
+{
+ x+=_pos[0];
+ y+=_pos[1];
+ surface()->DrawSetTextPos(x,y);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw a text string
+//-----------------------------------------------------------------------------
+void Image::DrawPrintText(const wchar_t *str,int strlen)
+{
+ surface()->DrawPrintText(str, strlen);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw a text string at the given coords.
+//-----------------------------------------------------------------------------
+void Image::DrawPrintText(int x, int y, const wchar_t *str, int strlen)
+{
+ x += _pos[0];
+ y += _pos[1];
+
+ surface()->DrawSetTextPos(x, y);
+ surface()->DrawPrintText(str, strlen);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw a character
+//-----------------------------------------------------------------------------
+void Image::DrawPrintChar(wchar_t ch)
+{
+ surface()->DrawUnicodeChar(ch);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw a character at the given coords
+//-----------------------------------------------------------------------------
+void Image::DrawPrintChar(int x, int y, wchar_t ch)
+{
+ x+=_pos[0];
+ y+=_pos[1];
+
+ surface()->DrawSetTextPos(x, y);
+ surface()->DrawUnicodeChar(ch);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set a texture
+//-----------------------------------------------------------------------------
+void Image::DrawSetTexture(int id)
+{
+ surface()->DrawSetTexture(id);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw a rectangle filled with the current texture
+//-----------------------------------------------------------------------------
+void Image::DrawTexturedRect(int x0,int y0,int x1,int y1)
+{
+ surface()->DrawTexturedRect(x0,y0,x1,y1);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Paint the contents of the image on screen.
+// You must call this explicitly each frame.
+//-----------------------------------------------------------------------------
+void Image::Paint()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the current color using a color struct
+//-----------------------------------------------------------------------------
+void Image::SetColor(Color color)
+{
+ _color=color;
+ DrawSetTextColor(color); // now update the device context underneath us :)
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the current color as a color struct
+//-----------------------------------------------------------------------------
+Color Image::GetColor()
+{
+ return _color;
+}
+
+bool Image::Evict()
+{
+ return false;
+}
+
+int Image::GetNumFrames()
+{
+ return 0;
+}
+
+void Image::SetFrame( int nFrame )
+{
+}
+
+HTexture Image::GetID()
+{
+ return 0;
+}
+
diff --git a/mp/src/vgui2/vgui_controls/ImageList.cpp b/mp/src/vgui2/vgui_controls/ImageList.cpp
new file mode 100644
index 00000000..3778478b
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ImageList.cpp
@@ -0,0 +1,106 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <vgui/VGUI.h>
+#include <Color.h>
+
+#include <vgui_controls/ImageList.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: blank image, intentially draws nothing
+//-----------------------------------------------------------------------------
+class BlankImage : public IImage
+{
+public:
+ virtual void Paint() {}
+ virtual void SetPos(int x, int y) {}
+ virtual void GetContentSize(int &wide, int &tall) { wide = 0; tall = 0; }
+ virtual void GetSize(int &wide, int &tall) { wide = 0; tall = 0; }
+ virtual void SetSize(int wide, int tall) {}
+ virtual void SetColor(Color col) {}
+ virtual bool Evict() { return false; }
+ virtual int GetNumFrames() { return 0; }
+ virtual void SetFrame( int nFrame ) {}
+ virtual HTexture GetID() { return 0; }
+ virtual void SetRotation( int iRotation ) { return; };
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+ImageList::ImageList(bool deleteImagesWhenDone)
+{
+ m_bDeleteImagesWhenDone = deleteImagesWhenDone;
+ AddImage(new BlankImage());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+ImageList::~ImageList()
+{
+ if (m_bDeleteImagesWhenDone)
+ {
+ // delete all the images, except for the first image (which is always the blank image)
+ for (int i = 1; i < m_Images.Count(); i++)
+ {
+ delete m_Images[i];
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds a new image to the list, returning the index it was placed at
+//-----------------------------------------------------------------------------
+int ImageList::AddImage(vgui::IImage *image)
+{
+ return m_Images.AddToTail(image);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets an image at a specified index, growing and adding NULL images if necessary
+//-----------------------------------------------------------------------------
+void ImageList::SetImageAtIndex(int index, vgui::IImage *image)
+{
+ // allocate more images if necessary
+ while (m_Images.Count() <= index)
+ {
+ m_Images.AddToTail(NULL);
+ }
+
+ m_Images[index] = image;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the number of images
+//-----------------------------------------------------------------------------
+int ImageList::GetImageCount()
+{
+ return m_Images.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets an image, imageIndex is of range [0, GetImageCount)
+//-----------------------------------------------------------------------------
+vgui::IImage *ImageList::GetImage(int imageIndex)
+{
+ return m_Images[imageIndex];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if an index is valid
+//-----------------------------------------------------------------------------
+bool ImageList::IsValidIndex(int imageIndex)
+{
+ return m_Images.IsValidIndex(imageIndex);
+}
+
diff --git a/mp/src/vgui2/vgui_controls/ImagePanel.cpp b/mp/src/vgui2/vgui_controls/ImagePanel.cpp
new file mode 100644
index 00000000..bce00908
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ImagePanel.cpp
@@ -0,0 +1,463 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdio.h>
+
+#include <vgui/IBorder.h>
+#include <vgui/ISurface.h>
+#include <vgui/IScheme.h>
+#include <vgui/IBorder.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/ImagePanel.h>
+#include <vgui_controls/Image.h>
+#include <vgui_controls/Controls.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( ImagePanel );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ImagePanel::ImagePanel(Panel *parent, const char *name) : Panel(parent, name)
+{
+ m_pImage = NULL;
+ m_pszImageName = NULL;
+ m_pszFillColorName = NULL;
+ m_pszDrawColorName = NULL; // HPE addition
+ m_bCenterImage = false;
+ m_bScaleImage = false;
+ m_bTileImage = false;
+ m_bTileHorizontally = false;
+ m_bTileVertically = false;
+ m_fScaleAmount = 0.0f;
+ m_FillColor = Color(0, 0, 0, 0);
+ m_DrawColor = Color(255,255,255,255);
+ m_iRotation = ROTATED_UNROTATED;
+
+ SetImage( m_pImage );
+
+ REGISTER_COLOR_AS_OVERRIDABLE( m_FillColor, "fillcolor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( m_DrawColor, "drawcolor_override" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ImagePanel::~ImagePanel()
+{
+ delete [] m_pszImageName;
+ delete [] m_pszFillColorName;
+ delete [] m_pszDrawColorName; // HPE addition
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: handles size changing
+//-----------------------------------------------------------------------------
+void ImagePanel::OnSizeChanged(int newWide, int newTall)
+{
+ BaseClass::OnSizeChanged(newWide, newTall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ImagePanel::SetImage(IImage *image)
+{
+ m_pImage = image;
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets an image by file name
+//-----------------------------------------------------------------------------
+void ImagePanel::SetImage(const char *imageName)
+{
+ if ( imageName && m_pszImageName && V_stricmp( imageName, m_pszImageName ) == 0 )
+ return;
+
+ int len = Q_strlen(imageName) + 1;
+ delete [] m_pszImageName;
+ m_pszImageName = new char[ len ];
+ Q_strncpy(m_pszImageName, imageName, len );
+ InvalidateLayout(false, true); // force applyschemesettings to run
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+IImage *ImagePanel::GetImage()
+{
+ return m_pImage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Color ImagePanel::GetDrawColor( void )
+{
+ return m_DrawColor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ImagePanel::SetDrawColor( Color drawColor )
+{
+ m_DrawColor = drawColor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ImagePanel::PaintBackground()
+{
+ if (m_FillColor[3] > 0)
+ {
+ // draw the specified fill color
+ int wide, tall;
+ GetSize(wide, tall);
+ surface()->DrawSetColor(m_FillColor);
+ surface()->DrawFilledRect(0, 0, wide, tall);
+ }
+ if ( m_pImage )
+ {
+ //=============================================================================
+ // HPE_BEGIN:
+ // [pfreese] Color should be always set from GetDrawColor(), not just when
+ // scaling is true (see previous code)
+ //=============================================================================
+
+ // surface()->DrawSetColor( 255, 255, 255, GetAlpha() );
+ m_pImage->SetColor( GetDrawColor() );
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ if ( m_bCenterImage )
+ {
+ int wide, tall;
+ GetSize(wide, tall);
+
+ int imageWide, imageTall;
+ m_pImage->GetSize( imageWide, imageTall );
+
+ if ( m_bScaleImage && m_fScaleAmount > 0.0f )
+ {
+ imageWide = static_cast<int>( static_cast<float>(imageWide) * m_fScaleAmount );
+ imageTall = static_cast<int>( static_cast<float>(imageTall) * m_fScaleAmount );
+ }
+
+ m_pImage->SetPos( (wide - imageWide) / 2, (tall - imageTall) / 2 );
+ }
+ else
+ {
+ m_pImage->SetPos(0, 0);
+ }
+
+ if (m_bScaleImage)
+ {
+ // Image size is stored in the bitmap, so temporarily set its size
+ // to our panel size and then restore after we draw it.
+
+ int imageWide, imageTall;
+ m_pImage->GetSize( imageWide, imageTall );
+
+ if ( m_fScaleAmount > 0.0f )
+ {
+ float wide, tall;
+ wide = static_cast<float>(imageWide) * m_fScaleAmount;
+ tall = static_cast<float>(imageTall) * m_fScaleAmount;
+ m_pImage->SetSize( static_cast<int>(wide), static_cast<int>(tall) );
+ }
+ else
+ {
+ int wide, tall;
+ GetSize( wide, tall );
+ m_pImage->SetSize( wide, tall );
+ }
+
+ m_pImage->Paint();
+
+ m_pImage->SetSize( imageWide, imageTall );
+ }
+ else if ( m_bTileImage || m_bTileHorizontally || m_bTileVertically )
+ {
+ int wide, tall;
+ GetSize(wide, tall);
+ int imageWide, imageTall;
+ m_pImage->GetSize( imageWide, imageTall );
+
+ int y = 0;
+ while ( y < tall )
+ {
+ int x = 0;
+ while (x < wide)
+ {
+ m_pImage->SetPos(x,y);
+ m_pImage->Paint();
+
+ x += imageWide;
+
+ if ( !m_bTileHorizontally )
+ break;
+ }
+
+ y += imageTall;
+
+ if ( !m_bTileVertically )
+ break;
+ }
+ m_pImage->SetPos(0, 0);
+ }
+ else
+ {
+ m_pImage->SetColor( GetDrawColor() );
+ m_pImage->Paint();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets control settings for editing
+//-----------------------------------------------------------------------------
+void ImagePanel::GetSettings(KeyValues *outResourceData)
+{
+ BaseClass::GetSettings(outResourceData);
+ if (m_pszImageName)
+ {
+ outResourceData->SetString("image", m_pszImageName);
+ }
+ if (m_pszFillColorName)
+ {
+ outResourceData->SetString("fillcolor", m_pszFillColorName);
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [pfreese] Added support for specifying drawcolor
+ //=============================================================================
+ if (m_pszDrawColorName)
+ {
+ outResourceData->SetString("drawcolor", m_pszDrawColorName);
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ if (GetBorder())
+ {
+ outResourceData->SetString("border", GetBorder()->GetName());
+ }
+
+ outResourceData->SetInt("scaleImage", m_bScaleImage);
+ outResourceData->SetFloat("scaleAmount", m_fScaleAmount);
+ outResourceData->SetInt("tileImage", m_bTileImage);
+ outResourceData->SetInt("tileHorizontally", m_bTileHorizontally);
+ outResourceData->SetInt("tileVertically", m_bTileVertically);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies designer settings from res file
+//-----------------------------------------------------------------------------
+void ImagePanel::ApplySettings(KeyValues *inResourceData)
+{
+ delete [] m_pszImageName;
+ delete [] m_pszFillColorName;
+ delete [] m_pszDrawColorName; // HPE addition
+ m_pszImageName = NULL;
+ m_pszFillColorName = NULL;
+ m_pszDrawColorName = NULL; // HPE addition
+
+ m_bScaleImage = inResourceData->GetInt("scaleImage", 0);
+ m_fScaleAmount = inResourceData->GetFloat("scaleAmount", 0.0f);
+ m_bTileImage = inResourceData->GetInt("tileImage", 0);
+ m_bTileHorizontally = inResourceData->GetInt("tileHorizontally", m_bTileImage);
+ m_bTileVertically = inResourceData->GetInt("tileVertically", m_bTileImage);
+ const char *imageName = inResourceData->GetString("image", "");
+ if ( *imageName )
+ {
+ SetImage( imageName );
+ }
+
+ const char *pszFillColor = inResourceData->GetString("fillcolor", "");
+ if (*pszFillColor)
+ {
+ int r = 0, g = 0, b = 0, a = 255;
+ int len = Q_strlen(pszFillColor) + 1;
+ m_pszFillColorName = new char[ len ];
+ Q_strncpy( m_pszFillColorName, pszFillColor, len );
+
+ if (sscanf(pszFillColor, "%d %d %d %d", &r, &g, &b, &a) >= 3)
+ {
+ // it's a direct color
+ m_FillColor = Color(r, g, b, a);
+ }
+ else
+ {
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+ m_FillColor = pScheme->GetColor(pszFillColor, Color(0, 0, 0, 0));
+ }
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [pfreese] Added support for specifying drawcolor
+ //=============================================================================
+ const char *pszDrawColor = inResourceData->GetString("drawcolor", "");
+ if (*pszDrawColor)
+ {
+ int r = 255, g = 255, b = 255, a = 255;
+ int len = Q_strlen(pszDrawColor) + 1;
+ m_pszDrawColorName = new char[ len ];
+ Q_strncpy( m_pszDrawColorName, pszDrawColor, len );
+
+ if (sscanf(pszDrawColor, "%d %d %d %d", &r, &g, &b, &a) >= 3)
+ {
+ // it's a direct color
+ m_DrawColor = Color(r, g, b, a);
+ }
+ else
+ {
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+ m_DrawColor = pScheme->GetColor(pszDrawColor, Color(255, 255, 255, 255));
+ }
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ const char *pszBorder = inResourceData->GetString("border", "");
+ if (*pszBorder)
+ {
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+ SetBorder(pScheme->GetBorder(pszBorder));
+ }
+
+ BaseClass::ApplySettings(inResourceData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: load the image, this is done just before this control is displayed
+//-----------------------------------------------------------------------------
+void ImagePanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+ if ( m_pszImageName && strlen( m_pszImageName ) > 0 )
+ {
+ SetImage(scheme()->GetImage(m_pszImageName, m_bScaleImage));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Describes editing details
+//-----------------------------------------------------------------------------
+const char *ImagePanel::GetDescription()
+{
+ static char buf[1024];
+ _snprintf(buf, sizeof(buf), "%s, string image, string border, string fillcolor, bool scaleImage", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets whether or not the image should scale to fit the size of the ImagePanel (defaults to false)
+//-----------------------------------------------------------------------------
+void ImagePanel::SetShouldScaleImage( bool state )
+{
+ m_bScaleImage = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets whether or not the image should be scaled to fit the size of the ImagePanel
+//-----------------------------------------------------------------------------
+bool ImagePanel::GetShouldScaleImage()
+{
+ return m_bScaleImage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: used in conjunction with setting that the image should scale and defines an absolute scale amount
+//-----------------------------------------------------------------------------
+void ImagePanel::SetScaleAmount( float scale )
+{
+ m_fScaleAmount = scale;
+}
+
+float ImagePanel::GetScaleAmount( void )
+{
+ return m_fScaleAmount;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set the color to fill with, if no Image is specified
+//-----------------------------------------------------------------------------
+void ImagePanel::SetFillColor( Color col )
+{
+ m_FillColor = col;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+Color ImagePanel::GetFillColor()
+{
+ return m_FillColor;
+}
+
+char *ImagePanel::GetImageName()
+{
+ return m_pszImageName;
+}
+
+bool ImagePanel::EvictImage()
+{
+ if ( !m_pImage )
+ {
+ // nothing to do
+ return false;
+ }
+
+ if ( !scheme()->DeleteImage( m_pszImageName ) )
+ {
+ // no eviction occured, could have an outstanding reference
+ return false;
+ }
+
+ // clear out our cached concept of it
+ // as it may change
+ // the next SetImage() will re-establish
+ m_pImage = NULL;
+ delete [] m_pszImageName;
+ m_pszImageName = NULL;
+
+ return true;
+}
+
+int ImagePanel::GetNumFrames()
+{
+ if ( !m_pImage )
+ {
+ return 0;
+ }
+
+ return m_pImage->GetNumFrames();
+}
+
+void ImagePanel::SetFrame( int nFrame )
+{
+ if ( !m_pImage )
+ {
+ return;
+ }
+
+ return m_pImage->SetFrame( nFrame );
+}
diff --git a/mp/src/vgui2/vgui_controls/InputDialog.cpp b/mp/src/vgui2/vgui_controls/InputDialog.cpp
new file mode 100644
index 00000000..2e1e0624
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/InputDialog.cpp
@@ -0,0 +1,236 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include <vgui_controls/InputDialog.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/Button.h>
+#include <vgui_controls/TextEntry.h>
+#include "tier1/KeyValues.h"
+#include "vgui/IInput.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+BaseInputDialog::BaseInputDialog( vgui::Panel *parent, const char *title ) :
+ BaseClass( parent, NULL )
+{
+ m_pContextKeyValues = NULL;
+
+ SetDeleteSelfOnClose( true );
+ SetTitle(title, true);
+ SetSize(320, 180);
+ SetSizeable( false );
+
+ m_pCancelButton = new Button(this, "CancelButton", "#VGui_Cancel");
+ m_pOKButton = new Button(this, "OKButton", "#VGui_OK");
+ m_pCancelButton->SetCommand("Cancel");
+ m_pOKButton->SetCommand("OK");
+ m_pOKButton->SetAsDefaultButton( true );
+
+ if ( parent )
+ {
+ AddActionSignalTarget( parent );
+ }
+}
+
+BaseInputDialog::~BaseInputDialog()
+{
+ CleanUpContextKeyValues();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Cleans up the keyvalues
+//-----------------------------------------------------------------------------
+void BaseInputDialog::CleanUpContextKeyValues()
+{
+ if ( m_pContextKeyValues )
+ {
+ m_pContextKeyValues->deleteThis();
+ m_pContextKeyValues = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void BaseInputDialog::DoModal( KeyValues *pContextKeyValues )
+{
+ CleanUpContextKeyValues();
+ m_pContextKeyValues = pContextKeyValues;
+ BaseClass::DoModal();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: lays out controls
+//-----------------------------------------------------------------------------
+void BaseInputDialog::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int w, h;
+ GetSize( w, h );
+
+ // lay out all the controls
+ int topy = IsSmallCaption() ? 15 : 30;
+ int halfw = w / 2;
+
+ PerformLayout( 12, topy, w - 24, h - 100 );
+
+ m_pOKButton->SetBounds( halfw - 84, h - 30, 72, 24 );
+ m_pCancelButton->SetBounds( halfw + 12, h - 30, 72, 24 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: handles button commands
+//-----------------------------------------------------------------------------
+void BaseInputDialog::OnCommand(const char *command)
+{
+ KeyValues *kv = NULL;
+ if ( !stricmp( command, "OK" ) )
+ {
+ kv = new KeyValues( "InputCompleted" );
+ kv->SetPtr( "dialog", this );
+ }
+ else if ( !stricmp( command, "Cancel" ) )
+ {
+ kv = new KeyValues( "InputCanceled" );
+ }
+ else
+ {
+ BaseClass::OnCommand( command );
+ return;
+ }
+
+ if ( m_pContextKeyValues )
+ {
+ kv->AddSubKey( m_pContextKeyValues );
+ m_pContextKeyValues = NULL;
+ }
+ PostActionSignal( kv );
+ CloseModal();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Utility dialog, used to ask yes/no questions of the user
+//-----------------------------------------------------------------------------
+InputMessageBox::InputMessageBox( vgui::Panel *parent, const char *title, char const *prompt )
+: BaseClass( parent, title )
+{
+ SetSize( 320, 120 );
+
+ m_pPrompt = new Label( this, "Prompt", prompt );
+}
+
+InputMessageBox::~InputMessageBox()
+{
+}
+
+void InputMessageBox::PerformLayout( int x, int y, int w, int h )
+{
+ m_pPrompt->SetBounds( x, y, w, 24 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+InputDialog::InputDialog(vgui::Panel *parent, const char *title, char const *prompt, char const *defaultValue /*=""*/ ) :
+ BaseClass(parent, title)
+{
+ SetSize( 320, 120 );
+
+ m_pPrompt = new Label( this, "Prompt", prompt );
+
+ m_pInput = new TextEntry( this, "Text" );
+ m_pInput->SetText( defaultValue );
+ m_pInput->SelectAllText( true );
+ m_pInput->RequestFocus();
+}
+
+
+InputDialog::~InputDialog()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the dialog to be multiline
+//-----------------------------------------------------------------------------
+void InputDialog::SetMultiline( bool state )
+{
+ m_pInput->SetMultiline( state );
+ m_pInput->SetCatchEnterKey( state );
+}
+
+
+//-----------------------------------------------------------------------------
+// Allow numeric input only
+//-----------------------------------------------------------------------------
+void InputDialog::AllowNumericInputOnly( bool bOnlyNumeric )
+{
+ if ( m_pInput )
+ {
+ m_pInput->SetAllowNumericInputOnly( bOnlyNumeric );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: lays out controls
+//-----------------------------------------------------------------------------
+void InputDialog::PerformLayout( int x, int y, int w, int h )
+{
+ m_pPrompt->SetBounds( x, y, w, 24 );
+ m_pInput ->SetBounds( x, y + 30, w, m_pInput->IsMultiline() ? h - 30 : 24 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: handles button commands
+//-----------------------------------------------------------------------------
+void InputDialog::OnCommand(const char *command)
+{
+ // overriding OnCommand for backwards compatability
+ // it'd be nice at some point to find all uses of InputDialog and just use BaseInputDialog's OnCommand
+
+ if (!stricmp(command, "OK"))
+ {
+ int nTextLength = m_pInput->GetTextLength() + 1;
+ char* txt = (char*)_alloca( nTextLength * sizeof(char) );
+ m_pInput->GetText( txt, nTextLength );
+ KeyValues *kv = new KeyValues( "InputCompleted", "text", txt );
+ if ( m_pContextKeyValues )
+ {
+ kv->AddSubKey( m_pContextKeyValues );
+ m_pContextKeyValues = NULL;
+ }
+ PostActionSignal( kv );
+ CloseModal();
+ }
+ else if (!stricmp(command, "Cancel"))
+ {
+ KeyValues *kv = new KeyValues( "InputCanceled" );
+ if ( m_pContextKeyValues )
+ {
+ kv->AddSubKey( m_pContextKeyValues );
+ m_pContextKeyValues = NULL;
+ }
+ PostActionSignal( kv );
+ CloseModal();
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
diff --git a/mp/src/vgui2/vgui_controls/KeyBindingHelpDialog.cpp b/mp/src/vgui2/vgui_controls/KeyBindingHelpDialog.cpp
new file mode 100644
index 00000000..531ff1df
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/KeyBindingHelpDialog.cpp
@@ -0,0 +1,355 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "vgui_controls/KeyBindingHelpDialog.h"
+#include "vgui_controls/ListPanel.h"
+#include "vgui/ISurface.h"
+#include "vgui/IVGui.h"
+#include "vgui/ILocalize.h"
+#include "vgui/IInput.h"
+#include "vgui/ISystem.h"
+#include "KeyValues.h"
+#include "vgui/Cursor.h"
+#include "tier1/utldict.h"
+#include "vgui_controls/KeyBoardEditorDialog.h"
+
+// NOTE: This has to be the last file included!
+#include "tier0/memdbgon.h"
+
+
+using namespace vgui;
+
+// If the user holds the key bound to help down for this long, then the dialog will stay on automatically
+#define KB_HELP_CONTINUE_SHOWING_TIME 1.0
+
+static bool BindingLessFunc( KeyValues * const & lhs, KeyValues * const &rhs )
+{
+ KeyValues *p1, *p2;
+
+ p1 = const_cast< KeyValues * >( lhs );
+ p2 = const_cast< KeyValues * >( rhs );
+ return ( Q_stricmp( p1->GetString( "Action" ), p2->GetString( "Action" ) ) < 0 ) ? true : false;
+}
+
+CKeyBindingHelpDialog::CKeyBindingHelpDialog( Panel *parent, Panel *panelToView, KeyBindingContextHandle_t handle, KeyCode code, int modifiers )
+ : BaseClass( parent, "KeyBindingHelpDialog" ),
+ m_Handle( handle ),
+ m_KeyCode( code ),
+ m_Modifiers( modifiers ),
+ m_bPermanent( false )
+{
+ Assert( panelToView );
+ m_hPanel = panelToView;
+
+ m_pList = new ListPanel( this, "KeyBindings" );
+ m_pList->SetIgnoreDoubleClick( true );
+ m_pList->AddColumnHeader(0, "Action", "#KBEditorBindingName", 175, 0);
+ m_pList->AddColumnHeader(1, "Binding", "#KBEditorBinding", 175, 0);
+ m_pList->AddColumnHeader(2, "Description", "#KBEditorDescription", 300, 0);
+
+ LoadControlSettings( "resource/KeyBindingHelpDialog.res" );
+
+ if ( panelToView && panelToView->GetName() && panelToView->GetName()[0] )
+ {
+ SetTitle( panelToView->GetName(), true );
+ }
+ else
+ {
+ SetTitle( "#KBHelpDialogTitle", true );
+ }
+
+ SetSmallCaption( true );
+ SetMinimumSize( 400, 400 );
+ SetMinimizeButtonVisible( false );
+ SetMaximizeButtonVisible( false );
+ SetSizeable( true );
+ SetMoveable( true );
+ SetMenuButtonVisible( false );
+
+ SetVisible( true );
+
+ MoveToCenterOfScreen();
+
+ PopulateList();
+
+ m_flShowTime = system()->GetCurrentTime();
+ ivgui()->AddTickSignal( GetVPanel(), 0 );
+
+ input()->SetAppModalSurface( GetVPanel() );
+}
+
+CKeyBindingHelpDialog::~CKeyBindingHelpDialog()
+{
+ if ( input()->GetAppModalSurface() == GetVPanel() )
+ {
+ input()->SetAppModalSurface( 0 );
+ }
+}
+
+void CKeyBindingHelpDialog::OnTick()
+{
+ BaseClass::OnTick();
+
+ bool keyStillDown = IsHelpKeyStillBeingHeld();
+
+ double curtime = system()->GetCurrentTime();
+ double elapsed = curtime - m_flShowTime;
+ // After a second of holding the key, releasing the key will close the dialog
+ if ( elapsed > KB_HELP_CONTINUE_SHOWING_TIME )
+ {
+ if ( !keyStillDown )
+ {
+ MarkForDeletion();
+ return;
+ }
+ }
+ // Otherwise, if they tapped the key within a second and now have released...
+ else if ( !keyStillDown )
+ {
+ // Continue showing dialog indefinitely
+ ivgui()->RemoveTickSignal( GetVPanel() );
+ m_bPermanent = true;
+ }
+}
+
+// The key originally bound to help was pressed
+void CKeyBindingHelpDialog::HelpKeyPressed()
+{
+ // Don't kill while editor is being shown...
+ if ( m_hKeyBindingsEditor.Get() )
+ return;
+
+ if ( m_bPermanent )
+ {
+ MarkForDeletion();
+ }
+}
+
+bool CKeyBindingHelpDialog::IsHelpKeyStillBeingHeld()
+{
+ bool keyDown = input()->IsKeyDown( m_KeyCode );
+ if ( !keyDown )
+ return false;
+
+ bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+ bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+ bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
+
+ int modifiers = 0;
+ if ( shift )
+ {
+ modifiers |= MODIFIER_SHIFT;
+ }
+ if ( ctrl )
+ {
+ modifiers |= MODIFIER_CONTROL;
+ }
+ if ( alt )
+ {
+ modifiers |= MODIFIER_ALT;
+ }
+
+ if ( modifiers != m_Modifiers )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void CKeyBindingHelpDialog::OnCommand( char const *cmd )
+{
+ if ( !Q_stricmp( cmd, "OK" ) ||
+ !Q_stricmp( cmd, "cancel" ) ||
+ !Q_stricmp( cmd, "Close" ) )
+ {
+ MarkForDeletion();
+ }
+ else if ( !Q_stricmp( cmd, "edit" ) )
+ {
+ // Show the keybindings edit dialog
+ if ( m_hKeyBindingsEditor.Get() )
+ {
+ delete m_hKeyBindingsEditor.Get();
+ }
+
+ // Don't delete panel if H key is released...
+
+ m_hKeyBindingsEditor = new CKeyBoardEditorDialog( this, m_hPanel, m_Handle );
+ m_hKeyBindingsEditor->DoModal();
+
+ ivgui()->RemoveTickSignal( GetVPanel() );
+ m_bPermanent = true;
+ }
+ else
+ {
+ BaseClass::OnCommand( cmd );
+ }
+}
+
+void CKeyBindingHelpDialog::OnKeyCodeTyped(vgui::KeyCode code)
+{
+ BaseClass::OnKeyCodeTyped( code );
+}
+
+void CKeyBindingHelpDialog::GetMappingList( Panel *panel, CUtlVector< PanelKeyBindingMap * >& maps )
+{
+ PanelKeyBindingMap *map = panel->GetKBMap();
+ while ( map )
+ {
+ maps.AddToTail( map );
+ map = map->baseMap;
+ }
+}
+
+
+void CKeyBindingHelpDialog::AnsiText( char const *token, char *out, size_t buflen )
+{
+ out[ 0 ] = 0;
+
+ wchar_t *str = g_pVGuiLocalize->Find( token );
+ if ( !str )
+ {
+ Q_strncpy( out, token, buflen );
+ }
+ else
+ {
+ g_pVGuiLocalize->ConvertUnicodeToANSI( str, out, buflen );
+ }
+}
+
+struct ListInfo_t
+{
+ PanelKeyBindingMap *m_pMap;
+ Panel *m_pPanel;
+};
+
+void CKeyBindingHelpDialog::PopulateList()
+{
+ m_pList->DeleteAllItems();
+
+ int i, j;
+
+ CUtlVector< ListInfo_t > maps;
+ vgui::Panel *pPanel = m_hPanel;
+ while ( pPanel->IsKeyBindingChainToParentAllowed() )
+ {
+ PanelKeyBindingMap *map = pPanel->GetKBMap();
+ while ( map )
+ {
+ int k;
+ int c = maps.Count();
+ for ( k = 0; k < c; ++k )
+ {
+ if ( maps[k].m_pMap == map )
+ break;
+ }
+ if ( k == c )
+ {
+ int k = maps.AddToTail( );
+ maps[k].m_pMap = map;
+ maps[k].m_pPanel = pPanel;
+ }
+ map = map->baseMap;
+ }
+
+ pPanel = pPanel->GetParent();
+ if ( !pPanel )
+ break;
+ }
+
+ CUtlRBTree< KeyValues *, int > sorted( 0, 0, BindingLessFunc );
+
+ // add header item
+ int c = maps.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ PanelKeyBindingMap *m = maps[ i ].m_pMap;
+ Panel *pPanel = maps[i].m_pPanel;
+ Assert( m );
+
+ int bindings = m->boundkeys.Count();
+ for ( j = 0; j < bindings; ++j )
+ {
+ BoundKey_t *kbMap = &m->boundkeys[ j ];
+ Assert( kbMap );
+
+ // Create a new: blank item
+ KeyValues *item = new KeyValues( "Item" );
+
+ // Fill in data
+ char loc[ 128 ];
+ Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname );
+
+ char ansi[ 256 ];
+ AnsiText( loc, ansi, sizeof( ansi ) );
+
+ item->SetString( "Action", ansi );
+ item->SetWString( "Binding", Panel::KeyCodeModifiersToDisplayString( (KeyCode)kbMap->keycode, kbMap->modifiers ) );
+
+ // Find the binding
+ KeyBindingMap_t *bindingMap = pPanel->LookupBinding( kbMap->bindingname );
+ if ( bindingMap &&
+ bindingMap->helpstring )
+ {
+ AnsiText( bindingMap->helpstring, ansi, sizeof( ansi ) );
+ item->SetString( "Description", ansi );
+ }
+
+ item->SetPtr( "Item", kbMap );
+
+ sorted.Insert( item );
+ }
+
+ // Now try and find any "unbound" keys...
+ int mappings = m->entries.Count();
+ for ( j = 0; j < mappings; ++j )
+ {
+ KeyBindingMap_t *kbMap = &m->entries[ j ];
+
+ // See if it's bound
+ CUtlVector< BoundKey_t * > list;
+ pPanel->LookupBoundKeys( kbMap->bindingname, list );
+ if ( list.Count() > 0 )
+ continue;
+
+ // Not bound, add a placeholder entry
+ // Create a new: blank item
+ KeyValues *item = new KeyValues( "Item" );
+
+ // fill in data
+ char loc[ 128 ];
+ Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname );
+
+ char ansi[ 256 ];
+ AnsiText( loc, ansi, sizeof( ansi ) );
+
+ item->SetString( "Action", ansi );
+ item->SetWString( "Binding", L"" );
+ if ( kbMap->helpstring )
+ {
+ AnsiText( kbMap->helpstring, ansi, sizeof( ansi ) );
+ item->SetString( "Description", ansi );
+ }
+
+ item->SetPtr( "Unbound", kbMap );
+
+ sorted.Insert( item );
+ }
+ }
+
+ for ( j = sorted.FirstInorder() ; j != sorted.InvalidIndex(); j = sorted.NextInorder( j ) )
+ {
+ KeyValues *item = sorted[ j ];
+
+ // Add to list
+ m_pList->AddItem( item, 0, false, false );
+
+ item->deleteThis();
+ }
+
+ sorted.RemoveAll();
+}
diff --git a/mp/src/vgui2/vgui_controls/KeyBoardEditorDialog.cpp b/mp/src/vgui2/vgui_controls/KeyBoardEditorDialog.cpp
new file mode 100644
index 00000000..437f536a
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/KeyBoardEditorDialog.cpp
@@ -0,0 +1,849 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "vgui_controls/KeyBoardEditorDialog.h"
+#include "vgui_controls/ListPanel.h"
+#include "vgui_controls/Button.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui/ISurface.h"
+#include "vgui/IInput.h"
+#include "vgui/IVGui.h"
+#include "vgui/ILocalize.h"
+#include "KeyValues.h"
+#include "vgui/Cursor.h"
+#include "tier1/utldict.h"
+
+// NOTE: This has to be the last file included!
+#include "tier0/memdbgon.h"
+
+
+using namespace vgui;
+
+static char *CopyString( const char *in )
+{
+ if ( !in )
+ return NULL;
+
+ int len = strlen( in );
+ char *n = new char[ len + 1 ];
+ Q_strncpy( n, in, len + 1 );
+ return n;
+}
+
+CKeyBoardEditorPage::SaveMapping_t::SaveMapping_t() : map( 0 )
+{
+}
+
+CKeyBoardEditorPage::SaveMapping_t::SaveMapping_t( const SaveMapping_t& src )
+{
+ map = src.map;
+ current = src.current;
+ original = src.original;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Special list subclass to handle drawing of trap mode prompt on top of
+// lists client area
+//-----------------------------------------------------------------------------
+class VControlsListPanel : public ListPanel
+{
+ DECLARE_CLASS_SIMPLE( VControlsListPanel, ListPanel );
+
+public:
+ // Construction
+ VControlsListPanel( vgui::Panel *parent, const char *listName );
+ virtual ~VControlsListPanel();
+
+ // Start/end capturing
+ virtual void StartCaptureMode(vgui::HCursor hCursor = NULL);
+ virtual void EndCaptureMode(vgui::HCursor hCursor = NULL);
+ virtual bool IsCapturing();
+
+ // Set which item should be associated with the prompt
+ virtual void SetItemOfInterest(int itemID);
+ virtual int GetItemOfInterest();
+
+ virtual void OnMousePressed(vgui::MouseCode code);
+ virtual void OnMouseDoublePressed(vgui::MouseCode code);
+
+ KEYBINDING_FUNC( clearbinding, KEY_DELETE, 0, OnClearBinding, 0, 0 );
+
+private:
+ void ApplySchemeSettings(vgui::IScheme *pScheme );
+
+ // Are we showing the prompt?
+ bool m_bCaptureMode;
+ // If so, where?
+ int m_nClickRow;
+ // Font to use for showing the prompt
+ vgui::HFont m_hFont;
+ // panel used to edit
+ class CInlineEditPanel *m_pInlineEditPanel;
+ int m_iMouseX, m_iMouseY;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: panel used for inline editing of key bindings
+//-----------------------------------------------------------------------------
+class CInlineEditPanel : public vgui::Panel
+{
+ DECLARE_CLASS_SIMPLE( CInlineEditPanel, vgui::Panel );
+
+public:
+ CInlineEditPanel() : vgui::Panel(NULL, "InlineEditPanel")
+ {
+ }
+
+ virtual void Paint()
+ {
+ int wide, tall;
+ GetSize(wide, tall);
+
+ // Draw a white rectangle around that cell
+ vgui::surface()->DrawSetColor( 63, 63, 63, 255 );
+ vgui::surface()->DrawFilledRect( 0, 0, wide, tall );
+
+ vgui::surface()->DrawSetColor( 0, 255, 0, 255 );
+ vgui::surface()->DrawOutlinedRect( 0, 0, wide, tall );
+ }
+
+ virtual void OnKeyCodeTyped(KeyCode code)
+ {
+ // forward up
+ if (GetParent())
+ {
+ GetParent()->OnKeyCodeTyped(code);
+ }
+ }
+
+ virtual void ApplySchemeSettings(IScheme *pScheme)
+ {
+ Panel::ApplySchemeSettings(pScheme);
+ SetBorder(pScheme->GetBorder("DepressedButtonBorder"));
+ }
+
+ void OnMousePressed(vgui::MouseCode code)
+ {
+ // forward up mouse pressed messages to be handled by the key options
+ if (GetParent())
+ {
+ GetParent()->OnMousePressed(code);
+ }
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Construction
+//-----------------------------------------------------------------------------
+VControlsListPanel::VControlsListPanel( vgui::Panel *parent, const char *listName ) : BaseClass( parent, listName )
+{
+ m_bCaptureMode = false;
+ m_nClickRow = 0;
+ m_pInlineEditPanel = new CInlineEditPanel();
+ m_hFont = INVALID_FONT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+VControlsListPanel::~VControlsListPanel()
+{
+ m_pInlineEditPanel->MarkForDeletion();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void VControlsListPanel::ApplySchemeSettings(IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+ m_hFont = pScheme->GetFont("DefaultVerySmall", IsProportional() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start capture prompt display
+//-----------------------------------------------------------------------------
+void VControlsListPanel::StartCaptureMode( HCursor hCursor )
+{
+ m_bCaptureMode = true;
+ EnterEditMode(m_nClickRow, 1, m_pInlineEditPanel);
+ input()->SetMouseFocus(m_pInlineEditPanel->GetVPanel());
+ input()->SetMouseCapture(m_pInlineEditPanel->GetVPanel());
+
+ if (hCursor)
+ {
+ m_pInlineEditPanel->SetCursor(hCursor);
+
+ // save off the cursor position so we can restore it
+ vgui::input()->GetCursorPos( m_iMouseX, m_iMouseY );
+ }
+}
+
+void VControlsListPanel::OnClearBinding()
+{
+ if ( m_bCaptureMode )
+ return;
+
+ if ( GetItemOfInterest() < 0 )
+ return;
+
+ PostMessage( GetParent()->GetVPanel(), new KeyValues( "ClearBinding", "item", GetItemOfInterest() ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finish capture prompt display
+//-----------------------------------------------------------------------------
+void VControlsListPanel::EndCaptureMode( HCursor hCursor )
+{
+ m_bCaptureMode = false;
+ input()->SetMouseCapture(NULL);
+ LeaveEditMode();
+ RequestFocus();
+ input()->SetMouseFocus(GetVPanel());
+ if (hCursor)
+ {
+ m_pInlineEditPanel->SetCursor(hCursor);
+ surface()->SetCursor(hCursor);
+ if ( hCursor != dc_none )
+ {
+ vgui::input()->SetCursorPos ( m_iMouseX, m_iMouseY );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set active row column
+//-----------------------------------------------------------------------------
+void VControlsListPanel::SetItemOfInterest(int itemID)
+{
+ m_nClickRow = itemID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Retrieve row, column of interest
+//-----------------------------------------------------------------------------
+int VControlsListPanel::GetItemOfInterest()
+{
+ return m_nClickRow;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if we're currently waiting to capture a key
+//-----------------------------------------------------------------------------
+bool VControlsListPanel::IsCapturing( void )
+{
+ return m_bCaptureMode;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Forwards mouse pressed message up to keyboard page when in capture
+//-----------------------------------------------------------------------------
+void VControlsListPanel::OnMousePressed(vgui::MouseCode code)
+{
+ if (IsCapturing())
+ {
+ // forward up mouse pressed messages to be handled by the key options
+ if (GetParent())
+ {
+ GetParent()->OnMousePressed(code);
+ }
+ }
+ else
+ {
+ BaseClass::OnMousePressed(code);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: input handler
+//-----------------------------------------------------------------------------
+void VControlsListPanel::OnMouseDoublePressed( vgui::MouseCode code )
+{
+ int c = GetSelectedItemsCount();
+ if ( c > 0 )
+ {
+ // enter capture mode
+ OnKeyCodeTyped(KEY_ENTER);
+ }
+ else
+ {
+ BaseClass::OnMouseDoublePressed(code);
+ }
+}
+
+CKeyBoardEditorPage::CKeyBoardEditorPage( Panel *parent, Panel *panelToEdit, KeyBindingContextHandle_t handle )
+ : BaseClass( parent, "KeyBoardEditorPage" ),
+ m_pPanel( panelToEdit ),
+ m_Handle( handle )
+{
+ Assert( m_pPanel );
+
+ m_pList = new VControlsListPanel( this, "KeyBindings" );
+ m_pList->SetIgnoreDoubleClick( true );
+ m_pList->AddColumnHeader(0, "Action", "#KBEditorBindingName", 175, 0);
+ m_pList->AddColumnHeader(1, "Binding", "#KBEditorBinding", 175, 0);
+ m_pList->AddColumnHeader(2, "Description", "#KBEditorDescription", 300, 0);
+
+ LoadControlSettings( "resource/KeyBoardEditorPage.res" );
+
+ SaveMappings();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+CKeyBoardEditorPage::~CKeyBoardEditorPage()
+{
+ int c = m_Save.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ delete m_Save[ i ];
+ }
+ m_Save.RemoveAll();
+}
+
+void CKeyBoardEditorPage::ApplySchemeSettings( IScheme *scheme )
+{
+ BaseClass::ApplySchemeSettings( scheme );
+ PopulateList();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void CKeyBoardEditorPage::SaveMappings()
+{
+ Assert( m_Save.Count() == 0 );
+
+ CUtlVector< PanelKeyBindingMap * > maps;
+ GetMappingList( m_pPanel, maps );
+
+ // add header item
+ int c = maps.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ PanelKeyBindingMap *m = maps[ i ];
+ SaveMapping_t *sm = new SaveMapping_t;
+ sm->map = m;
+ sm->current = m->boundkeys;
+ sm->original = m->boundkeys;
+ m_Save.AddToTail( sm );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void CKeyBoardEditorPage::UpdateCurrentMappings()
+{
+ int c = m_Save.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ PanelKeyBindingMap *m = m_Save[ i ]->map;
+ Assert( m );
+ m_Save[ i ]->current = m->boundkeys;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void CKeyBoardEditorPage::RestoreMappings()
+{
+ int c = m_Save.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ SaveMapping_t *sm = m_Save[ i ];
+ sm->current = sm->original;
+ }
+}
+
+void CKeyBoardEditorPage::ApplyMappings()
+{
+ int c = m_Save.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ SaveMapping_t *sm = m_Save[ i ];
+ sm->map->boundkeys = sm->current;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: User clicked on item: remember where last active row/column was
+//-----------------------------------------------------------------------------
+void CKeyBoardEditorPage::ItemSelected()
+{
+ int c = m_pList->GetSelectedItemsCount();
+ if ( c > 0 )
+ {
+ m_pList->SetItemOfInterest( m_pList->GetSelectedItem( 0 ) );
+ }
+}
+
+void CKeyBoardEditorPage::BindKey( KeyCode code )
+{
+ bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+ bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+ bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
+
+ int modifiers = 0;
+ if ( shift )
+ {
+ modifiers |= MODIFIER_SHIFT;
+ }
+ if ( ctrl )
+ {
+ modifiers |= MODIFIER_CONTROL;
+ }
+ if ( alt )
+ {
+ modifiers |= MODIFIER_ALT;
+ }
+
+ int r = m_pList->GetItemOfInterest();
+
+ // Retrieve clicked row and column
+ m_pList->EndCaptureMode(dc_arrow);
+
+ // Find item for this row
+ KeyValues *item = m_pList->GetItem(r);
+ if ( item )
+ {
+ BoundKey_t *kbMap = reinterpret_cast< BoundKey_t * >( item->GetPtr( "Item", 0 ) );
+ if ( kbMap )
+ {
+ KeyBindingMap_t *binding = m_pPanel->LookupBindingByKeyCode( code, modifiers );
+ if ( binding && Q_stricmp( kbMap->bindingname, binding->bindingname ) )
+ {
+ // Key is already rebound!!!
+ Warning( "Can't bind to '%S', key is already bound to '%s'\n",
+ Panel::KeyCodeToDisplayString( code ), binding->bindingname );
+ return;
+ }
+
+ kbMap->keycode = code;
+ kbMap->modifiers = modifiers;
+
+ PopulateList();
+ }
+
+ KeyBindingMap_t *bindingMap = reinterpret_cast< KeyBindingMap_t * >( item->GetPtr( "Unbound", 0 ) );
+ if ( bindingMap )
+ {
+ KeyBindingMap_t *binding = m_pPanel->LookupBindingByKeyCode( code, modifiers );
+ if ( binding && Q_stricmp( bindingMap->bindingname, binding->bindingname ) )
+ {
+ // Key is already rebound!!!
+ Warning( "Can't bind to '%S', key is already bound to '%s'\n",
+ Panel::KeyCodeToDisplayString( code ), binding->bindingname );
+ return;
+ }
+
+ // Need to add to current entries
+ m_pPanel->AddKeyBinding( bindingMap->bindingname, code, modifiers );
+ UpdateCurrentMappings();
+ PopulateList();
+ }
+ }
+}
+
+void CKeyBoardEditorPage::OnPageHide()
+{
+ if ( m_pList->IsCapturing() )
+ {
+ // Cancel capturing
+ m_pList->EndCaptureMode(dc_arrow);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: binds double-clicking or hitting enter in the keybind list to changing the key
+//-----------------------------------------------------------------------------
+void CKeyBoardEditorPage::OnKeyCodeTyped(vgui::KeyCode code)
+{
+ switch ( code )
+ {
+ case KEY_ENTER:
+ {
+ if ( !m_pList->IsCapturing() )
+ {
+ OnCommand( "ChangeKey" );
+ }
+ else
+ {
+ BindKey( code );
+ }
+ }
+ break;
+ case KEY_LSHIFT:
+ case KEY_RSHIFT:
+ case KEY_LALT:
+ case KEY_RALT:
+ case KEY_LCONTROL:
+ case KEY_RCONTROL:
+ {
+ // Swallow these
+ break;
+ }
+ break;
+ default:
+ {
+ if ( m_pList->IsCapturing() )
+ {
+ BindKey( code );
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped(code);
+ }
+ }
+ }
+}
+
+void CKeyBoardEditorPage::OnCommand( char const *cmd )
+{
+ if ( !m_pList->IsCapturing() && !Q_stricmp( cmd, "ChangeKey" ) )
+ {
+ m_pList->StartCaptureMode(dc_blank);
+ }
+ else
+ {
+ BaseClass::OnCommand( cmd );
+ }
+}
+
+void CKeyBoardEditorPage::OnSaveChanges()
+{
+ ApplyMappings();
+}
+
+void CKeyBoardEditorPage::OnRevert()
+{
+ RestoreMappings();
+ PopulateList();
+}
+
+void CKeyBoardEditorPage::OnUseDefaults()
+{
+ m_pPanel->RevertKeyBindingsToDefault();
+ UpdateCurrentMappings();
+ PopulateList();
+}
+
+void CKeyBoardEditorPage::GetMappingList( Panel *panel, CUtlVector< PanelKeyBindingMap * >& maps )
+{
+ PanelKeyBindingMap *map = panel->GetKBMap();
+ while ( map )
+ {
+ maps.AddToTail( map );
+ map = map->baseMap;
+ }
+}
+
+static bool BindingLessFunc( KeyValues * const & lhs, KeyValues * const &rhs )
+{
+ KeyValues *p1, *p2;
+
+ p1 = const_cast< KeyValues * >( lhs );
+ p2 = const_cast< KeyValues * >( rhs );
+ return ( Q_stricmp( p1->GetString( "Action" ), p2->GetString( "Action" ) ) < 0 ) ? true : false;
+}
+
+void CKeyBoardEditorPage::AnsiText( char const *token, char *out, size_t buflen )
+{
+ out[ 0 ] = 0;
+
+ wchar_t *str = g_pVGuiLocalize->Find( token );
+ if ( !str )
+ {
+ Q_strncpy( out, token, buflen );
+ }
+ else
+ {
+ g_pVGuiLocalize->ConvertUnicodeToANSI( str, out, buflen );
+ }
+}
+
+void CKeyBoardEditorPage::PopulateList()
+{
+ m_pList->DeleteAllItems();
+
+ int i, j;
+
+ CUtlRBTree< KeyValues *, int > sorted( 0, 0, BindingLessFunc );
+
+ // add header item
+ int c = m_Save.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ SaveMapping_t* sm = m_Save[ i ];
+
+ PanelKeyBindingMap *m = sm->map;
+ Assert( m );
+
+ int bindings = sm->current.Count();
+ for ( j = 0; j < bindings; ++j )
+ {
+ BoundKey_t *kbMap = &sm->current[ j ];
+ Assert( kbMap );
+
+ // Create a new: blank item
+ KeyValues *item = new KeyValues( "Item" );
+
+ // Fill in data
+ char loc[ 128 ];
+ Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname );
+
+ char ansi[ 256 ];
+ AnsiText( loc, ansi, sizeof( ansi ) );
+
+ item->SetString( "Action", ansi );
+ item->SetWString( "Binding", Panel::KeyCodeModifiersToDisplayString( (KeyCode)kbMap->keycode, kbMap->modifiers ) );
+
+ // Find the binding
+ KeyBindingMap_t *bindingMap = m_pPanel->LookupBinding( kbMap->bindingname );
+ if ( bindingMap &&
+ bindingMap->helpstring )
+ {
+ AnsiText( bindingMap->helpstring, ansi, sizeof( ansi ) );
+ item->SetString( "Description", ansi);
+ }
+
+ item->SetPtr( "Item", kbMap );
+
+ sorted.Insert( item );
+ }
+
+ // Now try and find any "unbound" keys...
+ int mappings = m->entries.Count();
+ for ( j = 0; j < mappings; ++j )
+ {
+ KeyBindingMap_t *kbMap = &m->entries[ j ];
+
+ // See if it's bound
+ CUtlVector< BoundKey_t * > list;
+ m_pPanel->LookupBoundKeys( kbMap->bindingname, list );
+ if ( list.Count() > 0 )
+ continue;
+
+ // Not bound, add a placeholder entry
+ // Create a new: blank item
+ KeyValues *item = new KeyValues( "Item" );
+
+ // fill in data
+ char loc[ 128 ];
+ Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname );
+
+ char ansi[ 256 ];
+ AnsiText( loc, ansi, sizeof( ansi ) );
+
+ item->SetString( "Action", ansi );
+ item->SetWString( "Binding", L"" );
+ if ( kbMap->helpstring )
+ {
+ AnsiText( kbMap->helpstring, ansi, sizeof( ansi ) );
+ item->SetString( "Description", ansi );
+ }
+
+ item->SetPtr( "Unbound", kbMap );
+
+ sorted.Insert( item );
+ }
+ }
+
+ for ( j = sorted.FirstInorder() ; j != sorted.InvalidIndex(); j = sorted.NextInorder( j ) )
+ {
+ KeyValues *item = sorted[ j ];
+
+ // Add to list
+ m_pList->AddItem( item, 0, false, false );
+
+ item->deleteThis();
+ }
+
+ sorted.RemoveAll();
+}
+
+void CKeyBoardEditorPage::OnClearBinding( int item )
+{
+ // Find item for this row
+ KeyValues *kv = m_pList->GetItem(item );
+ if ( !kv )
+ {
+ return;
+ }
+
+ BoundKey_t *kbMap = reinterpret_cast< BoundKey_t * >( kv->GetPtr( "Item", 0 ) );
+ if ( !kbMap )
+ {
+ return;
+ }
+
+ kbMap->keycode = KEY_NONE;
+ kbMap->modifiers = 0;
+
+ PopulateList();
+}
+
+CKeyBoardEditorSheet::CKeyBoardEditorSheet( Panel *parent, Panel *panelToEdit, KeyBindingContextHandle_t handle )
+ : BaseClass( parent, "KeyBoardEditorSheet" ),
+ m_bSaveToExternalFile( false ),
+ m_Handle( handle ),
+ m_SaveFileName( UTL_INVAL_SYMBOL ),
+ m_SaveFilePathID( UTL_INVAL_SYMBOL )
+{
+ m_hPanel = panelToEdit;
+
+ SetSmallTabs( true );
+
+ // Create this sheet and add the subcontrols
+ CKeyBoardEditorPage *active = NULL;
+
+ int subCount = Panel::GetPanelsWithKeyBindingsCount( handle );
+ for ( int i = 0; i < subCount; ++i )
+ {
+ Panel *p = Panel::GetPanelWithKeyBindings( handle, i );
+ if ( !p )
+ continue;
+
+ // Don't display panels with no keymappings
+ if ( p->GetKeyMappingCount() == 0 )
+ continue;
+
+ CKeyBoardEditorPage *newPage = new CKeyBoardEditorPage( this, p, handle );
+ AddPage( newPage, p->GetName() );
+ if ( p == panelToEdit )
+ {
+ active = newPage;
+ }
+ }
+
+ if ( active )
+ {
+ SetActivePage( active );
+ }
+
+ LoadControlSettings( "resource/KeyBoardEditorSheet.res" );
+}
+
+void CKeyBoardEditorSheet::SetKeybindingsSaveFile( char const *filename, char const *pathID /*= 0*/ )
+{
+ Assert( filename );
+ m_bSaveToExternalFile = true;
+ m_SaveFileName = filename;
+ if ( pathID != NULL )
+ {
+ m_SaveFilePathID = pathID;
+ }
+ else
+ {
+ m_SaveFilePathID = UTL_INVAL_SYMBOL;
+ }
+}
+
+void CKeyBoardEditorSheet::OnSaveChanges()
+{
+ int c = GetNumPages();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ CKeyBoardEditorPage *page = static_cast< CKeyBoardEditorPage * >( GetPage( i ) );
+ page->OnSaveChanges();
+ }
+
+ if ( m_bSaveToExternalFile )
+ {
+ m_hPanel->SaveKeyBindingsToFile( m_Handle, m_SaveFileName.String(), m_SaveFilePathID.IsValid() ? m_SaveFilePathID.String() : NULL );
+ }
+ else
+ {
+ m_hPanel->SaveKeyBindings( m_Handle );
+ }
+}
+
+void CKeyBoardEditorSheet::OnRevert()
+{
+ int c = GetNumPages();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ CKeyBoardEditorPage *page = static_cast< CKeyBoardEditorPage * >( GetPage( i ) );
+ page->OnRevert();
+ }
+}
+
+void CKeyBoardEditorSheet::OnUseDefaults()
+{
+ int c = GetNumPages();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ CKeyBoardEditorPage *page = static_cast< CKeyBoardEditorPage * >( GetPage( i ) );
+ page->OnUseDefaults();
+ }
+}
+
+CKeyBoardEditorDialog::CKeyBoardEditorDialog( Panel *parent, Panel *panelToEdit, KeyBindingContextHandle_t handle )
+ : BaseClass( parent, "KeyBoardEditorDialog" )
+{
+ m_pSave = new Button( this, "Save", "#KBEditorSave", this, "save" );
+ m_pCancel = new Button( this, "Cancel", "#KBEditorCancel", this, "cancel" );
+ m_pRevert = new Button( this, "Revert", "#KBEditorRevert", this, "revert" );
+ m_pUseDefaults = new Button( this, "Defaults", "#KBEditorUseDefaults", this, "defaults" );
+
+ m_pKBEditor = new CKeyBoardEditorSheet( this, panelToEdit, handle );
+
+ LoadControlSettings( "resource/KeyBoardEditorDialog.res" );
+
+ SetTitle( "#KBEditorTitle", true );
+
+ SetSmallCaption( true );
+ SetMinimumSize( 640, 200 );
+ SetMinimizeButtonVisible( false );
+ SetMaximizeButtonVisible( false );
+ SetSizeable( true );
+ SetMoveable( true );
+ SetMenuButtonVisible( false );
+
+ SetVisible( true );
+
+ MoveToCenterOfScreen();
+}
+
+void CKeyBoardEditorDialog::OnCommand( char const *cmd )
+{
+ if ( !Q_stricmp( cmd, "save" ) )
+ {
+ m_pKBEditor->OnSaveChanges();
+ MarkForDeletion();
+ }
+ else if ( !Q_stricmp( cmd, "cancel" ) ||
+ !Q_stricmp( cmd, "Close" ) )
+ {
+ m_pKBEditor->OnRevert();
+ MarkForDeletion();
+ }
+ else if ( !Q_stricmp( cmd, "revert" ) )
+ {
+ m_pKBEditor->OnRevert();
+ }
+ else if ( !Q_stricmp( cmd, "defaults" ) )
+ {
+ m_pKBEditor->OnUseDefaults();
+ }
+ else
+ {
+ BaseClass::OnCommand( cmd );
+ }
+}
+
+void CKeyBoardEditorDialog::SetKeybindingsSaveFile( char const *filename, char const *pathID /*= 0*/ )
+{
+ m_pKBEditor->SetKeybindingsSaveFile( filename, pathID );
+}
diff --git a/mp/src/vgui2/vgui_controls/KeyRepeat.cpp b/mp/src/vgui2/vgui_controls/KeyRepeat.cpp
new file mode 100644
index 00000000..665fb02c
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/KeyRepeat.cpp
@@ -0,0 +1,98 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vgui_controls/pch_vgui_controls.h"
+#include <vgui_controls/KeyRepeat.h>
+
+// memdbgon must be the last include file in a .cpp file
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+vgui::KeyCode g_iCodesForAliases[FM_NUM_KEYREPEAT_ALIASES] =
+{
+ KEY_XBUTTON_UP,
+ KEY_XBUTTON_DOWN,
+ KEY_XBUTTON_LEFT,
+ KEY_XBUTTON_RIGHT,
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CKeyRepeatHandler::KeyDown( vgui::KeyCode code )
+{
+ int iIndex = GetIndexForCode(code);
+ if ( iIndex == -1 )
+ return;
+
+ if ( m_bAliasDown[ iIndex ] )
+ return;
+
+ Reset();
+ m_bAliasDown[ iIndex ] = true;
+ m_flNextKeyRepeat = system()->GetCurrentTime() + 0.4;
+ m_bHaveKeyDown = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CKeyRepeatHandler::KeyUp( vgui::KeyCode code )
+{
+ int iIndex = GetIndexForCode(code);
+ if ( iIndex == -1 )
+ return;
+
+ m_bAliasDown[ GetIndexForCode(code) ] = false;
+
+ m_bHaveKeyDown = false;
+ for ( int i = 0; i < FM_NUM_KEYREPEAT_ALIASES; i++ )
+ {
+ if ( m_bAliasDown[i] )
+ {
+ m_bHaveKeyDown = true;
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+vgui::KeyCode CKeyRepeatHandler::KeyRepeated( void )
+{
+ if ( IsPC() )
+ return BUTTON_CODE_NONE;
+
+ if ( !m_bHaveKeyDown )
+ return BUTTON_CODE_NONE;
+
+ if ( m_flNextKeyRepeat < system()->GetCurrentTime() )
+ {
+ for ( int i = 0; i < FM_NUM_KEYREPEAT_ALIASES; i++ )
+ {
+ if ( m_bAliasDown[i] )
+ {
+ m_flNextKeyRepeat = system()->GetCurrentTime() + m_flRepeatTimes[i];
+ return g_iCodesForAliases[i];
+ }
+ }
+ }
+
+ return BUTTON_CODE_NONE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CKeyRepeatHandler::SetKeyRepeatTime( vgui::KeyCode code, float flRepeat )
+{
+ int iIndex = GetIndexForCode(code);
+ Assert( iIndex != -1 );
+ m_flRepeatTimes[ iIndex ] = flRepeat;
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/Label.cpp b/mp/src/vgui2/vgui_controls/Label.cpp
new file mode 100644
index 00000000..26199308
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/Label.cpp
@@ -0,0 +1,1386 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <vgui/IInput.h>
+#include <vgui/ILocalize.h>
+#include <vgui/IPanel.h>
+#include <vgui/ISurface.h>
+#include <vgui/IScheme.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/Label.h>
+#include <vgui_controls/Image.h>
+#include <vgui_controls/TextImage.h>
+#include <vgui_controls/Controls.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+DECLARE_BUILD_FACTORY_DEFAULT_TEXT( Label, Label );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+Label::Label(Panel *parent, const char *panelName, const char *text) : BaseClass(parent, panelName)
+{
+ Init();
+
+ _textImage = new TextImage(text);
+ _textImage->SetColor(Color(0, 0, 0, 0));
+ SetText(text);
+ _textImageIndex = AddImage(_textImage, 0);
+
+ REGISTER_COLOR_AS_OVERRIDABLE( _disabledFgColor2, "disabledfgcolor2_override" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+Label::Label(Panel *parent, const char *panelName, const wchar_t *wszText) : BaseClass(parent, panelName)
+{
+ Init();
+
+ _textImage = new TextImage(wszText);
+ _textImage->SetColor(Color(0, 0, 0, 0));
+ SetText(wszText);
+ _textImageIndex = AddImage(_textImage, 0);
+
+ REGISTER_COLOR_AS_OVERRIDABLE( _disabledFgColor2, "disabledfgcolor2_override" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+Label::~Label()
+{
+ delete _textImage;
+ delete [] _associateName;
+ delete [] _fontOverrideName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Construct the label
+//-----------------------------------------------------------------------------
+void Label::Init()
+{
+ _contentAlignment = a_west;
+ _textColorState = CS_NORMAL;
+ _textInset[0] = 0;
+ _textInset[1] = 0;
+ _hotkey = 0;
+ _associate = NULL;
+ _associateName = NULL;
+ _fontOverrideName = NULL;
+ m_bWrap = false;
+ m_bCenterWrap = false;
+ m_bAutoWideToContents = false;
+ m_bUseProportionalInsets = false;
+ m_bAutoWideDirty = false;
+
+// SetPaintBackgroundEnabled(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set whether the text is displayed bright or dull
+//-----------------------------------------------------------------------------
+void Label::SetTextColorState(EColorState state)
+{
+ if (_textColorState != state)
+ {
+ _textColorState = state;
+ InvalidateLayout();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the full size of the contained content
+//-----------------------------------------------------------------------------
+void Label::GetContentSize(int &wide, int &tall)
+{
+ if( GetFont() == INVALID_FONT ) // we haven't loaded our font yet, so load it now
+ {
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+ if ( pScheme )
+ {
+ SetFont( pScheme->GetFont( "Default", IsProportional() ) );
+ }
+ }
+
+
+ int tx0, ty0, tx1, ty1;
+ ComputeAlignment(tx0, ty0, tx1, ty1);
+
+ // the +8 is padding to the content size
+ // the code which uses it should really set that itself;
+ // however a lot of existing code relies on this
+ wide = (tx1 - tx0) + _textInset[0];
+
+ // get the size of the text image and remove it
+ int iWide, iTall;
+ _textImage->GetSize(iWide, iTall);
+ wide -= iWide;
+ // get the full, untruncated (no elipsis) size of the text image.
+ _textImage->GetContentSize(iWide, iTall);
+ wide += iWide;
+
+ // addin the image offsets as well
+ for (int i=0; i < _imageDar.Size(); i++)
+ wide += _imageDar[i].offset;
+
+ tall = max((ty1 - ty0) + _textInset[1], iTall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculate the keyboard key that is a hotkey
+//-----------------------------------------------------------------------------
+wchar_t Label::CalculateHotkey(const char *text)
+{
+ for (const char *ch = text; *ch != 0; ch++)
+ {
+ if (*ch == '&')
+ {
+ // get the next character
+ ch++;
+
+ if (*ch == '&')
+ {
+ // just an &
+ continue;
+ }
+ else if (*ch == 0)
+ {
+ break;
+ }
+ else if (V_isalnum(*ch))
+ {
+ // found the hotkey
+ return (wchar_t)tolower(*ch);
+ }
+ }
+ }
+
+ return '\0';
+}
+
+wchar_t Label::CalculateHotkey(const wchar_t *text)
+{
+ if( text )
+ {
+ for (const wchar_t *ch = text; *ch != 0; ch++)
+ {
+ if (*ch == '&')
+ {
+ // get the next character
+ ch++;
+
+ if (*ch == '&')
+ {
+ // just an &
+ continue;
+ }
+ else if (*ch == 0)
+ {
+ break;
+ }
+ else if (iswalnum(*ch))
+ {
+ // found the hotkey
+ return (wchar_t)towlower(*ch);
+ }
+ }
+ }
+ }
+
+ return '\0';
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if this label has a hotkey that is the key passed in.
+//-----------------------------------------------------------------------------
+Panel *Label::HasHotkey(wchar_t key)
+{
+#ifdef VGUI_HOTKEYS_ENABLED
+ if ( iswalnum( key ) )
+ key = towlower( key );
+
+ if (_hotkey == key)
+ return this;
+#endif
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the hotkey for this label
+//-----------------------------------------------------------------------------
+void Label::SetHotkey(wchar_t ch)
+{
+ _hotkey = ch;
+}
+
+wchar_t Label::GetHotKey()
+{
+ return _hotkey;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle a hotkey by passing on focus to associate
+//-----------------------------------------------------------------------------
+void Label::OnHotkeyPressed()
+{
+ // we can't accept focus, but if we are associated to a control give it to that
+ if (_associate.Get() && !IsBuildModeActive())
+ {
+ _associate->RequestFocus();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Redirect mouse pressed to giving focus to associate
+//-----------------------------------------------------------------------------
+void Label::OnMousePressed(MouseCode code)
+{
+ if (_associate.Get() && !IsBuildModeActive())
+ {
+ _associate->RequestFocus();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the text in the label
+//-----------------------------------------------------------------------------
+void Label::GetText(char *textOut, int bufferLen)
+{
+ _textImage->GetText(textOut, bufferLen);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the text in the label
+//-----------------------------------------------------------------------------
+void Label::GetText(wchar_t *textOut, int bufLenInBytes)
+{
+ _textImage->GetText(textOut, bufLenInBytes);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Take the string and looks it up in the localization file
+// to convert it to unicode
+// Setting the text will not set the size of the label.
+// Set the size explicitly or use setToContent()
+//-----------------------------------------------------------------------------
+void Label::SetText(const char *text)
+{
+ // if set to null, just make blank
+ if (!text)
+ {
+ text = "";
+ }
+
+ // let the text image do the translation itself
+ _textImage->SetText(text);
+
+ if( text[0] == '#' )
+ {
+ SetHotkey(CalculateHotkey(g_pVGuiLocalize->Find(text)));
+ }
+ else
+ {
+ SetHotkey(CalculateHotkey(text));
+ }
+
+ m_bAutoWideDirty = m_bAutoWideToContents;
+
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set unicode text directly
+//-----------------------------------------------------------------------------
+void Label::SetText(const wchar_t *unicodeString, bool bClearUnlocalizedSymbol)
+{
+ m_bAutoWideDirty = m_bAutoWideToContents;
+
+ if ( unicodeString && _textImage->GetUText() && !Q_wcscmp(unicodeString,_textImage->GetUText()) )
+ return;
+
+ _textImage->SetText(unicodeString, bClearUnlocalizedSymbol);
+
+//!! need to calculate hotkey from translated string
+ SetHotkey(CalculateHotkey(unicodeString));
+
+ InvalidateLayout(); // possible that the textimage needs to expand
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: updates localized text
+//-----------------------------------------------------------------------------
+void Label::OnDialogVariablesChanged(KeyValues *dialogVariables )
+{
+ StringIndex_t index = _textImage->GetUnlocalizedTextSymbol();
+ if (index != INVALID_LOCALIZE_STRING_INDEX)
+ {
+ // reconstruct the string from the variables
+ wchar_t buf[1024];
+ g_pVGuiLocalize->ConstructString(buf, sizeof(buf), index, dialogVariables);
+ SetText(buf);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Additional offset at the Start of the text (from whichever side it is aligned)
+//-----------------------------------------------------------------------------
+void Label::SetTextInset(int xInset, int yInset)
+{
+ _textInset[0] = xInset;
+ _textInset[1] = yInset;
+
+ int wide, tall;
+ GetSize( wide, tall);
+ _textImage->SetDrawWidth(wide - _textInset[0]);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Label::GetTextInset(int *xInset, int *yInset )
+{
+ *xInset = _textInset[0];
+ *yInset = _textInset[1];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the enabled state
+//-----------------------------------------------------------------------------
+void Label::SetEnabled(bool state)
+{
+ Panel::SetEnabled(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculates where in the panel the content resides
+// Input : &tx0 - [out] position of the content
+// &ty0 -
+// &tx1 -
+// &ty1 -
+// Note: horizontal alignment is west if the image dar has
+// more than one image in it, this is because we use image sizes
+// to determine layout in classes for example, Menu.
+//-----------------------------------------------------------------------------
+void Label::ComputeAlignment(int &tx0, int &ty0, int &tx1, int &ty1)
+{
+ int wide, tall;
+ GetPaintSize(wide, tall);
+ int tWide,tTall;
+
+ // text bounding box
+ tx0 = 0;
+ ty0 = 0;
+
+ // loop through all the images and calculate the complete bounds
+ int maxX = 0, maxY = 0;
+
+ int actualXAlignment = _contentAlignment;
+ for (int i = 0; i < _imageDar.Count(); i++)
+ {
+ TImageInfo &imageInfo = _imageDar[i];
+ IImage *image = imageInfo.image;
+ if (!image)
+ continue; // skip over null images
+
+ // add up the bounds
+ int iWide, iTall;
+ image->GetSize(iWide, iTall);
+ if (iWide > wide) // if the image is larger than the label just do a west alignment
+ actualXAlignment = Label::a_west;
+
+ // get the max height
+ maxY = max(maxY, iTall);
+ maxX += iWide;
+
+ // add the offset to x
+ maxX += imageInfo.offset;
+ }
+
+ tWide = maxX;
+ tTall = maxY;
+
+ // x align text
+ switch (actualXAlignment)
+ {
+ // left
+ case Label::a_northwest:
+ case Label::a_west:
+ case Label::a_southwest:
+ {
+ tx0 = 0;
+ break;
+ }
+ // center
+ case Label::a_north:
+ case Label::a_center:
+ case Label::a_south:
+ {
+ tx0 = (wide - tWide) / 2;
+ break;
+ }
+ // right
+ case Label::a_northeast:
+ case Label::a_east:
+ case Label::a_southeast:
+ {
+ tx0 = wide - tWide;
+ break;
+ }
+ }
+
+ // y align text
+ switch (_contentAlignment)
+ {
+ //top
+ case Label::a_northwest:
+ case Label::a_north:
+ case Label::a_northeast:
+ {
+ ty0 = 0;
+ break;
+ }
+ // center
+ case Label::a_west:
+ case Label::a_center:
+ case Label::a_east:
+ {
+ ty0 = (tall - tTall) / 2;
+ break;
+ }
+ // south
+ case Label::a_southwest:
+ case Label::a_south:
+ case Label::a_southeast:
+ {
+ ty0 = tall - tTall;
+ break;
+ }
+ }
+
+ tx1 = tx0 + tWide;
+ ty1 = ty0 + tTall;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: overridden main drawing function for the panel
+//-----------------------------------------------------------------------------
+void Label::Paint()
+{
+ int tx0, ty0, tx1, ty1;
+ ComputeAlignment(tx0, ty0, tx1, ty1);
+
+ // calculate who our associate is if we haven't already
+ if (_associateName)
+ {
+ SetAssociatedControl(FindSiblingByName(_associateName));
+ delete [] _associateName;
+ _associateName = NULL;
+ }
+
+ int labelWide, labelTall;
+ GetSize(labelWide, labelTall);
+ int x = tx0, y = _textInset[1] + ty0;
+ int imageYPos = 0; // a place to save the y offset for when we draw the disable version of the image
+
+ // draw the set of images
+ for (int i = 0; i < _imageDar.Count(); i++)
+ {
+ TImageInfo &imageInfo = _imageDar[i];
+ IImage *image = imageInfo.image;
+ if (!image)
+ continue; // skip over null images
+
+ // add the offset to x
+ x += imageInfo.offset;
+
+ // if this is the text image then add its inset to the left or from the right
+ if (i == _textImageIndex)
+ {
+ switch ( _contentAlignment )
+ {
+ // left
+ case Label::a_northwest:
+ case Label::a_west:
+ case Label::a_southwest:
+ {
+ x += _textInset[0];
+ break;
+ }
+ // right
+ case Label::a_northeast:
+ case Label::a_east:
+ case Label::a_southeast:
+ {
+ x -= _textInset[0];
+ break;
+ }
+ }
+ }
+
+ // see if the image is in a fixed position
+ if (imageInfo.xpos >= 0)
+ {
+ x = imageInfo.xpos;
+ }
+
+ // draw
+ imageYPos = y;
+ image->SetPos(x, y);
+
+ // fix up y for center-aligned text
+ if (_contentAlignment == Label::a_west || _contentAlignment == Label::a_center || _contentAlignment == Label::a_east)
+ {
+ int iw, it;
+ image->GetSize(iw, it);
+ if (it < (ty1 - ty0))
+ {
+ imageYPos = ((ty1 - ty0) - it) / 2 + y;
+ image->SetPos(x, ((ty1 - ty0) - it) / 2 + y);
+ }
+ }
+
+ // don't resize the image unless its too big
+ if (imageInfo.width >= 0)
+ {
+ int w, t;
+ image->GetSize(w, t);
+ if (w > imageInfo.width)
+ {
+ image->SetSize(imageInfo.width, t);
+ }
+ }
+
+ // if it's the basic text image then draw specially
+ if (image == _textImage)
+ {
+ if (IsEnabled())
+ {
+ if (_associate.Get() && ipanel()->HasParent(input()->GetFocus(), _associate->GetVPanel()))
+ {
+ _textImage->SetColor(_associateColor);
+ }
+ else
+ {
+ _textImage->SetColor(GetFgColor());
+ }
+
+ _textImage->Paint();
+ }
+ else
+ {
+ // draw disabled version, with embossed look
+ // offset image
+ _textImage->SetPos(x + 1, imageYPos + 1);
+ _textImage->SetColor(_disabledFgColor1);
+ _textImage->Paint();
+
+ surface()->DrawFlushText();
+
+ // overlayed image
+ _textImage->SetPos(x, imageYPos);
+ _textImage->SetColor(_disabledFgColor2);
+ _textImage->Paint();
+ }
+ }
+ else
+ {
+ image->Paint();
+ }
+
+ // add the image size to x
+ int wide, tall;
+ image->GetSize(wide, tall);
+ x += wide;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper function, draws a simple line with dashing parameters
+//-----------------------------------------------------------------------------
+void Label::DrawDashedLine(int x0, int y0, int x1, int y1, int dashLen, int gapLen)
+{
+ // work out which way the line goes
+ if ((x1 - x0) > (y1 - y0))
+ {
+ // x direction line
+ while (1)
+ {
+ if (x0 + dashLen > x1)
+ {
+ // draw partial
+ surface()->DrawFilledRect(x0, y0, x1, y1);
+ }
+ else
+ {
+ surface()->DrawFilledRect(x0, y0, x0 + dashLen, y1);
+ }
+
+ x0 += dashLen;
+
+ if (x0 + gapLen > x1)
+ break;
+
+ x0 += gapLen;
+ }
+ }
+ else
+ {
+ // y direction
+ while (1)
+ {
+ if (y0 + dashLen > y1)
+ {
+ // draw partial
+ surface()->DrawFilledRect(x0, y0, x1, y1);
+ }
+ else
+ {
+ surface()->DrawFilledRect(x0, y0, x1, y0 + dashLen);
+ }
+
+ y0 += dashLen;
+
+ if (y0 + gapLen > y1)
+ break;
+
+ y0 += gapLen;
+ }
+ }
+}
+
+void Label::SetContentAlignment(Alignment alignment)
+{
+ _contentAlignment=alignment;
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Size the width of the label to its contents - only works from in ApplySchemeSettings or PerformLayout()
+//-----------------------------------------------------------------------------
+void Label::SizeToContents()
+{
+ int wide, tall;
+ GetContentSize(wide, tall);
+
+ SetSize(wide, tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the font the text is drawn in
+//-----------------------------------------------------------------------------
+void Label::SetFont(HFont font)
+{
+ _textImage->SetFont(font);
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Resond to resizing of the panel
+//-----------------------------------------------------------------------------
+void Label::OnSizeChanged(int wide, int tall)
+{
+ InvalidateLayout();
+ Panel::OnSizeChanged(wide, tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the font the textImage is drawn in.
+//-----------------------------------------------------------------------------
+HFont Label::GetFont()
+{
+ return _textImage->GetFont();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the foreground color of the Label
+//-----------------------------------------------------------------------------
+void Label::SetFgColor(Color color)
+{
+ if (!(GetFgColor() == color))
+ {
+ BaseClass::SetFgColor(color);
+ _textImage->SetColor(color);
+ Repaint();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the foreground color of the Label
+//-----------------------------------------------------------------------------
+Color Label::GetFgColor()
+{
+ Color clr = Panel::GetFgColor();
+ return clr;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the foreground color 1 color of the Label
+//-----------------------------------------------------------------------------
+void Label::SetDisabledFgColor1(Color color)
+{
+ _disabledFgColor1 = color;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Label::SetDisabledFgColor2(Color color)
+{
+ _disabledFgColor2 = color;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Color Label::GetDisabledFgColor1()
+{
+ return _disabledFgColor1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Color Label::GetDisabledFgColor2()
+{
+ return _disabledFgColor2;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+TextImage *Label::GetTextImage()
+{
+ return _textImage;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool Label::RequestInfo(KeyValues *outputData)
+{
+ if (!stricmp(outputData->GetName(), "GetText"))
+ {
+ wchar_t wbuf[256];
+ _textImage->GetText(wbuf, 255);
+ outputData->SetWString("text", wbuf);
+ return true;
+ }
+
+ return Panel::RequestInfo(outputData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the text from the message
+//-----------------------------------------------------------------------------
+void Label::OnSetText(KeyValues *params)
+{
+ KeyValues *pkvText = params->FindKey("text", false);
+ if (!pkvText)
+ return;
+
+ if (pkvText->GetDataType() == KeyValues::TYPE_STRING)
+ {
+ SetText(pkvText->GetString());
+ }
+ else if (pkvText->GetDataType() == KeyValues::TYPE_WSTRING)
+ {
+ SetText(pkvText->GetWString());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add an image to the list
+// returns the index the image was placed in
+//-----------------------------------------------------------------------------
+int Label::AddImage(IImage *image, int offset)
+{
+ int newImage = _imageDar.AddToTail();
+ _imageDar[newImage].image = image;
+ _imageDar[newImage].offset = (short)offset;
+ _imageDar[newImage].xpos = -1;
+ _imageDar[newImage].width = -1;
+ InvalidateLayout();
+ return newImage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: removes all images from the list
+// user is responsible for the memory
+//-----------------------------------------------------------------------------
+void Label::ClearImages()
+{
+ _imageDar.RemoveAll();
+ _textImageIndex = -1;
+}
+
+void Label::ResetToSimpleTextImage()
+{
+ ClearImages();
+ _textImageIndex = AddImage(_textImage, 0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Multiple image handling
+// Images are drawn from left to right across the label, ordered by index
+// By default there is a TextImage in position 0
+// Set the contents of an IImage in the IImage array.
+//-----------------------------------------------------------------------------
+void Label::SetImageAtIndex(int index, IImage *image, int offset)
+{
+ EnsureImageCapacity(index);
+// Assert( image );
+
+ if ( _imageDar[index].image != image || _imageDar[index].offset != offset)
+ {
+ _imageDar[index].image = image;
+ _imageDar[index].offset = (short)offset;
+ InvalidateLayout();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get an IImage in the IImage array.
+//-----------------------------------------------------------------------------
+IImage *Label::GetImageAtIndex(int index)
+{
+ if ( _imageDar.IsValidIndex( index ) )
+ return _imageDar[index].image;
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the number of images in the array.
+//-----------------------------------------------------------------------------
+int Label::GetImageCount()
+{
+ return _imageDar.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move where the default text image is within the image array
+// (it starts in position 0)
+// Input : newIndex -
+// Output : int - the index the default text image was previously in
+//-----------------------------------------------------------------------------
+int Label::SetTextImageIndex(int newIndex)
+{
+ if (newIndex == _textImageIndex)
+ return _textImageIndex;
+
+ EnsureImageCapacity(newIndex);
+
+ int oldIndex = _textImageIndex;
+ if ( _textImageIndex >= 0 )
+ {
+ _imageDar[_textImageIndex].image = NULL;
+ }
+ if (newIndex > -1)
+ {
+ _imageDar[newIndex].image = _textImage;
+ }
+ _textImageIndex = newIndex;
+ return oldIndex;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ensure that the maxIndex will be a valid index
+//-----------------------------------------------------------------------------
+void Label::EnsureImageCapacity(int maxIndex)
+{
+ while (_imageDar.Size() <= maxIndex)
+ {
+ AddImage(NULL, 0);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the offset in pixels before the image
+//-----------------------------------------------------------------------------
+void Label::SetImagePreOffset(int index, int preOffset)
+{
+ if (_imageDar.IsValidIndex(index) && _imageDar[index].offset != preOffset)
+ {
+ _imageDar[index].offset = (short)preOffset;
+ InvalidateLayout();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: fixes the layout bounds of the image within the label
+//-----------------------------------------------------------------------------
+void Label::SetImageBounds(int index, int x, int width)
+{
+ _imageDar[index].xpos = (short)x;
+ _imageDar[index].width = (short)width;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Labels can be associated with controls, and alter behaviour based on the associates behaviour
+// If the associate is disabled, so are we
+// If the associate has focus, we may alter how we draw
+// If we get a hotkey press or focus message, we forward the focus to the associate
+//-----------------------------------------------------------------------------
+void Label::SetAssociatedControl(Panel *control)
+{
+ if (control != this)
+ {
+ _associate = control;
+ }
+ else
+ {
+ // don't let the associate ever be set to be ourself
+ _associate = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called after a panel requests focus to fix up the whole chain
+//-----------------------------------------------------------------------------
+void Label::OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel)
+{
+ if (_associate.Get() && subFocus == GetVPanel() && !IsBuildModeActive())
+ {
+ // we've received focus; pass the focus onto the associate instead
+ _associate->RequestFocus();
+ }
+ else
+ {
+ BaseClass::OnRequestFocus(subFocus, defaultPanel);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets custom settings from the scheme file
+//-----------------------------------------------------------------------------
+void Label::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ if (_fontOverrideName)
+ {
+ // use the custom specified font since we have one set
+ SetFont(pScheme->GetFont(_fontOverrideName, IsProportional()));
+ }
+ if ( GetFont() == INVALID_FONT )
+ {
+ SetFont( pScheme->GetFont( "Default", IsProportional() ) );
+ }
+
+ if ( m_bWrap || m_bCenterWrap )
+ {
+ //tell it how big it is
+ int wide, tall;
+ Panel::GetSize(wide, tall);
+ wide -= _textInset[0]; // take inset into account
+ _textImage->SetSize(wide, tall);
+
+ _textImage->RecalculateNewLinePositions();
+ }
+ else
+ {
+ // if you don't set the size of the image, many, many buttons will break - we might want to look into fixing this all over the place later
+ int wide, tall;
+ _textImage->GetContentSize(wide, tall);
+ _textImage->SetSize(wide, tall);
+ }
+
+ if ( m_bAutoWideToContents )
+ {
+ m_bAutoWideDirty = true;
+ HandleAutoSizing();
+ }
+
+ // clear out any the images, since they will have been invalidated
+ for (int i = 0; i < _imageDar.Count(); i++)
+ {
+ IImage *image = _imageDar[i].image;
+ if (!image)
+ continue; // skip over null images
+
+ if (i == _textImageIndex)
+ continue;
+
+ _imageDar[i].image = NULL;
+ }
+
+ SetDisabledFgColor1(GetSchemeColor("Label.DisabledFgColor1", pScheme));
+ SetDisabledFgColor2(GetSchemeColor("Label.DisabledFgColor2", pScheme));
+ SetBgColor(GetSchemeColor("Label.BgColor", pScheme));
+
+ switch (_textColorState)
+ {
+ case CS_DULL:
+ SetFgColor(GetSchemeColor("Label.TextDullColor", pScheme));
+ break;
+ case CS_BRIGHT:
+ SetFgColor(GetSchemeColor("Label.TextBrightColor", pScheme));
+ break;
+ case CS_NORMAL:
+ default:
+ SetFgColor(GetSchemeColor("Label.TextColor", pScheme));
+ break;
+ }
+
+ _associateColor = GetSchemeColor("Label.SelectedTextColor", pScheme);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Label::GetSettings( KeyValues *outResourceData )
+{
+ // panel settings
+ Panel::GetSettings( outResourceData );
+
+ // label settings
+ char buf[256];
+ _textImage->GetUnlocalizedText( buf, 255 );
+ if (!strnicmp(buf, "#var_", 5))
+ {
+ // strip off the variable prepender on save
+ outResourceData->SetString( "labelText", buf + 5 );
+ }
+ else
+ {
+ outResourceData->SetString( "labelText", buf );
+ }
+
+ const char *alignmentString = "";
+ switch ( _contentAlignment )
+ {
+ case a_northwest: alignmentString = "north-west"; break;
+ case a_north: alignmentString = "north"; break;
+ case a_northeast: alignmentString = "north-east"; break;
+ case a_center: alignmentString = "center"; break;
+ case a_east: alignmentString = "east"; break;
+ case a_southwest: alignmentString = "south-west"; break;
+ case a_south: alignmentString = "south"; break;
+ case a_southeast: alignmentString = "south-east"; break;
+ case a_west:
+ default: alignmentString = "west"; break;
+ }
+
+ outResourceData->SetString( "textAlignment", alignmentString );
+
+ if (_associate)
+ {
+ outResourceData->SetString("associate", _associate->GetName());
+ }
+
+ outResourceData->SetInt("dulltext", (int)(_textColorState == CS_DULL));
+ outResourceData->SetInt("brighttext", (int)(_textColorState == CS_BRIGHT));
+
+ if (_fontOverrideName)
+ {
+ outResourceData->SetString("font", _fontOverrideName);
+ }
+
+ outResourceData->SetInt("wrap", ( m_bWrap ? 1 : 0 ));
+ outResourceData->SetInt("centerwrap", ( m_bCenterWrap ? 1 : 0 ));
+
+ if ( m_bUseProportionalInsets )
+ {
+ outResourceData->SetInt("textinsetx", scheme()->GetProportionalNormalizedValueEx( GetScheme(), _textInset[0] ) );
+ outResourceData->SetInt("textinsety", _textInset[1]);
+ }
+ else
+ {
+ outResourceData->SetInt("textinsetx", _textInset[0]);
+ outResourceData->SetInt("textinsety", _textInset[1]);
+ }
+ outResourceData->SetInt("auto_wide_tocontents", ( m_bAutoWideToContents ? 1 : 0 ));
+ outResourceData->SetInt("use_proportional_insets", ( m_bUseProportionalInsets ? 1 : 0 ));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Label::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings( inResourceData );
+
+ // label settings
+ const char *labelText = inResourceData->GetString( "labelText", NULL );
+ if ( labelText )
+ {
+ if (labelText[0] == '%' && labelText[strlen(labelText) - 1] == '%')
+ {
+ // it's a variable, set it to be a special variable localized string
+ wchar_t unicodeVar[256];
+ g_pVGuiLocalize->ConvertANSIToUnicode(labelText, unicodeVar, sizeof(unicodeVar));
+
+ char var[256];
+ _snprintf(var, sizeof(var), "#var_%s", labelText);
+ g_pVGuiLocalize->AddString(var + 1, unicodeVar, "");
+ SetText(var);
+ }
+ else
+ {
+ SetText(labelText);
+ }
+ }
+
+ // text alignment
+ const char *alignmentString = inResourceData->GetString( "textAlignment", "" );
+ int align = -1;
+
+ if ( !stricmp(alignmentString, "north-west") )
+ {
+ align = a_northwest;
+ }
+ else if ( !stricmp(alignmentString, "north") )
+ {
+ align = a_north;
+ }
+ else if ( !stricmp(alignmentString, "north-east") )
+ {
+ align = a_northeast;
+ }
+ else if ( !stricmp(alignmentString, "west") )
+ {
+ align = a_west;
+ }
+ else if ( !stricmp(alignmentString, "center") )
+ {
+ align = a_center;
+ }
+ else if ( !stricmp(alignmentString, "east") )
+ {
+ align = a_east;
+ }
+ else if ( !stricmp(alignmentString, "south-west") )
+ {
+ align = a_southwest;
+ }
+ else if ( !stricmp(alignmentString, "south") )
+ {
+ align = a_south;
+ }
+ else if ( !stricmp(alignmentString, "south-east") )
+ {
+ align = a_southeast;
+ }
+
+ if ( align != -1 )
+ {
+ SetContentAlignment( (Alignment)align );
+ }
+
+ // the control we are to be associated with may not have been created yet,
+ // so keep a pointer to it's name and calculate it when we can
+ const char *associateName = inResourceData->GetString("associate", "");
+ if (associateName[0] != 0)
+ {
+ int len = Q_strlen(associateName) + 1;
+ _associateName = new char[ len ];
+ Q_strncpy( _associateName, associateName, len );
+ }
+
+ if (inResourceData->GetInt("dulltext", 0) == 1)
+ {
+ SetTextColorState(CS_DULL);
+ }
+ else if (inResourceData->GetInt("brighttext", 0) == 1)
+ {
+ SetTextColorState(CS_BRIGHT);
+ }
+ else
+ {
+ SetTextColorState(CS_NORMAL);
+ }
+
+ // font settings
+ const char *overrideFont = inResourceData->GetString("font", "");
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+
+ if (*overrideFont)
+ {
+ delete [] _fontOverrideName;
+ int len = Q_strlen(overrideFont) + 1;
+ _fontOverrideName = new char[ len ];
+ Q_strncpy(_fontOverrideName, overrideFont, len );
+ SetFont(pScheme->GetFont(_fontOverrideName, IsProportional()));
+ }
+ else if (_fontOverrideName)
+ {
+ delete [] _fontOverrideName;
+ _fontOverrideName = NULL;
+ SetFont(pScheme->GetFont("Default", IsProportional()));
+ }
+
+ bool bWrapText = inResourceData->GetInt("centerwrap", 0) > 0;
+ SetCenterWrap( bWrapText );
+
+ m_bAutoWideToContents = inResourceData->GetInt("auto_wide_tocontents", 0) > 0;
+
+ bWrapText = inResourceData->GetInt("wrap", 0) > 0;
+ SetWrap( bWrapText );
+
+ int inset_x = inResourceData->GetInt("textinsetx", _textInset[0]);
+ int inset_y = inResourceData->GetInt("textinsety", _textInset[1]);
+ // Had to play it safe and add a new key for backwards compatibility
+ m_bUseProportionalInsets = inResourceData->GetInt("use_proportional_insets", 0) > 0;
+ if ( m_bUseProportionalInsets )
+ {
+ inset_x = scheme()->GetProportionalScaledValueEx( GetScheme(), inset_x );
+ }
+
+ SetTextInset( inset_x, inset_y );
+
+ bool bAllCaps = inResourceData->GetInt("allcaps", 0) > 0;
+ SetAllCaps( bAllCaps );
+
+ InvalidateLayout(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a description of the label string
+//-----------------------------------------------------------------------------
+const char *Label::GetDescription( void )
+{
+ static char buf[1024];
+ Q_snprintf(buf, sizeof(buf), "%s, string labelText, string associate, alignment textAlignment, int wrap, int dulltext, int brighttext, string font", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If a label has images in _imageDar, the size
+// must take those into account as well as the textImage part
+// Textimage part will shrink ONLY if there is not enough room.
+//-----------------------------------------------------------------------------
+void Label::PerformLayout()
+{
+ int wide, tall;
+ Panel::GetSize(wide, tall);
+ wide -= _textInset[0]; // take inset into account
+
+ // if we just have a textImage, this is trivial.
+ if (_imageDar.Count() == 1 && _textImageIndex == 0)
+ {
+ if ( m_bWrap || m_bCenterWrap )
+ {
+ int twide, ttall;
+ _textImage->GetContentSize(twide, ttall);
+ _textImage->SetSize(wide, ttall);
+ }
+ else
+ {
+ int twide, ttall;
+ _textImage->GetContentSize(twide, ttall);
+
+ // tell the textImage how much space we have to draw in
+ if ( wide < twide)
+ _textImage->SetSize(wide, ttall);
+ else
+ _textImage->SetSize(twide, ttall);
+ }
+
+ HandleAutoSizing();
+
+ HandleAutoSizing();
+
+ return;
+ }
+
+ // assume the images in the dar cannot be resized, and if
+ // the images + the textimage are too wide we shring the textimage part
+ if (_textImageIndex < 0)
+ return;
+
+ // get the size of the images
+ int widthOfImages = 0;
+ for (int i = 0; i < _imageDar.Count(); i++)
+ {
+ TImageInfo &imageInfo = _imageDar[i];
+ IImage *image = imageInfo.image;
+ if (!image)
+ continue; // skip over null images
+
+ if (i == _textImageIndex)
+ continue;
+
+ // add up the bounds
+ int iWide, iTall;
+ image->GetSize(iWide, iTall);
+ widthOfImages += iWide;
+ }
+
+ // so this is how much room we have to draw the textimage part
+ int spaceAvail = wide - widthOfImages;
+
+ // if we have no space at all just leave everything as is.
+ if (spaceAvail < 0)
+ return;
+
+ int twide, ttall;
+ _textImage->GetSize (twide, ttall);
+ // tell the textImage how much space we have to draw in
+ _textImage->SetSize(spaceAvail, ttall);
+
+ HandleAutoSizing();
+}
+
+void Label::SetWrap( bool bWrap )
+{
+ m_bWrap = bWrap;
+ _textImage->SetWrap( m_bWrap );
+
+ InvalidateLayout();
+}
+
+void Label::SetCenterWrap( bool bWrap )
+{
+ m_bCenterWrap = bWrap;
+ _textImage->SetCenterWrap( m_bCenterWrap );
+
+ InvalidateLayout();
+}
+
+void Label::SetAllCaps( bool bAllCaps )
+{
+ m_bAllCaps = bAllCaps;
+ _textImage->SetAllCaps( m_bAllCaps );
+
+ InvalidateLayout();
+}
+
+void Label::HandleAutoSizing( void )
+{
+ if ( m_bAutoWideDirty )
+ {
+ m_bAutoWideDirty = false;
+
+ // Only change our width to match our content
+ int wide, tall;
+ GetContentSize(wide, tall);
+ SetSize(wide, GetTall());
+ }
+}
+
+
+
diff --git a/mp/src/vgui2/vgui_controls/ListPanel.cpp b/mp/src/vgui2/vgui_controls/ListPanel.cpp
new file mode 100644
index 00000000..f91230dc
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ListPanel.cpp
@@ -0,0 +1,3283 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdio.h>
+
+#define PROTECTED_THINGS_DISABLE
+
+#include <vgui/Cursor.h>
+#include <vgui/IInput.h>
+#include <vgui/ILocalize.h>
+#include <vgui/IPanel.h>
+#include <vgui/IScheme.h>
+#include <vgui/ISystem.h>
+#include <vgui/ISurface.h>
+#include <vgui/IVGui.h>
+#include <vgui/KeyCode.h>
+#include <KeyValues.h>
+#include <vgui/MouseCode.h>
+
+#include <vgui_controls/Button.h>
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/ImageList.h>
+#include <vgui_controls/ImagePanel.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/ListPanel.h>
+#include <vgui_controls/ScrollBar.h>
+#include <vgui_controls/TextImage.h>
+#include <vgui_controls/Menu.h>
+#include <vgui_controls/Tooltip.h>
+
+// memdbgon must be the last include file in a .cpp file
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+enum
+{
+ WINDOW_BORDER_WIDTH=2 // the width of the window's border
+};
+
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef clamp
+#define clamp( val, min, max ) ( ((val) > (max)) ? (max) : ( ((val) < (min)) ? (min) : (val) ) )
+#endif
+
+//-----------------------------------------------------------------------------
+//
+// Button at the top of columns used to re-sort
+//
+//-----------------------------------------------------------------------------
+class ColumnButton : public Button
+{
+public:
+ ColumnButton(vgui::Panel *parent, const char *name, const char *text);
+
+ // Inherited from Button
+ virtual void ApplySchemeSettings(IScheme *pScheme);
+ virtual void OnMousePressed(MouseCode code);
+
+ void OpenColumnChoiceMenu();
+};
+
+ColumnButton::ColumnButton(vgui::Panel *parent, const char *name, const char *text) : Button(parent, name, text)
+{
+ SetBlockDragChaining( true );
+}
+
+void ColumnButton::ApplySchemeSettings(IScheme *pScheme)
+{
+ Button::ApplySchemeSettings(pScheme);
+
+ SetContentAlignment(Label::a_west);
+ SetFont(pScheme->GetFont("DefaultSmall", IsProportional()));
+}
+
+// Don't request focus.
+// This will keep items in the listpanel selected.
+void ColumnButton::OnMousePressed(MouseCode code)
+{
+ if (!IsEnabled())
+ return;
+
+ if (code == MOUSE_RIGHT)
+ {
+ OpenColumnChoiceMenu();
+ return;
+ }
+
+ if (!IsMouseClickEnabled(code))
+ return;
+
+ if (IsUseCaptureMouseEnabled())
+ {
+ {
+ SetSelected(true);
+ Repaint();
+ }
+
+ // lock mouse input to going to this button
+ input()->SetMouseCapture(GetVPanel());
+ }
+}
+
+void ColumnButton::OpenColumnChoiceMenu()
+{
+ CallParentFunction(new KeyValues("OpenColumnChoiceMenu"));
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Purpose: Handles resizing of columns
+//
+//-----------------------------------------------------------------------------
+class Dragger : public Panel
+{
+public:
+ Dragger(int column);
+
+ // Inherited from Panel
+ virtual void OnMousePressed(MouseCode code);
+ virtual void OnMouseDoublePressed(MouseCode code);
+ virtual void OnMouseReleased(MouseCode code);
+ virtual void OnCursorMoved(int x, int y);
+ virtual void SetMovable(bool state);
+
+private:
+ int m_iDragger;
+ bool m_bDragging;
+ int m_iDragPos;
+ bool m_bMovable; // whether this dragger is movable using mouse or not
+};
+
+
+Dragger::Dragger(int column)
+{
+ m_iDragger = column;
+ SetPaintBackgroundEnabled(false);
+ SetPaintEnabled(false);
+ SetPaintBorderEnabled(false);
+ SetCursor(dc_sizewe);
+ m_bDragging = false;
+ m_bMovable = true; // movable by default
+ m_iDragPos = 0;
+ SetBlockDragChaining( true );
+}
+
+void Dragger::OnMousePressed(MouseCode code)
+{
+ if (m_bMovable)
+ {
+ input()->SetMouseCapture(GetVPanel());
+
+ int x, y;
+ input()->GetCursorPos(x, y);
+ m_iDragPos = x;
+ m_bDragging = true;
+ }
+}
+
+void Dragger::OnMouseDoublePressed(MouseCode code)
+{
+ if (m_bMovable)
+ {
+ // resize the column to the size of it's contents
+ PostMessage(GetParent(), new KeyValues("ResizeColumnToContents", "column", m_iDragger));
+ }
+}
+
+void Dragger::OnMouseReleased(MouseCode code)
+{
+ if (m_bMovable)
+ {
+ input()->SetMouseCapture(NULL);
+ m_bDragging = false;
+ }
+}
+
+void Dragger::OnCursorMoved(int x, int y)
+{
+ if (m_bDragging)
+ {
+ input()->GetCursorPos(x, y);
+ KeyValues *msg = new KeyValues("ColumnResized");
+ msg->SetInt("column", m_iDragger);
+ msg->SetInt("delta", x - m_iDragPos);
+ m_iDragPos = x;
+ if (GetVParent())
+ {
+ ivgui()->PostMessage(GetVParent(), msg, GetVPanel());
+ }
+ }
+}
+
+void Dragger::SetMovable(bool state)
+{
+ m_bMovable = state;
+ // disable cursor change if the dragger is not movable
+ if( IsVisible() )
+ {
+ if (state)
+ {
+ // if its not movable we stick with the default arrow
+ // if parent windows Start getting fancy cursors we should probably retrive a parent
+ // cursor and set it to that
+ SetCursor(dc_sizewe);
+ }
+ else
+ {
+ SetCursor(dc_arrow);
+ }
+ }
+}
+
+
+
+namespace vgui
+{
+// optimized for sorting
+class FastSortListPanelItem : public ListPanelItem
+{
+public:
+ // index into accessing item to sort
+ CUtlVector<int> m_SortedTreeIndexes;
+
+ // visibility flag (for quick hide/filter)
+ bool visible;
+
+ // precalculated sort orders
+ int primarySortIndexValue;
+ int secondarySortIndexValue;
+};
+}
+
+static ListPanel *s_pCurrentSortingListPanel = NULL;
+static const char *s_pCurrentSortingColumn = NULL;
+static bool s_currentSortingColumnTypeIsText = false;
+
+static SortFunc *s_pSortFunc = NULL;
+static bool s_bSortAscending = true;
+static SortFunc *s_pSortFuncSecondary = NULL;
+static bool s_bSortAscendingSecondary = true;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Basic sort function, for use in qsort
+//-----------------------------------------------------------------------------
+static int __cdecl AscendingSortFunc(const void *elem1, const void *elem2)
+{
+ int itemID1 = *((int *) elem1);
+ int itemID2 = *((int *) elem2);
+
+ // convert the item index into the ListPanelItem pointers
+ vgui::ListPanelItem *p1, *p2;
+ p1 = s_pCurrentSortingListPanel->GetItemData(itemID1);
+ p2 = s_pCurrentSortingListPanel->GetItemData(itemID2);
+
+ int result = s_pSortFunc( s_pCurrentSortingListPanel, *p1, *p2 );
+ if (result == 0)
+ {
+ // use the secondary sort functino
+ result = s_pSortFuncSecondary( s_pCurrentSortingListPanel, *p1, *p2 );
+
+ if (!s_bSortAscendingSecondary)
+ {
+ result = -result;
+ }
+
+ if (result == 0)
+ {
+ // sort by the pointers to make sure we get consistent results
+ if (p1 > p2)
+ {
+ result = 1;
+ }
+ else
+ {
+ result = -1;
+ }
+ }
+ }
+ else
+ {
+ // flip result if not doing an ascending sort
+ if (!s_bSortAscending)
+ {
+ result = -result;
+ }
+ }
+
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Default column sorting function, puts things in alpabetical order
+// If images are the same returns 1, else 0
+//-----------------------------------------------------------------------------
+static int __cdecl DefaultSortFunc(
+ ListPanel *pPanel,
+ const ListPanelItem &item1,
+ const ListPanelItem &item2 )
+{
+ const vgui::ListPanelItem *p1 = &item1;
+ const vgui::ListPanelItem *p2 = &item2;
+
+ if ( !p1 || !p2 ) // No meaningful comparison
+ {
+ return 0;
+ }
+
+ const char *col = s_pCurrentSortingColumn;
+ if (s_currentSortingColumnTypeIsText) // textImage column
+ {
+ if (p1->kv->FindKey(col, true)->GetDataType() == KeyValues::TYPE_INT)
+ {
+ // compare ints
+ int s1 = p1->kv->GetInt(col, 0);
+ int s2 = p2->kv->GetInt(col, 0);
+
+ if (s1 < s2)
+ {
+ return -1;
+ }
+ else if (s1 > s2)
+ {
+ return 1;
+ }
+ return 0;
+ }
+ else
+ {
+ // compare as string
+ const char *s1 = p1->kv->GetString(col, "");
+ const char *s2 = p2->kv->GetString(col, "");
+
+ return Q_stricmp(s1, s2);
+ }
+ }
+ else // its an imagePanel column
+ {
+ const ImagePanel *s1 = (const ImagePanel *)p1->kv->GetPtr(col, NULL);
+ const ImagePanel *s2 = (const ImagePanel *)p2->kv->GetPtr(col, NULL);
+
+ if (s1 < s2)
+ {
+ return -1;
+ }
+ else if (s1 > s2)
+ {
+ return 1;
+ }
+ return 0;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sorts items by comparing precalculated list values
+//-----------------------------------------------------------------------------
+static int __cdecl FastSortFunc(
+ ListPanel *pPanel,
+ const ListPanelItem &item1,
+ const ListPanelItem &item2 )
+{
+ const vgui::FastSortListPanelItem *p1 = (vgui::FastSortListPanelItem *)&item1;
+ const vgui::FastSortListPanelItem *p2 = (vgui::FastSortListPanelItem *)&item2;
+
+ Assert(p1 && p2);
+
+ // compare the precalculated indices
+ if (p1->primarySortIndexValue < p2->primarySortIndexValue)
+ {
+ return 1;
+ }
+ else if (p1->primarySortIndexValue > p2->primarySortIndexValue)
+ {
+ return -1;
+
+ }
+
+ // they're equal, compare the secondary indices
+ if (p1->secondarySortIndexValue < p2->secondarySortIndexValue)
+ {
+ return 1;
+ }
+ else if (p1->secondarySortIndexValue > p2->secondarySortIndexValue)
+ {
+ return -1;
+
+ }
+
+ // still equal; just compare the pointers (so we get deterministic results)
+ return (p1 < p2) ? 1 : -1;
+}
+
+static int s_iDuplicateIndex = 1;
+
+//-----------------------------------------------------------------------------
+// Purpose: sorting function used in the column index redblack tree
+//-----------------------------------------------------------------------------
+bool ListPanel::RBTreeLessFunc(vgui::ListPanel::IndexItem_t &item1, vgui::ListPanel::IndexItem_t &item2)
+{
+ int result = s_pSortFunc( s_pCurrentSortingListPanel, *item1.dataItem, *item2.dataItem);
+ if (result == 0)
+ {
+ // they're the same value, set their duplicate index to reflect that
+ if (item1.duplicateIndex)
+ {
+ item2.duplicateIndex = item1.duplicateIndex;
+ }
+ else if (item2.duplicateIndex)
+ {
+ item1.duplicateIndex = item2.duplicateIndex;
+ }
+ else
+ {
+ item1.duplicateIndex = item2.duplicateIndex = s_iDuplicateIndex++;
+ }
+ }
+ return (result > 0);
+}
+
+
+DECLARE_BUILD_FACTORY( ListPanel );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+ListPanel::ListPanel(Panel *parent, const char *panelName) : BaseClass(parent, panelName)
+{
+ m_bIgnoreDoubleClick = false;
+ m_bMultiselectEnabled = true;
+ m_iEditModeItemID = 0;
+ m_iEditModeColumn = 0;
+
+ m_iHeaderHeight = 20;
+ m_iRowHeight = 20;
+ m_bCanSelectIndividualCells = false;
+ m_iSelectedColumn = -1;
+ m_bAllowUserAddDeleteColumns = false;
+
+ m_hbar = new ScrollBar(this, "HorizScrollBar", false);
+ m_hbar->AddActionSignalTarget(this);
+ m_hbar->SetVisible(false);
+ m_vbar = new ScrollBar(this, "VertScrollBar", true);
+ m_vbar->SetVisible(false);
+ m_vbar->AddActionSignalTarget(this);
+
+ m_pLabel = new Label(this, NULL, "");
+ m_pLabel->SetVisible(false);
+ m_pLabel->SetPaintBackgroundEnabled(false);
+ m_pLabel->SetContentAlignment(Label::a_west);
+
+ m_pTextImage = new TextImage( "" );
+ m_pImagePanel = new ImagePanel(NULL, "ListImage");
+ m_pImagePanel->SetAutoDelete(false);
+
+ m_iSortColumn = -1;
+ m_iSortColumnSecondary = -1;
+ m_bSortAscending = true;
+ m_bSortAscendingSecondary = true;
+
+ m_lastBarWidth = 0;
+ m_iColumnDraggerMoved = -1;
+ m_bNeedsSort = false;
+ m_LastItemSelected = -1;
+
+ m_pImageList = NULL;
+ m_bDeleteImageListWhenDone = false;
+ m_pEmptyListText = new TextImage("");
+
+ m_nUserConfigFileVersion = 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ListPanel::~ListPanel()
+{
+ // free data from table
+ RemoveAll();
+
+ // free column headers
+ unsigned char i;
+ for ( i = m_ColumnsData.Head(); i != m_ColumnsData.InvalidIndex(); i= m_ColumnsData.Next( i ) )
+ {
+ m_ColumnsData[i].m_pHeader->MarkForDeletion();
+ m_ColumnsData[i].m_pResizer->MarkForDeletion();
+ }
+ m_ColumnsData.RemoveAll();
+
+ delete m_pTextImage;
+ delete m_pImagePanel;
+ delete m_vbar;
+
+ if ( m_bDeleteImageListWhenDone )
+ {
+ delete m_pImageList;
+ }
+
+ delete m_pEmptyListText;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone)
+{
+ // get rid of existing list image if there's one and we're supposed to get rid of it
+ if ( m_pImageList && m_bDeleteImageListWhenDone )
+ {
+ delete m_pImageList;
+ }
+
+ m_bDeleteImageListWhenDone = deleteImageListWhenDone;
+ m_pImageList = imageList;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::SetColumnHeaderHeight( int height )
+{
+ m_iHeaderHeight = height;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds a column header.
+// this->FindChildByName(columnHeaderName) can be used to retrieve a pointer to a header panel by name
+//
+// if minWidth and maxWidth are BOTH NOTRESIZABLE or RESIZABLE
+// the min and max size will be calculated automatically for you with that attribute
+// columns are resizable by default
+// if min and max size are specified column is resizable
+//
+// A small note on passing numbers for minWidth and maxWidth,
+// If the initial window size is larger than the sum of the original widths of the columns,
+// you can wind up with the columns "snapping" to size after the first window focus
+// This is because the dxPerBar being calculated in PerformLayout()
+// is making resizable bounded headers exceed thier maxWidths at the Start.
+// Solution is to either put in support for redistributing the extra dx being truncated and
+// therefore added to the last column on window opening, which is what causes the snapping.
+// OR to
+// ensure the difference between the starting sum of widths is not too much smaller/bigger
+// than the starting window size so the starting dx doesn't cause snapping to occur.
+// The easiest thing is to simply set it so your column widths add up to the starting size of the window on opening.
+//
+// Another note: Always give bounds for the last column you add or make it not resizable.
+//
+// Columns can have text headers or images for headers (e.g. password icon)
+//-----------------------------------------------------------------------------
+void ListPanel::AddColumnHeader(int index, const char *columnName, const char *columnText, int width, int columnFlags)
+{
+ if (columnFlags & COLUMN_FIXEDSIZE && !(columnFlags & COLUMN_RESIZEWITHWINDOW))
+ {
+ // for fixed size columns, set the min & max widths to be the same as the initial width
+ AddColumnHeader( index, columnName, columnText, width, width, width, columnFlags);
+ }
+ else
+ {
+ AddColumnHeader( index, columnName, columnText, width, 20, 10000, columnFlags);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a new column
+//-----------------------------------------------------------------------------
+void ListPanel::AddColumnHeader(int index, const char *columnName, const char *columnText, int width, int minWidth, int maxWidth, int columnFlags)
+{
+ Assert (minWidth <= width);
+ Assert (maxWidth >= width);
+
+ // get our permanent index
+ unsigned char columnDataIndex = m_ColumnsData.AddToTail();
+
+ // put this index on the tail, so all item's m_SortedTreeIndexes have a consistent mapping
+ m_ColumnsHistory.AddToTail(columnDataIndex);
+
+ // put this column in the right place visually
+ m_CurrentColumns.InsertBefore(index, columnDataIndex);
+
+ // create the actual column object
+ column_t &column = m_ColumnsData[columnDataIndex];
+
+ // create the column header button
+ Button *pButton = SETUP_PANEL(new ColumnButton(this, columnName, columnText)); // the cell rendering mucks with the button visibility during the solvetraverse loop,
+ //so force applyschemesettings to make sure its run
+ pButton->SetSize(width, 24);
+ pButton->AddActionSignalTarget(this);
+ pButton->SetContentAlignment(Label::a_west);
+ pButton->SetTextInset(5, 0);
+
+ column.m_pHeader = pButton;
+ column.m_iMinWidth = minWidth;
+ column.m_iMaxWidth = maxWidth;
+ column.m_bResizesWithWindow = columnFlags & COLUMN_RESIZEWITHWINDOW;
+ column.m_bTypeIsText = !(columnFlags & COLUMN_IMAGE);
+ column.m_bHidden = false;
+ column.m_bUnhidable = (columnFlags & COLUMN_UNHIDABLE);
+ column.m_nContentAlignment = Label::a_west;
+
+ Dragger *dragger = new Dragger(index);
+ dragger->SetParent(this);
+ dragger->AddActionSignalTarget(this);
+ dragger->MoveToFront();
+ if (minWidth == maxWidth || (columnFlags & COLUMN_FIXEDSIZE))
+ {
+ // not resizable so disable the slider
+ dragger->SetMovable(false);
+ }
+ column.m_pResizer = dragger;
+
+ // add default sort function
+ column.m_pSortFunc = NULL;
+
+ // Set the SortedTree less than func to the generic RBTreeLessThanFunc
+ m_ColumnsData[columnDataIndex].m_SortedTree.SetLessFunc((IndexRBTree_t::LessFunc_t)RBTreeLessFunc);
+
+ // go through all the headers and make sure their Command has the right column ID
+ ResetColumnHeaderCommands();
+
+ // create the new data index
+ ResortColumnRBTree(index);
+
+ // ensure scroll bar is topmost compared to column headers
+ m_vbar->MoveToFront();
+
+ // fix up our visibility
+ SetColumnVisible(index, !(columnFlags & COLUMN_HIDDEN));
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recreates a column's RB Sorted Tree
+//-----------------------------------------------------------------------------
+void ListPanel::ResortColumnRBTree(int col)
+{
+ Assert(m_CurrentColumns.IsValidIndex(col));
+
+ unsigned char dataColumnIndex = m_CurrentColumns[col];
+ int columnHistoryIndex = m_ColumnsHistory.Find(dataColumnIndex);
+ column_t &column = m_ColumnsData[dataColumnIndex];
+
+ IndexRBTree_t &rbtree = column.m_SortedTree;
+
+ // remove all elements - we're going to create from scratch
+ rbtree.RemoveAll();
+
+ s_pCurrentSortingListPanel = this;
+ s_currentSortingColumnTypeIsText = column.m_bTypeIsText; // type of data in the column
+ SortFunc *sortFunc = column.m_pSortFunc;
+ if ( !sortFunc )
+ {
+ sortFunc = DefaultSortFunc;
+ }
+ s_pSortFunc = sortFunc;
+ s_bSortAscending = true;
+ s_pSortFuncSecondary = NULL;
+
+ // sort all current data items for this column
+ FOR_EACH_LL( m_DataItems, i )
+ {
+ IndexItem_t item;
+ item.dataItem = m_DataItems[i];
+ item.duplicateIndex = 0;
+
+ FastSortListPanelItem *dataItem = (FastSortListPanelItem*) m_DataItems[i];
+
+ // if this item doesn't already have a SortedTreeIndex for this column,
+ // if can only be because this is the brand new column, so add it to the SortedTreeIndexes
+ if (dataItem->m_SortedTreeIndexes.Count() == m_ColumnsHistory.Count() - 1 &&
+ columnHistoryIndex == m_ColumnsHistory.Count() - 1)
+ {
+ dataItem->m_SortedTreeIndexes.AddToTail();
+ }
+
+ Assert( dataItem->m_SortedTreeIndexes.IsValidIndex(columnHistoryIndex) );
+
+ dataItem->m_SortedTreeIndexes[columnHistoryIndex] = rbtree.Insert(item);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Resets the "SetSortColumn" command for each column - in case columns were added or removed
+//-----------------------------------------------------------------------------
+void ListPanel::ResetColumnHeaderCommands()
+{
+ int i;
+ for ( i = 0 ; i < m_CurrentColumns.Count() ; i++ )
+ {
+ Button *pButton = m_ColumnsData[m_CurrentColumns[i]].m_pHeader;
+ pButton->SetCommand(new KeyValues("SetSortColumn", "column", i));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the header text for a particular column.
+//-----------------------------------------------------------------------------
+void ListPanel::SetColumnHeaderText(int col, const char *text)
+{
+ m_ColumnsData[m_CurrentColumns[col]].m_pHeader->SetText(text);
+}
+void ListPanel::SetColumnHeaderText(int col, wchar_t *text)
+{
+ m_ColumnsData[m_CurrentColumns[col]].m_pHeader->SetText(text);
+}
+
+void ListPanel::SetColumnTextAlignment( int col, int align )
+{
+ m_ColumnsData[m_CurrentColumns[col]].m_nContentAlignment = align;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the column header to have an image instead of text
+//-----------------------------------------------------------------------------
+void ListPanel::SetColumnHeaderImage(int column, int imageListIndex)
+{
+ Assert(m_pImageList);
+ m_ColumnsData[m_CurrentColumns[column]].m_pHeader->SetTextImageIndex(-1);
+ m_ColumnsData[m_CurrentColumns[column]].m_pHeader->SetImageAtIndex(0, m_pImageList->GetImage(imageListIndex), 0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: associates a tooltip with the column header
+//-----------------------------------------------------------------------------
+void ListPanel::SetColumnHeaderTooltip(int column, const char *tooltipText)
+{
+ m_ColumnsData[m_CurrentColumns[column]].m_pHeader->GetTooltip()->SetText(tooltipText);
+ m_ColumnsData[m_CurrentColumns[column]].m_pHeader->GetTooltip()->SetTooltipFormatToSingleLine();
+ m_ColumnsData[m_CurrentColumns[column]].m_pHeader->GetTooltip()->SetTooltipDelay(0);
+}
+
+int ListPanel::GetNumColumnHeaders() const
+{
+ return m_CurrentColumns.Count();
+}
+
+bool ListPanel::GetColumnHeaderText( int index, char *pOut, int maxLen )
+{
+ if ( index < m_CurrentColumns.Count() )
+ {
+ m_ColumnsData[m_CurrentColumns[index]].m_pHeader->GetText( pOut, maxLen );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::SetColumnSortable(int col, bool sortable)
+{
+ if (sortable)
+ {
+ m_ColumnsData[m_CurrentColumns[col]].m_pHeader->SetCommand(new KeyValues("SetSortColumn", "column", col));
+ }
+ else
+ {
+ m_ColumnsData[m_CurrentColumns[col]].m_pHeader->SetCommand((const char *)NULL);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Changes the visibility of a column
+//-----------------------------------------------------------------------------
+void ListPanel::SetColumnVisible(int col, bool visible)
+{
+ column_t &column = m_ColumnsData[m_CurrentColumns[col]];
+ bool bHidden = !visible;
+ if (column.m_bHidden == bHidden)
+ return;
+
+ if (column.m_bUnhidable)
+ return;
+
+ column.m_bHidden = bHidden;
+ if (bHidden)
+ {
+ column.m_pHeader->SetVisible(false);
+ column.m_pResizer->SetVisible(false);
+ }
+ else
+ {
+ column.m_pHeader->SetVisible(true);
+ column.m_pResizer->SetVisible(true);
+ }
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::RemoveColumn(int col)
+{
+ if ( !m_CurrentColumns.IsValidIndex( col ) )
+ return;
+
+ // find the appropriate column data
+ unsigned char columnDataIndex = m_CurrentColumns[col];
+
+ // remove it from the current columns
+ m_CurrentColumns.Remove(col);
+
+ // zero out this entry in m_ColumnsHistory
+ unsigned char i;
+ for ( i = 0 ; i < m_ColumnsHistory.Count() ; i++ )
+ {
+ if ( m_ColumnsHistory[i] == columnDataIndex )
+ {
+ m_ColumnsHistory[i] = m_ColumnsData.InvalidIndex();
+ break;
+ }
+ }
+ Assert( i != m_ColumnsHistory.Count() );
+
+ // delete and remove the column data
+ m_ColumnsData[columnDataIndex].m_SortedTree.RemoveAll();
+ m_ColumnsData[columnDataIndex].m_pHeader->MarkForDeletion();
+ m_ColumnsData[columnDataIndex].m_pResizer->MarkForDeletion();
+ m_ColumnsData.Remove(columnDataIndex);
+
+ ResetColumnHeaderCommands();
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the index of a column by column->GetName()
+//-----------------------------------------------------------------------------
+int ListPanel::FindColumn(const char *columnName)
+{
+ for (int i = 0; i < m_CurrentColumns.Count(); i++)
+ {
+ if (!stricmp(columnName, m_ColumnsData[m_CurrentColumns[i]].m_pHeader->GetName()))
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: adds an item to the view
+// data->GetName() is used to uniquely identify an item
+// data sub items are matched against column header name to be used in the table
+//-----------------------------------------------------------------------------
+int ListPanel::AddItem( const KeyValues *item, unsigned int userData, bool bScrollToItem, bool bSortOnAdd)
+{
+ FastSortListPanelItem *newitem = new FastSortListPanelItem;
+ newitem->kv = item->MakeCopy();
+ newitem->userData = userData;
+ newitem->m_pDragData = NULL;
+ newitem->m_bImage = newitem->kv->GetInt( "image" ) != 0 ? true : false;
+ newitem->m_nImageIndex = newitem->kv->GetInt( "image" );
+ newitem->m_nImageIndexSelected = newitem->kv->GetInt( "imageSelected" );
+ newitem->m_pIcon = reinterpret_cast< IImage * >( newitem->kv->GetPtr( "iconImage" ) );
+
+ int itemID = m_DataItems.AddToTail(newitem);
+ int displayRow = m_VisibleItems.AddToTail(itemID);
+ newitem->visible = true;
+
+ // put the item in each column's sorted Tree Index
+ IndexItem(itemID);
+
+ if ( bSortOnAdd )
+ {
+ m_bNeedsSort = true;
+ }
+
+ InvalidateLayout();
+
+ if ( bScrollToItem )
+ {
+ // scroll to last item
+ m_vbar->SetValue(displayRow);
+ }
+ return itemID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::SetUserData( int itemID, unsigned int userData )
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return;
+
+ m_DataItems[itemID]->userData = userData;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the first itemID with a matching userData
+//-----------------------------------------------------------------------------
+int ListPanel::GetItemIDFromUserData( unsigned int userData )
+{
+ FOR_EACH_LL( m_DataItems, itemID )
+ {
+ if (m_DataItems[itemID]->userData == userData)
+ return itemID;
+ }
+ // not found
+ return InvalidItemID();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ListPanel::GetItemCount( void )
+{
+ return m_VisibleItems.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the item ID of an item by name (data->GetName())
+//-----------------------------------------------------------------------------
+int ListPanel::GetItem(const char *itemName)
+{
+ FOR_EACH_LL( m_DataItems, i )
+ {
+ if (!stricmp(m_DataItems[i]->kv->GetName(), itemName))
+ {
+ return i;
+ }
+ }
+
+ // failure
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns pointer to data the itemID holds
+//-----------------------------------------------------------------------------
+KeyValues *ListPanel::GetItem(int itemID)
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return NULL;
+
+ return m_DataItems[itemID]->kv;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ListPanel::GetItemCurrentRow(int itemID)
+{
+ return m_VisibleItems.Find(itemID);
+}
+
+
+//-----------------------------------------------------------------------------
+// Attaches drag data to a particular item
+//-----------------------------------------------------------------------------
+void ListPanel::SetItemDragData( int itemID, const KeyValues *data )
+{
+ ListPanelItem *pItem = m_DataItems[ itemID ];
+ if ( pItem->m_pDragData )
+ {
+ pItem->m_pDragData->deleteThis();
+ }
+ pItem->m_pDragData = data->MakeCopy();
+}
+
+
+//-----------------------------------------------------------------------------
+// Attaches drag data to a particular item
+//-----------------------------------------------------------------------------
+void ListPanel::OnCreateDragData( KeyValues *msg )
+{
+ int nCount = GetSelectedItemsCount();
+ if ( nCount == 0 )
+ return;
+
+ for ( int i = 0; i < nCount; ++i )
+ {
+ int nItemID = GetSelectedItem( i );
+
+ KeyValues *pDragData = m_DataItems[ nItemID ]->m_pDragData;
+ if ( pDragData )
+ {
+ KeyValues *pDragDataCopy = pDragData->MakeCopy();
+ msg->AddSubKey( pDragDataCopy );
+ }
+ }
+
+ // Add the keys of the last item directly into the root also
+ int nLastItemID = GetSelectedItem( nCount - 1 );
+ KeyValues *pLastItemDrag = m_DataItems[ nLastItemID ]->m_pDragData;
+ if ( pLastItemDrag )
+ {
+ pLastItemDrag->CopySubkeys( msg );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ListPanel::GetItemIDFromRow(int currentRow)
+{
+ if (!m_VisibleItems.IsValidIndex(currentRow))
+ return -1;
+
+ return m_VisibleItems[currentRow];
+}
+
+
+int ListPanel::FirstItem() const
+{
+ return m_DataItems.Head();
+}
+
+
+int ListPanel::NextItem( int iItem ) const
+{
+ return m_DataItems.Next( iItem );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ListPanel::InvalidItemID() const
+{
+ return m_DataItems.InvalidIndex();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool ListPanel::IsValidItemID(int itemID)
+{
+ return m_DataItems.IsValidIndex(itemID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ListPanelItem *ListPanel::GetItemData( int itemID )
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return NULL;
+
+ return m_DataItems[ itemID ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns user data for itemID
+//-----------------------------------------------------------------------------
+unsigned int ListPanel::GetItemUserData(int itemID)
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return 0;
+
+ return m_DataItems[itemID]->userData;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: updates the view with any changes to the data
+// Input : itemID - index to update
+//-----------------------------------------------------------------------------
+void ListPanel::ApplyItemChanges(int itemID)
+{
+ // reindex the item and then redraw
+ IndexItem(itemID);
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds the item into the column indexes
+//-----------------------------------------------------------------------------
+void ListPanel::IndexItem(int itemID)
+{
+ FastSortListPanelItem *newitem = (FastSortListPanelItem*) m_DataItems[itemID];
+
+ // remove the item from the indexes and re-add
+ int maxCount = min(m_ColumnsHistory.Count(), newitem->m_SortedTreeIndexes.Count());
+ for (int i = 0; i < maxCount; i++)
+ {
+ IndexRBTree_t &rbtree = m_ColumnsData[m_ColumnsHistory[i]].m_SortedTree;
+ rbtree.RemoveAt(newitem->m_SortedTreeIndexes[i]);
+ }
+
+ // make sure it's all free
+ newitem->m_SortedTreeIndexes.RemoveAll();
+
+ // reserve one index per historical column - pad it out
+ newitem->m_SortedTreeIndexes.AddMultipleToTail(m_ColumnsHistory.Count());
+
+ // set the current sorting list (since the insert will need to sort)
+ s_pCurrentSortingListPanel = this;
+
+ // add the item into the RB tree for each column
+ for (int i = 0; i < m_ColumnsHistory.Count(); i++)
+ {
+ // skip over any removed columns
+ if ( m_ColumnsHistory[i] == m_ColumnsData.InvalidIndex() )
+ continue;
+
+ column_t &column = m_ColumnsData[m_ColumnsHistory[i]];
+
+ IndexItem_t item;
+ item.dataItem = newitem;
+ item.duplicateIndex = 0;
+
+ IndexRBTree_t &rbtree = column.m_SortedTree;
+
+ // setup sort state
+ s_pCurrentSortingListPanel = this;
+ s_pCurrentSortingColumn = column.m_pHeader->GetName(); // name of current column for sorting
+ s_currentSortingColumnTypeIsText = column.m_bTypeIsText; // type of data in the column
+
+ SortFunc *sortFunc = column.m_pSortFunc;
+ if (!sortFunc)
+ {
+ sortFunc = DefaultSortFunc;
+ }
+ s_pSortFunc = sortFunc;
+ s_bSortAscending = true;
+ s_pSortFuncSecondary = NULL;
+
+ // insert index
+ newitem->m_SortedTreeIndexes[i] = rbtree.Insert(item);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::RereadAllItems()
+{
+ //!! need to make this more efficient
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Cleans up allocations associated with a particular item
+//-----------------------------------------------------------------------------
+void ListPanel::CleanupItem( FastSortListPanelItem *data )
+{
+ if ( data )
+ {
+ if (data->kv)
+ {
+ data->kv->deleteThis();
+ }
+ if (data->m_pDragData)
+ {
+ data->m_pDragData->deleteThis();
+ }
+ delete data;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Removes an item at the specified item
+//-----------------------------------------------------------------------------
+void ListPanel::RemoveItem(int itemID)
+{
+#ifdef _X360
+ bool renavigate = false;
+ if(HasFocus())
+ {
+ for(int i = 0; i < GetSelectedItemsCount(); ++i)
+ {
+ if(itemID == GetSelectedItem(i))
+ {
+ renavigate = true;
+ break;
+ }
+ }
+ }
+#endif
+
+ FastSortListPanelItem *data = (FastSortListPanelItem*) m_DataItems[itemID];
+ if (!data)
+ return;
+
+ // remove from column sorted indexes
+ int i;
+ for ( i = 0; i < m_ColumnsHistory.Count(); i++ )
+ {
+ if ( m_ColumnsHistory[i] == m_ColumnsData.InvalidIndex())
+ continue;
+
+ IndexRBTree_t &rbtree = m_ColumnsData[m_ColumnsHistory[i]].m_SortedTree;
+ rbtree.RemoveAt(data->m_SortedTreeIndexes[i]);
+ }
+
+ // remove from selection
+ m_SelectedItems.FindAndRemove(itemID);
+ PostActionSignal( new KeyValues("ItemDeselected") );
+
+ // remove from visible items
+ m_VisibleItems.FindAndRemove(itemID);
+
+ // remove from data
+ m_DataItems.Remove(itemID);
+ CleanupItem( data );
+ InvalidateLayout();
+
+#ifdef _X360
+ if(renavigate)
+ {
+ NavigateTo();
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: clears and deletes all the memory used by the data items
+//-----------------------------------------------------------------------------
+void ListPanel::RemoveAll()
+{
+ // remove all sort indexes
+ for (int i = 0; i < m_ColumnsHistory.Count(); i++)
+ {
+ m_ColumnsData[m_ColumnsHistory[i]].m_SortedTree.RemoveAll();
+ }
+
+ FOR_EACH_LL( m_DataItems, index )
+ {
+ FastSortListPanelItem *pItem = m_DataItems[index];
+ CleanupItem( pItem );
+ }
+
+ m_DataItems.RemoveAll();
+ m_VisibleItems.RemoveAll();
+ ClearSelectedItems();
+
+ InvalidateLayout();
+
+#ifdef _X360
+ if(HasFocus())
+ {
+ NavigateTo();
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: obselete, use RemoveAll();
+//-----------------------------------------------------------------------------
+void ListPanel::DeleteAllItems()
+{
+ RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::ResetScrollBar()
+{
+ // delete and reallocate to besure the scroll bar's
+ // information is correct.
+ delete m_vbar;
+ m_vbar = new ScrollBar(this, "VertScrollBar", true);
+ m_vbar->SetVisible(false);
+ m_vbar->AddActionSignalTarget(this);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the count of selected rows
+//-----------------------------------------------------------------------------
+int ListPanel::GetSelectedItemsCount()
+{
+ return m_SelectedItems.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the selected item by selection index
+// Input : selectionIndex - valid in range [0, GetNumSelectedRows)
+// Output : int - itemID
+//-----------------------------------------------------------------------------
+int ListPanel::GetSelectedItem(int selectionIndex)
+{
+ if ( m_SelectedItems.IsValidIndex(selectionIndex))
+ return m_SelectedItems[selectionIndex];
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ListPanel::GetSelectedColumn()
+{
+ return m_iSelectedColumn;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Clears all selected rows
+//-----------------------------------------------------------------------------
+void ListPanel::ClearSelectedItems()
+{
+ int nPrevCount = m_SelectedItems.Count();
+ m_SelectedItems.RemoveAll();
+ if ( nPrevCount > 0 )
+ {
+ PostActionSignal( new KeyValues("ItemDeselected") );
+ }
+ m_LastItemSelected = -1;
+ m_iSelectedColumn = -1;
+}
+
+
+//-----------------------------------------------------------------------------
+bool ListPanel::IsItemSelected( int itemID )
+{
+ return m_DataItems.IsValidIndex( itemID ) && m_SelectedItems.HasElement( itemID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::AddSelectedItem( int itemID )
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return;
+
+ Assert( !m_SelectedItems.HasElement( itemID ) );
+
+ m_LastItemSelected = itemID;
+ m_SelectedItems.AddToTail( itemID );
+ PostActionSignal( new KeyValues("ItemSelected") );
+ Repaint();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::SetSingleSelectedItem( int itemID )
+{
+ ClearSelectedItems();
+ AddSelectedItem(itemID);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::SetSelectedCell(int itemID, int col)
+{
+ if ( !m_bCanSelectIndividualCells )
+ {
+ SetSingleSelectedItem(itemID);
+ return;
+ }
+
+ // make sure it's a valid cell
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return;
+
+ if ( !m_CurrentColumns.IsValidIndex(col) )
+ return;
+
+ SetSingleSelectedItem( itemID );
+ m_iSelectedColumn = col;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the data held by a specific cell
+//-----------------------------------------------------------------------------
+void ListPanel::GetCellText(int itemID, int col, wchar_t *wbuffer, int bufferSizeInBytes)
+{
+ if ( !wbuffer || !bufferSizeInBytes )
+ return;
+
+ wcscpy( wbuffer, L"" );
+
+ KeyValues *itemData = GetItem( itemID );
+ if ( !itemData )
+ {
+ return;
+ }
+
+ // Look up column header
+ if ( col < 0 || col >= m_CurrentColumns.Count() )
+ {
+ return;
+ }
+
+ const char *key = m_ColumnsData[m_CurrentColumns[col]].m_pHeader->GetName();
+ if ( !key || !key[ 0 ] )
+ {
+ return;
+ }
+
+ char const *val = itemData->GetString( key, "" );
+ if ( !val || !key[ 0 ] )
+ return;
+
+ const wchar_t *wval = NULL;
+
+ if ( val[ 0 ] == '#' )
+ {
+ StringIndex_t si = g_pVGuiLocalize->FindIndex( val + 1 );
+ if ( si != INVALID_LOCALIZE_STRING_INDEX )
+ {
+ wval = g_pVGuiLocalize->GetValueByIndex( si );
+ }
+ }
+
+ if ( !wval )
+ {
+ wval = itemData->GetWString( key, L"" );
+ }
+
+ wcsncpy( wbuffer, wval, bufferSizeInBytes/sizeof(wchar_t) );
+ wbuffer[ (bufferSizeInBytes/sizeof(wchar_t)) - 1 ] = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the data held by a specific cell
+//-----------------------------------------------------------------------------
+IImage *ListPanel::GetCellImage(int itemID, int col) //, ImagePanel *&buffer)
+{
+// if ( !buffer )
+// return;
+
+ KeyValues *itemData = GetItem( itemID );
+ if ( !itemData )
+ {
+ return NULL;
+ }
+
+ // Look up column header
+ if ( col < 0 || col >= m_CurrentColumns.Count() )
+ {
+ return NULL;
+ }
+
+ const char *key = m_ColumnsData[m_CurrentColumns[col]].m_pHeader->GetName();
+ if ( !key || !key[ 0 ] )
+ {
+ return NULL;
+ }
+
+ if ( !m_pImageList )
+ {
+ return NULL;
+ }
+
+ int imageIndex = itemData->GetInt( key, 0 );
+ if ( m_pImageList->IsValidIndex(imageIndex) )
+ {
+ if ( imageIndex > 0 )
+ {
+ return m_pImageList->GetImage(imageIndex);
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the panel to use to render a cell
+//-----------------------------------------------------------------------------
+Panel *ListPanel::GetCellRenderer(int itemID, int col)
+{
+ Assert( m_pTextImage );
+ Assert( m_pImagePanel );
+
+ column_t &column = m_ColumnsData[ m_CurrentColumns[col] ];
+
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+
+ m_pLabel->SetContentAlignment( (Label::Alignment)column.m_nContentAlignment );
+
+ if ( column.m_bTypeIsText )
+ {
+ wchar_t tempText[ 256 ];
+
+ // Grab cell text
+ GetCellText( itemID, col, tempText, 256 );
+ KeyValues *item = GetItem( itemID );
+ m_pTextImage->SetText(tempText);
+ int cw, tall;
+ m_pTextImage->GetContentSize(cw, tall);
+
+ // set cell size
+ Panel *header = column.m_pHeader;
+ int wide = header->GetWide();
+ m_pTextImage->SetSize( min( cw, wide - 5 ), tall);
+
+ m_pLabel->SetTextImageIndex( 0 );
+ m_pLabel->SetImageAtIndex(0, m_pTextImage, 3);
+
+ bool selected = false;
+ if ( m_SelectedItems.HasElement(itemID) && ( !m_bCanSelectIndividualCells || col == m_iSelectedColumn ) )
+ {
+ selected = true;
+ VPANEL focus = input()->GetFocus();
+ // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
+ if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
+ {
+ m_pLabel->SetBgColor(GetSchemeColor("ListPanel.SelectedBgColor", pScheme));
+ // selection
+ }
+ else
+ {
+ m_pLabel->SetBgColor(GetSchemeColor("ListPanel.SelectedOutOfFocusBgColor", pScheme));
+ }
+
+ if ( item->IsEmpty("cellcolor") == false )
+ {
+ m_pTextImage->SetColor( item->GetColor( "cellcolor" ) );
+ }
+ else if ( item->GetInt("disabled", 0) == 0 )
+ {
+ m_pTextImage->SetColor(m_SelectionFgColor);
+ }
+ else
+ {
+ m_pTextImage->SetColor(m_DisabledSelectionFgColor);
+ }
+
+ m_pLabel->SetPaintBackgroundEnabled(true);
+ }
+ else
+ {
+ if ( item->IsEmpty("cellcolor") == false )
+ {
+ m_pTextImage->SetColor( item->GetColor( "cellcolor" ) );
+ }
+ else if ( item->GetInt("disabled", 0) == 0 )
+ {
+ m_pTextImage->SetColor(m_LabelFgColor);
+ }
+ else
+ {
+ m_pTextImage->SetColor(m_DisabledColor);
+ }
+ m_pLabel->SetPaintBackgroundEnabled(false);
+ }
+
+ FastSortListPanelItem *listItem = m_DataItems[ itemID ];
+ if ( col == 0 &&
+ listItem->m_bImage && m_pImageList )
+ {
+ IImage *pImage = NULL;
+ if ( listItem->m_pIcon )
+ {
+ pImage = listItem->m_pIcon;
+ }
+ else
+ {
+ int imageIndex = selected ? listItem->m_nImageIndexSelected : listItem->m_nImageIndex;
+ if ( m_pImageList->IsValidIndex(imageIndex) )
+ {
+ pImage = m_pImageList->GetImage(imageIndex);
+ }
+ }
+
+ if ( pImage )
+ {
+ m_pLabel->SetTextImageIndex( 1 );
+ m_pLabel->SetImageAtIndex(0, pImage, 0);
+ m_pLabel->SetImageAtIndex(1, m_pTextImage, 3);
+ }
+ }
+
+ return m_pLabel;
+ }
+ else // if its an Image Panel
+ {
+ if ( m_SelectedItems.HasElement(itemID) && ( !m_bCanSelectIndividualCells || col == m_iSelectedColumn ) )
+ {
+ VPANEL focus = input()->GetFocus();
+ // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
+ if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
+ {
+ m_pLabel->SetBgColor(GetSchemeColor("ListPanel.SelectedBgColor", pScheme));
+ // selection
+ }
+ else
+ {
+ m_pLabel->SetBgColor(GetSchemeColor("ListPanel.SelectedOutOfFocusBgColor", pScheme));
+ }
+ // selection
+ m_pLabel->SetPaintBackgroundEnabled(true);
+ }
+ else
+ {
+ m_pLabel->SetPaintBackgroundEnabled(false);
+ }
+
+ IImage *pIImage = GetCellImage(itemID, col);
+ m_pLabel->SetImageAtIndex(0, pIImage, 0);
+
+ return m_pLabel;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: relayouts out the panel after any internal changes
+//-----------------------------------------------------------------------------
+void ListPanel::PerformLayout()
+{
+ if ( m_CurrentColumns.Count() == 0 )
+ return;
+
+ if (m_bNeedsSort)
+ {
+ SortList();
+ }
+
+ int rowsperpage = (int) GetRowsPerPage();
+
+ // count the number of visible items
+ int visibleItemCount = m_VisibleItems.Count();
+
+ //!! need to make it recalculate scroll positions
+ m_vbar->SetVisible(true);
+ m_vbar->SetEnabled(false);
+ m_vbar->SetRangeWindow( rowsperpage );
+ m_vbar->SetRange( 0, visibleItemCount);
+ m_vbar->SetButtonPressedScrollValue( 1 );
+
+ int wide, tall;
+ GetSize( wide, tall );
+ m_vbar->SetPos(wide - (m_vbar->GetWide()+WINDOW_BORDER_WIDTH), 0);
+ m_vbar->SetSize(m_vbar->GetWide(), tall - 2);
+ m_vbar->InvalidateLayout();
+
+ int buttonMaxXPos = wide - (m_vbar->GetWide()+WINDOW_BORDER_WIDTH);
+
+ int nColumns = m_CurrentColumns.Count();
+ // number of bars that can be resized
+ int numToResize=0;
+ if (m_iColumnDraggerMoved != -1) // we're resizing in response to a column dragger
+ {
+ numToResize = 1; // only one column will change size, the one we dragged
+ }
+ else // we're resizing in response to a window resize
+ {
+ for (int i = 0; i < nColumns; i++)
+ {
+ if ( m_ColumnsData[m_CurrentColumns[i]].m_bResizesWithWindow // column is resizable in response to window
+ && !m_ColumnsData[m_CurrentColumns[i]].m_bHidden)
+ {
+ numToResize++;
+ }
+ }
+ }
+
+ int dxPerBar; // zero on window first opening
+
+ // location of the last column resizer
+ int oldSizeX = 0, oldSizeY = 0;
+ int lastColumnIndex = nColumns-1;
+ for (int i = nColumns-1; i >= 0; --i)
+ {
+ if (!m_ColumnsData[m_CurrentColumns[i]].m_bHidden)
+ {
+ m_ColumnsData[m_CurrentColumns[i]].m_pHeader->GetPos(oldSizeX, oldSizeY);
+ lastColumnIndex = i;
+ break;
+ }
+ }
+
+ bool bForceShrink = false;
+ if ( numToResize == 0 )
+ {
+ // make sure we've got enough to be within minwidth
+ int minWidth=0;
+ for (int i = 0; i < nColumns; i++)
+ {
+ if (!m_ColumnsData[m_CurrentColumns[i]].m_bHidden)
+ {
+ minWidth += m_ColumnsData[m_CurrentColumns[i]].m_iMinWidth;
+ }
+ }
+
+ // if all the minimum widths cannot fit in the space given, then we will shrink ALL columns an equal amount
+ if (minWidth > buttonMaxXPos)
+ {
+ int dx = buttonMaxXPos - minWidth;
+ dxPerBar=(int)((float)dx/(float)nColumns);
+ bForceShrink = true;
+ }
+ else
+ {
+ dxPerBar = 0;
+ }
+ m_lastBarWidth = buttonMaxXPos;
+
+ }
+ else if ( oldSizeX != 0 ) // make sure this isnt the first time we opened the window
+ {
+ int dx = buttonMaxXPos - m_lastBarWidth; // this is how much we grew or shrank.
+
+ // see how many bars we have and now much each should grow/shrink
+ dxPerBar=(int)((float)dx/(float)numToResize);
+ m_lastBarWidth = buttonMaxXPos;
+ }
+ else // this is the first time we've opened the window, make sure all our colums fit! resize if needed
+ {
+ int startingBarWidth=0;
+ for (int i = 0; i < nColumns; i++)
+ {
+ if (!m_ColumnsData[m_CurrentColumns[i]].m_bHidden)
+ {
+ startingBarWidth += m_ColumnsData[m_CurrentColumns[i]].m_pHeader->GetWide();
+ }
+ }
+ int dx = buttonMaxXPos - startingBarWidth; // this is how much we grew or shrank.
+ // see how many bars we have and now much each should grow/shrink
+ dxPerBar=(int)((float)dx/(float)numToResize);
+ m_lastBarWidth = buttonMaxXPos;
+ }
+
+ // Make sure nothing is smaller than minwidth to start with or else we'll get into trouble below.
+ for ( int i=0; i < nColumns; i++ )
+ {
+ column_t &column = m_ColumnsData[m_CurrentColumns[i]];
+ Panel *header = column.m_pHeader;
+ if ( header->GetWide() < column.m_iMinWidth )
+ header->SetWide( column.m_iMinWidth );
+ }
+
+ // This was a while(1) loop and we hit an infinite loop case, so now we max out the # of times it can loop.
+ for ( int iLoopSanityCheck=0; iLoopSanityCheck < 1000; iLoopSanityCheck++ )
+ {
+ // try and place headers as is - before we have to force items to be minimum width
+ int x = -1;
+ int i;
+ for ( i = 0; i < nColumns; i++)
+ {
+ column_t &column = m_ColumnsData[m_CurrentColumns[i]];
+ Panel *header = column.m_pHeader;
+ if (column.m_bHidden)
+ {
+ header->SetVisible(false);
+ continue;
+ }
+
+ header->SetPos(x, 0);
+ header->SetVisible(true);
+
+ // if we couldn't fit this column - then we need to force items to be minimum width
+ if ( x+column.m_iMinWidth >= buttonMaxXPos && !bForceShrink )
+ {
+ break;
+ }
+
+ int hWide = header->GetWide();
+
+ // calculate the column's width
+ // make it so the last column always attaches to the scroll bar
+ if ( i == lastColumnIndex )
+ {
+ hWide = buttonMaxXPos-x;
+ }
+ else if (i == m_iColumnDraggerMoved ) // column resizing using dragger
+ {
+ hWide += dxPerBar; // adjust width of column
+ }
+ else if ( m_iColumnDraggerMoved == -1 ) // window is resizing
+ {
+ // either this column is allowed to resize OR we are forcing it because we're shrinking all columns
+ if ( column.m_bResizesWithWindow || bForceShrink )
+ {
+ Assert ( column.m_iMinWidth <= column.m_iMaxWidth );
+ hWide += dxPerBar; // adjust width of column
+ }
+ }
+
+ // enforce column mins and max's - unless we're FORCING it to shrink
+ if ( hWide < column.m_iMinWidth && !bForceShrink )
+ {
+ hWide = column.m_iMinWidth; // adjust width of column
+ }
+ else if ( hWide > column.m_iMaxWidth )
+ {
+ hWide = column.m_iMaxWidth;
+ }
+
+ header->SetSize(hWide, m_vbar->GetWide());
+ x += hWide;
+
+ // set the resizers
+ Panel *sizer = column.m_pResizer;
+ if ( i == lastColumnIndex )
+ {
+ sizer->SetVisible(false);
+ }
+ else
+ {
+ sizer->SetVisible(true);
+ }
+ sizer->MoveToFront();
+ sizer->SetPos(x - 4, 0);
+ sizer->SetSize(8, m_vbar->GetWide());
+ }
+
+ // we made it all the way through
+ if ( i == nColumns )
+ break;
+
+ // we do this AFTER trying first, to let as many columns as possible try and get to their
+ // desired width before we forcing the minimum width on them
+
+ // get the total desired width of all the columns
+ int totalDesiredWidth = 0;
+ for ( i = 0 ; i < nColumns ; i++ )
+ {
+ if (!m_ColumnsData[m_CurrentColumns[i]].m_bHidden)
+ {
+ Panel *pHeader = m_ColumnsData[m_CurrentColumns[i]].m_pHeader;
+ totalDesiredWidth += pHeader->GetWide();
+ }
+ }
+
+ // shrink from the most right column to minimum width until we can fit them all
+ Assert(totalDesiredWidth > buttonMaxXPos);
+ for ( i = nColumns-1; i >= 0 ; i--)
+ {
+ column_t &column = m_ColumnsData[m_CurrentColumns[i]];
+ if (!column.m_bHidden)
+ {
+ Panel *pHeader = column.m_pHeader;
+
+ totalDesiredWidth -= pHeader->GetWide();
+ if ( totalDesiredWidth + column.m_iMinWidth <= buttonMaxXPos )
+ {
+ int newWidth = buttonMaxXPos - totalDesiredWidth;
+ pHeader->SetSize( newWidth, m_vbar->GetWide() );
+ break;
+ }
+
+ totalDesiredWidth += column.m_iMinWidth;
+ pHeader->SetSize(column.m_iMinWidth, m_vbar->GetWide());
+ }
+ }
+ // If we don't allow this to shrink, then as we resize, it can get stuck in an infinite loop.
+ dxPerBar -= 5;
+ if ( dxPerBar < 0 )
+ dxPerBar = 0;
+
+ if ( i == -1 )
+ {
+ break;
+ }
+ }
+
+ // setup edit mode
+ if ( m_hEditModePanel.Get() )
+ {
+ m_iTableStartX = 0;
+ m_iTableStartY = m_iHeaderHeight + 1;
+
+ int nTotalRows = m_VisibleItems.Count();
+ int nRowsPerPage = GetRowsPerPage();
+
+ // find the first visible item to display
+ int nStartItem = 0;
+ if (nRowsPerPage <= nTotalRows)
+ {
+ nStartItem = m_vbar->GetValue();
+ }
+
+ bool bDone = false;
+ int drawcount = 0;
+ for (int i = nStartItem; i < nTotalRows && !bDone; i++)
+ {
+ int x = 0;
+ if (!m_VisibleItems.IsValidIndex(i))
+ continue;
+
+ int itemID = m_VisibleItems[i];
+
+ // iterate the columns
+ for (int j = 0; j < m_CurrentColumns.Count(); j++)
+ {
+ Panel *header = m_ColumnsData[m_CurrentColumns[j]].m_pHeader;
+
+ if (!header->IsVisible())
+ continue;
+
+ int wide = header->GetWide();
+
+ if ( itemID == m_iEditModeItemID &&
+ j == m_iEditModeColumn )
+ {
+
+ m_hEditModePanel->SetPos( x + m_iTableStartX + 2, (drawcount * m_iRowHeight) + m_iTableStartY);
+ m_hEditModePanel->SetSize( wide, m_iRowHeight - 1 );
+
+ bDone = true;
+ }
+
+ x += wide;
+ }
+
+ drawcount++;
+ }
+ }
+
+ Repaint();
+ m_iColumnDraggerMoved = -1; // reset to invalid column
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::OnSizeChanged(int wide, int tall)
+{
+ BaseClass::OnSizeChanged(wide, tall);
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Renders the cells
+//-----------------------------------------------------------------------------
+void ListPanel::Paint()
+{
+ if (m_bNeedsSort)
+ {
+ SortList();
+ }
+
+ // draw selection areas if any
+ int wide, tall;
+ GetSize( wide, tall );
+
+ m_iTableStartX = 0;
+ m_iTableStartY = m_iHeaderHeight + 1;
+
+ int nTotalRows = m_VisibleItems.Count();
+ int nRowsPerPage = GetRowsPerPage();
+
+ // find the first visible item to display
+ int nStartItem = 0;
+ if (nRowsPerPage <= nTotalRows)
+ {
+ nStartItem = m_vbar->GetValue();
+ }
+
+ int vbarInset = m_vbar->IsVisible() ? m_vbar->GetWide() : 0;
+ int maxw = wide - vbarInset - 8;
+
+// debug timing functions
+// double startTime, endTime;
+// startTime = system()->GetCurrentTime();
+
+ // iterate through and draw each cell
+ bool bDone = false;
+ int drawcount = 0;
+ for (int i = nStartItem; i < nTotalRows && !bDone; i++)
+ {
+ int x = 0;
+ if (!m_VisibleItems.IsValidIndex(i))
+ continue;
+
+ int itemID = m_VisibleItems[i];
+
+ // iterate the columns
+ for (int j = 0; j < m_CurrentColumns.Count(); j++)
+ {
+ Panel *header = m_ColumnsData[m_CurrentColumns[j]].m_pHeader;
+ Panel *render = GetCellRenderer(itemID, j);
+
+ if (!header->IsVisible())
+ continue;
+
+ int wide = header->GetWide();
+
+ if (render)
+ {
+ // setup render panel
+ if (render->GetVParent() != GetVPanel())
+ {
+ render->SetParent(GetVPanel());
+ }
+ if (!render->IsVisible())
+ {
+ render->SetVisible(true);
+ }
+ int xpos = x + m_iTableStartX + 2;
+
+ render->SetPos( xpos, (drawcount * m_iRowHeight) + m_iTableStartY);
+
+ int right = min( xpos + wide, maxw );
+ int usew = right - xpos;
+ render->SetSize( usew, m_iRowHeight - 1 );
+
+ // mark the panel to draw immediately (since it will probably be recycled to draw other cells)
+ render->Repaint();
+ surface()->SolveTraverse(render->GetVPanel());
+ int x0, y0, x1, y1;
+ render->GetClipRect(x0, y0, x1, y1);
+ if ((y1 - y0) < (m_iRowHeight - 3))
+ {
+ bDone = true;
+ break;
+ }
+ surface()->PaintTraverse(render->GetVPanel());
+ }
+ /*
+ // work in progress, optimized paint for text
+ else
+ {
+ // just paint it ourselves
+ char tempText[256];
+ // Grab cell text
+ GetCellText(i, j, tempText, sizeof(tempText));
+ surface()->DrawSetTextPos(x + m_iTableStartX + 2, (drawcount * m_iRowHeight) + m_iTableStartY);
+
+ for (const char *pText = tempText; *pText != 0; pText++)
+ {
+ surface()->DrawUnicodeChar((wchar_t)*pText);
+ }
+ }
+ */
+
+ x += wide;
+ }
+
+ drawcount++;
+ }
+
+ m_pLabel->SetVisible(false);
+
+ // if the list is empty, draw some help text
+ if (m_VisibleItems.Count() < 1 && m_pEmptyListText)
+ {
+ m_pEmptyListText->SetPos(m_iTableStartX + 8, m_iTableStartY + 4);
+ m_pEmptyListText->SetSize(wide - 8, m_iRowHeight);
+ m_pEmptyListText->Paint();
+ }
+
+// endTime = system()->GetCurrentTime();
+// ivgui()->DPrintf2("ListPanel::Paint() (%.3f sec)\n", (float)(endTime - startTime));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::PaintBackground()
+{
+ BaseClass::PaintBackground();
+}
+
+
+//-----------------------------------------------------------------------------
+// Handles multiselect
+//-----------------------------------------------------------------------------
+void ListPanel::HandleMultiSelection( int itemID, int row, int column )
+{
+ // deal with 'multiple' row selection
+
+ // convert the last item selected to a row so we can multiply select by rows NOT items
+ int lastSelectedRow = (m_LastItemSelected != -1) ? m_VisibleItems.Find( m_LastItemSelected ) : row;
+ int startRow, endRow;
+ if ( row < lastSelectedRow )
+ {
+ startRow = row;
+ endRow = lastSelectedRow;
+ }
+ else
+ {
+ startRow = lastSelectedRow;
+ endRow = row;
+ }
+
+ // clear the selection if neither control key was down - we are going to readd ALL selected items
+ // in case the user changed the 'direction' of the shift add
+ if ( !input()->IsKeyDown(KEY_LCONTROL) && !input()->IsKeyDown(KEY_RCONTROL) )
+ {
+ ClearSelectedItems();
+ }
+
+ // add any items that we haven't added
+ for (int i = startRow; i <= endRow; i++)
+ {
+ // get the item indexes for these rows
+ int selectedItemID = m_VisibleItems[i];
+ if ( !m_SelectedItems.HasElement(selectedItemID) )
+ {
+ AddSelectedItem( selectedItemID );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Handles multiselect
+//-----------------------------------------------------------------------------
+void ListPanel::HandleAddSelection( int itemID, int row, int column )
+{
+ // dealing with row selection
+ if ( m_SelectedItems.HasElement( itemID ) )
+ {
+ // this row is already selected, remove
+ m_SelectedItems.FindAndRemove( itemID );
+ PostActionSignal( new KeyValues("ItemDeselected") );
+ m_LastItemSelected = itemID;
+ }
+ else
+ {
+ // add the row to the selection
+ AddSelectedItem( itemID );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::UpdateSelection( MouseCode code, int x, int y, int row, int column )
+{
+ // make sure we're clicking on a real item
+ if ( row < 0 || row >= m_VisibleItems.Count() )
+ {
+ ClearSelectedItems();
+ return;
+ }
+
+ int itemID = m_VisibleItems[ row ];
+
+ // if we've right-clicked on a selection, don't change the selection
+ if ( code == MOUSE_RIGHT && m_SelectedItems.HasElement( itemID ) )
+ return;
+
+ if ( m_bCanSelectIndividualCells )
+ {
+ if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) )
+ {
+ // we're ctrl selecting the same cell, clear it
+ if ( ( m_LastItemSelected == itemID ) && ( m_iSelectedColumn == column ) && ( m_SelectedItems.Count() == 1 ) )
+ {
+ ClearSelectedItems();
+ }
+ else
+ {
+ SetSelectedCell( itemID, column );
+ }
+ }
+ else
+ {
+ SetSelectedCell( itemID, column );
+ }
+ return;
+ }
+
+ if ( !m_bMultiselectEnabled )
+ {
+ SetSingleSelectedItem( itemID );
+ return;
+ }
+
+ if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) )
+ {
+ // check for multi-select
+ HandleMultiSelection( itemID, row, column );
+ }
+ else if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) )
+ {
+ // check for row-add select
+ HandleAddSelection( itemID, row, column );
+ }
+ else
+ {
+ // no CTRL or SHIFT keys
+ // reset the selection Start point
+// if ( ( m_LastItemSelected != itemID ) || ( m_SelectedItems.Count() > 1 ) )
+ {
+ SetSingleSelectedItem( itemID );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::OnMousePressed( MouseCode code )
+{
+ if (code == MOUSE_LEFT || code == MOUSE_RIGHT)
+ {
+ if ( m_VisibleItems.Count() > 0 )
+ {
+ // determine where we were pressed
+ int x, y, row, column;
+ input()->GetCursorPos(x, y);
+ GetCellAtPos(x, y, row, column);
+
+ UpdateSelection( code, x, y, row, column );
+ }
+
+ // get the key focus
+ RequestFocus();
+ }
+
+ // check for context menu open
+ if (code == MOUSE_RIGHT)
+ {
+ if ( m_SelectedItems.Count() > 0 )
+ {
+ PostActionSignal( new KeyValues("OpenContextMenu", "itemID", m_SelectedItems[0] ));
+ }
+ else
+ {
+ // post it, but with the invalid row
+ PostActionSignal( new KeyValues("OpenContextMenu", "itemID", -1 ));
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Scrolls the list according to the mouse wheel movement
+//-----------------------------------------------------------------------------
+void ListPanel::OnMouseWheeled(int delta)
+{
+ if (m_hEditModePanel.Get())
+ {
+ // ignore mouse wheel in edit mode, forward right up to parent
+ CallParentFunction(new KeyValues("MouseWheeled", "delta", delta));
+ return;
+ }
+
+ int val = m_vbar->GetValue();
+ val -= (delta * 3);
+ m_vbar->SetValue(val);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Double-click act like the the item under the mouse was selected
+// and then the enter key hit
+//-----------------------------------------------------------------------------
+void ListPanel::OnMouseDoublePressed(MouseCode code)
+{
+ if (code == MOUSE_LEFT)
+ {
+ // select the item
+ OnMousePressed(code);
+
+ // post up an enter key being hit if anything was selected
+ if (GetSelectedItemsCount() > 0 && !m_bIgnoreDoubleClick )
+ {
+ OnKeyCodeTyped(KEY_ENTER);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#ifdef _X360
+void ListPanel::OnKeyCodePressed(KeyCode code)
+{
+ int nTotalRows = m_VisibleItems.Count();
+ int nTotalColumns = m_CurrentColumns.Count();
+ if ( nTotalRows == 0 )
+ return;
+
+ // calculate info for adjusting scrolling
+ int nStartItem = GetStartItem();
+ int nRowsPerPage = (int)GetRowsPerPage();
+
+ int nSelectedRow = 0;
+ if ( m_DataItems.IsValidIndex( m_LastItemSelected ) )
+ {
+ nSelectedRow = m_VisibleItems.Find( m_LastItemSelected );
+ }
+ int nSelectedColumn = m_iSelectedColumn;
+
+ switch(code)
+ {
+ case KEY_XBUTTON_UP:
+ case KEY_XSTICK1_UP:
+ case KEY_XSTICK2_UP:
+ if(GetItemCount() < 1 || nSelectedRow == nStartItem)
+ {
+ ClearSelectedItems();
+ BaseClass::OnKeyCodePressed(code);
+ return;
+ }
+ else
+ {
+ nSelectedRow -= 1;
+ }
+ break;
+ case KEY_XBUTTON_DOWN:
+ case KEY_XSTICK1_DOWN:
+ case KEY_XSTICK2_DOWN:
+ {
+ int itemId = GetSelectedItem(0);
+ if(itemId != -1 && GetItemCurrentRow(itemId) == (nTotalRows - 1))
+ {
+ ClearSelectedItems();
+ BaseClass::OnKeyCodePressed(code);
+ return;
+ }
+ else
+ {
+ nSelectedRow += 1;
+ }
+ }
+ break;
+ case KEY_XBUTTON_LEFT:
+ case KEY_XSTICK1_LEFT:
+ case KEY_XSTICK2_LEFT:
+ if (m_bCanSelectIndividualCells && (GetSelectedItemsCount() == 1) && (nSelectedColumn >= 0) )
+ {
+ nSelectedColumn--;
+ if (nSelectedColumn < 0)
+ {
+ nSelectedColumn = 0;
+ }
+ break;
+ }
+ break;
+ case KEY_XBUTTON_RIGHT:
+ case KEY_XSTICK1_RIGHT:
+ case KEY_XSTICK2_RIGHT:
+ if (m_bCanSelectIndividualCells && (GetSelectedItemsCount() == 1) && (nSelectedColumn >= 0) )
+ {
+ nSelectedColumn++;
+ if (nSelectedColumn >= nTotalColumns)
+ {
+ nSelectedColumn = nTotalColumns - 1;
+ }
+ break;
+ }
+ break;
+ case KEY_XBUTTON_A:
+ PostActionSignal( new KeyValues("ListPanelItemChosen", "itemID", m_SelectedItems[0] ));
+ break;
+ default:
+ BaseClass::OnKeyCodePressed(code);
+ break;
+ }
+
+ // make sure newly selected item is a valid range
+ nSelectedRow = clamp(nSelectedRow, 0, nTotalRows - 1);
+
+ int row = m_VisibleItems[ nSelectedRow ];
+
+ // This will select the cell if in single select mode, or the row in multiselect mode
+ if ( ( row != m_LastItemSelected ) || ( nSelectedColumn != m_iSelectedColumn ) || ( m_SelectedItems.Count() > 1 ) )
+ {
+ SetSelectedCell( row, nSelectedColumn );
+ }
+
+ // move the newly selected item to within the visible range
+ if ( nRowsPerPage < nTotalRows )
+ {
+ int nStartItem = m_vbar->GetValue();
+ if ( nSelectedRow < nStartItem )
+ {
+ // move the list back to match
+ m_vbar->SetValue( nSelectedRow );
+ }
+ else if ( nSelectedRow >= nStartItem + nRowsPerPage )
+ {
+ // move list forward to match
+ m_vbar->SetValue( nSelectedRow - nRowsPerPage + 1);
+ }
+ }
+
+ // redraw
+ InvalidateLayout();
+}
+
+#else
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::OnKeyCodePressed(KeyCode code)
+{
+ if (m_hEditModePanel.Get())
+ {
+ // ignore arrow keys in edit mode
+ // forward right up to parent so that tab focus change doesn't occur
+ CallParentFunction(new KeyValues("KeyCodePressed", "code", code));
+ return;
+ }
+
+ int nTotalRows = m_VisibleItems.Count();
+ int nTotalColumns = m_CurrentColumns.Count();
+ if ( nTotalRows == 0 )
+ {
+ BaseClass::OnKeyCodePressed(code);
+ return;
+ }
+
+ // calculate info for adjusting scrolling
+ int nStartItem = GetStartItem();
+ int nRowsPerPage = (int)GetRowsPerPage();
+
+ int nSelectedRow = 0;
+ if ( m_DataItems.IsValidIndex( m_LastItemSelected ) )
+ {
+ nSelectedRow = m_VisibleItems.Find( m_LastItemSelected );
+ }
+ int nSelectedColumn = m_iSelectedColumn;
+
+ switch (code)
+ {
+ case KEY_HOME:
+ nSelectedRow = 0;
+ break;
+
+ case KEY_END:
+ nSelectedRow = nTotalRows - 1;
+ break;
+
+ case KEY_PAGEUP:
+ if (nSelectedRow <= nStartItem)
+ {
+ // move up a page
+ nSelectedRow -= (nRowsPerPage - 1);
+ }
+ else
+ {
+ // move to the top of the current page
+ nSelectedRow = nStartItem;
+ }
+ break;
+
+ case KEY_PAGEDOWN:
+ if (nSelectedRow >= (nStartItem + nRowsPerPage-1))
+ {
+ // move down a page
+ nSelectedRow += (nRowsPerPage - 1);
+ }
+ else
+ {
+ // move to the bottom of the current page
+ nSelectedRow = nStartItem + (nRowsPerPage - 1);
+ }
+ break;
+
+ case KEY_UP:
+ case KEY_XBUTTON_UP:
+ case KEY_XSTICK1_UP:
+ case KEY_XSTICK2_UP:
+ if ( nTotalRows > 0 )
+ {
+ nSelectedRow--;
+ break;
+ }
+ // fall through
+
+ case KEY_DOWN:
+ case KEY_XBUTTON_DOWN:
+ case KEY_XSTICK1_DOWN:
+ case KEY_XSTICK2_DOWN:
+ if ( nTotalRows > 0 )
+ {
+ nSelectedRow++;
+ break;
+ }
+ // fall through
+
+ case KEY_LEFT:
+ case KEY_XBUTTON_LEFT:
+ case KEY_XSTICK1_LEFT:
+ case KEY_XSTICK2_LEFT:
+ if (m_bCanSelectIndividualCells && (GetSelectedItemsCount() == 1) && (nSelectedColumn >= 0) )
+ {
+ nSelectedColumn--;
+ if (nSelectedColumn < 0)
+ {
+ nSelectedColumn = 0;
+ }
+ break;
+ }
+ // fall through
+
+ case KEY_RIGHT:
+ case KEY_XBUTTON_RIGHT:
+ case KEY_XSTICK1_RIGHT:
+ case KEY_XSTICK2_RIGHT:
+ if (m_bCanSelectIndividualCells && (GetSelectedItemsCount() == 1) && (nSelectedColumn >= 0) )
+ {
+ nSelectedColumn++;
+ if (nSelectedColumn >= nTotalColumns)
+ {
+ nSelectedColumn = nTotalColumns - 1;
+ }
+ break;
+ }
+ // fall through
+
+ default:
+ // chain back
+ BaseClass::OnKeyCodePressed(code);
+ return;
+ };
+
+ // make sure newly selected item is a valid range
+ nSelectedRow = clamp(nSelectedRow, 0, nTotalRows - 1);
+
+ int row = m_VisibleItems[ nSelectedRow ];
+
+ // This will select the cell if in single select mode, or the row in multiselect mode
+ if ( ( row != m_LastItemSelected ) || ( nSelectedColumn != m_iSelectedColumn ) || ( m_SelectedItems.Count() > 1 ) )
+ {
+ SetSelectedCell( row, nSelectedColumn );
+ }
+
+ // move the newly selected item to within the visible range
+ if ( nRowsPerPage < nTotalRows )
+ {
+ int nStartItem = m_vbar->GetValue();
+ if ( nSelectedRow < nStartItem )
+ {
+ // move the list back to match
+ m_vbar->SetValue( nSelectedRow );
+ }
+ else if ( nSelectedRow >= nStartItem + nRowsPerPage )
+ {
+ // move list forward to match
+ m_vbar->SetValue( nSelectedRow - nRowsPerPage + 1);
+ }
+ }
+
+ // redraw
+ InvalidateLayout();
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool ListPanel::GetCellBounds( int row, int col, int& x, int& y, int& wide, int& tall )
+{
+ if ( col < 0 || col >= m_CurrentColumns.Count() )
+ return false;
+
+ if ( row < 0 || row >= m_VisibleItems.Count() )
+ return false;
+
+ // Is row on screen?
+ int startitem = GetStartItem();
+ if ( row < startitem || row >= ( startitem + GetRowsPerPage() ) )
+ return false;
+
+ y = m_iTableStartY;
+ y += ( row - startitem ) * m_iRowHeight;
+ tall = m_iRowHeight;
+
+ // Compute column cell
+ x = m_iTableStartX;
+ // walk columns
+ int c = 0;
+ while ( c < col)
+ {
+ x += m_ColumnsData[m_CurrentColumns[c]].m_pHeader->GetWide();
+ c++;
+ }
+ wide = m_ColumnsData[m_CurrentColumns[c]].m_pHeader->GetWide();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if any found, row and column are filled out
+//-----------------------------------------------------------------------------
+bool ListPanel::GetCellAtPos(int x, int y, int &row, int &col)
+{
+ // convert to local
+ ScreenToLocal(x, y);
+
+ // move to Start of table
+ x -= m_iTableStartX;
+ y -= m_iTableStartY;
+
+ int startitem = GetStartItem();
+ // make sure it's still in valid area
+ if ( x >= 0 && y >= 0 )
+ {
+ // walk the rows (for when row height is independant each row)
+ // NOTE: if we do height independent rows, we will need to change GetCellBounds as well
+ for ( row = startitem ; row < m_VisibleItems.Count() ; row++ )
+ {
+ if ( y < ( ( ( row - startitem ) + 1 ) * m_iRowHeight ) )
+ break;
+ }
+
+ // walk columns
+ int startx = 0;
+ for ( col = 0 ; col < m_CurrentColumns.Count() ; col++ )
+ {
+ startx += m_ColumnsData[m_CurrentColumns[col]].m_pHeader->GetWide();
+
+ if ( x < startx )
+ break;
+ }
+
+ // make sure we're not out of range
+ if ( ! ( row == m_VisibleItems.Count() || col == m_CurrentColumns.Count() ) )
+ {
+ return true;
+ }
+ }
+
+ // out-of-bounds
+ row = col = -1;
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::ApplySchemeSettings(IScheme *pScheme)
+{
+ // force label to apply scheme settings now so we can override it
+ m_pLabel->InvalidateLayout(true);
+
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetBgColor(GetSchemeColor("ListPanel.BgColor", pScheme));
+ SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
+
+ m_pLabel->SetBgColor(GetSchemeColor("ListPanel.BgColor", pScheme));
+
+ m_LabelFgColor = GetSchemeColor("ListPanel.TextColor", pScheme);
+ m_DisabledColor = GetSchemeColor("ListPanel.DisabledTextColor", m_LabelFgColor, pScheme);
+ m_SelectionFgColor = GetSchemeColor("ListPanel.SelectedTextColor", m_LabelFgColor, pScheme);
+ m_DisabledSelectionFgColor = GetSchemeColor("ListPanel.DisabledSelectedTextColor", m_LabelFgColor, pScheme);
+
+ m_pEmptyListText->SetColor(GetSchemeColor("ListPanel.EmptyListInfoTextColor", pScheme));
+
+ SetFont( pScheme->GetFont("Default", IsProportional() ) );
+ m_pEmptyListText->SetFont( pScheme->GetFont( "Default", IsProportional() ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::SetSortFunc(int col, SortFunc *func)
+{
+ Assert(col < m_CurrentColumns.Count());
+ unsigned char dataColumnIndex = m_CurrentColumns[col];
+
+ if ( !m_ColumnsData[dataColumnIndex].m_bTypeIsText && func != NULL)
+ {
+ m_ColumnsData[dataColumnIndex].m_pHeader->SetMouseClickEnabled(MOUSE_LEFT, 1);
+ }
+
+ m_ColumnsData[dataColumnIndex].m_pSortFunc = func;
+
+ // resort this column according to new sort func
+ ResortColumnRBTree(col);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::SetSortColumn(int column)
+{
+ m_iSortColumn = column;
+}
+
+int ListPanel::GetSortColumn() const
+{
+ return m_iSortColumn;
+}
+
+void ListPanel::SetSortColumnEx( int iPrimarySortColumn, int iSecondarySortColumn, bool bSortAscending )
+{
+ m_iSortColumn = iPrimarySortColumn;
+ m_iSortColumnSecondary = iSecondarySortColumn;
+ m_bSortAscending = bSortAscending;
+}
+
+void ListPanel::GetSortColumnEx( int &iPrimarySortColumn, int &iSecondarySortColumn, bool &bSortAscending ) const
+{
+ iPrimarySortColumn = m_iSortColumn;
+ iSecondarySortColumn = m_iSortColumnSecondary;
+ bSortAscending = m_bSortAscending;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::SortList( void )
+{
+ m_bNeedsSort = false;
+
+ if ( m_VisibleItems.Count() <= 1 )
+ {
+ return;
+ }
+
+ // check if the last selected item is on the screen - if so, we should try to maintain it on screen
+ int startItem = GetStartItem();
+ int rowsperpage = (int) GetRowsPerPage();
+ int screenPosition = -1;
+ if ( m_LastItemSelected != -1 && m_SelectedItems.Count() > 0 )
+ {
+ int selectedItemRow = m_VisibleItems.Find(m_LastItemSelected);
+ if ( selectedItemRow >= startItem && selectedItemRow <= ( startItem + rowsperpage ) )
+ {
+ screenPosition = selectedItemRow - startItem;
+ }
+ }
+
+ // get the required sorting functions
+ s_pCurrentSortingListPanel = this;
+
+ // setup globals for use in qsort
+ s_pSortFunc = FastSortFunc;
+ s_bSortAscending = m_bSortAscending;
+ s_pSortFuncSecondary = FastSortFunc;
+ s_bSortAscendingSecondary = m_bSortAscendingSecondary;
+
+ // walk the tree and set up the current indices
+ if (m_CurrentColumns.IsValidIndex(m_iSortColumn))
+ {
+ IndexRBTree_t &rbtree = m_ColumnsData[m_CurrentColumns[m_iSortColumn]].m_SortedTree;
+ unsigned int index = rbtree.FirstInorder();
+ unsigned int lastIndex = rbtree.LastInorder();
+ int prevDuplicateIndex = 0;
+ int sortValue = 1;
+ while (1)
+ {
+ FastSortListPanelItem *dataItem = (FastSortListPanelItem*) rbtree[index].dataItem;
+ if (dataItem->visible)
+ {
+ // only increment the sort value if we're a different token from the previous
+ if (!prevDuplicateIndex || prevDuplicateIndex != rbtree[index].duplicateIndex)
+ {
+ sortValue++;
+ }
+ dataItem->primarySortIndexValue = sortValue;
+ prevDuplicateIndex = rbtree[index].duplicateIndex;
+ }
+
+ if (index == lastIndex)
+ break;
+
+ index = rbtree.NextInorder(index);
+ }
+ }
+
+ // setup secondary indices
+ if (m_CurrentColumns.IsValidIndex(m_iSortColumnSecondary))
+ {
+ IndexRBTree_t &rbtree = m_ColumnsData[m_CurrentColumns[m_iSortColumnSecondary]].m_SortedTree;
+ unsigned int index = rbtree.FirstInorder();
+ unsigned int lastIndex = rbtree.LastInorder();
+ int sortValue = 1;
+ int prevDuplicateIndex = 0;
+ while (1)
+ {
+ FastSortListPanelItem *dataItem = (FastSortListPanelItem*) rbtree[index].dataItem;
+ if (dataItem->visible)
+ {
+ // only increment the sort value if we're a different token from the previous
+ if (!prevDuplicateIndex || prevDuplicateIndex != rbtree[index].duplicateIndex)
+ {
+ sortValue++;
+ }
+ dataItem->secondarySortIndexValue = sortValue;
+
+ prevDuplicateIndex = rbtree[index].duplicateIndex;
+ }
+
+ if (index == lastIndex)
+ break;
+
+ index = rbtree.NextInorder(index);
+ }
+ }
+
+ // quick sort the list
+ qsort(m_VisibleItems.Base(), (size_t) m_VisibleItems.Count(), (size_t) sizeof(int), AscendingSortFunc);
+
+ if ( screenPosition != -1 )
+ {
+ int selectedItemRow = m_VisibleItems.Find(m_LastItemSelected);
+
+ // if we can put the last selected item in exactly the same spot, put it there, otherwise
+ // we need to be at the top of the list
+ if (selectedItemRow > screenPosition)
+ {
+ m_vbar->SetValue(selectedItemRow - screenPosition);
+ }
+ else
+ {
+ m_vbar->SetValue(0);
+ }
+ }
+
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::SetFont(HFont font)
+{
+ Assert( font );
+ if ( !font )
+ return;
+
+ m_pTextImage->SetFont(font);
+ m_iRowHeight = surface()->GetFontTall(font) + 2;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListPanel::OnSliderMoved()
+{
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : deltax - deltas from current position
+//-----------------------------------------------------------------------------
+void ListPanel::OnColumnResized(int col, int delta)
+{
+ m_iColumnDraggerMoved = col;
+
+ column_t& column = m_ColumnsData[m_CurrentColumns[col]];
+
+ Panel *header = column.m_pHeader;
+ int wide, tall;
+ header->GetSize(wide, tall);
+
+
+ wide += delta;
+
+ // enforce minimum sizes for the header
+ if ( wide < column.m_iMinWidth )
+ {
+ wide = column.m_iMinWidth;
+ }
+ // enforce maximum sizes for the header
+ if ( wide > column.m_iMaxWidth )
+ {
+ wide = column.m_iMaxWidth;
+ }
+
+ // make sure we have enough space for the columns to our right
+ int panelWide, panelTall;
+ GetSize( panelWide, panelTall );
+ int x, y;
+ header->GetPos(x, y);
+ int restColumnsMinWidth = 0;
+ int i;
+ for ( i = col+1 ; i < m_CurrentColumns.Count() ; i++ )
+ {
+ column_t& nextCol = m_ColumnsData[m_CurrentColumns[i]];
+ restColumnsMinWidth += nextCol.m_iMinWidth;
+ }
+ panelWide -= ( x + restColumnsMinWidth + m_vbar->GetWide() + WINDOW_BORDER_WIDTH );
+ if ( wide > panelWide )
+ {
+ wide = panelWide;
+ }
+
+ header->SetSize(wide, tall);
+
+ // the adjacent header will be moved automatically in PerformLayout()
+ header->InvalidateLayout();
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets which column we should sort with
+//-----------------------------------------------------------------------------
+void ListPanel::OnSetSortColumn(int column)
+{
+ // if it's the primary column already, flip the sort direction
+ if (m_iSortColumn == column)
+ {
+ m_bSortAscending = !m_bSortAscending;
+ }
+ else
+ {
+ // switching sort columns, keep the old one as the secondary sort
+ m_iSortColumnSecondary = m_iSortColumn;
+ m_bSortAscendingSecondary = m_bSortAscending;
+ }
+
+ SetSortColumn(column);
+
+ SortList();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets whether the item is visible or not
+//-----------------------------------------------------------------------------
+void ListPanel::SetItemVisible(int itemID, bool state)
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return;
+
+ FastSortListPanelItem *data = (FastSortListPanelItem*) m_DataItems[itemID];
+ if (data->visible == state)
+ return;
+
+ m_bNeedsSort = true;
+
+ data->visible = state;
+ if (data->visible)
+ {
+ // add back to end of list
+ m_VisibleItems.AddToTail(itemID);
+ }
+ else
+ {
+ // remove from selection if it is there.
+ if (m_SelectedItems.HasElement(itemID))
+ {
+ m_SelectedItems.FindAndRemove(itemID);
+ PostActionSignal( new KeyValues("ItemDeselected") );
+ }
+
+ // remove from data
+ m_VisibleItems.FindAndRemove(itemID);
+
+ InvalidateLayout();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Is the item visible?
+//-----------------------------------------------------------------------------
+bool ListPanel::IsItemVisible( int itemID )
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return false;
+
+ FastSortListPanelItem *data = (FastSortListPanelItem*) m_DataItems[itemID];
+ return data->visible;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: sets whether the item is disabled or not (effects item color)
+//-----------------------------------------------------------------------------
+void ListPanel::SetItemDisabled(int itemID, bool state)
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return;
+
+ m_DataItems[itemID]->kv->SetInt( "disabled", state );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculate number of rows per page
+//-----------------------------------------------------------------------------
+float ListPanel::GetRowsPerPage()
+{
+ float rowsperpage = (float)( GetTall() - m_iHeaderHeight ) / (float)m_iRowHeight;
+ return rowsperpage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculate the item we should Start on
+//-----------------------------------------------------------------------------
+int ListPanel::GetStartItem()
+{
+ // if rowsperpage < total number of rows
+ if ( GetRowsPerPage() < (float) m_VisibleItems.Count() )
+ {
+ return m_vbar->GetValue();
+ }
+ return 0; // otherwise Start at top
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: whether or not to select specific cells (off by default)
+//-----------------------------------------------------------------------------
+void ListPanel::SetSelectIndividualCells(bool state)
+{
+ m_bCanSelectIndividualCells = state;
+}
+
+
+//-----------------------------------------------------------------------------
+// whether or not multiple cells/rows can be selected
+//-----------------------------------------------------------------------------
+void ListPanel::SetMultiselectEnabled( bool bState )
+{
+ m_bMultiselectEnabled = bState;
+}
+
+bool ListPanel::IsMultiselectEnabled() const
+{
+ return m_bMultiselectEnabled;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the text which is displayed when the list is empty
+//-----------------------------------------------------------------------------
+void ListPanel::SetEmptyListText(const char *text)
+{
+ m_pEmptyListText->SetText(text);
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the text which is displayed when the list is empty
+//-----------------------------------------------------------------------------
+void ListPanel::SetEmptyListText(const wchar_t *text)
+{
+ m_pEmptyListText->SetText(text);
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: opens the content menu
+//-----------------------------------------------------------------------------
+void ListPanel::OpenColumnChoiceMenu()
+{
+ if (!m_bAllowUserAddDeleteColumns)
+ return;
+
+ Menu *menu = new Menu(this, "ContextMenu");
+
+ int x, y;
+ input()->GetCursorPos(x, y);
+ menu->SetPos(x, y);
+
+ // add all the column choices to the menu
+ for ( int i = 0 ; i < m_CurrentColumns.Count() ; i++ )
+ {
+ column_t &column = m_ColumnsData[m_CurrentColumns[i]];
+
+ char name[128];
+ column.m_pHeader->GetText(name, sizeof(name));
+ int itemID = menu->AddCheckableMenuItem(name, new KeyValues("ToggleColumnVisible", "col", m_CurrentColumns[i]), this);
+ menu->SetMenuItemChecked(itemID, !column.m_bHidden);
+
+ if (column.m_bUnhidable)
+ {
+ menu->SetItemEnabled(itemID, false);
+ }
+ }
+
+ menu->SetVisible(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Resizes a column
+//-----------------------------------------------------------------------------
+void ListPanel::ResizeColumnToContents(int column)
+{
+ // iterate all the items in the column, getting the size of each
+ column_t &col = m_ColumnsData[m_CurrentColumns[column]];
+
+ if (!col.m_bTypeIsText)
+ return;
+
+ // start with the size of the column text
+ int wide = 0, minRequiredWidth = 0, tall = 0;
+ col.m_pHeader->GetContentSize( minRequiredWidth, tall );
+
+ // iterate every item
+ for (int i = 0; i < m_VisibleItems.Count(); i++)
+ {
+ if (!m_VisibleItems.IsValidIndex(i))
+ continue;
+
+ // get the cell
+ int itemID = m_VisibleItems[i];
+
+ // get the text
+ wchar_t tempText[ 256 ];
+ GetCellText( itemID, column, tempText, 256 );
+ m_pTextImage->SetText(tempText);
+
+ m_pTextImage->GetContentSize(wide, tall);
+
+ if ( wide > minRequiredWidth )
+ {
+ minRequiredWidth = wide;
+ }
+ }
+
+ // Introduce a slight buffer between columns
+ minRequiredWidth += 4;
+
+ // call the resize
+ col.m_pHeader->GetSize(wide, tall);
+ OnColumnResized(column, minRequiredWidth - wide);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Changes the visibilty of a column
+//-----------------------------------------------------------------------------
+void ListPanel::OnToggleColumnVisible(int col)
+{
+ if (!m_CurrentColumns.IsValidIndex(col))
+ return;
+
+ // toggle the state of the column
+ column_t &column = m_ColumnsData[m_CurrentColumns[col]];
+ SetColumnVisible(col, column.m_bHidden);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets user settings
+//-----------------------------------------------------------------------------
+void ListPanel::ApplyUserConfigSettings(KeyValues *userConfig)
+{
+ // Check for version mismatch, then don't load settings. (Just revert to the defaults.)
+ int version = userConfig->GetInt( "configVersion", 1 );
+ if ( version != m_nUserConfigFileVersion )
+ {
+ return;
+ }
+
+ // We save/restore m_lastBarWidth because all of the column widths are saved relative to that size.
+ // If we don't save it, you can run into this case:
+ // - Window width is 500, load sizes setup relative to a 1000-width window
+ // - Set window size to 1000
+ // - In PerformLayout, it thinks the window has grown by 500 (since m_lastBarWidth is 500 and new window width is 1000)
+ // so it pushes out any COLUMN_RESIZEWITHWINDOW columns to their max extent and shrinks everything else to its min extent.
+ m_lastBarWidth = userConfig->GetInt( "lastBarWidth", 0 );
+
+ // read which columns are hidden
+ for ( int i = 0; i < m_CurrentColumns.Count(); i++ )
+ {
+ char name[64];
+ _snprintf(name, sizeof(name), "%d_hidden", i);
+
+ int hidden = userConfig->GetInt(name, -1);
+ if (hidden == 0)
+ {
+ SetColumnVisible(i, true);
+ }
+ else if (hidden == 1)
+ {
+ SetColumnVisible(i, false);
+ }
+
+ _snprintf(name, sizeof(name), "%d_width", i);
+ int nWidth = userConfig->GetInt( name, -1 );
+ if ( nWidth >= 0 )
+ {
+ column_t &column = m_ColumnsData[m_CurrentColumns[i]];
+ column.m_pHeader->SetWide( nWidth );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns user config settings for this control
+//-----------------------------------------------------------------------------
+void ListPanel::GetUserConfigSettings(KeyValues *userConfig)
+{
+ if ( m_nUserConfigFileVersion != 1 )
+ {
+ userConfig->SetInt( "configVersion", m_nUserConfigFileVersion );
+ }
+
+ userConfig->SetInt( "lastBarWidth", m_lastBarWidth );
+
+ // save which columns are hidden
+ for ( int i = 0 ; i < m_CurrentColumns.Count() ; i++ )
+ {
+ column_t &column = m_ColumnsData[m_CurrentColumns[i]];
+
+ char name[64];
+ _snprintf(name, sizeof(name), "%d_hidden", i);
+ userConfig->SetInt(name, column.m_bHidden ? 1 : 0);
+
+ _snprintf(name, sizeof(name), "%d_width", i);
+ userConfig->SetInt( name, column.m_pHeader->GetWide() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: optimization, return true if this control has any user config settings
+//-----------------------------------------------------------------------------
+bool ListPanel::HasUserConfigSettings()
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void ListPanel::SetAllowUserModificationOfColumns(bool allowed)
+{
+ m_bAllowUserAddDeleteColumns = allowed;
+}
+
+void ListPanel::SetIgnoreDoubleClick( bool state )
+{
+ m_bIgnoreDoubleClick = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set up a field for editing
+//-----------------------------------------------------------------------------
+void ListPanel::EnterEditMode(int itemID, int column, vgui::Panel *editPanel)
+{
+ m_hEditModePanel = editPanel;
+ m_iEditModeItemID = itemID;
+ m_iEditModeColumn = column;
+ editPanel->SetParent(this);
+ editPanel->SetVisible(true);
+ editPanel->RequestFocus();
+ editPanel->MoveToFront();
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: leaves editing mode
+//-----------------------------------------------------------------------------
+void ListPanel::LeaveEditMode()
+{
+ if (m_hEditModePanel.Get())
+ {
+ m_hEditModePanel->SetVisible(false);
+ m_hEditModePanel->SetParent((Panel *)NULL);
+ m_hEditModePanel = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if we are currently in inline editing mode
+//-----------------------------------------------------------------------------
+bool ListPanel::IsInEditMode()
+{
+ return (m_hEditModePanel.Get() != NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#ifdef _X360
+void ListPanel::NavigateTo()
+{
+ BaseClass::NavigateTo();
+ // attempt to select the first item in the list when we get focus
+ if(GetItemCount())
+ {
+ SetSingleSelectedItem(FirstItem());
+ }
+ else // if we have no items, change focus
+ {
+ if(!NavigateDown())
+ {
+ NavigateUp();
+ }
+ }
+}
+#endif
diff --git a/mp/src/vgui2/vgui_controls/ListViewPanel.cpp b/mp/src/vgui2/vgui_controls/ListViewPanel.cpp
new file mode 100644
index 00000000..b188a01b
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ListViewPanel.cpp
@@ -0,0 +1,1082 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <assert.h>
+#include <ctype.h>
+
+#include <vgui/MouseCode.h>
+#include <vgui/KeyCode.h>
+#include <KeyValues.h>
+#include <vgui/ISurface.h>
+#include <vgui/IVGui.h>
+#include <vgui/IInput.h>
+#include <vgui/IPanel.h>
+#include <vgui/ILocalize.h>
+
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/ScrollBar.h>
+#include <vgui_controls/ImageList.h>
+#include <vgui_controls/ImagePanel.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/TextImage.h>
+#include <vgui_controls/ListViewPanel.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+enum
+{
+ WINDOW_BORDER_WIDTH=2 // the width of the window's border
+};
+
+namespace vgui
+{
+class ListViewItem : public Label
+{
+ DECLARE_CLASS_SIMPLE( ListViewItem, Label );
+
+public:
+ ListViewItem(Panel *parent) : Label(parent, NULL, "")
+ {
+ m_pListViewPanel = (ListViewPanel*) parent;
+ m_pData = NULL;
+ m_bSelected = false;
+ SetPaintBackgroundEnabled(true);
+ }
+
+ ~ListViewItem()
+ {
+ if (m_pData)
+ {
+ m_pData->deleteThis();
+ m_pData = NULL;
+ }
+ }
+
+ void SetData(const KeyValues *data)
+ {
+ if (m_pData)
+ {
+ m_pData->deleteThis();
+ }
+ m_pData = data->MakeCopy();
+ }
+
+ virtual void OnMousePressed( MouseCode code)
+ {
+ m_pListViewPanel->OnItemMousePressed(this, code);
+ }
+
+ virtual void OnMouseDoublePressed( MouseCode code)
+ {
+ // double press should only select the item
+ m_pListViewPanel->OnItemMouseDoublePressed(this, code);
+ }
+
+ KeyValues *GetData()
+ {
+ return m_pData;
+ }
+
+ void SetSelected(bool bSelected)
+ {
+ if (bSelected == m_bSelected)
+ return;
+
+ m_bSelected = bSelected;
+ if (bSelected)
+ {
+ RequestFocus();
+ }
+
+ UpdateImage();
+ InvalidateLayout();
+ Repaint();
+ }
+
+ virtual void PerformLayout()
+ {
+ TextImage *textImage = GetTextImage();
+ if (m_bSelected)
+ {
+ VPANEL focus = input()->GetFocus();
+ // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
+ if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
+ {
+ textImage->SetColor(m_ArmedFgColor2);
+ }
+ else
+ {
+ textImage->SetColor(m_FgColor2);
+ }
+ }
+ else
+ {
+ textImage->SetColor(GetFgColor());
+ }
+ BaseClass::PerformLayout();
+ Repaint();
+ }
+
+ virtual void PaintBackground()
+ {
+ int wide, tall;
+ GetSize(wide, tall);
+
+ if ( m_bSelected )
+ {
+ VPANEL focus = input()->GetFocus();
+ // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
+ if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
+ {
+ surface()->DrawSetColor(m_ArmedBgColor);
+ }
+ else
+ {
+ surface()->DrawSetColor(m_SelectionBG2Color);
+ }
+ }
+ else
+ {
+ surface()->DrawSetColor(GetBgColor());
+ }
+ surface()->DrawFilledRect(0, 0, wide, tall);
+ }
+
+ virtual void ApplySchemeSettings(IScheme *pScheme)
+ {
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ m_ArmedFgColor2 = GetSchemeColor("ListPanel.SelectedTextColor", pScheme);
+ m_ArmedBgColor = GetSchemeColor("ListPanel.SelectedBgColor", pScheme);
+
+ m_FgColor1 = GetSchemeColor("ListPanel.TextColor", pScheme);
+ m_FgColor2 = GetSchemeColor("ListPanel.SelectedTextColor", pScheme);
+
+ m_BgColor = GetSchemeColor("ListPanel.BgColor", GetBgColor(), pScheme);
+ m_BgColor = GetSchemeColor("ListPanel.TextBgColor", m_BgColor, pScheme);
+ m_SelectionBG2Color = GetSchemeColor("ListPanel.SelectedOutOfFocusBgColor", pScheme);
+ SetBgColor(m_BgColor);
+ SetFgColor(m_FgColor1);
+
+ UpdateImage();
+ }
+
+ void UpdateImage()
+ {
+ if ( m_pListViewPanel->m_pImageList )
+ {
+ int imageIndex = 0;
+ if ( m_bSelected )
+ {
+ imageIndex = m_pData->GetInt("imageSelected", 0);
+ }
+ if ( imageIndex == 0 )
+ {
+ imageIndex = m_pData->GetInt("image", 0);
+ }
+ if ( m_pListViewPanel->m_pImageList->IsValidIndex(imageIndex) )
+ {
+ SetImageAtIndex(0, m_pListViewPanel->m_pImageList->GetImage(imageIndex), 0);
+ }
+ else
+ {
+ // use the default
+ SetImageAtIndex(0, m_pListViewPanel->m_pImageList->GetImage(1), 0);
+ }
+ SizeToContents();
+ InvalidateLayout();
+ }
+ }
+
+private:
+
+ Color m_FgColor1;
+ Color m_FgColor2;
+ Color m_BgColor;
+ Color m_ArmedFgColor2;
+ Color m_ArmedBgColor;
+ Color m_SelectionBG2Color;
+
+ //IBorder *_keyFocusBorder; // maybe in the future when I'm the 'active' but not selected item, I'll have a border
+
+ KeyValues *m_pData;
+ ListViewPanel *m_pListViewPanel;
+ bool m_bSelected;
+};
+}
+
+static bool DefaultSortFunc(KeyValues *kv1, KeyValues *kv2)
+{
+ const char *string1 = kv1->GetString("text");
+ const char *string2 = kv2->GetString("text");
+ return Q_stricmp(string1, string2) < 0;
+}
+
+DECLARE_BUILD_FACTORY( ListViewPanel );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+ListViewPanel::ListViewPanel(Panel *parent, const char *panelName) : Panel(parent, panelName)
+{
+ m_iRowHeight = 20;
+ m_bNeedsSort = false;
+ m_hFont = NULL;
+ m_pImageList = NULL;
+ m_bDeleteImageListWhenDone = false;
+ m_pSortFunc = DefaultSortFunc;
+ m_ShiftStartItemID = -1;
+
+ m_hbar = new ScrollBar(this, "HorizScrollBar", false);
+ m_hbar->AddActionSignalTarget(this);
+ m_hbar->SetVisible(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ListViewPanel::~ListViewPanel()
+{
+ DeleteAllItems();
+
+ delete m_hbar;
+
+ if ( m_bDeleteImageListWhenDone )
+ {
+ delete m_pImageList;
+ m_pImageList = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ListViewPanel::AddItem(const KeyValues *data, bool bScrollToItem, bool bSortOnAdd)
+{
+ ListViewItem *pNewItem = new ListViewItem(this);
+ pNewItem->SetData(data);
+ if (m_hFont)
+ {
+ pNewItem->SetFont(m_hFont);
+ }
+ int itemID = m_DataItems.AddToTail(pNewItem);
+ ApplyItemChanges(itemID);
+ m_SortedItems.AddToTail(itemID);
+
+ if ( bSortOnAdd )
+ {
+ m_bNeedsSort = true;
+ }
+
+ InvalidateLayout();
+
+ if ( bScrollToItem )
+ {
+ ScrollToItem(itemID);
+ }
+
+ return itemID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::ScrollToItem(int itemID)
+{
+ if (!m_hbar->IsVisible())
+ {
+ return;
+ }
+ int val = m_hbar->GetValue();
+
+ int wide, tall;
+ GetSize( wide, tall );
+
+ int maxWidth = GetItemsMaxWidth();
+ int maxColVisible = wide / maxWidth;
+ int itemsPerCol = GetItemsPerColumn();
+
+ int itemIndex = m_SortedItems.Find(itemID);
+ int desiredCol = itemIndex / itemsPerCol;
+ if (desiredCol < val || desiredCol >= (val + maxColVisible) )
+ {
+ m_hbar->SetValue(desiredCol);
+ }
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ListViewPanel::GetItemCount()
+{
+ return m_DataItems.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues *ListViewPanel::GetItem(int itemID)
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return NULL;
+
+ return m_DataItems[itemID]->GetData();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get ItemID from position in panel - valid from [0, GetItemCount)
+//-----------------------------------------------------------------------------
+int ListViewPanel::GetItemIDFromPos(int iPos)
+{
+ if ( m_SortedItems.IsValidIndex(iPos) )
+ {
+ return m_SortedItems[iPos];
+ }
+ else
+ {
+ return m_DataItems.InvalidIndex();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::ApplyItemChanges(int itemID)
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return;
+
+ KeyValues *kv = m_DataItems[itemID]->GetData();
+ ListViewItem *pLabel = m_DataItems[itemID];
+
+ pLabel->SetText(kv->GetString("text"));
+ pLabel->SetTextImageIndex(1);
+ pLabel->SetImagePreOffset(1, 5);
+
+ TextImage *pTextImage = pLabel->GetTextImage();
+ pTextImage->ResizeImageToContent();
+
+ pLabel->UpdateImage();
+ pLabel->SizeToContents();
+ pLabel->InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::RemoveItem(int itemID)
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return;
+
+ m_DataItems[itemID]->MarkForDeletion();
+
+ // mark the keyValues for deletion
+ m_DataItems.Remove(itemID);
+ m_SortedItems.FindAndRemove(itemID);
+ m_SelectedItems.FindAndRemove(itemID);
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::DeleteAllItems()
+{
+ FOR_EACH_LL( m_DataItems, index )
+ {
+ m_DataItems[index]->MarkForDeletion();
+ }
+ m_DataItems.RemoveAll();
+ m_SortedItems.RemoveAll();
+ m_SelectedItems.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ListViewPanel::InvalidItemID()
+{
+ return m_DataItems.InvalidIndex();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool ListViewPanel::IsValidItemID(int itemID)
+{
+ return m_DataItems.IsValidIndex(itemID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::SetSortFunc(ListViewSortFunc_t func)
+{
+ if ( func )
+ {
+ m_pSortFunc = func;
+ SortList();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::SortList()
+{
+ m_SortedItems.RemoveAll();
+
+ // find all the items in this section
+ for( int i = m_DataItems.Head(); i != m_DataItems.InvalidIndex(); i = m_DataItems.Next( i ) )
+ {
+ // insert the items sorted
+ if (m_pSortFunc)
+ {
+ int insertionPoint;
+ for (insertionPoint = 0; insertionPoint < m_SortedItems.Count(); insertionPoint++)
+ {
+ if ( m_pSortFunc(m_DataItems[i]->GetData(), m_DataItems[m_SortedItems[insertionPoint]]->GetData() ) )
+ break;
+ }
+
+ if (insertionPoint == m_SortedItems.Count())
+ {
+ m_SortedItems.AddToTail(i);
+ }
+ else
+ {
+ m_SortedItems.InsertBefore(insertionPoint, i);
+ }
+ }
+ else
+ {
+ // just add to the end
+ m_SortedItems.AddToTail(i);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone)
+{
+ // get rid of existing list image if there's one and we're supposed to get rid of it
+ if ( m_pImageList && m_bDeleteImageListWhenDone )
+ {
+ delete m_pImageList;
+ m_pImageList = NULL;
+ }
+
+ m_bDeleteImageListWhenDone = deleteImageListWhenDone;
+ m_pImageList = imageList;
+
+ FOR_EACH_LL( m_DataItems, i )
+ {
+ m_DataItems[i]->UpdateImage();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::SetFont(HFont font)
+{
+ Assert( font );
+ if ( !font )
+ return;
+
+ m_hFont = font;
+ m_iRowHeight = surface()->GetFontTall(font) + 1;
+
+ FOR_EACH_LL( m_DataItems, i )
+ {
+ m_DataItems[i]->SetFont(m_hFont);
+ TextImage *pTextImage = m_DataItems[i]->GetTextImage();
+ pTextImage->ResizeImageToContent();
+ m_DataItems[i]->SizeToContents();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ListViewPanel::GetSelectedItemsCount()
+{
+ return m_SelectedItems.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ListViewPanel::GetSelectedItem(int selectionIndex)
+{
+ if ( m_SelectedItems.IsValidIndex(selectionIndex) )
+ return m_SelectedItems[selectionIndex];
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::ClearSelectedItems()
+{
+ int i;
+ for (i = 0 ; i < m_SelectedItems.Count(); i++)
+ {
+ if ( m_DataItems.IsValidIndex(m_SelectedItems[i]) )
+ {
+ m_DataItems[m_SelectedItems[i]]->SetSelected(false);
+ }
+ }
+ m_SelectedItems.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::AddSelectedItem(int itemID)
+{
+ if ( m_SelectedItems.Find(itemID) == -1 )
+ {
+ m_SelectedItems.AddToTail(itemID);
+ m_DataItems[itemID]->SetSelected(true);
+ m_LastSelectedItemID = itemID;
+ m_ShiftStartItemID = itemID;
+ PostActionSignal(new KeyValues("ListViewItemSelected"));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::SetSingleSelectedItem(int itemID)
+{
+ ClearSelectedItems();
+ AddSelectedItem(itemID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::OnMouseWheeled(int delta)
+{
+ int val = m_hbar->GetValue();
+ val -= delta;
+ m_hbar->SetValue(val);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::OnSizeChanged(int wide, int tall)
+{
+ BaseClass::OnSizeChanged(wide, tall);
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ListViewPanel::GetItemsMaxWidth()
+{
+ int maxWidth = 0;
+ FOR_EACH_LL( m_DataItems, i )
+ {
+ int labelWide, labelTall;
+ m_DataItems[i]->GetSize(labelWide, labelTall);
+ if (labelWide > maxWidth)
+ {
+ maxWidth = labelWide + 25;
+ }
+ }
+ return maxWidth;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::PerformLayout()
+{
+ if (m_bNeedsSort)
+ {
+ SortList();
+ }
+
+ if ( m_DataItems.Count() == 0 )
+ return;
+
+ int wide, tall;
+ GetSize(wide, tall);
+
+ int maxWidth = GetItemsMaxWidth();
+ if (maxWidth < 24)
+ {
+ maxWidth = 24;
+ }
+ int maxColVisible = wide / maxWidth;
+
+ m_hbar->SetVisible(false);
+ int itemsPerCol = GetItemsPerColumn();
+ if (itemsPerCol < 1)
+ {
+ itemsPerCol = 1;
+ }
+ int cols = ( GetItemCount() + (itemsPerCol - 1) ) / itemsPerCol;
+
+ int startItem = 0;
+ if ( cols > maxColVisible)
+ {
+ m_hbar->SetVisible(true);
+
+ // recalulate # per column now that we've made the hbar visible
+ itemsPerCol = GetItemsPerColumn();
+ cols = ( GetItemCount() + (itemsPerCol - 1) ) / (itemsPerCol > 0 ? itemsPerCol : 1 );
+
+ m_hbar->SetEnabled(false);
+ m_hbar->SetRangeWindow( maxColVisible );
+ m_hbar->SetRange( 0, cols);
+ m_hbar->SetButtonPressedScrollValue( 1 );
+
+ m_hbar->SetPos(0, tall - (m_hbar->GetTall()+WINDOW_BORDER_WIDTH));
+ m_hbar->SetSize(wide - (WINDOW_BORDER_WIDTH*2), m_hbar->GetTall());
+ m_hbar->InvalidateLayout();
+
+ int val = m_hbar->GetValue();
+ startItem += val*itemsPerCol;
+ }
+ else
+ {
+ m_hbar->SetVisible(false);
+ }
+ int lastItemVisible = startItem + (( maxColVisible + 1 )* itemsPerCol) - 1;
+
+ int itemsThisCol = 0;
+ int x = 0;
+ int y = 0;
+ int i;
+ for ( i = 0 ; i < m_SortedItems.Count() ; i++ )
+ {
+ if ( i >= startItem && i <= lastItemVisible )
+ {
+ m_DataItems[ m_SortedItems[i] ]->SetVisible(true);
+ m_DataItems[ m_SortedItems[i] ]->SetPos(x, y);
+ itemsThisCol++;
+ if ( itemsThisCol == itemsPerCol )
+ {
+ y = 0;
+ x += maxWidth;
+ itemsThisCol = 0;
+ }
+ else
+ {
+ y += m_iRowHeight;
+ }
+ }
+ else
+ {
+ m_DataItems[ m_SortedItems[i] ]->SetVisible(false);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::Paint()
+{
+ BaseClass::Paint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetBgColor(GetSchemeColor("ListPanel.BgColor", pScheme));
+ SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
+
+ m_LabelFgColor = GetSchemeColor("ListPanel.TextColor", pScheme);
+ m_SelectionFgColor = GetSchemeColor("ListPanel.SelectedTextColor", m_LabelFgColor, pScheme);
+
+ m_hFont = pScheme->GetFont("Default", IsProportional());
+ SetFont(m_hFont);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::OnMousePressed( MouseCode code)
+{
+ if (code == MOUSE_LEFT || code == MOUSE_RIGHT)
+ {
+ ClearSelectedItems();
+ RequestFocus();
+ }
+ // check for context menu open
+ if (code == MOUSE_RIGHT)
+ {
+ // post it, but with the invalid row
+ PostActionSignal(new KeyValues("OpenContextMenu", "itemID", -1));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::OnShiftSelect(int itemID)
+{
+ // if we dont' have a valid selected ItemID - then we just choose the first item
+ if ( !m_DataItems.IsValidIndex(m_ShiftStartItemID) )
+ {
+ m_ShiftStartItemID = m_DataItems.Head();
+ }
+
+ // find out if the just pressed item is "earlier" or is the 'last selected item'
+ int lowerPos = -1, upperPos = -1;
+ int i;
+ for ( i = 0 ; i < m_SortedItems.Count() ; i++ )
+ {
+ if ( m_SortedItems[i] == itemID )
+ {
+ lowerPos = i;
+ upperPos = m_SortedItems.Find(m_ShiftStartItemID);
+ break;
+ }
+ else if ( m_SortedItems[i] == m_ShiftStartItemID )
+ {
+ lowerPos = m_SortedItems.Find(m_ShiftStartItemID);
+ upperPos = i;
+ break;
+ }
+ }
+ assert(lowerPos <= upperPos);
+ if ( !input()->IsKeyDown(KEY_LCONTROL) && !input()->IsKeyDown(KEY_RCONTROL) )
+ {
+ ClearSelectedItems();
+ }
+
+ for ( i = lowerPos ; i <= upperPos ; i ++)
+ {
+ // do not use AddSelectedItem because we don't want to switch the shiftStartItemID
+ m_DataItems[ m_SortedItems[i] ]->SetSelected(true);
+ m_SelectedItems.AddToTail(m_SortedItems[i]);
+ m_LastSelectedItemID = itemID;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::OnItemMousePressed(ListViewItem* pItem, MouseCode code)
+{
+ int itemID = m_DataItems.Find(pItem);
+ if (!m_DataItems.IsValidIndex(itemID))
+ return;
+
+ // check for context menu open
+ if (code == MOUSE_RIGHT)
+ {
+ // if this is a new item - unselect everything else
+ if ( m_SelectedItems.Find(itemID) == -1)
+ {
+ ClearSelectedItems();
+ AddSelectedItem(itemID);
+ }
+
+ PostActionSignal(new KeyValues("OpenContextMenu", "itemID", itemID));
+ }
+ else
+ {
+ if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) )
+ {
+ OnShiftSelect(itemID);
+ }
+ else if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) )
+ {
+ if ( m_SelectedItems.Find(itemID) != -1)
+ {
+ m_SelectedItems.FindAndRemove(itemID);
+ pItem->SetSelected(false);
+
+ // manually select these since we 'last' clicked on these items
+ m_ShiftStartItemID = itemID;
+ m_LastSelectedItemID = itemID;
+ m_DataItems[itemID]->RequestFocus();
+ }
+ else
+ {
+ AddSelectedItem(itemID);
+ }
+ }
+ else
+ {
+ ClearSelectedItems();
+ AddSelectedItem(itemID);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::OnMouseDoublePressed( MouseCode code)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::OnItemMouseDoublePressed(ListViewItem* pItem, MouseCode code)
+{
+ if (code == MOUSE_LEFT)
+ {
+ OnKeyCodeTyped(KEY_ENTER);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::FinishKeyPress(int itemID)
+{
+ if ( input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT) )
+ {
+ OnShiftSelect(itemID);
+ }
+ else if ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) )
+ {
+ m_DataItems[itemID]->RequestFocus();
+ m_LastSelectedItemID = itemID;
+ }
+ else
+ {
+ SetSingleSelectedItem(itemID);
+ }
+ ScrollToItem(itemID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::OnKeyCodeTyped( KeyCode code )
+{
+ if ( m_DataItems.Count() == 0 )
+ return;
+
+ switch (code)
+ {
+ case KEY_HOME:
+ {
+ if (m_SortedItems.Count() > 0)
+ {
+ int itemID = m_SortedItems[0];
+ FinishKeyPress(itemID);
+ }
+ break;
+ }
+ case KEY_END:
+ {
+ if (m_DataItems.Count() > 0)
+ {
+ int itemID = m_SortedItems[ m_SortedItems.Count() - 1 ];
+ FinishKeyPress(itemID);
+ }
+ break;
+ }
+
+ case KEY_UP:
+ {
+ int itemPos = m_SortedItems.Find( m_LastSelectedItemID );
+ itemPos--;
+ if (itemPos < 0)
+ itemPos = 0;
+
+ FinishKeyPress(m_SortedItems[itemPos]);
+ break;
+ }
+
+ case KEY_DOWN:
+ {
+ int itemPos = m_SortedItems.Find( m_LastSelectedItemID );
+ itemPos++;
+ if (itemPos >= m_DataItems.Count())
+ itemPos = m_DataItems.Count() - 1;
+
+ FinishKeyPress(m_SortedItems[itemPos]);
+ break;
+ }
+
+ case KEY_LEFT:
+ {
+ int itemPos = m_SortedItems.Find( m_LastSelectedItemID );
+ itemPos -= GetItemsPerColumn();
+ if (itemPos < 0)
+ {
+ itemPos = 0;
+ }
+ FinishKeyPress(m_SortedItems[itemPos]);
+ break;
+ }
+
+ case KEY_RIGHT:
+ {
+ int itemPos = m_SortedItems.Find( m_LastSelectedItemID );
+ itemPos += GetItemsPerColumn();
+ if (itemPos >= m_SortedItems.Count())
+ {
+ itemPos = m_SortedItems.Count() - 1;
+ }
+ FinishKeyPress(m_SortedItems[itemPos]);
+ break;
+ }
+
+ case KEY_PAGEUP:
+ {
+ int wide, tall;
+ GetSize(wide, tall);
+
+ int maxWidth = GetItemsMaxWidth();
+ if (maxWidth == 0)
+ {
+ maxWidth = wide;
+ }
+ int maxColVisible = wide / maxWidth;
+ int delta = maxColVisible * GetItemsPerColumn();
+
+ int itemPos = m_SortedItems.Find( m_LastSelectedItemID );
+ itemPos -= delta;
+ if (itemPos < 0)
+ {
+ itemPos = 0;
+ }
+
+ FinishKeyPress(m_SortedItems[itemPos]);
+ break;
+ }
+ case KEY_PAGEDOWN:
+ {
+ int wide, tall;
+ GetSize(wide, tall);
+
+ int maxWidth = GetItemsMaxWidth();
+ if (maxWidth == 0)
+ {
+ maxWidth = wide;
+ }
+ int maxColVisible = wide / maxWidth;
+ int delta = maxColVisible * GetItemsPerColumn();
+
+ int itemPos = m_SortedItems.Find( m_LastSelectedItemID );
+ itemPos += delta;
+ if (itemPos >= m_SortedItems.Count())
+ {
+ itemPos = m_SortedItems.Count() - 1;
+ }
+
+ FinishKeyPress(m_SortedItems[itemPos]);
+ break;
+ }
+ default:
+ {
+ BaseClass::OnKeyCodeTyped(code);
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::OnKeyTyped(wchar_t unichar)
+{
+ if (!iswcntrl(unichar))
+ {
+ wchar_t uniString[2];
+ uniString[0] = unichar;
+ uniString[1] = 0;
+
+ char buf[2];
+ g_pVGuiLocalize->ConvertUnicodeToANSI(uniString, buf, sizeof(buf));
+
+ int i;
+ int itemPos = m_SortedItems.Find(m_LastSelectedItemID);
+ if ( m_SortedItems.IsValidIndex(itemPos))
+ {
+ itemPos++;
+ // start from the item AFTER our last selected Item and go to end
+ for ( i = itemPos ; i != m_SortedItems.Count(); i++)
+ {
+ KeyValues *kv = m_DataItems[ m_SortedItems[i] ]->GetData();
+ const char *pszText = kv->GetString("text");
+ if (!strnicmp(pszText, buf, 1))
+ {
+ // select the next of this letter
+ SetSingleSelectedItem(m_SortedItems[i]);
+ ScrollToItem(m_SortedItems[i]);
+ return;
+ }
+ }
+ // if the after this item we couldn't fine an item with the same letter, fall through and just start from the beginning of list to the last selected item
+ }
+
+ for ( i = 0 ; i < m_SortedItems.Count() ; i++ )
+ {
+ // we've gone all the way around - break - if we had a valid index, this is one more that last selectedItem, if not it's an illegal index
+ if ( i == itemPos)
+ break;
+
+ KeyValues *kv = m_DataItems[ m_SortedItems[i] ]->GetData();
+ const char *pszText = kv->GetString("text");
+ if (!strnicmp(pszText, buf, 1))
+ {
+ SetSingleSelectedItem(m_SortedItems[i]);
+ ScrollToItem(m_SortedItems[i]);
+ return;
+ }
+ }
+ }
+ else
+ BaseClass::OnKeyTyped(unichar);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ListViewPanel::OnSliderMoved()
+{
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int ListViewPanel::GetItemsPerColumn()
+{
+ int wide, tall;
+ GetSize(wide, tall);
+
+ if ( m_hbar->IsVisible() )
+ {
+ tall -= m_hbar->GetTall();
+ }
+
+ return tall / m_iRowHeight; // should round down
+}
+
diff --git a/mp/src/vgui2/vgui_controls/Menu.cpp b/mp/src/vgui2/vgui_controls/Menu.cpp
new file mode 100644
index 00000000..ceb875a4
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/Menu.cpp
@@ -0,0 +1,2703 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vgui_controls/pch_vgui_controls.h"
+
+// memdbgon must be the last include file in a .cpp file
+#include "tier0/memdbgon.h"
+#define MENU_SEPARATOR_HEIGHT 3
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: divider line in a menu
+//-----------------------------------------------------------------------------
+class vgui::MenuSeparator : public Panel
+{
+public:
+ DECLARE_CLASS_SIMPLE( MenuSeparator, Panel );
+
+ MenuSeparator( Panel *parent, char const *panelName ) :
+ BaseClass( parent, panelName )
+ {
+ SetPaintEnabled( true );
+ SetPaintBackgroundEnabled( true );
+ SetPaintBorderEnabled( false );
+ }
+
+ virtual void Paint()
+ {
+ int w, h;
+ GetSize( w, h );
+
+ surface()->DrawSetColor( GetFgColor() );
+ surface()->DrawFilledRect( 4, 1, w-1, 2 );
+ }
+
+ virtual void ApplySchemeSettings( IScheme *pScheme )
+ {
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ SetFgColor( pScheme->GetColor( "Menu.SeparatorColor", Color( 142, 142, 142, 255 ) ) );
+ SetBgColor( pScheme->GetColor( "Menu.BgColor", Color( 0, 0, 0, 255 ) ) );
+ }
+};
+
+DECLARE_BUILD_FACTORY( Menu );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+Menu::Menu(Panel *parent, const char *panelName) : Panel(parent, panelName)
+{
+ m_Alignment = Label::a_west;
+ m_iFixedWidth = 0;
+ m_iMinimumWidth = 0;
+ m_iNumVisibleLines = -1; // No limit
+ m_iCurrentlySelectedItemID = m_MenuItems.InvalidIndex();
+ m_pScroller = new ScrollBar(this, "MenuScrollBar", true);
+ m_pScroller->SetVisible(false);
+ m_pScroller->AddActionSignalTarget(this);
+ _sizedForScrollBar = false;
+ SetZPos(1);
+ SetVisible(false);
+ MakePopup(false);
+ SetParent(parent);
+ _recalculateWidth = true;
+ m_bUseMenuManager = true;
+ m_iInputMode = MOUSE;
+ m_iCheckImageWidth = 0;
+ m_iActivatedItem = 0;
+
+ m_bUseFallbackFont = false;
+ m_hFallbackItemFont = INVALID_FONT;
+
+ if (IsProportional())
+ {
+ m_iMenuItemHeight = scheme()->GetProportionalScaledValueEx( GetScheme(), DEFAULT_MENU_ITEM_HEIGHT );
+ }
+ else
+ {
+ m_iMenuItemHeight = DEFAULT_MENU_ITEM_HEIGHT;
+ }
+ m_hItemFont = INVALID_FONT;
+
+
+ m_eTypeAheadMode = COMPAT_MODE;
+ m_szTypeAheadBuf[0] = '\0';
+ m_iNumTypeAheadChars = 0;
+ m_fLastTypeAheadTime = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+Menu::~Menu()
+{
+ delete m_pScroller;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove all menu items from the menu.
+//-----------------------------------------------------------------------------
+void Menu::DeleteAllItems()
+{
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ m_MenuItems[i]->MarkForDeletion();
+ }
+
+ m_MenuItems.RemoveAll();
+ m_SortedItems.RemoveAll();
+ m_VisibleSortedItems.RemoveAll();
+ m_Separators.RemoveAll();
+ int c = m_SeparatorPanels.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ m_SeparatorPanels[ i ]->MarkForDeletion();
+ }
+ m_SeparatorPanels.RemoveAll();
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a menu item to the menu.
+//-----------------------------------------------------------------------------
+int Menu::AddMenuItem( MenuItem *panel )
+{
+ panel->SetParent( this );
+ MEM_ALLOC_CREDIT();
+ int itemID = m_MenuItems.AddToTail( panel );
+ m_SortedItems.AddToTail(itemID);
+ InvalidateLayout(false);
+ _recalculateWidth = true;
+ panel->SetContentAlignment( m_Alignment );
+ if ( INVALID_FONT != m_hItemFont )
+ {
+ panel->SetFont( m_hItemFont );
+ }
+ if ( m_bUseFallbackFont && INVALID_FONT != m_hFallbackItemFont )
+ {
+ Label *l = panel;
+ TextImage *ti = l->GetTextImage();
+ if ( ti )
+ {
+ ti->SetUseFallbackFont( m_bUseFallbackFont, m_hFallbackItemFont );
+ }
+ }
+
+ if ( panel->GetHotKey() )
+ {
+ SetTypeAheadMode( HOT_KEY_MODE );
+ }
+
+ return itemID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Remove a single item
+//-----------------------------------------------------------------------------
+void Menu::DeleteItem( int itemID )
+{
+ // FIXME: This doesn't work with separator panels yet
+ Assert( m_SeparatorPanels.Count() == 0 );
+
+ m_MenuItems[itemID]->MarkForDeletion();
+ m_MenuItems.Remove( itemID );
+
+ m_SortedItems.FindAndRemove( itemID );
+ m_VisibleSortedItems.FindAndRemove( itemID );
+
+ InvalidateLayout(false);
+ _recalculateWidth = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a menu item to the menu.
+// Input : *item - MenuItem
+// *command - Command text to be sent when menu item is selected
+// *target - Target panel of the command
+// *userData - any user data associated with this menu item
+// Output: itemID - ID of this item
+//-----------------------------------------------------------------------------
+int Menu::AddMenuItemCharCommand(MenuItem *item, const char *command, Panel *target, const KeyValues *userData)
+{
+ item->SetCommand(command);
+ item->AddActionSignalTarget( target );
+ item->SetUserData(userData);
+ return AddMenuItem( item );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a menu item to the menu.
+// Input : *itemName - Name of item
+// *itemText - Name of item text that will appear in the manu.
+// *message - pointer to the message to send when the item is selected
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+// Output: itemID - ID of this item
+//-----------------------------------------------------------------------------
+int Menu::AddMenuItemKeyValuesCommand( MenuItem *item, KeyValues *message, Panel *target, const KeyValues *userData )
+{
+ item->SetCommand(message);
+ item->AddActionSignalTarget(target);
+ item->SetUserData(userData);
+ return AddMenuItem(item);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a menu item to the menu.
+// Input : *itemName - Name of item
+// *itemText - Name of item text that will appear in the manu.
+// *command - Command text to be sent when menu item is selected
+// *target - Target panel of the command
+// Output: itemID - ID of this item
+//-----------------------------------------------------------------------------
+int Menu::AddMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData )
+{
+ MenuItem *item = new MenuItem(this, itemName, itemText );
+ return AddMenuItemCharCommand(item, command, target, userData);
+}
+
+int Menu::AddMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData )
+{
+ MenuItem *item = new MenuItem(this, itemName, wszItemText );
+ return AddMenuItemCharCommand(item, command, target, userData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a menu item to the menu.
+// Input : *itemText - Name of item text that will appear in the manu.
+// This will also be used as the name of the menu item panel.
+// *command - Command text to be sent when menu item is selected
+// *target - Target panel of the command
+// Output: itemID - ID of this item
+//-----------------------------------------------------------------------------
+int Menu::AddMenuItem( const char *itemText, const char *command, Panel *target, const KeyValues *userData )
+{
+ return AddMenuItem(itemText, itemText, command, target, userData ) ;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a menu item to the menu.
+// Input : *itemName - Name of item
+// *itemText - Name of item text that will appear in the manu.
+// *message - pointer to the message to send when the item is selected
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+//-----------------------------------------------------------------------------
+int Menu::AddMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData )
+{
+ MenuItem *item = new MenuItem(this, itemName, itemText );
+ return AddMenuItemKeyValuesCommand(item, message, target, userData);
+}
+
+int Menu::AddMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, const KeyValues *userData )
+{
+ MenuItem *item = new MenuItem(this, itemName, wszItemText );
+ return AddMenuItemKeyValuesCommand(item, message, target, userData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a menu item to the menu.
+// Input : *itemText - Name of item text that will appear in the manu.
+// This will also be used as the name of the menu item panel.
+// *message - pointer to the message to send when the item is selected
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+//-----------------------------------------------------------------------------
+int Menu::AddMenuItem( const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData )
+{
+ return AddMenuItem(itemText, itemText, message, target, userData );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a menu item to the menu.
+// Input : *itemText - Name of item text that will appear in the manu.
+// This will also be the text of the command sent when the
+// item is selected.
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+//-----------------------------------------------------------------------------
+int Menu::AddMenuItem( const char *itemText, Panel *target , const KeyValues *userData )
+{
+ return AddMenuItem(itemText, itemText, target, userData );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a checkable menu item to the menu.
+// Input : *itemName - Name of item
+// *itemText - Name of item text that will appear in the manu.
+// *command - Command text to be sent when menu item is selected
+// *target - Target panel of the command
+//-----------------------------------------------------------------------------
+int Menu::AddCheckableMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData )
+{
+ MenuItem *item = new MenuItem(this, itemName, itemText, NULL, true);
+ return AddMenuItemCharCommand(item, command, target, userData);
+}
+
+int Menu::AddCheckableMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData )
+{
+ MenuItem *item = new MenuItem(this, itemName, wszItemText, NULL, true);
+ return AddMenuItemCharCommand(item, command, target, userData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a checkable menu item to the menu.
+// Input : *itemText - Name of item text that will appear in the manu.
+// This will also be used as the name of the menu item panel.
+// *command - Command text to be sent when menu item is selected
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+//-----------------------------------------------------------------------------
+int Menu::AddCheckableMenuItem( const char *itemText, const char *command, Panel *target, const KeyValues *userData )
+{
+ return AddCheckableMenuItem(itemText, itemText, command, target, userData );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a checkable menu item to the menu.
+// Input : *itemName - Name of item
+// *itemText - Name of item text that will appear in the manu.
+// *message - pointer to the message to send when the item is selected
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+//-----------------------------------------------------------------------------
+int Menu::AddCheckableMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData )
+{
+ MenuItem *item = new MenuItem(this, itemName, itemText, NULL, true);
+ return AddMenuItemKeyValuesCommand(item, message, target, userData);
+}
+
+int Menu::AddCheckableMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, const KeyValues *userData )
+{
+ MenuItem *item = new MenuItem(this, itemName, wszItemText, NULL, true);
+ return AddMenuItemKeyValuesCommand(item, message, target, userData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a checkable menu item to the menu.
+// Input : *itemText - Name of item text that will appear in the manu.
+// This will also be used as the name of the menu item panel.
+// *message - pointer to the message to send when the item is selected
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+//-----------------------------------------------------------------------------
+int Menu::AddCheckableMenuItem( const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData )
+{
+ return AddCheckableMenuItem(itemText, itemText, message, target, userData );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a checkable menu item to the menu.
+// Input : *itemText - Name of item text that will appear in the manu.
+// This will also be the text of the command sent when the
+// item is selected.
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+//-----------------------------------------------------------------------------
+int Menu::AddCheckableMenuItem( const char *itemText, Panel *target, const KeyValues *userData )
+{
+ return AddCheckableMenuItem(itemText, itemText, target, userData );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a Cascading menu item to the menu.
+// Input : *itemName - Name of item
+// *itemText - Name of item text that will appear in the manu.
+// *command - Command text to be sent when menu item is selected
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+//-----------------------------------------------------------------------------
+int Menu::AddCascadingMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, Menu *cascadeMenu , const KeyValues *userData )
+{
+ MenuItem *item = new MenuItem(this, itemName, itemText, cascadeMenu );
+ return AddMenuItemCharCommand(item, command, target, userData);
+}
+
+int Menu::AddCascadingMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, Menu *cascadeMenu , const KeyValues *userData )
+{
+ MenuItem *item = new MenuItem(this, itemName, wszItemText, cascadeMenu );
+ return AddMenuItemCharCommand(item, command, target, userData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a Cascading menu item to the menu.
+// Input : *itemText - Name of item text that will appear in the manu.
+// This will also be used as the name of the menu item panel.
+// *command - Command text to be sent when menu item is selected
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+//-----------------------------------------------------------------------------
+int Menu::AddCascadingMenuItem( const char *itemText, const char *command, Panel *target, Menu *cascadeMenu , const KeyValues *userData )
+{
+ return AddCascadingMenuItem( itemText, itemText, command, target, cascadeMenu, userData );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a Cascading menu item to the menu.
+// Input : *itemName - Name of item
+// *itemText - Name of item text that will appear in the manu.
+// *message - pointer to the message to send when the item is selected
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+//-----------------------------------------------------------------------------
+int Menu::AddCascadingMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData )
+{
+ MenuItem *item = new MenuItem( this, itemName, itemText, cascadeMenu);
+ return AddMenuItemKeyValuesCommand(item, message, target, userData);
+}
+
+int Menu::AddCascadingMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData )
+{
+ MenuItem *item = new MenuItem( this, itemName, wszItemText, cascadeMenu);
+ return AddMenuItemKeyValuesCommand(item, message, target, userData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a Cascading menu item to the menu.
+// Input : *itemText - Name of item text that will appear in the manu.
+// This will also be used as the name of the menu item panel.
+// *message - pointer to the message to send when the item is selected
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+//-----------------------------------------------------------------------------
+int Menu::AddCascadingMenuItem( const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData )
+{
+ return AddCascadingMenuItem(itemText, itemText, message, target, cascadeMenu, userData );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a Cascading menu item to the menu.
+// Input : *itemText - Name of item text that will appear in the manu.
+// This will also be the text of the command sent when the
+// item is selected.
+// *target - Target panel of the command
+// *cascadeMenu - if the menu item opens a cascading menu, this is a
+// ptr to the menu that opens on selecting the item
+//-----------------------------------------------------------------------------
+int Menu::AddCascadingMenuItem( const char *itemText, Panel *target, Menu *cascadeMenu, const KeyValues *userData )
+{
+ return AddCascadingMenuItem(itemText, itemText, target, cascadeMenu, userData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the values of a menu item at the specified index
+// Input : index - the index of this item entry
+// *message - pointer to the message to send when the item is selected
+//-----------------------------------------------------------------------------
+void Menu::UpdateMenuItem(int itemID, const char *itemText, KeyValues *message, const KeyValues *userData)
+{
+ Assert( m_MenuItems.IsValidIndex(itemID) );
+ if ( m_MenuItems.IsValidIndex(itemID) )
+ {
+ MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
+ // make sure its enabled since disabled items get highlighted.
+ if (menuItem)
+ {
+ menuItem->SetText(itemText);
+ menuItem->SetCommand(message);
+ if(userData)
+ {
+ menuItem->SetUserData(userData);
+ }
+ }
+ }
+ _recalculateWidth = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the values of a menu item at the specified index
+//-----------------------------------------------------------------------------
+void Menu::UpdateMenuItem(int itemID, const wchar_t *wszItemText, KeyValues *message, const KeyValues *userData)
+{
+ Assert( m_MenuItems.IsValidIndex(itemID) );
+ if ( m_MenuItems.IsValidIndex(itemID) )
+ {
+ MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
+ // make sure its enabled since disabled items get highlighted.
+ if (menuItem)
+ {
+ menuItem->SetText(wszItemText);
+ menuItem->SetCommand(message);
+ if(userData)
+ {
+ menuItem->SetUserData(userData);
+ }
+ }
+ }
+ _recalculateWidth = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the content alignment of all items in the menu
+//-----------------------------------------------------------------------------
+void Menu::SetContentAlignment( Label::Alignment alignment )
+{
+ if ( m_Alignment != alignment )
+ {
+ m_Alignment = alignment;
+
+ // Change the alignment of existing menu items
+ int nCount = m_MenuItems.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ m_MenuItems[i]->SetContentAlignment( alignment );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Locks down a specific width
+//-----------------------------------------------------------------------------
+void Menu::SetFixedWidth(int width)
+{
+ // the padding makes it so the menu has the label padding on each side of the menu.
+ // makes the menu items look centered.
+ m_iFixedWidth = width;
+ InvalidateLayout(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the height of each menu item
+//-----------------------------------------------------------------------------
+void Menu::SetMenuItemHeight(int itemHeight)
+{
+ m_iMenuItemHeight = itemHeight;
+}
+
+int Menu::GetMenuItemHeight() const
+{
+ return m_iMenuItemHeight;
+}
+
+int Menu::CountVisibleItems()
+{
+ int count = 0;
+ int c = m_SortedItems.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ if ( m_MenuItems[ m_SortedItems[ i ] ]->IsVisible() )
+ ++count;
+ }
+ return count;
+}
+
+void Menu::ComputeWorkspaceSize( int& workWide, int& workTall )
+{
+ // make sure we factor in insets
+ int ileft, iright, itop, ibottom;
+ GetInset(ileft, iright, itop, ibottom);
+
+ int workX, workY;
+ surface()->GetWorkspaceBounds(workX, workY, workWide, workTall);
+ workTall -= 20;
+ workTall -= itop;
+ workTall -= ibottom;
+}
+
+// Assumes relative coords in screenspace
+void Menu::PositionRelativeToPanel( Panel *relative, MenuDirection_e direction, int nAdditionalYOffset /*=0*/, bool showMenu /*=false*/ )
+{
+ Assert( relative );
+ int rx, ry, rw, rh;
+ relative->GetBounds( rx, ry, rw, rh );
+ relative->LocalToScreen( rx, ry );
+
+ if ( direction == CURSOR )
+ {
+ // force the menu to appear where the mouse button was pressed
+ input()->GetCursorPos(rx, ry);
+ rw = rh = 0;
+ }
+ else if ( direction == ALIGN_WITH_PARENT && relative->GetVParent() )
+ {
+ rx = 0, ry = 0;
+ relative->ParentLocalToScreen(rx, ry);
+ rx -= 1; // take border into account
+ ry += rh + nAdditionalYOffset;
+ rw = rh = 0;
+ }
+ else
+ {
+ rx = 0, ry = 0;
+ relative->LocalToScreen(rx, ry);
+ }
+
+ int workWide, workTall;
+ ComputeWorkspaceSize( workWide, workTall );
+
+ // Final pos
+ int x = 0, y = 0;
+
+ int mWide, mTall;
+ GetSize( mWide, mTall );
+
+ switch( direction )
+ {
+ case Menu::UP: // Menu prefers to open upward
+ {
+ x = rx;
+ int topOfReference = ry;
+ y = topOfReference - mTall;
+ if ( y < 0 )
+ {
+ int bottomOfReference = ry + rh + 1;
+ int remainingPixels = workTall - bottomOfReference;
+
+ // Can't fit on bottom, either, move to side
+ if ( mTall >= remainingPixels )
+ {
+ y = workTall - mTall;
+ x = rx + rw;
+ // Try and place it to the left of the button
+ if ( x + mWide > workWide )
+ {
+ x = rx - mWide;
+ }
+ }
+ else
+ {
+ // Room at bottom
+ y = bottomOfReference;
+ }
+ }
+ }
+ break;
+ // Everyone else aligns downward...
+ default:
+ case Menu::LEFT:
+ case Menu::RIGHT:
+ case Menu::DOWN:
+ {
+ x = rx;
+ int bottomOfReference = ry + rh + 1;
+ y = bottomOfReference;
+ if ( bottomOfReference + mTall >= workTall )
+ {
+ // See if there's run straight above
+ if ( mTall >= ry ) // No room, try and push menu to right or left
+ {
+ y = workTall - mTall;
+ x = rx + rw;
+ // Try and place it to the left of the button
+ if ( x + mWide > workWide )
+ {
+ x = rx - mWide;
+ }
+ }
+ else
+ {
+ // Room at top
+ y = ry - mTall;
+ }
+ }
+ }
+ break;
+ }
+
+ // Check left rightness
+ if ( x + mWide > workWide )
+ {
+ x = workWide - mWide;
+ Assert( x >= 0 ); // yikes!!!
+ }
+ else if ( x < 0 )
+ {
+ x = 0;
+ }
+
+ SetPos( x, y );
+ if ( showMenu )
+ {
+ SetVisible( true );
+ }
+}
+
+int Menu::ComputeFullMenuHeightWithInsets()
+{
+ // make sure we factor in insets
+ int ileft, iright, itop, ibottom;
+ GetInset(ileft, iright, itop, ibottom);
+
+ int separatorHeight = 3;
+
+ // add up the size of all the child panels
+ // move the child panels to the correct place in the menu
+ int totalTall = itop + ibottom;
+ int i;
+ for ( i = 0 ; i < m_SortedItems.Count() ; i++ ) // use sortedItems instead of MenuItems due to SetPos()
+ {
+ int itemId = m_SortedItems[i];
+
+ MenuItem *child = m_MenuItems[ itemId ];
+ Assert( child );
+ if ( !child )
+ continue;
+ // These should all be visible at this point
+ if ( !child->IsVisible() )
+ continue;
+
+ totalTall += m_iMenuItemHeight;
+
+ // Add a separator if needed...
+ int sepIndex = m_Separators.Find( itemId );
+ if ( sepIndex != m_Separators.InvalidIndex() )
+ {
+ totalTall += separatorHeight;
+ }
+ }
+
+ return totalTall;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Reformat according to the new layout
+//-----------------------------------------------------------------------------
+void Menu::PerformLayout()
+{
+ MenuItem *parent = GetParentMenuItem();
+ bool cascading = parent != NULL ? true : false;
+
+ // make sure we factor in insets
+ int ileft, iright, itop, ibottom;
+ GetInset(ileft, iright, itop, ibottom);
+
+ int workWide, workTall;
+
+ ComputeWorkspaceSize( workWide, workTall );
+
+ int fullHeightWouldRequire = ComputeFullMenuHeightWithInsets();
+
+ bool bNeedScrollbar = fullHeightWouldRequire >= workTall;
+
+ int maxVisibleItems = CountVisibleItems();
+
+ if ( m_iNumVisibleLines > 0 &&
+ maxVisibleItems > m_iNumVisibleLines )
+ {
+ bNeedScrollbar = true;
+ maxVisibleItems = m_iNumVisibleLines;
+ }
+
+ // if we have a scroll bar
+ if ( bNeedScrollbar )
+ {
+ // add it to the display
+ AddScrollBar();
+
+ // This fills in m_VisibleSortedItems as needed
+ MakeItemsVisibleInScrollRange( m_iNumVisibleLines, min( fullHeightWouldRequire, workTall ) );
+ }
+ else
+ {
+ RemoveScrollBar();
+ // Make everything visible
+ m_VisibleSortedItems.RemoveAll();
+ int i;
+ int c = m_SortedItems.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ int itemID = m_SortedItems[ i ];
+ MenuItem *child = m_MenuItems[ itemID ];
+ if ( !child || !child->IsVisible() )
+ continue;
+
+ m_VisibleSortedItems.AddToTail( itemID );
+ }
+
+ // Hide the separators, the needed ones will be readded below
+ c = m_SeparatorPanels.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ if ( m_SeparatorPanels[ i ] )
+ {
+ m_SeparatorPanels[ i ]->SetVisible( false );
+ }
+ }
+ }
+
+ // get the appropriate menu border
+ LayoutMenuBorder();
+
+ int trueW = GetWide();
+ if ( bNeedScrollbar )
+ {
+ trueW -= m_pScroller->GetWide();
+ }
+ int separatorHeight = MENU_SEPARATOR_HEIGHT;
+
+ // add up the size of all the child panels
+ // move the child panels to the correct place in the menu
+ int menuTall = 0;
+ int totalTall = itop + ibottom;
+ int i;
+ for ( i = 0 ; i < m_VisibleSortedItems.Count() ; i++ ) // use sortedItems instead of MenuItems due to SetPos()
+ {
+ int itemId = m_VisibleSortedItems[i];
+
+ MenuItem *child = m_MenuItems[ itemId ];
+ Assert( child );
+ if ( !child )
+ continue;
+ // These should all be visible at this point
+ if ( !child->IsVisible() )
+ continue;
+
+ if ( totalTall >= workTall )
+ break;
+
+ if ( INVALID_FONT != m_hItemFont )
+ {
+ child->SetFont( m_hItemFont );
+ }
+
+ // take into account inset
+ child->SetPos (0, menuTall);
+ child->SetTall( m_iMenuItemHeight ); // Width is set in a second pass
+ menuTall += m_iMenuItemHeight;
+ totalTall += m_iMenuItemHeight;
+
+ // this will make all the menuitems line up in a column with space for the checks to the left.
+ if ( ( !child->IsCheckable() ) && ( m_iCheckImageWidth > 0 ) )
+ {
+ // Non checkable items have to move over
+ child->SetTextInset( m_iCheckImageWidth, 0 );
+ }
+ else if ( child->IsCheckable() )
+ {
+ child->SetTextInset(0, 0); //TODO: for some reason I can't comment this out.
+ }
+
+ // Add a separator if needed...
+ int sepIndex = m_Separators.Find( itemId );
+ if ( sepIndex != m_Separators.InvalidIndex() )
+ {
+ MenuSeparator *sep = m_SeparatorPanels[ sepIndex ];
+ Assert( sep );
+ sep->SetVisible( true );
+ sep->SetBounds( 0, menuTall, trueW, separatorHeight );
+ menuTall += separatorHeight;
+ totalTall += separatorHeight;
+ }
+ }
+
+ if (!m_iFixedWidth)
+ {
+ _recalculateWidth = true;
+ CalculateWidth();
+ }
+ else if (m_iFixedWidth)
+ {
+ _menuWide = m_iFixedWidth;
+ // fixed width menus include the scroll bar in their width.
+ if (_sizedForScrollBar)
+ {
+ _menuWide -= m_pScroller->GetWide();
+ }
+ }
+
+ SizeMenuItems();
+
+ int extraWidth = 0;
+ if (_sizedForScrollBar)
+ {
+ extraWidth = m_pScroller->GetWide();
+ }
+
+ int mwide = _menuWide + extraWidth;
+ if ( mwide > workWide )
+ {
+ mwide = workWide;
+ }
+ int mtall = menuTall + itop + ibottom;
+ if ( mtall > workTall )
+ {
+ // Shouldn't happen
+ mtall = workTall;
+ }
+
+ // set the new size of the menu
+ SetSize( mwide, mtall );
+
+ // move the menu to the correct position if it is a cascading menu.
+ if ( cascading )
+ {
+ // move the menu to the correct position if it is a cascading menu.
+ PositionCascadingMenu();
+ }
+
+ // set up scroll bar as appropriate
+ if ( m_pScroller->IsVisible() )
+ {
+ LayoutScrollBar();
+ }
+
+ FOR_EACH_LL( m_MenuItems, j )
+ {
+ m_MenuItems[j]->InvalidateLayout(); // cause each menu item to redo its apply settings now we have sized ourselves
+ }
+
+ Repaint();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Force the menu to work out how wide it should be
+//-----------------------------------------------------------------------------
+void Menu::ForceCalculateWidth()
+{
+ _recalculateWidth = true;
+ CalculateWidth();
+ PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Figure out how wide the menu should be if the menu is not fixed width
+//-----------------------------------------------------------------------------
+void Menu::CalculateWidth()
+{
+ if (!_recalculateWidth)
+ return;
+
+ _menuWide = 0;
+ if (!m_iFixedWidth)
+ {
+ // find the biggest menu item
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ int wide, tall;
+ m_MenuItems[i]->GetContentSize(wide, tall);
+ if (wide > _menuWide - Label::Content)
+ {
+ _menuWide = wide + Label::Content;
+ }
+ }
+ }
+
+ // enfoce a minimumWidth
+ if (_menuWide < m_iMinimumWidth)
+ {
+ _menuWide = m_iMinimumWidth;
+ }
+
+ _recalculateWidth = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set up the scroll bar attributes,size and location.
+//-----------------------------------------------------------------------------
+void Menu::LayoutScrollBar()
+{
+ //!! need to make it recalculate scroll positions
+ m_pScroller->SetEnabled(false);
+ m_pScroller->SetRangeWindow( m_VisibleSortedItems.Count() );
+ m_pScroller->SetRange( 0, CountVisibleItems() );
+ m_pScroller->SetButtonPressedScrollValue( 1 );
+
+ int wide, tall;
+ GetSize (wide, tall);
+
+ // make sure we factor in insets
+ int ileft, iright, itop, ibottom;
+ GetInset(ileft, iright, itop, ibottom);
+
+ // with a scroll bar we take off the inset
+ wide -= iright;
+
+ m_pScroller->SetPos(wide - m_pScroller->GetWide(), 1);
+
+ // scrollbar is inside the menu's borders.
+ m_pScroller->SetSize(m_pScroller->GetWide(), tall - ibottom - itop);
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Figure out where to open menu if it is a cascading menu
+//-----------------------------------------------------------------------------
+void Menu::PositionCascadingMenu()
+{
+ Assert(GetVParent());
+ int parentX, parentY, parentWide, parentTall;
+ // move the menu to the correct place below the menuItem
+ ipanel()->GetSize(GetVParent(), parentWide, parentTall);
+ ipanel()->GetPos(GetVParent(), parentX, parentY);
+
+ parentX += parentWide, parentY = 0;
+
+ ParentLocalToScreen(parentX, parentY);
+
+ SetPos(parentX, parentY);
+
+ // for cascading menus,
+ // make sure we're on the screen
+ int workX, workY, workWide, workTall, x, y, wide, tall;
+ GetBounds(x, y, wide, tall);
+ surface()->GetWorkspaceBounds(workX, workY, workWide, workTall);
+
+ if (x + wide > workX + workWide)
+ {
+ // we're off the right, move the menu to the left side
+ // orignalX - width of the parentmenuitem - width of this menu.
+ // add 2 pixels to offset one pixel onto the parent menu.
+ x -= (parentWide + wide);
+ x -= 2;
+ }
+ else
+ {
+ // alignment move it in the amount of the insets.
+ x += 1;
+ }
+
+ if ( y + tall > workY + workTall )
+ {
+ int lastWorkY = workY + workTall;
+ int pixelsOffBottom = ( y + tall ) - lastWorkY;
+
+ y -= pixelsOffBottom;
+ y -= 2;
+ }
+ else
+ {
+ y -= 1;
+ }
+ SetPos(x, y);
+
+ MoveToFront();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Size the menu items so they are the width of the menu.
+// Also size the menu items with cascading menus so the arrow fits in there.
+//-----------------------------------------------------------------------------
+void Menu::SizeMenuItems()
+{
+ int ileft, iright, itop, ibottom;
+ GetInset(ileft, iright, itop, ibottom);
+
+ // assign the sizes of all the menu item panels
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ MenuItem *child = m_MenuItems[i];
+ if (child )
+ {
+ // labels do thier own sizing. this will size the label to the width of the menu,
+ // this will put the cascading menu arrow on the right side automatically.
+ child->SetWide(_menuWide - ileft - iright);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Makes menu items visible in relation to where the scroll bar is
+//-----------------------------------------------------------------------------
+void Menu::MakeItemsVisibleInScrollRange( int maxVisibleItems, int nNumPixelsAvailable )
+{
+ // Detach all items from tree
+ int i;
+ FOR_EACH_LL( m_MenuItems, item )
+ {
+ m_MenuItems[ item ]->SetBounds( 0, 0, 0, 0 );
+ }
+ for ( i = 0; i < m_SeparatorPanels.Count(); ++i )
+ {
+ m_SeparatorPanels[ i ]->SetVisible( false );
+ }
+
+ m_VisibleSortedItems.RemoveAll();
+
+ int tall = 0;
+
+ int startItem = m_pScroller->GetValue();
+ Assert( startItem >= 0 );
+ do
+ {
+ if ( startItem >= m_SortedItems.Count() )
+ break;
+
+ int itemId = m_SortedItems[ startItem ];
+
+ if ( !m_MenuItems[ itemId ]->IsVisible() )
+ {
+ ++startItem;
+ continue;
+ }
+
+ int itemHeight = m_iMenuItemHeight;
+ int sepIndex = m_Separators.Find( itemId );
+ if ( sepIndex != m_Separators.InvalidIndex() )
+ {
+ itemHeight += MENU_SEPARATOR_HEIGHT;
+ }
+
+ if ( tall + itemHeight > nNumPixelsAvailable )
+ break;
+
+ // Too many items
+ if ( maxVisibleItems > 0 )
+ {
+ if ( m_VisibleSortedItems.Count() >= maxVisibleItems )
+ break;
+ }
+
+ tall += itemHeight;
+ // Re-attach this one
+ m_VisibleSortedItems.AddToTail( itemId );
+ ++startItem;
+ }
+ while ( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the approproate menu border
+//-----------------------------------------------------------------------------
+void Menu::LayoutMenuBorder()
+{
+ IBorder *menuBorder;
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+
+ menuBorder = pScheme->GetBorder("MenuBorder");
+
+ if ( menuBorder )
+ {
+ SetBorder(menuBorder);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw a black border on the right side of the menu items
+//-----------------------------------------------------------------------------
+void Menu::Paint()
+{
+ if ( m_pScroller->IsVisible() )
+ {
+ // draw black bar
+ int wide, tall;
+ GetSize (wide, tall);
+ surface()->DrawSetColor(_borderDark);
+ if( IsProportional() )
+ {
+ surface()->DrawFilledRect(wide - m_pScroller->GetWide(), -1, wide - m_pScroller->GetWide() + 1, tall);
+ }
+ else
+ {
+ surface()->DrawFilledRect(wide - m_pScroller->GetWide(), -1, wide - m_pScroller->GetWide() + 1, tall);
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the max number of items visible (scrollbar appears with more)
+// Input : numItems -
+//-----------------------------------------------------------------------------
+void Menu::SetNumberOfVisibleItems( int numItems )
+{
+ m_iNumVisibleLines = numItems;
+ InvalidateLayout(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Menu::EnableUseMenuManager( bool bUseMenuManager )
+{
+ m_bUseMenuManager = bUseMenuManager;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+MenuItem *Menu::GetMenuItem(int itemID)
+{
+ if ( !m_MenuItems.IsValidIndex(itemID) )
+ return NULL;
+
+ return m_MenuItems[itemID];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool Menu::IsValidMenuID(int itemID)
+{
+ return m_MenuItems.IsValidIndex(itemID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int Menu::GetInvalidMenuID()
+{
+ return m_MenuItems.InvalidIndex();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: When a menuItem is selected, close cascading menus
+// if the menuItem selected has a cascading menu attached, we
+// want to keep that one open so skip it.
+// Passing NULL will close all cascading menus.
+//-----------------------------------------------------------------------------
+void Menu::CloseOtherMenus(MenuItem *item)
+{
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ if (m_MenuItems[i] == item)
+ continue;
+
+ m_MenuItems[i]->CloseCascadeMenu();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Respond to string commands.
+//-----------------------------------------------------------------------------
+void Menu::OnCommand( const char *command )
+{
+ // forward on the message
+ PostActionSignal(new KeyValues("Command", "command", command));
+
+ Panel::OnCommand(command);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle key presses, Activate shortcuts
+//-----------------------------------------------------------------------------
+void Menu::OnKeyCodeTyped(KeyCode keycode)
+{
+ vgui::KeyCode code = GetBaseButtonCode( keycode );
+
+ // Don't allow key inputs when disabled!
+ if ( !IsEnabled() )
+ return;
+
+ bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
+ if (alt)
+ {
+ BaseClass::OnKeyCodeTyped( keycode );
+ // Ignore alt when in combobox mode
+ if (m_eTypeAheadMode != TYPE_AHEAD_MODE)
+ {
+ PostActionSignal(new KeyValues("MenuClose"));
+ }
+ }
+
+ switch (code)
+ {
+ case KEY_ESCAPE:
+ case KEY_XBUTTON_B:
+ {
+ // hide the menu on ESC
+ SetVisible(false);
+ break;
+ }
+ // arrow keys scroll through items on the list.
+ // they should also scroll the scroll bar if needed
+ case KEY_UP:
+ case KEY_XBUTTON_UP:
+ case KEY_XSTICK1_UP:
+ {
+ MoveAlongMenuItemList(MENU_UP, 0);
+ if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) )
+ {
+ m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( keycode ); // chain up
+ }
+ break;
+ }
+ case KEY_DOWN:
+ case KEY_XBUTTON_DOWN:
+ case KEY_XSTICK1_DOWN:
+ {
+ MoveAlongMenuItemList(MENU_DOWN, 0);
+ if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) )
+ {
+ m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( keycode ); // chain up
+ }
+ break;
+ }
+ // for now left and right arrows just open or close submenus if they are there.
+ case KEY_RIGHT:
+ case KEY_XBUTTON_RIGHT:
+ case KEY_XSTICK1_RIGHT:
+ {
+ // make sure a menuItem is currently selected
+ if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) )
+ {
+ if (m_MenuItems[m_iCurrentlySelectedItemID]->HasMenu())
+ {
+ ActivateItem(m_iCurrentlySelectedItemID);
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( keycode );
+ }
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( keycode );
+ }
+ break;
+ }
+ case KEY_LEFT:
+ case KEY_XBUTTON_LEFT:
+ case KEY_XSTICK1_LEFT:
+ {
+ // if our parent is a menu item then we are a submenu so close us.
+ if (GetParentMenuItem())
+ {
+ SetVisible(false);
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( keycode );
+ }
+ break;
+ }
+ case KEY_ENTER:
+ case KEY_XBUTTON_A:
+ {
+ // make sure a menuItem is currently selected
+ if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) )
+ {
+ ActivateItem(m_iCurrentlySelectedItemID);
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( keycode ); // chain up
+ }
+ break;
+ }
+
+ case KEY_PAGEUP:
+ {
+ if ( m_iNumVisibleLines > 1 )
+ {
+ if ( m_iCurrentlySelectedItemID < m_iNumVisibleLines )
+ {
+ MoveAlongMenuItemList( MENU_UP * m_iCurrentlySelectedItemID, 0 );
+ }
+ else
+ {
+ MoveAlongMenuItemList(MENU_UP * m_iNumVisibleLines - 1, 0);
+ }
+ }
+ else
+ {
+ MoveAlongMenuItemList(MENU_UP, 0);
+ }
+
+ if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) )
+ {
+ m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
+ }
+ break;
+ }
+
+
+ case KEY_PAGEDOWN:
+ {
+ if ( m_iNumVisibleLines > 1 )
+ {
+ if ( m_iCurrentlySelectedItemID + m_iNumVisibleLines >= GetItemCount() )
+ {
+ MoveAlongMenuItemList(MENU_DOWN * ( GetItemCount() - m_iCurrentlySelectedItemID - 1), 0);
+ }
+ else
+ {
+ MoveAlongMenuItemList(MENU_DOWN * m_iNumVisibleLines - 1, 0);
+ }
+ }
+ else
+ {
+ MoveAlongMenuItemList(MENU_DOWN, 0);
+ }
+
+ if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) )
+ {
+ m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
+ }
+ break;
+ }
+
+ case KEY_HOME:
+ {
+ MoveAlongMenuItemList( MENU_UP * m_iCurrentlySelectedItemID, 0 );
+ if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) )
+ {
+ m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
+ }
+ break;
+ }
+
+
+ case KEY_END:
+ {
+ MoveAlongMenuItemList(MENU_DOWN * ( GetItemCount() - m_iCurrentlySelectedItemID - 1), 0);
+ if ( m_MenuItems.IsValidIndex( m_iCurrentlySelectedItemID ) )
+ {
+ m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
+ }
+ break;
+ }
+ }
+
+ // don't chain back
+}
+
+void Menu::OnHotKey(wchar_t unichar)
+{
+ // iterate the menu items looking for one with the matching hotkey
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ MenuItem *panel = m_MenuItems[i];
+ if (panel->IsVisible())
+ {
+ Panel *hot = panel->HasHotkey(unichar);
+ if (hot)
+ {
+ // post a message to the menuitem telling it it's hotkey was pressed
+ PostMessage(hot, new KeyValues("Hotkey"));
+ return;
+ }
+ // if the menuitem is a cascading menuitem and it is open, check its hotkeys too
+ Menu *cascadingMenu = panel->GetMenu();
+ if (cascadingMenu && cascadingMenu->IsVisible())
+ {
+ cascadingMenu->OnKeyTyped(unichar);
+ }
+ }
+ }
+}
+
+void Menu::OnTypeAhead(wchar_t unichar)
+{
+ // Don't do anything if the menu is empty since there cannot be a selected item.
+ if ( m_MenuItems.Count() <= 0)
+ return;
+
+ // expire the type ahead buffer after 0.5 seconds
+ double tCurrentTime = Sys_FloatTime();
+ if ( (tCurrentTime - m_fLastTypeAheadTime) > 0.5f )
+ {
+ m_iNumTypeAheadChars = 0;
+ m_szTypeAheadBuf[0] = '\0';
+ }
+ m_fLastTypeAheadTime = tCurrentTime;
+
+ // add current character to the type ahead buffer
+ if ( m_iNumTypeAheadChars+1 < TYPEAHEAD_BUFSIZE )
+ {
+ m_szTypeAheadBuf[m_iNumTypeAheadChars++] = unichar;
+ }
+
+ int itemToSelect = m_iCurrentlySelectedItemID;
+ if ( itemToSelect < 0 || itemToSelect >= m_MenuItems.Count())
+ {
+ itemToSelect = 0;
+ }
+
+ int i = itemToSelect;
+ do
+ {
+ wchar_t menuItemName[255];
+ m_MenuItems[i]->GetText(menuItemName, 254);
+
+ // This is supposed to be case insensitive but we don't have a portable case
+ // insensitive wide-character routine.
+ if ( wcsncmp( m_szTypeAheadBuf, menuItemName, m_iNumTypeAheadChars) == 0 )
+ {
+ itemToSelect = i;
+ break;
+ }
+
+ i = (i+1) % m_MenuItems.Count();
+ } while ( i != itemToSelect );
+
+ if ( itemToSelect >= 0 )
+ {
+ SetCurrentlyHighlightedItem( itemToSelect );
+ InvalidateLayout();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle key presses, Activate shortcuts
+// Input : code -
+//-----------------------------------------------------------------------------
+void Menu::OnKeyTyped(wchar_t unichar)
+{
+ if (! unichar)
+ {
+ return;
+ }
+
+ switch( m_eTypeAheadMode )
+ {
+ case HOT_KEY_MODE:
+ OnHotKey(unichar);
+ return;
+
+ case TYPE_AHEAD_MODE:
+ OnTypeAhead(unichar);
+ return;
+
+ case COMPAT_MODE:
+ default:
+ break;
+ }
+
+ int itemToSelect = m_iCurrentlySelectedItemID;
+ if ( itemToSelect < 0 )
+ {
+ itemToSelect = 0;
+ }
+
+ int i;
+ wchar_t menuItemName[255];
+
+ i = itemToSelect + 1;
+ if ( i >= m_MenuItems.Count() )
+ {
+ i = 0;
+ }
+
+ while ( i != itemToSelect )
+ {
+ m_MenuItems[i]->GetText(menuItemName, 254);
+
+ if ( tolower( unichar ) == tolower( menuItemName[0] ) )
+ {
+ itemToSelect = i;
+ break;
+ }
+
+ i++;
+ if ( i >= m_MenuItems.Count() )
+ {
+ i = 0;
+ }
+ }
+
+ if ( itemToSelect >= 0 )
+ {
+ SetCurrentlyHighlightedItem( itemToSelect );
+ InvalidateLayout();
+ }
+
+ // don't chain back
+}
+
+
+void Menu::SetTypeAheadMode(MenuTypeAheadMode mode)
+{
+ m_eTypeAheadMode = mode;
+}
+
+int Menu::GetTypeAheadMode()
+{
+ return m_eTypeAheadMode;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle the mouse wheel event, scroll the selection
+//-----------------------------------------------------------------------------
+void Menu::OnMouseWheeled(int delta)
+{
+ if (!m_pScroller->IsVisible())
+ return;
+
+ int val = m_pScroller->GetValue();
+ val -= delta;
+
+ m_pScroller->SetValue(val);
+
+ // moving the slider redraws the scrollbar,
+ // and so we should redraw the menu since the
+ // menu draws the black border to the right of the scrollbar.
+ InvalidateLayout();
+
+ // don't chain back
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Lose focus, hide menu
+//-----------------------------------------------------------------------------
+void Menu::OnKillFocus()
+{
+ // check to see if it's a child taking it
+ if (!input()->GetFocus() || !ipanel()->HasParent(input()->GetFocus(), GetVPanel()))
+ {
+ // if we don't accept keyboard input, then we have to ignore the killfocus if it's not actually being stolen
+ if (!IsKeyBoardInputEnabled() && !input()->GetFocus())
+ return;
+
+ // get the parent of this menu.
+ MenuItem *item = GetParentMenuItem();
+ // if the parent is a menu item, this menu is a cascading menu
+ // if the panel that is getting focus is the parent menu, don't close this menu.
+ if ( (item) && (input()->GetFocus() == item->GetVParent()) )
+ {
+ // if we are in mouse mode and we clicked on the menuitem that
+ // triggers the cascading menu, leave it open.
+ if (m_iInputMode == MOUSE)
+ {
+ // return the focus to the cascading menu.
+ MoveToFront();
+ return;
+ }
+ }
+
+ // forward the message to the parent.
+ PostActionSignal(new KeyValues("MenuClose"));
+
+ // hide this menu
+ SetVisible(false);
+ }
+
+}
+
+namespace vgui
+{
+
+class CMenuManager
+{
+public:
+ void AddMenu( Menu *m )
+ {
+ if ( !m )
+ return;
+
+ int c = m_Menus.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ if ( m_Menus[ i ].Get() == m )
+ return;
+ }
+
+ DHANDLE< Menu > h;
+ h = m;
+ m_Menus.AddToTail( h );
+ }
+
+ void RemoveMenu( Menu *m )
+ {
+ if ( !m )
+ return;
+
+ int c = m_Menus.Count();
+ for ( int i = c - 1 ; i >= 0; --i )
+ {
+ if ( m_Menus[ i ].Get() == m )
+ {
+ m_Menus.Remove( i );
+ return;
+ }
+ }
+ }
+
+ void OnInternalMousePressed( Panel *other, MouseCode code )
+ {
+ int c = m_Menus.Count();
+ if ( !c )
+ return;
+
+ int x, y;
+ input()->GetCursorPos( x, y );
+
+ bool mouseInsideMenuRelatedPanel = false;
+
+ for ( int i = c - 1; i >= 0 ; --i )
+ {
+ Menu *m = m_Menus[ i ].Get();
+ if ( !m )
+ {
+ m_Menus.Remove( i );
+ continue;
+ }
+
+ // See if the mouse is within a menu
+ if ( IsWithinMenuOrRelative( m, x, y ) )
+ {
+ mouseInsideMenuRelatedPanel = true;
+ }
+ }
+
+ if ( mouseInsideMenuRelatedPanel )
+ {
+ return;
+ }
+
+ AbortMenus();
+ }
+
+ void AbortMenus()
+ {
+ // Close all of the menus
+ int c = m_Menus.Count();
+ for ( int i = c - 1; i >= 0 ; --i )
+ {
+ Menu *m = m_Menus[ i ].Get();
+ if ( !m )
+ {
+ continue;
+ }
+
+ m_Menus.Remove( i );
+
+ // Force it to close
+ m->SetVisible( false );
+ }
+
+ m_Menus.RemoveAll();
+ }
+
+ bool IsWithinMenuOrRelative( Panel *panel, int x, int y )
+ {
+ VPANEL topMost = panel->IsWithinTraverse( x, y, true );
+ if ( topMost )
+ {
+ // It's over the menu
+ if ( topMost == panel->GetVPanel() )
+ {
+ return true;
+ }
+
+ // It's over something which is parented to the menu (i.e., a menu item)
+ if ( ipanel()->HasParent( topMost, panel->GetVPanel() ) )
+ {
+ return true;
+ }
+ }
+
+ if ( panel->GetParent() )
+ {
+ Panel *parent = panel->GetParent();
+
+ topMost = parent->IsWithinTraverse( x, y, true );
+
+ if ( topMost )
+ {
+ if ( topMost == parent->GetVPanel() )
+ {
+ return true;
+ }
+
+ /*
+ // NOTE: this check used to not cast to MenuButton, but it seems wrong to me
+ // since if the mouse is over another child of the parent panel to the menu then
+ // the menu stays visible. I think this is bogus.
+ Panel *pTopMost = ipanel()->GetPanel(topMost, GetControlsModuleName());
+
+ if ( pTopMost &&
+ ipanel()->HasParent( topMost, parent->GetVPanel() ) &&
+ dynamic_cast< MenuButton * >( pTopMost ) )
+ {
+ Msg( "topMost %s has parent %s\n",
+ ipanel()->GetName( topMost ),
+ parent->GetName() );
+
+ return true;
+ }
+ */
+ }
+ }
+
+ return false;
+ }
+
+#ifdef DBGFLAG_VALIDATE
+ void Validate( CValidator &validator, char *pchName )
+ {
+ validator.Push( "CMenuManager", this, pchName );
+ m_Menus.Validate( validator, "m_Menus" );
+ validator.Pop();
+ }
+#endif
+
+private:
+
+ // List of visible menus
+ CUtlVector< DHANDLE< Menu > > m_Menus;
+};
+
+
+// Singleton helper class
+static CMenuManager g_MenuMgr;
+
+void ValidateMenuGlobals( CValidator &validator )
+{
+#ifdef DBGFLAG_VALIDATE
+ g_MenuMgr.Validate( validator, "g_MenuMgr" );
+#endif
+}
+
+} // end namespace vgui
+
+//-----------------------------------------------------------------------------
+// Purpose: Static method called on mouse released to see if Menu objects should be aborted
+// Input : *other -
+// code -
+//-----------------------------------------------------------------------------
+void Menu::OnInternalMousePressed( Panel *other, MouseCode code )
+{
+ g_MenuMgr.OnInternalMousePressed( other, code );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set visibility of menu and its children as appropriate.
+//-----------------------------------------------------------------------------
+void Menu::SetVisible(bool state)
+{
+ if (state == IsVisible())
+ return;
+
+ if ( state == false )
+ {
+ PostActionSignal(new KeyValues("MenuClose"));
+ CloseOtherMenus(NULL);
+
+ SetCurrentlySelectedItem(-1);
+
+ g_MenuMgr.RemoveMenu( this );
+ }
+ else if ( state == true )
+ {
+ MoveToFront();
+ RequestFocus();
+
+ // Add to menu manager?
+ if ( m_bUseMenuManager )
+ {
+ g_MenuMgr.AddMenu( this );
+ }
+ }
+
+ // must be after movetofront()
+ BaseClass::SetVisible(state);
+ _sizedForScrollBar = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Menu::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetFgColor(GetSchemeColor("Menu.TextColor", pScheme));
+ SetBgColor(GetSchemeColor("Menu.BgColor", pScheme));
+
+ _borderDark = pScheme->GetColor("BorderDark", Color(255, 255, 255, 0));
+
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ if( m_MenuItems[i]->IsCheckable() )
+ {
+ int wide, tall;
+ m_MenuItems[i]->GetCheckImageSize( wide, tall );
+
+ m_iCheckImageWidth = max ( m_iCheckImageWidth, wide );
+ }
+ }
+ _recalculateWidth = true;
+ CalculateWidth();
+
+ InvalidateLayout();
+}
+
+void Menu::SetBgColor( Color newColor )
+{
+ BaseClass::SetBgColor( newColor );
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ if( m_MenuItems[i]->HasMenu() )
+ {
+ m_MenuItems[i]->GetMenu()->SetBgColor( newColor );
+ }
+ }
+}
+
+void Menu::SetFgColor( Color newColor )
+{
+ BaseClass::SetFgColor( newColor );
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ if( m_MenuItems[i]->HasMenu() )
+ {
+ m_MenuItems[i]->GetMenu()->SetFgColor( newColor );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Menu::SetBorder(class IBorder *border)
+{
+ Panel::SetBorder(border);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns a pointer to a MenuItem that is this menus parent, if it has one
+//-----------------------------------------------------------------------------
+MenuItem *Menu::GetParentMenuItem()
+{
+ return dynamic_cast<MenuItem *>(GetParent());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Hide the menu when an item has been selected
+//-----------------------------------------------------------------------------
+void Menu::OnMenuItemSelected(Panel *panel)
+{
+ SetVisible(false);
+ m_pScroller->SetVisible(false);
+
+ // chain this message up through the hierarchy so
+ // all the parent menus will close
+
+ // get the parent of this menu.
+ MenuItem *item = GetParentMenuItem();
+ // if the parent is a menu item, this menu is a cascading menu
+ if (item)
+ {
+ // get the parent of the menuitem. it should be a menu.
+ Menu *parentMenu = item->GetParentMenu();
+ if (parentMenu)
+ {
+ // send the message to this parent menu
+ KeyValues *kv = new KeyValues("MenuItemSelected");
+ kv->SetPtr("panel", panel);
+ ivgui()->PostMessage(parentMenu->GetVPanel(), kv, GetVPanel());
+ }
+ }
+
+ bool activeItemSet = false;
+
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ if( m_MenuItems[i] == panel )
+ {
+ activeItemSet = true;
+ m_iActivatedItem = i;
+ break;
+ }
+ }
+ if( !activeItemSet )
+ {
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ if(m_MenuItems[i]->HasMenu() )
+ {
+ /*
+ // GetActiveItem needs to return -1 or similar if it hasn't been set...
+ if( m_MenuItems[i]->GetActiveItem() )
+ {
+ m_iActivatedItem = m_MenuItems[i]->GetActiveItem();
+ }*/
+ }
+ }
+ }
+
+ // also pass it to the parent so they can respond if they like
+ if (GetVParent())
+ {
+ KeyValues *kv = new KeyValues("MenuItemSelected");
+ kv->SetPtr("panel", panel);
+
+ ivgui()->PostMessage(GetVParent(), kv, GetVPanel());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int Menu::GetActiveItem()
+{
+ return m_iActivatedItem;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues *Menu::GetItemUserData(int itemID)
+{
+ if ( m_MenuItems.IsValidIndex( itemID ) )
+ {
+ MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
+ // make sure its enabled since disabled items get highlighted.
+ if (menuItem && menuItem->IsEnabled())
+ {
+ return menuItem->GetUserData();
+ }
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void Menu::GetItemText(int itemID, wchar_t *text, int bufLenInBytes)
+{
+ if ( m_MenuItems.IsValidIndex( itemID ) )
+ {
+ MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
+ if (menuItem)
+ {
+ menuItem->GetText(text, bufLenInBytes);
+ return;
+ }
+ }
+ text[0] = 0;
+}
+
+void Menu::GetItemText(int itemID, char *text, int bufLenInBytes)
+{
+ if ( m_MenuItems.IsValidIndex( itemID ) )
+ {
+ MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
+ if (menuItem)
+ {
+ menuItem->GetText( text, bufLenInBytes );
+ return;
+ }
+ }
+ text[0] = 0;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Activate the n'th item in the menu list, as if that menu item had been selected by the user
+//-----------------------------------------------------------------------------
+void Menu::ActivateItem(int itemID)
+{
+ if ( m_MenuItems.IsValidIndex( itemID ) )
+ {
+ MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
+ // make sure its enabled since disabled items get highlighted.
+ if (menuItem && menuItem->IsEnabled())
+ {
+ menuItem->FireActionSignal();
+ m_iActivatedItem = itemID;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Menu::SilentActivateItem(int itemID)
+{
+ if ( m_MenuItems.IsValidIndex( itemID ) )
+ {
+ MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
+ // make sure its enabled since disabled items get highlighted.
+ if (menuItem && menuItem->IsEnabled())
+ {
+ m_iActivatedItem = itemID;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Menu::ActivateItemByRow(int row)
+{
+ if (m_SortedItems.IsValidIndex(row))
+ {
+ ActivateItem(m_SortedItems[row]);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the number of items currently in the menu list
+//-----------------------------------------------------------------------------
+int Menu::GetItemCount()
+{
+ return m_MenuItems.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int Menu::GetMenuID(int index)
+{
+ if ( !m_SortedItems.IsValidIndex(index) )
+ return m_MenuItems.InvalidIndex();
+
+ return m_SortedItems[index];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the number of items currently visible in the menu list
+//-----------------------------------------------------------------------------
+int Menu::GetCurrentlyVisibleItemsCount()
+{
+ if (m_MenuItems.Count() < m_iNumVisibleLines)
+ {
+ int cMenuItems = 0;
+ FOR_EACH_LL(m_MenuItems, i)
+ {
+ if (m_MenuItems[i]->IsVisible())
+ {
+ ++cMenuItems;
+ }
+ }
+
+ return cMenuItems;
+ }
+ return m_iNumVisibleLines;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Enables/disables choices in the list
+// itemText - string name of item in the list
+// state - true enables, false disables
+//-----------------------------------------------------------------------------
+void Menu::SetItemEnabled(const char *itemName, bool state)
+{
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ if ((Q_stricmp(itemName, m_MenuItems[i]->GetName())) == 0)
+ {
+ m_MenuItems[i]->SetEnabled(state);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Enables/disables choices in the list
+//-----------------------------------------------------------------------------
+void Menu::SetItemEnabled(int itemID, bool state)
+{
+ if ( !m_MenuItems.IsValidIndex(itemID) )
+ return;
+
+ m_MenuItems[itemID]->SetEnabled(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: shows/hides choices in the list
+//-----------------------------------------------------------------------------
+void Menu::SetItemVisible(const char *itemName, bool state)
+{
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ if ((Q_stricmp(itemName, m_MenuItems[i]->GetName())) == 0)
+ {
+ m_MenuItems[i]->SetVisible(state);
+ InvalidateLayout();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: shows/hides choices in the list
+//-----------------------------------------------------------------------------
+void Menu::SetItemVisible(int itemID, bool state)
+{
+ if ( !m_MenuItems.IsValidIndex(itemID) )
+ return;
+
+ m_MenuItems[itemID]->SetVisible(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Make the scroll bar visible and narrow the menu
+// also make items visible or invisible in the list as appropriate
+//-----------------------------------------------------------------------------
+void Menu::AddScrollBar()
+{
+ m_pScroller->SetVisible(true);
+ _sizedForScrollBar = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Make the scroll bar invisible and widen the menu
+//-----------------------------------------------------------------------------
+void Menu::RemoveScrollBar()
+{
+ m_pScroller->SetVisible(false);
+ _sizedForScrollBar = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Invalidate layout if the slider is moved so items scroll
+//-----------------------------------------------------------------------------
+void Menu::OnSliderMoved()
+{
+ CloseOtherMenus(NULL); // close any cascading menus
+
+ // Invalidate so we redraw the menu!
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle into mouse mode.
+//-----------------------------------------------------------------------------
+void Menu::OnCursorMoved(int x, int y)
+{
+ m_iInputMode = MOUSE;
+
+ // chain up
+ CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y));
+ RequestFocus();
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle into keyboard mode.
+//-----------------------------------------------------------------------------
+void Menu::OnKeyCodePressed(KeyCode code)
+{
+ m_iInputMode = KEYBOARD;
+ // send the message to this parent in case this is a cascading menu
+ if (GetVParent())
+ {
+ ivgui()->PostMessage(GetVParent(), new KeyValues("KeyModeSet"), GetVPanel());
+ }
+
+ BaseClass::OnKeyCodePressed( code );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the item currently highlighted in the menu by ptr
+//-----------------------------------------------------------------------------
+void Menu::SetCurrentlySelectedItem(MenuItem *item)
+{
+ int itemNum = -1;
+ // find it in our list of menuitems
+ FOR_EACH_LL( m_MenuItems, i )
+ {
+ MenuItem *child = m_MenuItems[i];
+ if (child == item)
+ {
+ itemNum = i;
+ break;
+ }
+ }
+ Assert( itemNum >= 0 );
+
+ SetCurrentlySelectedItem(itemNum);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Menu::ClearCurrentlyHighlightedItem()
+{
+ if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) )
+ {
+ m_MenuItems[m_iCurrentlySelectedItemID]->DisarmItem();
+ }
+ m_iCurrentlySelectedItemID = m_MenuItems.InvalidIndex();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the item currently highlighted in the menu by index
+//-----------------------------------------------------------------------------
+void Menu::SetCurrentlySelectedItem(int itemID)
+{
+ // dont deselect if its the same item
+ if (itemID == m_iCurrentlySelectedItemID)
+ return;
+
+ if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) )
+ {
+ m_MenuItems[m_iCurrentlySelectedItemID]->DisarmItem();
+ }
+
+ PostActionSignal(new KeyValues("MenuItemHighlight", "itemID", itemID));
+ m_iCurrentlySelectedItemID = itemID;
+}
+
+//-----------------------------------------------------------------------------
+// This will set the item to be currenly selected and highlight it
+// will not open cascading menu. This was added for comboboxes
+// to have the combobox item highlighted in the menu when they open the
+// dropdown.
+//-----------------------------------------------------------------------------
+void Menu::SetCurrentlyHighlightedItem(int itemID)
+{
+ SetCurrentlySelectedItem(itemID);
+ int row = m_SortedItems.Find(itemID);
+ // If we have no items, then row will be -1. The dev console, for example...
+ Assert( ( m_SortedItems.Count() == 0 ) || ( row != -1 ) );
+ if ( row == -1 )
+ return;
+
+ // if there is a scroll bar, and we scroll off lets move it.
+ if ( m_pScroller->IsVisible() )
+ {
+ // now if we are off the scroll bar, it means we moved the scroll bar
+ // by hand or set the item off the list
+ // so just snap the scroll bar straight to the item.
+ if ( ( row > m_pScroller->GetValue() + m_iNumVisibleLines - 1 ) ||
+ ( row < m_pScroller->GetValue() ) )
+ {
+ if ( !m_pScroller->IsVisible() )
+ return;
+
+ m_pScroller->SetValue(row);
+ }
+ }
+
+ if ( m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID) )
+ {
+ if ( !m_MenuItems[m_iCurrentlySelectedItemID]->IsArmed() )
+ {
+ m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int Menu::GetCurrentlyHighlightedItem()
+{
+ return m_iCurrentlySelectedItemID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Respond to cursor entering a menuItem.
+//-----------------------------------------------------------------------------
+void Menu::OnCursorEnteredMenuItem(int VPanel)
+{
+ VPANEL menuItem = (VPANEL)VPanel;
+ // if we are in mouse mode
+ if (m_iInputMode == MOUSE)
+ {
+ MenuItem *item = static_cast<MenuItem *>(ipanel()->GetPanel(menuItem, GetModuleName()));
+ // arm the menu
+ item->ArmItem();
+ // open the cascading menu if there is one.
+ item->OpenCascadeMenu();
+ SetCurrentlySelectedItem(item);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Respond to cursor exiting a menuItem
+//-----------------------------------------------------------------------------
+void Menu::OnCursorExitedMenuItem(int VPanel)
+{
+ VPANEL menuItem = (VPANEL)VPanel;
+ // only care if we are in mouse mode
+ if (m_iInputMode == MOUSE)
+ {
+ MenuItem *item = static_cast<MenuItem *>(ipanel()->GetPanel(menuItem, GetModuleName()));
+ // unhighlight the item.
+ // note menuItems with cascading menus will stay lit.
+ item->DisarmItem();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move up or down one in the list of items in the menu
+// Direction is MENU_UP or MENU_DOWN
+//-----------------------------------------------------------------------------
+void Menu::MoveAlongMenuItemList(int direction, int loopCount)
+{
+ // Early out if no menu items to scroll through
+ if (m_MenuItems.Count() <= 0)
+ return;
+
+ int itemID = m_iCurrentlySelectedItemID;
+ int row = m_SortedItems.Find(itemID);
+ row += direction;
+
+ if ( row > m_SortedItems.Count() - 1 )
+ {
+ if ( m_pScroller->IsVisible() )
+ {
+ // stop at bottom of scrolled list
+ row = m_SortedItems.Count() - 1;
+ }
+ else
+ {
+ // if no scroll bar we circle around
+ row = 0;
+ }
+ }
+ else if (row < 0)
+ {
+ if ( m_pScroller->IsVisible() )
+ {
+ // stop at top of scrolled list
+ row = m_pScroller->GetValue();
+ }
+ else
+ {
+ // if no scroll bar circle around
+ row = m_SortedItems.Count()-1;
+ }
+ }
+
+ // if there is a scroll bar, and we scroll off lets move it.
+ if ( m_pScroller->IsVisible() )
+ {
+ if ( row > m_pScroller->GetValue() + m_iNumVisibleLines - 1)
+ {
+ int val = m_pScroller->GetValue();
+ val -= -direction;
+
+ m_pScroller->SetValue(val);
+
+ // moving the slider redraws the scrollbar,
+ // and so we should redraw the menu since the
+ // menu draws the black border to the right of the scrollbar.
+ InvalidateLayout();
+ }
+ else if ( row < m_pScroller->GetValue() )
+ {
+ int val = m_pScroller->GetValue();
+ val -= -direction;
+
+ m_pScroller->SetValue(val);
+
+ // moving the slider redraws the scrollbar,
+ // and so we should redraw the menu since the
+ // menu draws the black border to the right of the scrollbar.
+ InvalidateLayout();
+ }
+
+ // now if we are still off the scroll bar, it means we moved the scroll bar
+ // by hand and created a situation in which we moved an item down, but the
+ // scroll bar is already too far down and should scroll up or vice versa
+ // so just snap the scroll bar straight to the item.
+ if ( ( row > m_pScroller->GetValue() + m_iNumVisibleLines - 1) ||
+ ( row < m_pScroller->GetValue() ) )
+ {
+ m_pScroller->SetValue(row);
+ }
+ }
+
+ // switch it back to an itemID from row
+ if ( m_SortedItems.IsValidIndex( row ) )
+ {
+ SetCurrentlySelectedItem( m_SortedItems[row] );
+ }
+
+ // don't allow us to loop around more than once
+ if (loopCount < m_MenuItems.Count())
+ {
+ // see if the text is empty, if so skip
+ wchar_t text[256];
+ m_MenuItems[m_iCurrentlySelectedItemID]->GetText(text, 255);
+ if (text[0] == 0 || !m_MenuItems[m_iCurrentlySelectedItemID]->IsVisible())
+ {
+ // menu item is empty, keep moving along
+ MoveAlongMenuItemList(direction, loopCount + 1);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return which type of events the menu is currently interested in
+// MenuItems need to know because behaviour is different depending on mode.
+//-----------------------------------------------------------------------------
+int Menu::GetMenuMode()
+{
+ return m_iInputMode;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the menu to key mode if a child menu goes into keymode
+// This mode change has to be chained up through the menu heirarchy
+// so cascading menus will work when you do a bunch of stuff in keymode
+// in high level menus and then switch to keymode in lower level menus.
+//-----------------------------------------------------------------------------
+void Menu::OnKeyModeSet()
+{
+ m_iInputMode = KEYBOARD;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the checked state of a menuItem
+//-----------------------------------------------------------------------------
+void Menu::SetMenuItemChecked(int itemID, bool state)
+{
+ m_MenuItems[itemID]->SetChecked(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if item is checked.
+//-----------------------------------------------------------------------------
+bool Menu::IsChecked(int itemID)
+{
+ return m_MenuItems[itemID]->IsChecked();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the minmum width the menu has to be. This
+// is useful if you have a menu that is sized to the largest item in it
+// but you don't want the menu to be thinner than the menu button
+//-----------------------------------------------------------------------------
+void Menu::SetMinimumWidth(int width)
+{
+ m_iMinimumWidth = width;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the minmum width the menu
+//-----------------------------------------------------------------------------
+int Menu::GetMinimumWidth()
+{
+ return m_iMinimumWidth;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void Menu::AddSeparator()
+{
+ int lastID = m_MenuItems.Count() - 1;
+ m_Separators.AddToTail( lastID );
+ m_SeparatorPanels.AddToTail( new MenuSeparator( this, "MenuSeparator" ) );
+}
+
+void Menu::AddSeparatorAfterItem( int itemID )
+{
+ Assert( m_MenuItems.IsValidIndex( itemID ) );
+ m_Separators.AddToTail( itemID );
+ m_SeparatorPanels.AddToTail( new MenuSeparator( this, "MenuSeparator" ) );
+}
+
+void Menu::MoveMenuItem( int itemID, int moveBeforeThisItemID )
+{
+ int c = m_SortedItems.Count();
+ int i;
+ for ( i = 0; i < c; ++i )
+ {
+ if ( m_SortedItems[i] == itemID )
+ {
+ m_SortedItems.Remove( i );
+ break;
+ }
+ }
+
+ // Didn't find it
+ if ( i >= c )
+ {
+ return;
+ }
+
+ // Now find insert pos
+ c = m_SortedItems.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ if ( m_SortedItems[i] == moveBeforeThisItemID )
+ {
+ m_SortedItems.InsertBefore( i, itemID );
+ break;
+ }
+ }
+}
+
+void Menu::SetFont( HFont font )
+{
+ m_hItemFont = font;
+ if ( font )
+ {
+ m_iMenuItemHeight = surface()->GetFontTall( font ) + 2;
+ }
+ InvalidateLayout();
+}
+
+
+void Menu::SetCurrentKeyBinding( int itemID, char const *hotkey )
+{
+ if ( m_MenuItems.IsValidIndex( itemID ) )
+ {
+ MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
+ menuItem->SetCurrentKeyBinding( hotkey );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Static method to display a context menu
+// Input : *parent -
+// *menu -
+//-----------------------------------------------------------------------------
+void Menu::PlaceContextMenu( Panel *parent, Menu *menu )
+{
+ Assert( parent );
+ Assert( menu );
+ if ( !menu || !parent )
+ return;
+
+ menu->SetVisible(false);
+ menu->SetParent( parent );
+ menu->AddActionSignalTarget( parent );
+
+ // get cursor position, this is local to this text edit window
+ int cursorX, cursorY;
+ input()->GetCursorPos(cursorX, cursorY);
+
+ menu->SetVisible(true);
+
+ // relayout the menu immediately so that we know it's size
+ menu->InvalidateLayout(true);
+ int menuWide, menuTall;
+ menu->GetSize(menuWide, menuTall);
+
+ // work out where the cursor is and therefore the best place to put the menu
+ int wide, tall;
+ surface()->GetScreenSize(wide, tall);
+
+ if (wide - menuWide > cursorX)
+ {
+ // menu hanging right
+ if (tall - menuTall > cursorY)
+ {
+ // menu hanging down
+ menu->SetPos(cursorX, cursorY);
+ }
+ else
+ {
+ // menu hanging up
+ menu->SetPos(cursorX, cursorY - menuTall);
+ }
+ }
+ else
+ {
+ // menu hanging left
+ if (tall - menuTall > cursorY)
+ {
+ // menu hanging down
+ menu->SetPos(cursorX - menuWide, cursorY);
+ }
+ else
+ {
+ // menu hanging up
+ menu->SetPos(cursorX - menuWide, cursorY - menuTall);
+ }
+ }
+
+ menu->RequestFocus();
+}
+
+void Menu::SetUseFallbackFont( bool bState, HFont hFallback )
+{
+ m_hFallbackItemFont = hFallback;
+ m_bUseFallbackFont = bState;
+}
+
+#ifdef DBGFLAG_VALIDATE
+//-----------------------------------------------------------------------------
+// Purpose: Run a global validation pass on all of our data structures and memory
+// allocations.
+// Input: validator - Our global validator object
+// pchName - Our name (typically a member var in our container)
+//-----------------------------------------------------------------------------
+void Menu::Validate( CValidator &validator, char *pchName )
+{
+ validator.Push( "vgui::Menu", this, pchName );
+
+ m_MenuItems.Validate( validator, "m_MenuItems" );
+ m_SortedItems.Validate( validator, "m_SortedItems" );
+
+ BaseClass::Validate( validator, "vgui::Menu" );
+
+ validator.Pop();
+}
+#endif // DBGFLAG_VALIDATE
diff --git a/mp/src/vgui2/vgui_controls/MenuBar.cpp b/mp/src/vgui2/vgui_controls/MenuBar.cpp
new file mode 100644
index 00000000..f0f5dd11
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/MenuBar.cpp
@@ -0,0 +1,252 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <vgui/IInput.h>
+#include <vgui/IPanel.h>
+#include <vgui/IScheme.h>
+#include <vgui/IBorder.h>
+#include <vgui/ISurface.h>
+#include <vgui/KeyCode.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/MenuBar.h>
+#include <vgui_controls/MenuButton.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/Controls.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+
+enum
+{
+ MENUBARINDENT = 4, // indent from top and bottom of panel.
+};
+
+DECLARE_BUILD_FACTORY( MenuBar );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+MenuBar::MenuBar(Panel *parent, const char *panelName) :
+ Panel(parent, panelName),
+ m_nRightEdge( 0 )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+MenuBar::~MenuBar()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void MenuBar::AddButton(MenuButton *button)
+{
+ button->SetParent(this);
+ button->AddActionSignalTarget(this);
+ m_pMenuButtons.AddToTail(button);
+}
+
+//-----------------------------------------------------------------------------
+// This will add the menu to the menu bar
+//-----------------------------------------------------------------------------
+void MenuBar::AddMenu( const char *pButtonName, Menu *pMenu )
+{
+ MenuButton *pMenuButton = new MenuButton(this, pButtonName, pButtonName);
+ pMenuButton->SetMenu(pMenu);
+ AddButton(pMenuButton);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle key presses, Activate shortcuts
+//-----------------------------------------------------------------------------
+void MenuBar::OnKeyCodeTyped(KeyCode code)
+{
+ switch(code)
+ {
+ case KEY_RIGHT:
+ {
+ // iterate the menu items looking for one that is open
+ // if we find one open, open the one to the right
+ for (int i = 0; i < m_pMenuButtons.Count() - 1; i++)
+ {
+ MenuButton *panel = m_pMenuButtons[i];
+ if (panel->IsDepressed())
+ {
+ m_pMenuButtons[i]->DoClick();
+ m_pMenuButtons[i+1]->DoClick();
+ break;
+ }
+ }
+ break;
+ }
+ case KEY_LEFT:
+ {
+ // iterate the menu items looking for one that is open
+ // if we find one open, open the one to the left
+ for (int i = 1; i < m_pMenuButtons.Count(); i++)
+ {
+ MenuButton *panel = m_pMenuButtons[i];
+ if (panel->IsDepressed())
+ {
+ m_pMenuButtons[i]->DoClick();
+ m_pMenuButtons[i-1]->DoClick();
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // don't chain back
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle key presses, Activate shortcuts
+// Input : code -
+//-----------------------------------------------------------------------------
+void MenuBar::OnKeyTyped(wchar_t unichar)
+{
+ if (unichar)
+ {
+ // iterate the menu items looking for one with the matching hotkey
+ for (int i = 0; i < m_pMenuButtons.Count(); i++)
+ {
+ MenuButton *panel = m_pMenuButtons[i];
+ if (panel->IsVisible())
+ {
+ Panel *hot = panel->HasHotkey(unichar);
+ if (hot)
+ {
+ // post a message to the menuitem telling it it's hotkey was pressed
+ PostMessage(hot, new KeyValues("Hotkey"));
+ return;
+ }
+ }
+ }
+ }
+
+ // don't chain back
+}
+
+void MenuBar::Paint()
+{
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+ for ( int i = 0; i < m_pMenuButtons.Count(); i++)
+ {
+ if (!m_pMenuButtons[i]->IsArmed())
+ m_pMenuButtons[i]->SetDefaultBorder(NULL);
+ else
+ {
+ m_pMenuButtons[i]->SetDefaultBorder(pScheme->GetBorder( "ButtonBorder"));
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Message map
+//-----------------------------------------------------------------------------
+void MenuBar::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ // get the borders we need
+ SetBorder(pScheme->GetBorder("ButtonBorder"));
+
+ // get the background color
+ SetBgColor(pScheme->GetColor( "MenuBar.BgColor", GetBgColor() ));
+
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Reformat according to the new layout
+//-----------------------------------------------------------------------------
+void MenuBar::PerformLayout()
+{
+ int nBarWidth, nBarHeight;
+ GetSize( nBarWidth, nBarHeight );
+
+ // Now position + resize all buttons
+ int x = MENUBARINDENT;
+ for ( int i = 0; i < m_pMenuButtons.Count(); ++i )
+ {
+ int nWide, nTall;
+
+ m_pMenuButtons[i]->GetContentSize(nWide, nTall);
+ m_pMenuButtons[i]->SetPos( x, MENUBARINDENT );
+ m_pMenuButtons[i]->SetSize( nWide + Label::Content, nBarHeight - 2 * MENUBARINDENT );
+
+ x += nWide + MENUBARINDENT;
+ }
+
+ m_nRightEdge = x;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the size of the menus in the bar (so other children can be added to menu bar)
+// Input : w -
+// int&h -
+//-----------------------------------------------------------------------------
+void MenuBar::GetContentSize( int& w, int&h )
+{
+ w = m_nRightEdge + 2;
+ h = GetTall();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Message map
+//-----------------------------------------------------------------------------
+void MenuBar::OnMenuClose()
+{
+ RequestFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Message map
+//-----------------------------------------------------------------------------
+void MenuBar::OnCursorEnteredMenuButton(int VPanel)
+{
+ VPANEL menuButton = (VPANEL)VPanel;
+ // see if we had a menu open
+ for ( int i = 0; i < m_pMenuButtons.Count(); i++)
+ {
+ // one of our buttons was pressed.
+ if (m_pMenuButtons[i]->IsDepressed())
+ {
+ int oldbutton = i;
+ // now see if menuButton is one of ours.
+ for ( int j = 0; j < m_pMenuButtons.Count(); j++)
+ {
+ MenuButton *button = static_cast<MenuButton *>(ipanel()->GetPanel(menuButton, GetModuleName()));
+ // it is one of ours.
+ if ( button == m_pMenuButtons[j])
+ {
+ // if its a different button than the one we already had open,
+ if (j != oldbutton)
+ {
+ // close this menu and open the one we just entered
+ m_pMenuButtons[oldbutton]->DoClick();
+ button->DoClick();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/mp/src/vgui2/vgui_controls/MenuButton.cpp b/mp/src/vgui2/vgui_controls/MenuButton.cpp
new file mode 100644
index 00000000..27f8741a
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/MenuButton.cpp
@@ -0,0 +1,351 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#define PROTECTED_THINGS_DISABLE
+
+#include <vgui/IPanel.h>
+#include <vgui/IInput.h>
+#include <vgui/ISurface.h>
+#include <KeyValues.h>
+#include <vgui/IVGui.h>
+
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/MenuButton.h>
+#include <vgui_controls/Menu.h>
+#include <vgui_controls/TextImage.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY_DEFAULT_TEXT( MenuButton, MenuButton );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+MenuButton::MenuButton(Panel *parent, const char *panelName, const char *text) : Button(parent, panelName, text)
+{
+ m_pMenu = NULL;
+ m_iDirection = Menu::DOWN;
+ m_pDropMenuImage = NULL;
+ m_nImageIndex = -1;
+ _openOffsetY = 0;
+ m_bDropMenuButtonStyle = true; // set to true so SetDropMenuButtonStyle() forces real init.
+
+ SetDropMenuButtonStyle( false );
+ SetUseCaptureMouse( false );
+ SetButtonActivationType( ACTIVATE_ONPRESSED );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+MenuButton::~MenuButton()
+{
+ delete m_pDropMenuImage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: attaches a menu to the menu button
+//-----------------------------------------------------------------------------
+void MenuButton::SetMenu(Menu *menu)
+{
+ m_pMenu = menu;
+
+ if (menu)
+ {
+ m_pMenu->SetVisible(false);
+ m_pMenu->AddActionSignalTarget(this);
+ m_pMenu->SetParent(this);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Never draw a focus border
+//-----------------------------------------------------------------------------
+void MenuButton::DrawFocusBorder(int tx0, int ty0, int tx1, int ty1)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the direction from the menu button the menu should open
+//-----------------------------------------------------------------------------
+void MenuButton::SetOpenDirection(Menu::MenuDirection_e direction)
+{
+ m_iDirection = direction;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: hides the menu
+//-----------------------------------------------------------------------------
+void MenuButton::HideMenu(void)
+{
+ if (!m_pMenu)
+ return;
+
+ // hide the menu
+ m_pMenu->SetVisible(false);
+
+ // unstick the button
+ BaseClass::ForceDepressed(false);
+ Repaint();
+
+ OnHideMenu(m_pMenu);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the menu button loses focus; hides the menu
+//-----------------------------------------------------------------------------
+void MenuButton::OnKillFocus( KeyValues *pParams )
+{
+ VPANEL hPanel = (VPANEL)pParams->GetPtr( "newPanel" );
+ if ( m_pMenu && !m_pMenu->HasFocus() && hPanel != m_pMenu->GetVPanel() )
+ {
+ HideMenu();
+ }
+ BaseClass::OnKillFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the menu is closed
+//-----------------------------------------------------------------------------
+void MenuButton::OnMenuClose()
+{
+ HideMenu();
+ PostActionSignal(new KeyValues("MenuClose"));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the offset from where menu would normally be placed
+// Only is used if menu is ALIGN_WITH_PARENT
+//-----------------------------------------------------------------------------
+void MenuButton::SetOpenOffsetY(int yOffset)
+{
+ _openOffsetY = yOffset;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool MenuButton::CanBeDefaultButton(void)
+{
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles hotkey accesses
+//-----------------------------------------------------------------------------
+void MenuButton::DoClick()
+{
+ if ( IsDropMenuButtonStyle() &&
+ m_pDropMenuImage )
+ {
+ int mx, my;
+ // force the menu to appear where the mouse button was pressed
+ input()->GetCursorPos( mx, my );
+ ScreenToLocal( mx, my );
+
+ int contentW, contentH;
+ m_pDropMenuImage->GetContentSize( contentW, contentH );
+ int drawX = GetWide() - contentW - 2;
+ if ( mx <= drawX || !OnCheckMenuItemCount() )
+ {
+ // Treat it like a "regular" button click
+ BaseClass::DoClick();
+ return;
+ }
+ }
+
+ if ( !m_pMenu )
+ {
+ return;
+ }
+
+ // menu is already visible, hide the menu
+ if (m_pMenu->IsVisible())
+ {
+ HideMenu();
+ return;
+ }
+
+ // do nothing if menu is not enabled
+ if (!m_pMenu->IsEnabled())
+ {
+ return;
+ }
+ // force the menu to compute required width/height
+ m_pMenu->PerformLayout();
+
+ // Now position it so it can fit in the workspace
+ m_pMenu->PositionRelativeToPanel(this, m_iDirection, _openOffsetY );
+
+ // make sure we're at the top of the draw order (and therefore our children as well)
+ MoveToFront();
+
+ // notify
+ OnShowMenu(m_pMenu);
+
+ // keep the button depressed
+ BaseClass::ForceDepressed(true);
+
+ // show the menu
+ m_pMenu->SetVisible(true);
+
+ // bring to focus
+ m_pMenu->RequestFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void MenuButton::OnKeyCodeTyped(KeyCode code)
+{
+ bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+ bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+ bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
+
+ if (!shift && !ctrl && !alt)
+ {
+ switch (code)
+ {
+ case KEY_ENTER:
+ {
+ if ( !IsDropMenuButtonStyle() )
+ {
+ DoClick();
+ }
+ break;
+ }
+ }
+ }
+ BaseClass::OnKeyCodeTyped(code);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void MenuButton::OnCursorEntered()
+{
+ Button::OnCursorEntered();
+ // post a message to the parent menu.
+ // forward the message on to the parent of this menu.
+ KeyValues *msg = new KeyValues ("CursorEnteredMenuButton");
+ // tell the parent this menuitem is the one that was entered so it can open the menu if it wants
+ msg->SetInt("VPanel", GetVPanel());
+ ivgui()->PostMessage(GetVParent(), msg, NULL);
+}
+
+// This style is like the IE "back" button where the left side acts like a regular button, the the right side has a little
+// combo box dropdown indicator and presents and submenu
+void MenuButton::SetDropMenuButtonStyle( bool state )
+{
+ bool changed = m_bDropMenuButtonStyle != state;
+ m_bDropMenuButtonStyle = state;
+ if ( !changed )
+ return;
+
+ if ( state )
+ {
+ m_pDropMenuImage = new TextImage( "u" );
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+ m_pDropMenuImage->SetFont(pScheme->GetFont("Marlett", IsProportional()));
+ // m_pDropMenuImage->SetContentAlignment(Label::a_west);
+ // m_pDropMenuImage->SetTextInset(3, 0);
+ m_nImageIndex = AddImage( m_pDropMenuImage, 0 );
+ }
+ else
+ {
+ ResetToSimpleTextImage();
+ delete m_pDropMenuImage;
+ m_pDropMenuImage = NULL;
+ m_nImageIndex = -1;
+ }
+}
+
+void MenuButton::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ if ( m_pDropMenuImage )
+ {
+ SetImageAtIndex( 1, m_pDropMenuImage, 0 );
+ }
+}
+
+
+void MenuButton::PerformLayout()
+{
+ BaseClass::PerformLayout();
+ if ( !IsDropMenuButtonStyle() )
+ return;
+
+ Assert( m_nImageIndex >= 0 );
+ if ( m_nImageIndex < 0 || !m_pDropMenuImage )
+ return;
+
+ int w, h;
+ GetSize( w, h );
+
+ int contentW, contentH;
+ m_pDropMenuImage->ResizeImageToContent();
+ m_pDropMenuImage->GetContentSize( contentW, contentH );
+
+ SetImageBounds( m_nImageIndex, w - contentW - 2, contentW );
+}
+
+bool MenuButton::IsDropMenuButtonStyle() const
+{
+ return m_bDropMenuButtonStyle;
+}
+
+void MenuButton::Paint(void)
+{
+ BaseClass::Paint();
+
+ if ( !IsDropMenuButtonStyle() )
+ return;
+
+ int contentW, contentH;
+ m_pDropMenuImage->GetContentSize( contentW, contentH );
+ m_pDropMenuImage->SetColor( IsEnabled() ? GetButtonFgColor() : GetDisabledFgColor1() );
+
+ int drawX = GetWide() - contentW - 2;
+
+ surface()->DrawSetColor( IsEnabled() ? GetButtonFgColor() : GetDisabledFgColor1() );
+ surface()->DrawFilledRect( drawX, 3, drawX + 1, GetTall() - 3 );
+}
+
+void MenuButton::OnCursorMoved( int x, int y )
+{
+ BaseClass::OnCursorMoved( x, y );
+
+ if ( !IsDropMenuButtonStyle() )
+ return;
+
+ int contentW, contentH;
+ m_pDropMenuImage->GetContentSize( contentW, contentH );
+ int drawX = GetWide() - contentW - 2;
+ if ( x <= drawX || !OnCheckMenuItemCount() )
+ {
+ SetButtonActivationType(ACTIVATE_ONPRESSEDANDRELEASED);
+ SetUseCaptureMouse(true);
+ }
+ else
+ {
+ SetButtonActivationType(ACTIVATE_ONPRESSED);
+ SetUseCaptureMouse(false);
+ }
+}
+
+Menu *MenuButton::GetMenu()
+{
+ Assert( m_pMenu );
+ return m_pMenu;
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/MenuItem.cpp b/mp/src/vgui2/vgui_controls/MenuItem.cpp
new file mode 100644
index 00000000..355bfaa4
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/MenuItem.cpp
@@ -0,0 +1,647 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <vgui/IScheme.h>
+#include <vgui/IVGui.h>
+#include "vgui/ISurface.h"
+#include <KeyValues.h>
+
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/Menu.h>
+#include <vgui_controls/MenuItem.h>
+#include <vgui_controls/TextImage.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Check box image
+//-----------------------------------------------------------------------------
+class MenuItemCheckImage : public TextImage
+{
+public:
+ MenuItemCheckImage(MenuItem *item) : TextImage( "g" )
+ {
+ _menuItem = item;
+
+ SetSize(20, 13);
+ }
+
+ virtual void Paint()
+ {
+ DrawSetTextFont(GetFont());
+
+ // draw background
+ DrawSetTextColor(_menuItem->GetBgColor());
+ DrawPrintChar(0, 0, 'g');
+
+ // draw check
+ if (_menuItem->IsChecked())
+ {
+ if (_menuItem->IsEnabled())
+ {
+ DrawSetTextColor(_menuItem->GetButtonFgColor());
+ DrawPrintChar(0, 2, 'a');
+ }
+ else if (!_menuItem->IsEnabled())
+ {
+ // draw disabled version, with embossed look
+ // offset image
+ DrawSetTextColor(_menuItem->GetDisabledFgColor1());
+ DrawPrintChar(1, 3, 'a');
+
+ // overlayed image
+ DrawSetTextColor(_menuItem->GetDisabledFgColor2());
+ DrawPrintChar(0, 2, 'a');
+ }
+ }
+ }
+
+private:
+ MenuItem *_menuItem;
+};
+
+DECLARE_BUILD_FACTORY_DEFAULT_TEXT( MenuItem, MenuItem );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+// Input: parent - the parent of this menu item, usually a menu
+// text - the name of the menu item as it appears in the menu
+// cascadeMenu - if this item triggers the opening of a cascading menu
+// provide a pointer to it.
+// MenuItems cannot be both checkable and trigger a cascade menu.
+//-----------------------------------------------------------------------------
+MenuItem::MenuItem(Menu *parent, const char *panelName, const char *text, Menu *cascadeMenu, bool checkable) : Button(parent, panelName, text)
+{
+ m_pCascadeMenu = cascadeMenu;
+ m_bCheckable = checkable;
+ SetButtonActivationType(ACTIVATE_ONRELEASED);
+ m_pUserData = NULL;
+ m_pCurrentKeyBinding = NULL;
+
+ // only one arg should be passed in.
+ Assert (!(cascadeMenu && checkable));
+
+ Init();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+// Input: parent - the parent of this menu item, usually a menu
+// text - the name of the menu item as it appears in the menu
+// cascadeMenu - if this item triggers the opening of a cascading menu
+// provide a pointer to it.
+// MenuItems cannot be both checkable and trigger a cascade menu.
+//-----------------------------------------------------------------------------
+MenuItem::MenuItem(Menu *parent, const char *panelName, const wchar_t *wszText, Menu *cascadeMenu, bool checkable) : Button(parent, panelName, wszText)
+{
+ m_pCascadeMenu = cascadeMenu;
+ m_bCheckable = checkable;
+ SetButtonActivationType(ACTIVATE_ONRELEASED);
+ m_pUserData = NULL;
+ m_pCurrentKeyBinding = NULL;
+
+ // only one arg should be passed in.
+ Assert (!(cascadeMenu && checkable));
+
+ Init();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+MenuItem::~MenuItem()
+{
+ delete m_pCascadeMenu;
+ delete m_pCascadeArrow;
+ delete m_pCheck;
+ if (m_pUserData)
+ {
+ m_pUserData->deleteThis();
+ }
+ delete m_pCurrentKeyBinding;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Basic initializer
+//-----------------------------------------------------------------------------
+void MenuItem::Init( void )
+{
+ m_pCascadeArrow = NULL;
+ m_pCheck = NULL;
+
+ if (m_pCascadeMenu)
+ {
+ m_pCascadeMenu->SetParent(this);
+ m_pCascadeArrow = new TextImage("4"); // this makes a right pointing arrow.
+
+ m_pCascadeMenu->AddActionSignalTarget(this);
+ }
+ else if (m_bCheckable)
+ {
+ // move the text image over so we have room for the check
+ SetTextImageIndex(1);
+ m_pCheck = new MenuItemCheckImage(this);
+ SetImageAtIndex(0, m_pCheck, CHECK_INSET);
+ SetChecked(false);
+ }
+
+ SetButtonBorderEnabled( false );
+ SetUseCaptureMouse( false );
+ SetContentAlignment( Label::a_west );
+
+ // note menus handle all the sizing of menuItem panels
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Menu *MenuItem::GetParentMenu()
+{
+ return (Menu *)GetParent();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Layout the Textimage and the Arrow part of the menuItem
+//-----------------------------------------------------------------------------
+void MenuItem::PerformLayout()
+{
+ Button::PerformLayout();
+ // make the arrow image match the button layout.
+ // this will make it brighten and dim like the menu buttons.
+ if (m_pCascadeArrow)
+ {
+ m_pCascadeArrow->SetColor(GetButtonFgColor());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Close the cascading menu if we have one.
+//-----------------------------------------------------------------------------
+void MenuItem::CloseCascadeMenu()
+{
+ if (m_pCascadeMenu)
+ {
+ if (m_pCascadeMenu->IsVisible())
+ {
+ m_pCascadeMenu->SetVisible(false);
+ }
+ // disarm even if menu wasn't visible!
+ SetArmed(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle cursor moving in a menuItem.
+//-----------------------------------------------------------------------------
+void MenuItem::OnCursorMoved(int x, int y)
+{
+ // if menu is in keymode and we moved the mouse
+ // highlight this item
+ if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD)
+ {
+ OnCursorEntered();
+ }
+
+ // chain up to parent
+ CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle mouse cursor entering a menuItem.
+//-----------------------------------------------------------------------------
+void MenuItem::OnCursorEntered()
+{
+ // post a message to the parent menu.
+ // forward the message on to the parent of this menu.
+ KeyValues *msg = new KeyValues ("CursorEnteredMenuItem");
+ // tell the parent this menuitem is the one that was entered so it can highlight it
+ msg->SetInt("VPanel", GetVPanel());
+
+ ivgui()->PostMessage(GetVParent(), msg, NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle mouse cursor exiting a menuItem.
+//-----------------------------------------------------------------------------
+void MenuItem::OnCursorExited()
+{
+ // post a message to the parent menu.
+ // forward the message on to the parent of this menu.
+ KeyValues *msg = new KeyValues ("CursorExitedMenuItem");
+ // tell the parent this menuitem is the one that was entered so it can unhighlight it
+ msg->SetInt("VPanel", GetVPanel());
+
+ ivgui()->PostMessage(GetVParent(), msg, NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle mouse cursor exiting a menuItem.
+//-----------------------------------------------------------------------------
+void MenuItem::OnKeyCodeReleased(KeyCode code)
+{
+ if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD && m_pCascadeMenu)
+ {
+ return;
+ }
+ // only disarm if we are not opening a cascading menu using keys.
+ Button::OnKeyCodeReleased(code);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Highlight a menu item
+// Menu item buttons highlight if disabled, but won't activate.
+//-----------------------------------------------------------------------------
+void MenuItem::ArmItem()
+{
+ // close all other menus
+ GetParentMenu()->CloseOtherMenus(this);
+ // arm the menuItem.
+ Button::SetArmed(true);
+
+ // When you have a submenu with no scroll bar the menu
+ // border will not be drawn correctly. This fixes it.
+ Menu *parent = GetParentMenu();
+ if ( parent )
+ {
+ parent->ForceCalculateWidth();
+ }
+
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Unhighlight a menu item
+//-----------------------------------------------------------------------------
+void MenuItem::DisarmItem()
+{
+ // normal behaviour is that the button becomes unarmed
+ // do not unarm if there is a cascading menu. CloseCascadeMenu handles this.
+ // and the menu handles it since we close at different times depending
+ // on whether menu is handling mouse or key events.
+ if (!m_pCascadeMenu)
+ {
+ Button::OnCursorExited();
+ }
+
+ // When you have a submenu with no scroll bar the menu
+ // border will not be drawn correctly. This fixes it.
+ Menu *parent = GetParentMenu();
+ if ( parent )
+ {
+ parent->ForceCalculateWidth();
+ }
+ Repaint();
+}
+
+bool MenuItem::IsItemArmed()
+{
+ return Button::IsArmed();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Pass kill focus events up to parent, This will tell all panels
+// in the hierarchy to hide themselves, and enables cascading menus to
+// all disappear on selecting an item at the end of the tree.
+//-----------------------------------------------------------------------------
+void MenuItem::OnKillFocus()
+{
+ GetParentMenu()->OnKillFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: fire the menu item as if it has been selected and
+// Tell the owner that it is closing
+//-----------------------------------------------------------------------------
+void MenuItem::FireActionSignal()
+{
+ // cascading menus items don't trigger the parent menu to disappear
+ // (they trigger the cascading menu to open/close when cursor is moved over/off them)
+ if (!m_pCascadeMenu)
+ {
+ KeyValues *kv = new KeyValues("MenuItemSelected");
+ kv->SetPtr("panel", this);
+ ivgui()->PostMessage(GetVParent(), kv, GetVPanel());
+
+ // ivgui()->PostMessage(GetVParent(), new KeyValues("MenuItemSelected"), GetVPanel());
+ Button::FireActionSignal();
+ // toggle the check next to the item if it is checkable
+ if (m_bCheckable)
+ {
+ SetChecked( !m_bChecked );
+ }
+ }
+ else
+ {
+ // if we are in keyboard mode, open the child menu.
+ if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD)
+ {
+ OpenCascadeMenu();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Opens the cascading menu.
+//-----------------------------------------------------------------------------
+void MenuItem::OpenCascadeMenu()
+{
+ if (m_pCascadeMenu)
+ {
+ // perform layout on menu, this way it will open in the right spot
+ // if the window's been moved
+ m_pCascadeMenu->PerformLayout();
+ m_pCascadeMenu->SetVisible(true);
+ ArmItem();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpse: Return true if this item triggers a cascading menu
+//-----------------------------------------------------------------------------
+bool MenuItem::HasMenu()
+{
+ return (m_pCascadeMenu != NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply the resource scheme to the menu.
+//-----------------------------------------------------------------------------
+void MenuItem::ApplySchemeSettings(IScheme *pScheme)
+{
+ // chain back first
+ Button::ApplySchemeSettings(pScheme);
+
+ // get color settings
+ SetDefaultColor(GetSchemeColor("Menu.TextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.BgColor", GetBgColor(), pScheme));
+ SetArmedColor(GetSchemeColor("Menu.ArmedTextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.ArmedBgColor", GetBgColor(), pScheme));
+ SetDepressedColor(GetSchemeColor("Menu.ArmedTextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.ArmedBgColor", GetBgColor(), pScheme));
+
+ SetTextInset(atoi(pScheme->GetResourceString("Menu.TextInset")), 0);
+
+ // reload images since applyschemesettings in label wipes them out.
+ if ( m_pCascadeArrow )
+ {
+ m_pCascadeArrow->SetFont(pScheme->GetFont("Marlett", IsProportional() ));
+ m_pCascadeArrow->ResizeImageToContent();
+ AddImage(m_pCascadeArrow, 0);
+ }
+ else if (m_bCheckable)
+ {
+ ( static_cast<MenuItemCheckImage *>(m_pCheck) )->SetFont( pScheme->GetFont("Marlett", IsProportional()));
+ SetImageAtIndex(0, m_pCheck, CHECK_INSET);
+ ( static_cast<MenuItemCheckImage *>(m_pCheck) )->ResizeImageToContent();
+ }
+
+ if ( m_pCurrentKeyBinding )
+ {
+ m_pCurrentKeyBinding->SetFont(pScheme->GetFont("Default", IsProportional() ));
+ m_pCurrentKeyBinding->ResizeImageToContent();
+ }
+
+ // Have the menu redo the layout
+ // Get the parent to resize
+ Menu * parent = GetParentMenu();
+ if ( parent )
+ {
+ parent->ForceCalculateWidth();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the size of the text portion of the label.
+// for normal menu items this is the same as the label size, but for
+// cascading menus it gives you the size of the text portion only, without
+// the arrow.
+//-----------------------------------------------------------------------------
+void MenuItem::GetTextImageSize(int &wide, int &tall)
+{
+ GetTextImage()->GetSize(wide, tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the size of the text portion of the label.
+// For normal menu items this is the same as the label size, but for
+// cascading menus it sizes textImage portion only, without
+// the arrow.
+//-----------------------------------------------------------------------------
+void MenuItem::SetTextImageSize(int wide, int tall)
+{
+ GetTextImage()->SetSize(wide, tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the size of the arrow portion of the label.
+// If the menuItem is not a cascading menu, 0 is returned.
+//-----------------------------------------------------------------------------
+void MenuItem::GetArrowImageSize(int &wide, int &tall)
+{
+ wide = 0, tall = 0;
+ if (m_pCascadeArrow)
+ {
+ m_pCascadeArrow->GetSize(wide, tall);
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the size of the check portion of the label.
+//-----------------------------------------------------------------------------
+void MenuItem::GetCheckImageSize(int &wide, int &tall)
+{
+ wide = 0, tall = 0;
+ if (m_pCheck)
+ {
+ // resize the image to the contents size
+ ( static_cast<MenuItemCheckImage *>(m_pCheck) )->ResizeImageToContent();
+ m_pCheck->GetSize(wide, tall);
+
+ // include the inset for the check, since nobody but us know about the inset
+ wide += CHECK_INSET;
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return a the menu that this menuItem contains
+// This is useful when the parent menu's commands must be
+// sent through all menus that are open as well (like hotkeys)
+//-----------------------------------------------------------------------------
+Menu *MenuItem::GetMenu()
+{
+ return m_pCascadeMenu;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the border style for the button. Menu items have no border so
+// return null.
+//-----------------------------------------------------------------------------
+IBorder *MenuItem::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
+{
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the menu to key mode if a child menu goes into keymode
+//-----------------------------------------------------------------------------
+void MenuItem::OnKeyModeSet()
+{
+ // send the message to this parent in case this is a cascading menu
+ ivgui()->PostMessage(GetVParent(), new KeyValues("KeyModeSet"), GetVPanel());
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Return if this menuitem is checkable or not
+// This is used by menus to perform the layout properly.
+//-----------------------------------------------------------------------------
+bool MenuItem::IsCheckable()
+{
+ return m_bCheckable;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return if this menuitem is checked or not
+//-----------------------------------------------------------------------------
+bool MenuItem::IsChecked()
+{
+ return m_bChecked;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the checked state of a checkable menuitem
+// Does nothing if item is not checkable
+//-----------------------------------------------------------------------------
+void MenuItem::SetChecked(bool state)
+{
+ if (m_bCheckable)
+ {
+ m_bChecked = state;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool MenuItem::CanBeDefaultButton(void)
+{
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues *MenuItem::GetUserData()
+{
+ if ( HasMenu() )
+ {
+ return m_pCascadeMenu->GetItemUserData( m_pCascadeMenu->GetActiveItem() );
+ }
+ else
+ {
+ return m_pUserData;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the user data
+//-----------------------------------------------------------------------------
+void MenuItem::SetUserData(const KeyValues *kv)
+{
+ if (m_pUserData)
+ {
+ m_pUserData->deleteThis();
+ m_pUserData = NULL;
+ }
+
+ if ( kv )
+ {
+ m_pUserData = kv->MakeCopy();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Passing in NULL removes this object
+// Input : *keyName -
+//-----------------------------------------------------------------------------
+void MenuItem::SetCurrentKeyBinding( char const *keyName )
+{
+ if ( !keyName )
+ {
+ delete m_pCurrentKeyBinding;
+ m_pCurrentKeyBinding = NULL;
+ return;
+ }
+
+ if ( !m_pCurrentKeyBinding )
+ {
+ m_pCurrentKeyBinding = new TextImage( keyName );
+ }
+ else
+ {
+ char curtext[ 256 ];
+ m_pCurrentKeyBinding->GetText( curtext, sizeof( curtext ) );
+ if ( !Q_strcmp( curtext, keyName ) )
+ return;
+
+ m_pCurrentKeyBinding->SetText( keyName );
+ }
+
+ InvalidateLayout( false, true );
+}
+
+#define KEYBINDING_INSET 5
+
+void MenuItem::Paint()
+{
+ BaseClass::Paint();
+ if ( !m_pCurrentKeyBinding )
+ return;
+
+ int w, h;
+ GetSize( w, h );
+ int iw, ih;
+ m_pCurrentKeyBinding->GetSize( iw, ih );
+
+ int x = w - iw - KEYBINDING_INSET;
+ int y = ( h - ih ) / 2;
+
+ if ( IsEnabled() )
+ {
+ m_pCurrentKeyBinding->SetPos( x, y );
+ m_pCurrentKeyBinding->SetColor( GetButtonFgColor() );
+ m_pCurrentKeyBinding->Paint();
+ }
+ else
+ {
+ m_pCurrentKeyBinding->SetPos( x + 1 , y + 1 );
+ m_pCurrentKeyBinding->SetColor( GetDisabledFgColor1() );
+ m_pCurrentKeyBinding->Paint();
+
+ surface()->DrawFlushText();
+
+ m_pCurrentKeyBinding->SetPos( x, y );
+ m_pCurrentKeyBinding->SetColor( GetDisabledFgColor2() );
+ m_pCurrentKeyBinding->Paint();
+ }
+}
+
+void MenuItem::GetContentSize( int& cw, int &ch )
+{
+ BaseClass::GetContentSize( cw, ch );
+ if ( !m_pCurrentKeyBinding )
+ return;
+
+ int iw, ih;
+ m_pCurrentKeyBinding->GetSize( iw, ih );
+
+ cw += iw + KEYBINDING_INSET;
+ ch = max( ch, ih );
+}
diff --git a/mp/src/vgui2/vgui_controls/MessageBox.cpp b/mp/src/vgui2/vgui_controls/MessageBox.cpp
new file mode 100644
index 00000000..d46cb280
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/MessageBox.cpp
@@ -0,0 +1,395 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <vgui/ISurface.h>
+#include <KeyValues.h>
+#include <vgui/IInput.h>
+
+#include <vgui_controls/Button.h>
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/MessageBox.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+vgui::Panel *MessageBox_Factory()
+{
+ return new MessageBox("MessageBox", "MessageBoxText");
+}
+
+DECLARE_BUILD_FACTORY_CUSTOM( MessageBox, MessageBox_Factory );
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+MessageBox::MessageBox(const char *title, const char *text, Panel *parent) : Frame(parent, NULL, false)
+{
+ SetTitle(title, true);
+ m_pMessageLabel = new Label(this, NULL, text);
+
+ Init();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+MessageBox::MessageBox(const wchar_t *wszTitle, const wchar_t *wszText, Panel *parent) : Frame(parent, NULL, false)
+{
+ SetTitle(wszTitle, true);
+ m_pMessageLabel = new Label(this, NULL, wszText);
+
+ Init();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor Helper
+//-----------------------------------------------------------------------------
+void MessageBox::Init()
+{
+ SetDeleteSelfOnClose(true);
+ m_pFrameOver = NULL;
+ m_bShowMessageBoxOverCursor = false;
+
+ SetMenuButtonResponsive(false);
+ SetMinimizeButtonVisible(false);
+ SetCloseButtonVisible(false);
+ SetSizeable(false);
+
+ m_pOkButton = new Button(this, NULL, "#MessageBox_OK");
+ m_pOkButton->SetCommand( "OnOk" );
+ m_pOkButton->AddActionSignalTarget(this);
+
+ m_pCancelButton = new Button(this, NULL, "#MessageBox_Cancel");
+ m_pCancelButton->SetCommand( "OnCancel" );
+ m_pCancelButton->AddActionSignalTarget(this);
+ m_pCancelButton->SetVisible( false );
+
+ m_OkCommand = m_CancelCommand = NULL;
+ m_bNoAutoClose = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+MessageBox::~MessageBox()
+{
+ if ( m_OkCommand )
+ {
+ m_OkCommand->deleteThis();
+ }
+ if ( m_CancelCommand )
+ {
+ m_CancelCommand->deleteThis();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Shows the message box over the cursor
+//-----------------------------------------------------------------------------
+void MessageBox::ShowMessageBoxOverCursor( bool bEnable )
+{
+ m_bShowMessageBoxOverCursor = bEnable;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: size the message label properly
+//-----------------------------------------------------------------------------
+void MessageBox::OnCommand( const char *pCommand )
+{
+ if ( vgui::input()->GetAppModalSurface() == GetVPanel() )
+ {
+ vgui::input()->ReleaseAppModalSurface();
+ }
+
+ if ( !Q_stricmp( pCommand, "OnOk" ) )
+ {
+ if ( m_OkCommand )
+ {
+ PostActionSignal(m_OkCommand->MakeCopy());
+ }
+ }
+ else if ( !Q_stricmp( pCommand, "OnCancel" ) )
+ {
+ if ( m_CancelCommand )
+ {
+ PostActionSignal(m_CancelCommand->MakeCopy());
+ }
+ }
+
+ if ( !m_bNoAutoClose )
+ {
+ OnShutdownRequest();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: size the message label properly
+//-----------------------------------------------------------------------------
+void MessageBox::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ int wide, tall;
+ m_pMessageLabel->GetContentSize(wide, tall);
+ m_pMessageLabel->SetSize(wide, tall);
+
+ wide += 100;
+ tall += 100;
+ SetSize(wide, tall);
+
+ if ( m_bShowMessageBoxOverCursor )
+ {
+ PlaceUnderCursor();
+ return;
+ }
+
+ // move to the middle of the screen
+ if ( m_pFrameOver )
+ {
+ int frameX, frameY;
+ int frameWide, frameTall;
+ m_pFrameOver->GetPos(frameX, frameY);
+ m_pFrameOver->GetSize(frameWide, frameTall);
+
+ SetPos((frameWide - wide) / 2 + frameX, (frameTall - tall) / 2 + frameY);
+ }
+ else
+ {
+ int swide, stall;
+ surface()->GetScreenSize(swide, stall);
+ // put the dialog in the middle of the screen
+ SetPos((swide - wide) / 2, (stall - tall) / 2);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Put the message box into a modal state
+// Does not suspend execution - use addActionSignal to get return value
+//-----------------------------------------------------------------------------
+void MessageBox::DoModal(Frame* pFrameOver)
+{
+ ShowWindow(pFrameOver);
+/*
+ // move to the middle of the screen
+ // get the screen size
+ int wide, tall;
+ // get our dialog size
+ GetSize(wide, tall);
+
+ if (pFrameOver)
+ {
+ int frameX, frameY;
+ int frameWide, frameTall;
+ pFrameOver->GetPos(frameX, frameY);
+ pFrameOver->GetSize(frameWide, frameTall);
+
+ SetPos((frameWide - wide) / 2 + frameX, (frameTall - tall) / 2 + frameY);
+ }
+ else
+ {
+ int swide, stall;
+ surface()->GetScreenSize(swide, stall);
+ // put the dialog in the middle of the screen
+ SetPos((swide - wide) / 2, (stall - tall) / 2);
+ }
+
+ SetVisible( true );
+ SetEnabled( true );
+ MoveToFront();
+
+ if (m_pOkButton->IsVisible())
+ m_pOkButton->RequestFocus();
+ else // handle message boxes with no button
+ RequestFocus();
+*/
+ input()->SetAppModalSurface(GetVPanel());
+}
+
+void MessageBox::ShowWindow(Frame *pFrameOver)
+{
+ m_pFrameOver = pFrameOver;
+
+ SetVisible( true );
+ SetEnabled( true );
+ MoveToFront();
+
+ if ( m_pOkButton->IsVisible() )
+ {
+ m_pOkButton->RequestFocus();
+ }
+ else // handle message boxes with no button
+ {
+ RequestFocus();
+ }
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Put the text and OK buttons in correct place
+//-----------------------------------------------------------------------------
+void MessageBox::PerformLayout()
+{
+ int x, y, wide, tall;
+ GetClientArea(x, y, wide, tall);
+ wide += x;
+ tall += y;
+
+ int boxWidth, boxTall;
+ GetSize(boxWidth, boxTall);
+
+ int oldWide, oldTall;
+ m_pOkButton->GetSize(oldWide, oldTall);
+
+ int btnWide, btnTall;
+ m_pOkButton->GetContentSize(btnWide, btnTall);
+ btnWide = max(oldWide, btnWide + 10);
+ btnTall = max(oldTall, btnTall + 10);
+ m_pOkButton->SetSize(btnWide, btnTall);
+
+ int btnWide2 = 0, btnTall2 = 0;
+ if ( m_pCancelButton->IsVisible() )
+ {
+ m_pCancelButton->GetSize(oldWide, oldTall);
+
+ m_pCancelButton->GetContentSize(btnWide2, btnTall2);
+ btnWide2 = max(oldWide, btnWide2 + 10);
+ btnTall2 = max(oldTall, btnTall2 + 10);
+ m_pCancelButton->SetSize(btnWide2, btnTall2);
+ }
+
+ boxWidth = max(boxWidth, m_pMessageLabel->GetWide() + 100);
+ boxWidth = max(boxWidth, (btnWide + btnWide2) * 2 + 30);
+ SetSize(boxWidth, boxTall);
+
+ GetSize(boxWidth, boxTall);
+
+ m_pMessageLabel->SetPos((wide/2)-(m_pMessageLabel->GetWide()/2) + x, y + 5 );
+ if ( !m_pCancelButton->IsVisible() )
+ {
+ m_pOkButton->SetPos((wide/2)-(m_pOkButton->GetWide()/2) + x, tall - m_pOkButton->GetTall() - 15);
+ }
+ else
+ {
+ m_pOkButton->SetPos((wide/4)-(m_pOkButton->GetWide()/2) + x, tall - m_pOkButton->GetTall() - 15);
+ m_pCancelButton->SetPos((3*wide/4)-(m_pOkButton->GetWide()/2) + x, tall - m_pOkButton->GetTall() - 15);
+ }
+
+ BaseClass::PerformLayout();
+ GetSize(boxWidth, boxTall);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Set a string command to be sent when the OK button is pressed.
+//-----------------------------------------------------------------------------
+void MessageBox::SetCommand(const char *command)
+{
+ if (m_OkCommand)
+ {
+ m_OkCommand->deleteThis();
+ }
+ m_OkCommand = new KeyValues("Command", "command", command);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the command
+//-----------------------------------------------------------------------------
+void MessageBox::SetCommand(KeyValues *command)
+{
+ if (m_OkCommand)
+ {
+ m_OkCommand->deleteThis();
+ }
+ m_OkCommand = command;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void MessageBox::OnShutdownRequest()
+{
+ // Shutdown the dialog
+ PostMessage(this, new KeyValues("Close"));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the visibility of the OK button.
+//-----------------------------------------------------------------------------
+void MessageBox::SetOKButtonVisible(bool state)
+{
+ m_pOkButton->SetVisible(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the Text on the OK button
+//-----------------------------------------------------------------------------
+void MessageBox::SetOKButtonText(const char *buttonText)
+{
+ m_pOkButton->SetText(buttonText);
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the Text on the OK button
+//-----------------------------------------------------------------------------
+void MessageBox::SetOKButtonText(const wchar_t *wszButtonText)
+{
+ m_pOkButton->SetText(wszButtonText);
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Cancel button (off by default)
+//-----------------------------------------------------------------------------
+void MessageBox::SetCancelButtonVisible(bool state)
+{
+ m_pCancelButton->SetVisible(state);
+ InvalidateLayout();
+}
+
+void MessageBox::SetCancelButtonText(const char *buttonText)
+{
+ m_pCancelButton->SetText(buttonText);
+ InvalidateLayout();
+}
+
+void MessageBox::SetCancelButtonText(const wchar_t *wszButtonText)
+{
+ m_pCancelButton->SetText(wszButtonText);
+ InvalidateLayout();
+}
+
+void MessageBox::SetCancelCommand( KeyValues *command )
+{
+ if (m_CancelCommand)
+ {
+ m_CancelCommand->deleteThis();
+ }
+ m_CancelCommand = command;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggles visibility of the close box.
+//-----------------------------------------------------------------------------
+void MessageBox::DisableCloseButton(bool state)
+{
+ BaseClass::SetCloseButtonVisible(state);
+ m_bNoAutoClose = true;
+}
diff --git a/mp/src/vgui2/vgui_controls/MessageDialog.cpp b/mp/src/vgui2/vgui_controls/MessageDialog.cpp
new file mode 100644
index 00000000..202888c0
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/MessageDialog.cpp
@@ -0,0 +1,359 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "vgui_controls/MessageDialog.h"
+#include "vgui/ILocalize.h"
+#include "vgui/ISurface.h"
+
+// NOTE: This has to be the last file included!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// CMessageDialog
+//-----------------------------------------------------------------------------
+CMessageDialog::CMessageDialog( vgui::Panel *pParent, const uint nType, const char *pTitle, const char *pMsg, const char *pCmdA, const char *pCmdB, vgui::Panel *pCreator, bool bShowActivity )
+ : BaseClass( pParent, "MessageDialog" )
+{
+ SetSize( 500, 200 );
+ SetDeleteSelfOnClose( true );
+ SetTitleBarVisible( false );
+ SetCloseButtonVisible( false );
+ SetSizeable( false );
+
+ m_pControlSettings = NULL;
+ m_pCreator = pCreator ? pCreator : pParent;
+
+ m_nType = nType;
+ m_pTitle = new vgui::Label( this, "TitleLabel", pTitle );
+ m_pMsg = new vgui::Label( this, "MessageLabel", pMsg );
+ m_pAnimatingPanel = new vgui::AnimatingImagePanel( this, "AnimatingPanel" );
+
+ m_bShowActivity = bShowActivity;
+
+ if ( nType & MD_SIMPLEFRAME )
+ {
+ SetPaintBackgroundEnabled( true );
+ m_pBackground = NULL;
+ }
+ else
+ {
+ m_pBackground = new vgui::ImagePanel( this, "Background" );
+ if ( nType & MD_WARNING )
+ {
+ m_pBackground->SetName( "WarningBackground" );
+ }
+ else if ( nType & MD_ERROR )
+ {
+ m_pBackground->SetName( "ErrorBackground" );
+ }
+ }
+
+ Q_memset( m_pCommands, 0, sizeof( m_pCommands ) );
+ Q_memset( m_Buttons, 0, sizeof( m_Buttons ) );
+
+ if ( pCmdA )
+ {
+ const int len = Q_strlen( pCmdA ) + 1;
+ m_pCommands[BTN_A] = (char*)malloc( len );
+ Q_strncpy( m_pCommands[BTN_A], pCmdA, len );
+ }
+
+ if ( pCmdB )
+ {
+ const int len = Q_strlen( pCmdB ) + 1;
+ m_pCommands[BTN_B] = (char*)malloc( len );
+ Q_strncpy( m_pCommands[BTN_B], pCmdB, len );
+ }
+
+ // invalid until pressed
+ m_ButtonPressed = BTN_INVALID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CMessageDialog::~CMessageDialog()
+{
+ if ( m_ButtonPressed != BTN_INVALID && ( m_nType & MD_COMMANDAFTERCLOSE ) )
+ {
+ // caller wants the command sent after closure
+ m_pCreator->OnCommand( m_pCommands[m_ButtonPressed] );
+ }
+ else if ( m_nType & MD_COMMANDONFORCECLOSE )
+ {
+ // caller wants the command sent after closure
+ m_pCreator->OnCommand( m_pCommands[BTN_A] );
+ }
+
+ for ( int i = 0; i < MAX_BUTTONS; ++i )
+ {
+ free( m_pCommands[i] );
+ m_pCommands[i] = NULL;
+
+ delete m_Buttons[i].pIcon;
+ delete m_Buttons[i].pText;
+ }
+
+ delete m_pTitle;
+ m_pTitle = NULL;
+
+ delete m_pMsg;
+ m_pMsg = NULL;
+
+ delete m_pBackground;
+ m_pBackground = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the keyvalues to pass to LoadControlSettings()
+//-----------------------------------------------------------------------------
+void CMessageDialog::SetControlSettingsKeys( KeyValues *pKeys )
+{
+ m_pControlSettings = pKeys;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a new button
+//-----------------------------------------------------------------------------
+void CMessageDialog::CreateButtonLabel( ButtonLabel_s *pButton, const char *pIcon, const char *pText )
+{
+ pButton->nWide = m_ButtonIconLabelSpace;
+ pButton->bCreated = true;
+
+ pButton->pIcon = new vgui::Label( this, "icon", pIcon );
+ SETUP_PANEL( pButton->pIcon );
+ pButton->pIcon->SetFont( m_hButtonFont );
+ pButton->pIcon->SizeToContents();
+ pButton->nWide += pButton->pIcon->GetWide();
+
+ pButton->pText = new vgui::Label( this, "text", pText );
+ SETUP_PANEL( pButton->pText );
+ pButton->pText->SetFont( m_hTextFont );
+ pButton->pText->SizeToContents();
+ pButton->pText->SetFgColor( m_ButtonTextColor );
+ pButton->nWide += pButton->pText->GetWide();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create and arrange the panel button labels according to the dialog type
+//-----------------------------------------------------------------------------
+void CMessageDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings( "resource/UI/MessageDialog.res", "GAME", m_pControlSettings );
+
+ m_hButtonFont = pScheme->GetFont( "GameUIButtons" );
+ m_hTextFont = pScheme->GetFont( "MenuLarge" );
+
+ if ( m_nType & MD_OK )
+ {
+ CreateButtonLabel( &m_Buttons[BTN_A], "#GameUI_Icons_A_BUTTON", "#GameUI_OK" );
+ }
+ else if ( m_nType & MD_CANCEL )
+ {
+ CreateButtonLabel( &m_Buttons[BTN_B], "#GameUI_Icons_B_BUTTON", "#GameUI_Cancel" );
+ }
+ else if ( m_nType & MD_OKCANCEL )
+ {
+ CreateButtonLabel( &m_Buttons[BTN_A], "#GameUI_Icons_A_BUTTON", "#GameUI_OK" );
+ CreateButtonLabel( &m_Buttons[BTN_B], "#GameUI_Icons_B_BUTTON", "#GameUI_Cancel" );
+ }
+ else if ( m_nType & MD_YESNO )
+ {
+ CreateButtonLabel( &m_Buttons[BTN_A], "#GameUI_Icons_A_BUTTON", "#GameUI_Yes" );
+ CreateButtonLabel( &m_Buttons[BTN_B], "#GameUI_Icons_B_BUTTON", "#GameUI_No" );
+ }
+
+ // count the buttons and add up their widths
+ int cButtons = 0;
+ int nTotalWide = 0;
+ for ( int i = 0; i < MAX_BUTTONS; ++i )
+ {
+ if ( m_Buttons[i].bCreated )
+ {
+ ++cButtons;
+ nTotalWide += m_Buttons[i].nWide;
+ }
+ }
+
+ // make sure text and icons are center-aligned vertically with each other
+ int nButtonTall = vgui::surface()->GetFontTall( m_hButtonFont );
+ int nTextTall = vgui::surface()->GetFontTall( m_hTextFont );
+ int nVerticalAdjust = ( nButtonTall - nTextTall ) / 2;
+
+ // position the buttons with even horizontal spacing
+ int xpos = 0;
+ int ypos = GetTall() - max( nButtonTall, nTextTall ) - m_ButtonMargin;
+ int nSpacing = ( GetWide() - nTotalWide ) / ( cButtons + 1 );
+ for ( int i = 0; i < MAX_BUTTONS; ++i )
+ {
+ if ( m_Buttons[i].bCreated )
+ {
+ xpos += nSpacing;
+ m_Buttons[i].pIcon->SetPos( xpos, ypos );
+ xpos += m_Buttons[i].pIcon->GetWide() + m_ButtonIconLabelSpace;
+ m_Buttons[i].pText->SetPos( xpos, ypos + nVerticalAdjust );
+ xpos += m_Buttons[i].pText->GetWide();
+ }
+ }
+
+ m_clrNotSimpleBG = pScheme->GetColor( "MessageDialog.MatchmakingBG", Color( 200, 184, 151, 255 ) );
+ m_clrNotSimpleBGBlack = pScheme->GetColor( "MessageDialog.MatchmakingBGBlack", Color( 52, 48, 55, 255 ) );
+
+ if ( !m_bShowActivity )
+ {
+ if ( m_pAnimatingPanel )
+ {
+ if ( m_pAnimatingPanel->IsVisible() )
+ {
+
+ m_pAnimatingPanel->SetVisible( false );
+ }
+
+ m_pAnimatingPanel->StopAnimation();
+ }
+ }
+ else
+ {
+ if ( m_pAnimatingPanel )
+ {
+ if ( !m_pAnimatingPanel->IsVisible() )
+ {
+ m_pAnimatingPanel->SetVisible( true );
+ }
+
+ m_pAnimatingPanel->StartAnimation();
+ }
+ }
+
+ MoveToCenterOfScreen();
+
+ if ( m_bShowActivity && m_ActivityIndent )
+ {
+ // If we're animating, we push our text label in, and reduce its width
+ int iX,iY,iW,iH;
+ m_pMsg->GetBounds( iX, iY, iW, iH );
+ m_pMsg->SetBounds( iX + m_ActivityIndent, iY, max(0,iW-m_ActivityIndent), iH );
+ }
+
+ // Invalidate the scheme on our message label so that it recalculates
+ // its line breaks in case it was resized when we loaded our .res file.
+ m_pMsg->InvalidateLayout( false, true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create and arrange the panel button labels according to the dialog type
+//-----------------------------------------------------------------------------
+void CMessageDialog::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings( inResourceData );
+
+ m_pTitle->SetFgColor( inResourceData->GetColor( "titlecolor" ) );
+
+ m_pMsg->SetFgColor( inResourceData->GetColor( "messagecolor" ) );
+
+ m_ButtonTextColor = inResourceData->GetColor( "buttontextcolor" );
+
+ m_FooterTall = inResourceData->GetInt( "footer_tall", 0 );
+ m_ButtonMargin = inResourceData->GetInt( "button_margin", 25 );
+ m_ButtonIconLabelSpace = inResourceData->GetInt( "button_separator", 10 );
+ m_ActivityIndent = inResourceData->GetInt( "activity_indent", 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+uint CMessageDialog::GetType( void )
+{
+ return m_nType;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMessageDialog::DoCommand( int button )
+{
+ if ( button == BTN_INVALID || ( m_nType & MD_COMMANDONFORCECLOSE ) )
+ {
+ return;
+ }
+
+ if ( m_pCommands[button] )
+ {
+ m_ButtonPressed = button;
+ if ( !( m_nType & MD_COMMANDAFTERCLOSE ) )
+ {
+ // caller wants the command sent before closure
+ m_pCreator->OnCommand( m_pCommands[m_ButtonPressed] );
+ }
+ m_pCreator->OnCommand( "ReleaseModalWindow" );
+ Close();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMessageDialog::OnKeyCodePressed( vgui::KeyCode code )
+{
+ if ( m_ButtonPressed != BTN_INVALID || GetAlpha() != 255 )
+ {
+ // inhibit any further key activity or during transitions
+ return;
+ }
+
+ switch ( GetBaseButtonCode( code ) )
+ {
+ case KEY_XBUTTON_A:
+ DoCommand( BTN_A );
+ break;
+
+ case KEY_XBUTTON_B:
+ DoCommand( BTN_B );
+ break;
+
+ default:
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMessageDialog::PaintBackground( void )
+{
+ int wide, tall;
+ GetSize( wide, tall );
+
+ if ( !( m_nType & MD_SIMPLEFRAME ) )
+ {
+ int nAlpha = GetAlpha();
+
+ m_clrNotSimpleBG[3] = nAlpha;
+ m_clrNotSimpleBGBlack[3] = nAlpha;
+
+ DrawBox( 0, 0, wide, tall, m_clrNotSimpleBGBlack, 1.0f );
+ DrawBox( 0, 0, wide, tall - m_FooterTall, m_clrNotSimpleBG, 1.0f );
+
+ return;
+ }
+
+ Color col = GetBgColor();
+ DrawBox( 0, 0, wide, tall, col, 1.0f );
+
+ // offset the inset by title
+ int titleX, titleY, titleWide, titleTall;
+ m_pTitle->GetBounds( titleX, titleY, titleWide, titleTall );
+ int y = titleY + titleTall;
+
+ // draw an inset
+ Color darkColor;
+ darkColor.SetColor( 0.70f * (float)col.r(), 0.70f * (float)col.g(), 0.70f * (float)col.b(), col.a() );
+ vgui::surface()->DrawSetColor( darkColor );
+ vgui::surface()->DrawFilledRect( 8, y, wide - 8, tall - 8 );
+}
diff --git a/mp/src/vgui2/vgui_controls/Panel.cpp b/mp/src/vgui2/vgui_controls/Panel.cpp
new file mode 100644
index 00000000..7fb2e630
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/Panel.cpp
@@ -0,0 +1,8451 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+
+#include <stdio.h>
+#include <assert.h>
+#include <utlvector.h>
+#include <vstdlib/IKeyValuesSystem.h>
+#include <ctype.h> // isdigit()
+
+#include <materialsystem/imaterial.h>
+
+#include <vgui/IBorder.h>
+#include <vgui/IInput.h>
+#include <vgui/IPanel.h>
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <vgui/ISystem.h>
+#include <vgui/ILocalize.h>
+#include <vgui/IVGui.h>
+#include <KeyValues.h>
+#include <vgui/MouseCode.h>
+
+#include <vgui_controls/Panel.h>
+#include <vgui_controls/BuildGroup.h>
+#include <vgui_controls/Tooltip.h>
+#include <vgui_controls/PHandle.h>
+#include <vgui_controls/Controls.h>
+#include "vgui_controls/Menu.h"
+#include "vgui_controls/MenuItem.h"
+
+#include "UtlSortVector.h"
+
+#include "tier1/utldict.h"
+#include "tier1/utlbuffer.h"
+#include "mempool.h"
+#include "filesystem.h"
+#include "tier0/icommandline.h"
+
+#include "tier0/vprof.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+#define TRIPLE_PRESS_MSEC 300
+
+
+extern int GetBuildModeDialogCount();
+
+static char *CopyString( const char *in )
+{
+ if ( !in )
+ return NULL;
+
+ int len = strlen( in );
+ char *n = new char[ len + 1 ];
+ Q_strncpy( n, in, len + 1 );
+ return n;
+}
+
+#if defined( VGUI_USEDRAGDROP )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct vgui::DragDrop_t
+{
+ DragDrop_t() :
+ m_bDragEnabled( false ),
+ m_bShowDragHelper( true ),
+ m_bDropEnabled( false ),
+ m_bDragStarted( false ),
+ m_nDragStartTolerance( 8 ),
+ m_bDragging( false ),
+ m_lDropHoverTime( 0 ),
+ m_bDropMenuShown( false ),
+ m_bPreventChaining( false )
+ {
+ m_nStartPos[ 0 ] = m_nStartPos[ 1 ] = 0;
+ m_nLastPos[ 0 ] = m_nLastPos[ 1 ] = 0;
+ }
+
+ // Drag related data
+ bool m_bDragEnabled;
+ bool m_bShowDragHelper;
+ bool m_bDragging;
+ bool m_bDragStarted;
+ // How many pixels the dragged box must move before showing the outline rect...
+ int m_nDragStartTolerance;
+ int m_nStartPos[ 2 ];
+ int m_nLastPos[ 2 ];
+ CUtlVector< KeyValues * > m_DragData;
+ CUtlVector< PHandle > m_DragPanels;
+
+ // Drop related data
+ bool m_bDropEnabled;
+ // A droppable panel can have a hover context menu, which will show up after m_flHoverContextTime of hovering
+ float m_flHoverContextTime;
+
+ PHandle m_hCurrentDrop;
+ // Amount of time hovering over current drop target
+ long m_lDropHoverTime;
+ bool m_bDropMenuShown;
+ DHANDLE< Menu > m_hDropContextMenu;
+
+ // Misc data
+ bool m_bPreventChaining;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper for painting to the full screen...
+//-----------------------------------------------------------------------------
+class CDragDropHelperPanel : public Panel
+{
+ DECLARE_CLASS_SIMPLE( CDragDropHelperPanel, Panel );
+public:
+ CDragDropHelperPanel();
+
+ virtual VPANEL IsWithinTraverse(int x, int y, bool traversePopups);
+ virtual void PostChildPaint();
+
+ void AddPanel( Panel *current );
+
+ void RemovePanel( Panel *search );
+
+private:
+ struct DragHelperPanel_t
+ {
+ PHandle m_hPanel;
+ };
+
+ CUtlVector< DragHelperPanel_t > m_PaintList;
+};
+
+vgui::DHANDLE< CDragDropHelperPanel > s_DragDropHelper;
+#endif
+
+#if defined( VGUI_USEKEYBINDINGMAPS )
+
+BoundKey_t::BoundKey_t():
+ isbuiltin( true ),
+ bindingname( 0 ),
+ keycode( KEY_NONE ),
+ modifiers( 0 )
+{
+}
+
+BoundKey_t::BoundKey_t( const BoundKey_t& src )
+{
+ isbuiltin = src.isbuiltin;
+ bindingname = isbuiltin ? src.bindingname : CopyString( src.bindingname );
+ keycode = src.keycode;
+ modifiers = src.modifiers;
+}
+
+BoundKey_t& BoundKey_t::operator =( const BoundKey_t& src )
+{
+ if ( this == &src )
+ return *this;
+ isbuiltin = src.isbuiltin;
+ bindingname = isbuiltin ? src.bindingname : CopyString( src.bindingname );
+ keycode = src.keycode;
+ modifiers = src.modifiers;
+ return *this;
+}
+
+
+BoundKey_t::~BoundKey_t()
+{
+ if ( !isbuiltin )
+ {
+ delete[] bindingname;
+ }
+}
+
+KeyBindingMap_t::KeyBindingMap_t() :
+ bindingname( 0 ),
+ func( 0 ),
+ helpstring( 0 ),
+ docstring( 0 ),
+ passive( false )
+{
+}
+
+KeyBindingMap_t::KeyBindingMap_t( const KeyBindingMap_t& src )
+{
+ bindingname = src.bindingname;
+ helpstring = src.helpstring;
+ docstring = src.docstring;
+
+ func = src.func;
+ passive = src.passive;
+}
+
+KeyBindingMap_t::~KeyBindingMap_t()
+{
+}
+
+class CKeyBindingsMgr
+{
+public:
+
+ CKeyBindingsMgr() :
+ m_Bindings( 0, 0, KeyBindingContextHandleLessFunc ),
+ m_nKeyBindingContexts( 0 )
+ {
+ }
+
+ struct KBContext_t
+ {
+ KBContext_t() :
+ m_KeyBindingsFile( UTL_INVAL_SYMBOL ),
+ m_KeyBindingsPathID( UTL_INVAL_SYMBOL )
+ {
+ m_Handle = INVALID_KEYBINDINGCONTEXT_HANDLE;
+ }
+
+ KBContext_t( const KBContext_t& src )
+ {
+ m_Handle = src.m_Handle;
+ m_KeyBindingsFile = src.m_KeyBindingsFile;
+ m_KeyBindingsPathID = src.m_KeyBindingsPathID;
+ int c = src.m_Panels.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ m_Panels.AddToTail( src.m_Panels[ i ] );
+ }
+ }
+
+ KeyBindingContextHandle_t m_Handle;
+ CUtlSymbol m_KeyBindingsFile;
+ CUtlSymbol m_KeyBindingsPathID;
+ CUtlVector< Panel * > m_Panels;
+ };
+
+ static bool KeyBindingContextHandleLessFunc( const KBContext_t& lhs, const KBContext_t& rhs )
+ {
+ return lhs.m_Handle < rhs.m_Handle;
+ }
+
+ KeyBindingContextHandle_t CreateContext( char const *filename, char const *pathID )
+ {
+ KBContext_t entry;
+
+ entry.m_Handle = (KeyBindingContextHandle_t)++m_nKeyBindingContexts;
+ entry.m_KeyBindingsFile = filename;
+ if ( pathID )
+ {
+ entry.m_KeyBindingsPathID = pathID;
+ }
+ else
+ {
+ entry.m_KeyBindingsPathID = UTL_INVAL_SYMBOL;
+ }
+
+ m_Bindings.Insert( entry );
+
+ return entry.m_Handle;
+ }
+
+ void AddPanelToContext( KeyBindingContextHandle_t handle, Panel *panel )
+ {
+ if ( !panel->GetName() || !panel->GetName()[ 0 ] )
+ {
+ Warning( "Can't add Keybindings Context for unnamed panels\n" );
+ return;
+ }
+
+ KBContext_t *entry = Find( handle );
+ Assert( entry );
+ if ( entry )
+ {
+ int idx = entry->m_Panels.Find( panel );
+ if ( idx == entry->m_Panels.InvalidIndex() )
+ {
+ entry->m_Panels.AddToTail( panel );
+ }
+ }
+ }
+
+ void OnPanelDeleted( KeyBindingContextHandle_t handle, Panel *panel )
+ {
+ KBContext_t *kb = Find( handle );
+ if ( kb )
+ {
+ kb->m_Panels.FindAndRemove( panel );
+ }
+ }
+
+ KBContext_t *Find( KeyBindingContextHandle_t handle )
+ {
+ KBContext_t search;
+ search.m_Handle = handle;
+ int idx = m_Bindings.Find( search );
+ if ( idx == m_Bindings.InvalidIndex() )
+ {
+ return NULL;
+ }
+ return &m_Bindings[ idx ];
+ }
+
+ char const *GetKeyBindingsFile( KeyBindingContextHandle_t handle )
+ {
+ KBContext_t *kb = Find( handle );
+ if ( kb )
+ {
+ return kb->m_KeyBindingsFile.String();
+ }
+ Assert( 0 );
+ return "";
+ }
+
+ char const *GetKeyBindingsFilePathID( KeyBindingContextHandle_t handle )
+ {
+ KBContext_t *kb = Find( handle );
+ if ( kb )
+ {
+ return kb->m_KeyBindingsPathID.String();
+ }
+ Assert( 0 );
+ return NULL;
+ }
+
+ int GetPanelsWithKeyBindingsCount( KeyBindingContextHandle_t handle )
+ {
+ KBContext_t *kb = Find( handle );
+ if ( kb )
+ {
+ return kb->m_Panels.Count();
+ }
+ Assert( 0 );
+ return 0;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: static method
+ // Input : index -
+ // Output : Panel
+ //-----------------------------------------------------------------------------
+ Panel *GetPanelWithKeyBindings( KeyBindingContextHandle_t handle, int index )
+ {
+ KBContext_t *kb = Find( handle );
+ if ( kb )
+ {
+ Assert( index >= 0 && index < kb->m_Panels.Count() );
+ return kb->m_Panels[ index ];
+ }
+ Assert( 0 );
+ return 0;
+ }
+
+ CUtlRBTree< KBContext_t, int > m_Bindings;
+ int m_nKeyBindingContexts;
+};
+
+static CKeyBindingsMgr g_KBMgr;
+
+//-----------------------------------------------------------------------------
+// Purpose: Static method to allocate a context
+// Input : -
+// Output : KeyBindingContextHandle_t
+//-----------------------------------------------------------------------------
+KeyBindingContextHandle_t Panel::CreateKeyBindingsContext( char const *filename, char const *pathID /*=0*/ )
+{
+ return g_KBMgr.CreateContext( filename, pathID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: static method
+// Input : -
+// Output : int
+//-----------------------------------------------------------------------------
+int Panel::GetPanelsWithKeyBindingsCount( KeyBindingContextHandle_t handle )
+{
+ return g_KBMgr.GetPanelsWithKeyBindingsCount( handle );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: static method
+// Input : index -
+// Output : Panel
+//-----------------------------------------------------------------------------
+Panel *Panel::GetPanelWithKeyBindings( KeyBindingContextHandle_t handle, int index )
+{
+ return g_KBMgr.GetPanelWithKeyBindings( handle, index );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the number of keybindings
+//-----------------------------------------------------------------------------
+int Panel::GetKeyMappingCount( )
+{
+ int nCount = 0;
+ PanelKeyBindingMap *map = GetKBMap();
+ while ( map )
+ {
+ nCount += map->entries.Count();
+ map = map->baseMap;
+ }
+ return nCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: static method. Reverts key bindings for all registered panels (panels with keybindings actually
+// loaded from file
+// Input : -
+//-----------------------------------------------------------------------------
+void Panel::RevertKeyBindings( KeyBindingContextHandle_t handle )
+{
+ int c = GetPanelsWithKeyBindingsCount( handle );
+ for ( int i = 0; i < c; ++i )
+ {
+ Panel *kbPanel = GetPanelWithKeyBindings( handle, i );
+ Assert( kbPanel );
+ kbPanel->RevertKeyBindingsToDefault();
+ }
+}
+
+static void BufPrint( CUtlBuffer& buf, int level, char const *fmt, ... )
+{
+ char string[ 2048 ];
+ va_list argptr;
+ va_start( argptr, fmt );
+ _vsnprintf( string, sizeof( string ) - 1, fmt, argptr );
+ va_end( argptr );
+ string[ sizeof( string ) - 1 ] = 0;
+
+ while ( --level >= 0 )
+ {
+ buf.Printf( " " );
+ }
+ buf.Printf( "%s", string );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : handle -
+//-----------------------------------------------------------------------------
+void Panel::SaveKeyBindings( KeyBindingContextHandle_t handle )
+{
+ char const *filename = g_KBMgr.GetKeyBindingsFile( handle );
+ char const *pathID = g_KBMgr.GetKeyBindingsFilePathID( handle );
+
+ SaveKeyBindingsToFile( handle, filename, pathID );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: static method. Saves key binding files out for all keybindings
+// Input : -
+//-----------------------------------------------------------------------------
+void Panel::SaveKeyBindingsToFile( KeyBindingContextHandle_t handle, char const *filename, char const *pathID /*= 0*/ )
+{
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ BufPrint( buf, 0, "keybindings\n" );
+ BufPrint( buf, 0, "{\n" );
+
+ int c = GetPanelsWithKeyBindingsCount( handle );
+ for ( int i = 0; i < c; ++i )
+ {
+ Panel *kbPanel = GetPanelWithKeyBindings( handle, i );
+ Assert( kbPanel );
+ if ( !kbPanel )
+ continue;
+
+ Assert( kbPanel->GetName() );
+ Assert( kbPanel->GetName()[ 0 ] );
+
+ if ( !kbPanel->GetName() || !kbPanel->GetName()[ 0 ] )
+ continue;
+
+ BufPrint( buf, 1, "\"%s\"\n", kbPanel->GetName() );
+ BufPrint( buf, 1, "{\n" );
+
+ kbPanel->SaveKeyBindingsToBuffer( 2, buf );
+
+ BufPrint( buf, 1, "}\n" );
+ }
+
+ BufPrint( buf, 0, "}\n" );
+
+ if ( g_pFullFileSystem->FileExists( filename, pathID ) &&
+ !g_pFullFileSystem->IsFileWritable( filename, pathID ) )
+ {
+ Warning( "Panel::SaveKeyBindings '%s' is read-only!!!\n", filename );
+ }
+
+ FileHandle_t h = g_pFullFileSystem->Open( filename, "wb", pathID );
+ if ( FILESYSTEM_INVALID_HANDLE != h )
+ {
+ g_pFullFileSystem->Write( buf.Base(), buf.TellPut(), h );
+ g_pFullFileSystem->Close( h );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : handle -
+// *panelOfInterest -
+//-----------------------------------------------------------------------------
+void Panel::LoadKeyBindingsForOnePanel( KeyBindingContextHandle_t handle, Panel *panelOfInterest )
+{
+ if ( !panelOfInterest )
+ return;
+ if ( !panelOfInterest->GetName() )
+ return;
+ if ( !panelOfInterest->GetName()[ 0 ] )
+ return;
+
+ char const *filename = g_KBMgr.GetKeyBindingsFile( handle );
+ char const *pathID = g_KBMgr.GetKeyBindingsFilePathID( handle );
+
+ KeyValues *kv = new KeyValues( "keybindings" );
+ if ( kv->LoadFromFile( g_pFullFileSystem, filename, pathID ) )
+ {
+ int c = GetPanelsWithKeyBindingsCount( handle );
+ for ( int i = 0; i < c; ++i )
+ {
+ Panel *kbPanel = GetPanelWithKeyBindings( handle, i );
+ Assert( kbPanel );
+
+ char const *panelName = kbPanel->GetName();
+ if ( !panelName )
+ {
+ continue;
+ }
+
+ if ( Q_stricmp( panelOfInterest->GetName(), panelName ) )
+ continue;
+
+ KeyValues *subKey = kv->FindKey( panelName, false );
+ if ( !subKey )
+ {
+ Warning( "Panel::ReloadKeyBindings: Can't find entry for panel '%s'\n", panelName );
+ continue;
+ }
+
+ kbPanel->ParseKeyBindings( subKey );
+ }
+ }
+ kv->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: static method. Loads all key bindings again
+// Input : -
+//-----------------------------------------------------------------------------
+
+void Panel::ReloadKeyBindings( KeyBindingContextHandle_t handle )
+{
+ char const *filename = g_KBMgr.GetKeyBindingsFile( handle );
+ char const *pathID = g_KBMgr.GetKeyBindingsFilePathID( handle );
+
+ KeyValues *kv = new KeyValues( "keybindings" );
+ if ( kv->LoadFromFile( g_pFullFileSystem, filename, pathID ) )
+ {
+ int c = GetPanelsWithKeyBindingsCount( handle );
+ for ( int i = 0; i < c; ++i )
+ {
+ Panel *kbPanel = GetPanelWithKeyBindings( handle, i );
+ Assert( kbPanel );
+
+ char const *panelName = kbPanel->GetName();
+ if ( !panelName )
+ {
+ continue;
+ }
+
+ KeyValues *subKey = kv->FindKey( panelName, false );
+ if ( !subKey )
+ {
+ Warning( "Panel::ReloadKeyBindings: Can't find entry for panel '%s'\n", panelName );
+ continue;
+ }
+
+ kbPanel->ParseKeyBindings( subKey );
+ }
+ }
+ kv->deleteThis();
+}
+#endif // VGUI_USEKEYBINDINGMAPS
+
+DECLARE_BUILD_FACTORY( Panel );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+Panel::Panel()
+{
+ Init(0, 0, 64, 24);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+Panel::Panel(Panel *parent)
+{
+ Init(0, 0, 64, 24);
+ SetParent(parent);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+Panel::Panel(Panel *parent, const char *panelName)
+{
+ Init(0, 0, 64, 24);
+ SetName(panelName);
+ SetParent(parent);
+ SetBuildModeEditable(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+Panel::Panel( Panel *parent, const char *panelName, HScheme scheme )
+{
+ Init(0, 0, 64, 24);
+ SetName(panelName);
+ SetParent(parent);
+ SetBuildModeEditable(true);
+ SetScheme( scheme );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Setup
+//-----------------------------------------------------------------------------
+void Panel::Init( int x, int y, int wide, int tall )
+{
+ _panelName = NULL;
+ _tooltipText = NULL;
+ _pinToSibling = NULL;
+ m_hMouseEventHandler = NULL;
+ _pinCornerToSibling = PIN_TOPLEFT;
+ _pinToSiblingCorner = PIN_TOPLEFT;
+
+ // get ourselves an internal panel
+ _vpanel = ivgui()->AllocPanel();
+ ipanel()->Init(_vpanel, this);
+
+ SetPos(x, y);
+ SetSize(wide, tall);
+ _flags.SetFlag( NEEDS_LAYOUT | NEEDS_SCHEME_UPDATE | NEEDS_DEFAULT_SETTINGS_APPLIED );
+ _flags.SetFlag( AUTODELETE_ENABLED | PAINT_BORDER_ENABLED | PAINT_BACKGROUND_ENABLED | PAINT_ENABLED );
+#if defined( VGUI_USEKEYBINDINGMAPS )
+ _flags.SetFlag( ALLOW_CHAIN_KEYBINDING_TO_PARENT );
+#endif
+ m_nPinDeltaX = m_nPinDeltaY = 0;
+ m_nResizeDeltaX = m_nResizeDeltaY = 0;
+ _autoResizeDirection = AUTORESIZE_NO;
+ _pinCorner = PIN_TOPLEFT;
+ _cursor = dc_arrow;
+ _border = NULL;
+ _buildGroup = UTLHANDLE_INVALID;
+ _tabPosition = 0;
+ m_iScheme = 0;
+ m_bIsSilent = false;
+ m_bParentNeedsCursorMoveEvents = false;
+
+ _buildModeFlags = 0; // not editable or deletable in buildmode dialog by default
+
+ m_pTooltips = NULL;
+ m_bToolTipOverridden = false;
+
+ m_flAlpha = 255.0f;
+ m_nPaintBackgroundType = 0;
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Default to rounding all corners (for draw style 2)
+ //=============================================================================
+ m_roundedCorners = PANEL_ROUND_CORNER_ALL;
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ m_nBgTextureId1 = -1;
+ m_nBgTextureId2 = -1;
+ m_nBgTextureId3 = -1;
+ m_nBgTextureId4 = -1;
+#if defined( VGUI_USEDRAGDROP )
+ m_pDragDrop = new DragDrop_t;
+
+#endif
+
+ m_lLastDoublePressTime = 0L;
+
+#if defined( VGUI_USEKEYBINDINGMAPS )
+ m_hKeyBindingsContext = INVALID_KEYBINDINGCONTEXT_HANDLE;
+#endif
+
+ REGISTER_COLOR_AS_OVERRIDABLE( _fgColor, "fgcolor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _bgColor, "bgcolor_override" );
+
+ m_bIsConsoleStylePanel = false;
+ m_NavUp = NULL;
+ m_NavDown = NULL;
+ m_NavLeft = NULL;
+ m_NavRight = NULL;
+ m_NavToRelay = NULL;
+ m_NavActivate = NULL;
+ m_NavBack = NULL;
+ m_sNavUpName = NULL;
+ m_sNavDownName = NULL;
+ m_sNavLeftName = NULL;
+ m_sNavRightName = NULL;
+ m_sNavToRelayName = NULL;
+ m_sNavActivateName = NULL;
+ m_sNavBackName = NULL;
+
+ m_PassUnhandledInput = true;
+ m_LastNavDirection = ND_NONE;
+ m_bWorldPositionCurrentFrame = false;
+ m_bForceStereoRenderToFrameBuffer = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+Panel::~Panel()
+{
+ // @note Tom Bui: only cleanup if we've created it
+ if ( !m_bToolTipOverridden )
+ {
+ if ( m_pTooltips )
+ {
+ delete m_pTooltips;
+ }
+ }
+#if defined( VGUI_USEKEYBINDINGMAPS )
+ if ( IsValidKeyBindingsContext() )
+ {
+ g_KBMgr.OnPanelDeleted( m_hKeyBindingsContext, this );
+ }
+#endif // VGUI_USEKEYBINDINGMAPS
+#if defined( VGUI_USEDRAGDROP )
+ if ( m_pDragDrop->m_bDragging )
+ {
+ OnFinishDragging( false, (MouseCode)-1 );
+ }
+#endif // VGUI_USEDRAGDROP
+
+ _flags.ClearFlag( AUTODELETE_ENABLED );
+ _flags.SetFlag( MARKED_FOR_DELETION );
+
+ // remove panel from any list
+ SetParent((VPANEL)NULL);
+
+ // Stop our children from pointing at us, and delete them if possible
+ while (ipanel()->GetChildCount(GetVPanel()))
+ {
+ VPANEL child = ipanel()->GetChild(GetVPanel(), 0);
+ if (ipanel()->IsAutoDeleteSet(child))
+ {
+ ipanel()->DeletePanel(child);
+ }
+ else
+ {
+ ipanel()->SetParent(child, NULL);
+ }
+ }
+
+ // delete VPanel
+ ivgui()->FreePanel(_vpanel);
+ // free our name
+ delete [] _panelName;
+
+ if ( _tooltipText && _tooltipText[0] )
+ {
+ delete [] _tooltipText;
+ }
+
+ delete [] _pinToSibling;
+
+ _vpanel = NULL;
+#if defined( VGUI_USEDRAGDROP )
+ delete m_pDragDrop;
+#endif // VGUI_USEDRAGDROP
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: fully construct this panel so its ready for use right now (i.e fonts loaded, colors set, default label text set, ...)
+//-----------------------------------------------------------------------------
+void Panel::MakeReadyForUse()
+{
+// PerformApplySchemeSettings();
+ UpdateSiblingPin();
+ surface()->SolveTraverse( GetVPanel(), true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetName( const char *panelName )
+{
+ // No change?
+ if ( _panelName &&
+ panelName &&
+ !Q_strcmp( _panelName, panelName ) )
+ {
+ return;
+ }
+
+ if (_panelName)
+ {
+ delete [] _panelName;
+ _panelName = NULL;
+ }
+
+ if (panelName)
+ {
+ int len = Q_strlen(panelName) + 1;
+ _panelName = new char[ len ];
+ Q_strncpy( _panelName, panelName, len );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the given name of the panel
+//-----------------------------------------------------------------------------
+const char *Panel::GetName()
+{
+ if (_panelName)
+ return _panelName;
+
+ return "";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the name of the module that this instance of panel was compiled into
+//-----------------------------------------------------------------------------
+const char *Panel::GetModuleName()
+{
+ return vgui::GetControlsModuleName();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the classname of the panel (as specified in the panelmaps)
+//-----------------------------------------------------------------------------
+const char *Panel::GetClassName()
+{
+ // loop up the panel map name
+ PanelMessageMap *panelMap = GetMessageMap();
+ if ( panelMap )
+ {
+ return panelMap->pfnClassName();
+ }
+
+ return "Panel";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetPos(int x, int y)
+{
+ if (!CommandLine()->FindParm("-hushasserts"))
+ {
+ Assert( abs(x) < 32768 && abs(y) < 32768 );
+ }
+ ipanel()->SetPos(GetVPanel(), x, y);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::GetPos(int &x, int &y)
+{
+ ipanel()->GetPos(GetVPanel(), x, y);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetSize(int wide, int tall)
+{
+ Assert( abs(wide) < 32768 && abs(tall) < 32768 );
+ ipanel()->SetSize(GetVPanel(), wide, tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::GetSize(int &wide, int &tall)
+{
+ ipanel()->GetSize(GetVPanel(), wide, tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetBounds(int x, int y, int wide, int tall)
+{
+ SetPos(x,y);
+ SetSize(wide,tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::GetBounds(int &x, int &y, int &wide, int &tall)
+{
+ GetPos(x, y);
+ GetSize(wide, tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns safe handle to parent
+//-----------------------------------------------------------------------------
+VPANEL Panel::GetVParent()
+{
+ if ( ipanel() )
+ {
+ return ipanel()->GetParent(GetVPanel());
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns a pointer to a controls version of a Panel pointer
+//-----------------------------------------------------------------------------
+Panel *Panel::GetParent()
+{
+ // get the parent and convert it to a Panel *
+ // this is OK, the hierarchy is guaranteed to be all from the same module, except for the root node
+ // the root node always returns NULL when a GetParent() is done so everything is OK
+ if ( ipanel() )
+ {
+ VPANEL parent = ipanel()->GetParent(GetVPanel());
+ if (parent)
+ {
+ Panel *pParent = ipanel()->GetPanel(parent, GetControlsModuleName());
+ Assert(!pParent || !strcmp(pParent->GetModuleName(), GetControlsModuleName()));
+ return pParent;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Screen size change notification handler
+//-----------------------------------------------------------------------------
+void Panel::OnScreenSizeChanged(int nOldWide, int nOldTall)
+{
+ // post to all children
+ for (int i = 0; i < ipanel()->GetChildCount(GetVPanel()); i++)
+ {
+ VPANEL child = ipanel()->GetChild(GetVPanel(), i);
+ PostMessage(child, new KeyValues("OnScreenSizeChanged", "oldwide", nOldWide, "oldtall", nOldTall), NULL);
+ }
+
+ // make any currently fullsize window stay fullsize
+ int x, y, wide, tall;
+ GetBounds(x, y, wide, tall);
+ int screenWide, screenTall;
+ surface()->GetScreenSize(screenWide, screenTall);
+ if (x == 0 && y == 0 && nOldWide == wide && tall == nOldTall)
+ {
+ // fullsize
+ surface()->GetScreenSize(wide, tall);
+ SetBounds(0, 0, wide, tall);
+ }
+
+ // panel needs to re-get it's scheme settings
+ _flags.SetFlag( NEEDS_SCHEME_UPDATE );
+
+ // invalidate our settings
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetVisible(bool state)
+{
+ ipanel()->SetVisible(GetVPanel(), state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool Panel::IsVisible()
+{
+ if (ipanel())
+ {
+ return ipanel()->IsVisible(GetVPanel());
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetEnabled(bool state)
+{
+ if (state != ipanel()->IsEnabled( GetVPanel()))
+ {
+ ipanel()->SetEnabled(GetVPanel(), state);
+ InvalidateLayout(false);
+ Repaint();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool Panel::IsEnabled()
+{
+ return ipanel()->IsEnabled(GetVPanel());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool Panel::IsPopup()
+{
+ return ipanel()->IsPopup(GetVPanel());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::Repaint()
+{
+ _flags.SetFlag( NEEDS_REPAINT );
+ if (surface())
+ {
+ surface()->Invalidate(GetVPanel());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::Think()
+{
+ if (IsVisible())
+ {
+ // update any tooltips
+ if (m_pTooltips)
+ {
+ m_pTooltips->PerformLayout();
+ }
+ if ( _flags.IsFlagSet( NEEDS_LAYOUT ) )
+ {
+ InternalPerformLayout();
+ }
+ }
+
+ OnThink();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::PaintTraverse( bool repaint, bool allowForce )
+{
+ if ( m_bWorldPositionCurrentFrame )
+ {
+ surface()->SolveTraverse( GetVPanel() );
+ }
+
+ if ( !IsVisible() )
+ {
+ return;
+ }
+
+ float oldAlphaMultiplier = surface()->DrawGetAlphaMultiplier();
+ float newAlphaMultiplier = oldAlphaMultiplier * m_flAlpha * 1.0f/255.0f;
+
+ if ( IsXbox() && !newAlphaMultiplier )
+ {
+ // xbox optimization not suitable for pc
+ // xbox panels are compliant and can early out and not traverse their children
+ // when they have no opacity
+ return;
+ }
+
+ if ( !repaint &&
+ allowForce &&
+ _flags.IsFlagSet( NEEDS_REPAINT ) )
+ {
+ repaint = true;
+ _flags.ClearFlag( NEEDS_REPAINT );
+ }
+
+ VPANEL vpanel = GetVPanel();
+
+ bool bPushedViewport = false;
+ if( GetForceStereoRenderToFrameBuffer() )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ if( pRenderContext->GetRenderTarget() )
+ {
+ surface()->PushFullscreenViewport();
+ bPushedViewport = true;
+ }
+ }
+
+ int clipRect[4];
+ ipanel()->GetClipRect( vpanel, clipRect[0], clipRect[1], clipRect[2], clipRect[3] );
+ if ( ( clipRect[2] <= clipRect[0] ) || ( clipRect[3] <= clipRect[1] ) )
+ {
+ repaint = false;
+ }
+
+ // set global alpha
+ surface()->DrawSetAlphaMultiplier( newAlphaMultiplier );
+
+ bool bBorderPaintFirst = _border ? _border->PaintFirst() : false;
+
+ // draw the border first if requested to
+ if ( bBorderPaintFirst && repaint && _flags.IsFlagSet( PAINT_BORDER_ENABLED ) && ( _border != null ) )
+ {
+ // Paint the border over the background with no inset
+ surface()->PushMakeCurrent( vpanel, false );
+ PaintBorder();
+ surface()->PopMakeCurrent( vpanel );
+ }
+
+ if ( repaint )
+ {
+ // draw the background with no inset
+ if ( _flags.IsFlagSet( PAINT_BACKGROUND_ENABLED ) )
+ {
+ surface()->PushMakeCurrent( vpanel, false );
+ PaintBackground();
+ surface()->PopMakeCurrent( vpanel );
+ }
+
+ // draw the front of the panel with the inset
+ if ( _flags.IsFlagSet( PAINT_ENABLED ) )
+ {
+ surface()->PushMakeCurrent( vpanel, true );
+ Paint();
+ surface()->PopMakeCurrent( vpanel );
+ }
+ }
+
+ // traverse and paint all our children
+ CUtlVector< VPANEL > &children = ipanel()->GetChildren( vpanel );
+ int childCount = children.Count();
+ for (int i = 0; i < childCount; i++)
+ {
+ VPANEL child = children[ i ];
+ bool bVisible = ipanel()->IsVisible( child );
+
+ if ( surface()->ShouldPaintChildPanel( child ) )
+ {
+ if ( bVisible )
+ {
+ ipanel()->PaintTraverse( child, repaint, allowForce );
+ }
+ }
+ else
+ {
+ // Invalidate the child panel so that it gets redrawn
+ surface()->Invalidate( child );
+
+ // keep traversing the tree, just don't allow anyone to paint after here
+ if ( bVisible )
+ {
+ ipanel()->PaintTraverse( child, false, false );
+ }
+ }
+ }
+
+ // draw the border last
+ if ( repaint )
+ {
+ if ( !bBorderPaintFirst && _flags.IsFlagSet( PAINT_BORDER_ENABLED ) && ( _border != null ) )
+ {
+ // Paint the border over the background with no inset
+ surface()->PushMakeCurrent( vpanel, false );
+ PaintBorder();
+ surface()->PopMakeCurrent( vpanel );
+ }
+
+#ifdef _DEBUG
+ // IsBuildGroupEnabled recurses up all the parents and ends up being very expensive as it wanders all over memory
+ if ( GetBuildModeDialogCount() && IsBuildGroupEnabled() ) //&& HasFocus() )
+ {
+ // outline all selected panels
+ CUtlVector<PHandle> *controlGroup = _buildGroup->GetControlGroup();
+ for (int i=0; i < controlGroup->Size(); ++i)
+ {
+ // outline all selected panels
+ CUtlVector<PHandle> *controlGroup = _buildGroup->GetControlGroup();
+ for (int i=0; i < controlGroup->Size(); ++i)
+ {
+ surface()->PushMakeCurrent( ((*controlGroup)[i].Get())->GetVPanel(), false );
+ ((*controlGroup)[i].Get())->PaintBuildOverlay();
+ surface()->PopMakeCurrent( ((*controlGroup)[i].Get())->GetVPanel() );
+ }
+
+ _buildGroup->DrawRulers();
+ }
+ }
+#endif
+
+ // All of our children have painted, etc, now allow painting in top of them
+ if ( _flags.IsFlagSet( POST_CHILD_PAINT_ENABLED ) )
+ {
+ surface()->PushMakeCurrent( vpanel, false );
+ PostChildPaint();
+ surface()->PopMakeCurrent( vpanel );
+ }
+ }
+
+ surface()->DrawSetAlphaMultiplier( oldAlphaMultiplier );
+
+ surface()->SwapBuffers( vpanel );
+
+ if( bPushedViewport )
+ {
+ surface()->PopFullscreenViewport();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::PaintBorder()
+{
+ _border->Paint(GetVPanel());
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::PaintBackground()
+{
+ int wide, tall;
+ GetSize( wide, tall );
+ if ( m_SkipChild.Get() && m_SkipChild->IsVisible() )
+ {
+ if ( GetPaintBackgroundType() == 2 )
+ {
+ int cornerWide, cornerTall;
+ GetCornerTextureSize( cornerWide, cornerTall );
+
+ Color col = GetBgColor();
+ DrawHollowBox( 0, 0, wide, tall, col, 1.0f );
+
+ wide -= 2 * cornerWide;
+ tall -= 2 * cornerTall;
+
+ FillRectSkippingPanel( GetBgColor(), cornerWide, cornerTall, wide, tall, m_SkipChild.Get() );
+ }
+ else
+ {
+ FillRectSkippingPanel( GetBgColor(), 0, 0, wide, tall, m_SkipChild.Get() );
+ }
+ }
+ else
+ {
+ Color col = GetBgColor();
+
+ switch ( m_nPaintBackgroundType )
+ {
+ default:
+ case 0:
+ {
+ surface()->DrawSetColor(col);
+ surface()->DrawFilledRect(0, 0, wide, tall);
+ }
+ break;
+ case 1:
+ {
+ DrawTexturedBox( 0, 0, wide, tall, col, 1.0f );
+ }
+ break;
+ case 2:
+ {
+ DrawBox( 0, 0, wide, tall, col, 1.0f );
+ }
+ break;
+ case 3:
+ {
+ DrawBoxFade( 0, 0, wide, tall, col, 1.0f, 255, 0, true );
+ }
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::Paint()
+{
+ // empty on purpose
+ // PaintBackground is painted and default behavior is for Paint to do nothing
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::PostChildPaint()
+{
+ // Empty on purpose
+ // This is called if _postChildPaintEnabled is true and allows painting to
+ // continue on the surface after all of the panel's children have painted
+ // themselves. Allows drawing an overlay on top of the children, etc.
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws a black rectangle around the panel.
+//-----------------------------------------------------------------------------
+void Panel::PaintBuildOverlay()
+{
+ int wide,tall;
+ GetSize(wide,tall);
+ surface()->DrawSetColor(0, 0, 0, 255);
+
+ surface()->DrawFilledRect(0,0,wide,2); //top
+ surface()->DrawFilledRect(0,tall-2,wide,tall); //bottom
+ surface()->DrawFilledRect(0,2,2,tall-2); //left
+ surface()->DrawFilledRect(wide-2,2,wide,tall-2); //right
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if the panel's draw code will fully cover it's area
+//-----------------------------------------------------------------------------
+bool Panel::IsOpaque()
+{
+ // FIXME: Add code to account for the 'SkipChild' functionality in Frame
+ if ( IsVisible() && _flags.IsFlagSet( PAINT_BACKGROUND_ENABLED ) && ( _bgColor[3] == 255 ) )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the settings are aligned to the right of the screen
+//-----------------------------------------------------------------------------
+bool Panel::IsRightAligned()
+{
+ return (_buildModeFlags & BUILDMODE_SAVE_XPOS_RIGHTALIGNED);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the settings are aligned to the bottom of the screen
+//-----------------------------------------------------------------------------
+bool Panel::IsBottomAligned()
+{
+ return (_buildModeFlags & BUILDMODE_SAVE_YPOS_BOTTOMALIGNED);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the parent
+//-----------------------------------------------------------------------------
+void Panel::SetParent(Panel *newParent)
+{
+ // Assert that the parent is from the same module as the child
+ // FIXME: !!! work out how to handle this properly!
+ // Assert(!newParent || !strcmp(newParent->GetModuleName(), GetControlsModuleName()));
+
+ if (newParent)
+ {
+ SetParent(newParent->GetVPanel());
+ }
+ else
+ {
+ SetParent((VPANEL)NULL);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetParent(VPANEL newParent)
+{
+ if (newParent)
+ {
+ ipanel()->SetParent(GetVPanel(), newParent);
+ }
+ else
+ {
+ ipanel()->SetParent(GetVPanel(), NULL);
+ }
+
+ if (GetVParent() && !IsPopup())
+ {
+ SetProportional(ipanel()->IsProportional(GetVParent()));
+
+ // most of the time KBInput == parents kbinput
+ if (ipanel()->IsKeyBoardInputEnabled(GetVParent()) != IsKeyBoardInputEnabled())
+ {
+ SetKeyBoardInputEnabled(ipanel()->IsKeyBoardInputEnabled(GetVParent()));
+ }
+
+ if (ipanel()->IsMouseInputEnabled(GetVParent()) != IsMouseInputEnabled())
+ {
+ SetMouseInputEnabled(ipanel()->IsMouseInputEnabled(GetVParent()));
+ }
+ }
+
+ UpdateSiblingPin();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::OnChildAdded(VPANEL child)
+{
+ Assert( !_flags.IsFlagSet( IN_PERFORM_LAYOUT ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: default message handler
+//-----------------------------------------------------------------------------
+void Panel::OnSizeChanged(int newWide, int newTall)
+{
+ InvalidateLayout(); // our size changed so force us to layout again
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets Z ordering - lower numbers are always behind higher z's
+//-----------------------------------------------------------------------------
+void Panel::SetZPos(int z)
+{
+ ipanel()->SetZPos(GetVPanel(), z);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets Z ordering - lower numbers are always behind higher z's
+//-----------------------------------------------------------------------------
+int Panel::GetZPos()
+{
+ return ( ipanel()->GetZPos( GetVPanel() ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets alpha modifier for panel and all child panels [0..255]
+//-----------------------------------------------------------------------------
+void Panel::SetAlpha(int alpha)
+{
+ m_flAlpha = alpha;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+int Panel::GetAlpha()
+{
+ return (int)m_flAlpha;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Moves the panel to the front of the z-order
+//-----------------------------------------------------------------------------
+void Panel::MoveToFront(void)
+{
+ // FIXME: only use ipanel() as per src branch?
+ if (IsPopup())
+ {
+ surface()->BringToFront(GetVPanel());
+ }
+ else
+ {
+ ipanel()->MoveToFront(GetVPanel());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Iterates up the hierarchy looking for a particular parent
+//-----------------------------------------------------------------------------
+bool Panel::HasParent(VPANEL potentialParent)
+{
+ if (!potentialParent)
+ return false;
+
+ return ipanel()->HasParent(GetVPanel(), potentialParent);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the index of a child panel by string name
+// Output : int - -1 if no panel of that name is found
+//-----------------------------------------------------------------------------
+int Panel::FindChildIndexByName(const char *childName)
+{
+ for (int i = 0; i < GetChildCount(); i++)
+ {
+ Panel *pChild = GetChild(i);
+ if (!pChild)
+ continue;
+
+ if (!stricmp(pChild->GetName(), childName))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds a child panel by string name
+// Output : Panel * - NULL if no panel of that name is found
+//-----------------------------------------------------------------------------
+Panel *Panel::FindChildByName(const char *childName, bool recurseDown)
+{
+ for (int i = 0; i < GetChildCount(); i++)
+ {
+ Panel *pChild = GetChild(i);
+ if (!pChild)
+ continue;
+
+ if (!V_stricmp(pChild->GetName(), childName))
+ {
+ return pChild;
+ }
+
+ if (recurseDown)
+ {
+ Panel *panel = pChild->FindChildByName(childName, recurseDown);
+ if ( panel )
+ {
+ return panel;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds a sibling panel by name
+//-----------------------------------------------------------------------------
+Panel *Panel::FindSiblingByName(const char *siblingName)
+{
+ if ( !GetVParent() )
+ return NULL;
+
+ int siblingCount = ipanel()->GetChildCount(GetVParent());
+ for (int i = 0; i < siblingCount; i++)
+ {
+ VPANEL sibling = ipanel()->GetChild(GetVParent(), i);
+ Panel *panel = ipanel()->GetPanel(sibling, GetControlsModuleName());
+ if (!stricmp(panel->GetName(), siblingName))
+ {
+ return panel;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Dispatches immediately a message to the parent
+//-----------------------------------------------------------------------------
+void Panel::CallParentFunction(KeyValues *message)
+{
+ if (GetVParent())
+ {
+ ipanel()->SendMessage(GetVParent(), message, GetVPanel());
+ }
+ if (message)
+ {
+ message->deleteThis();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: if set to true, panel automatically frees itself when parent is deleted
+//-----------------------------------------------------------------------------
+void Panel::SetAutoDelete( bool state )
+{
+ _flags.SetFlag( AUTODELETE_ENABLED, state );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool Panel::IsAutoDeleteSet()
+{
+ return _flags.IsFlagSet( AUTODELETE_ENABLED );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Just calls 'delete this'
+//-----------------------------------------------------------------------------
+void Panel::DeletePanel()
+{
+ // Avoid re-entrancy
+ _flags.SetFlag( MARKED_FOR_DELETION );
+ _flags.ClearFlag( AUTODELETE_ENABLED );
+ delete this;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+HScheme Panel::GetScheme()
+{
+ if (m_iScheme)
+ {
+ return m_iScheme; // return our internal scheme
+ }
+
+ if (GetVParent()) // recurse down the heirarchy
+ {
+ return ipanel()->GetScheme(GetVParent());
+ }
+
+ return scheme()->GetDefaultScheme();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set the scheme to render this panel with by name
+//-----------------------------------------------------------------------------
+void Panel::SetScheme(const char *tag)
+{
+ if (strlen(tag) > 0 && scheme()->GetScheme(tag)) // check the scheme exists
+ {
+ SetScheme(scheme()->GetScheme(tag));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set the scheme to render this panel with
+//-----------------------------------------------------------------------------
+void Panel::SetScheme(HScheme scheme)
+{
+ if (scheme != m_iScheme)
+ {
+ m_iScheme = scheme;
+
+ // This will cause the new scheme to be applied at a later point
+// InvalidateLayout( false, true );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the char of this panels hotkey
+//-----------------------------------------------------------------------------
+Panel *Panel::HasHotkey(wchar_t key)
+{
+ return NULL;
+}
+
+#if defined( VGUI_USEDRAGDROP )
+static vgui::PHandle g_DragDropCapture;
+#endif // VGUI_USEDRAGDROP
+
+void Panel::InternalCursorMoved(int x, int y)
+{
+#if defined( VGUI_USEDRAGDROP )
+ if ( g_DragDropCapture.Get() )
+ {
+ bool started = g_DragDropCapture->GetDragDropInfo()->m_bDragStarted;
+
+ g_DragDropCapture->OnContinueDragging();
+
+ if ( started )
+ {
+ bool isEscapeKeyDown = input()->IsKeyDown( KEY_ESCAPE );
+ if ( isEscapeKeyDown )
+ {
+ g_DragDropCapture->OnFinishDragging( true, (MouseCode)-1, true );
+ }
+ return;
+ }
+ }
+#endif // VGUI_USEDRAGDROP
+
+ if ( !ShouldHandleInputMessage() )
+ return;
+
+ if ( IsCursorNone() )
+ return;
+
+ if ( !IsMouseInputEnabled() )
+ {
+ return;
+ }
+
+ if (IsBuildGroupEnabled())
+ {
+ if ( _buildGroup->CursorMoved(x, y, this) )
+ {
+ return;
+ }
+ }
+
+ if (m_pTooltips)
+ {
+ if ( _tooltipText )
+ {
+ m_pTooltips->SetText( _tooltipText );
+ }
+ m_pTooltips->ShowTooltip(this);
+ }
+
+ ScreenToLocal(x, y);
+
+ OnCursorMoved(x, y);
+}
+
+void Panel::InternalCursorEntered()
+{
+ if (IsCursorNone() || !IsMouseInputEnabled())
+ return;
+
+ if (IsBuildGroupEnabled())
+ return;
+
+ if (m_pTooltips)
+ {
+ m_pTooltips->ResetDelay();
+
+ if ( _tooltipText )
+ {
+ m_pTooltips->SetText( _tooltipText );
+ }
+ m_pTooltips->ShowTooltip(this);
+ }
+
+ OnCursorEntered();
+}
+
+void Panel::InternalCursorExited()
+{
+ if (IsCursorNone() || !IsMouseInputEnabled())
+ return;
+
+ if (IsBuildGroupEnabled())
+ return;
+
+ if (m_pTooltips)
+ {
+ m_pTooltips->HideTooltip();
+ }
+
+ OnCursorExited();
+}
+
+bool Panel::IsChildOfSurfaceModalPanel()
+{
+ VPANEL appModalPanel = input()->GetAppModalSurface();
+ if ( !appModalPanel )
+ return true;
+
+ if ( ipanel()->HasParent( GetVPanel(), appModalPanel ) )
+ return true;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Panel::IsChildOfModalSubTree()
+{
+ VPANEL subTree = input()->GetModalSubTree();
+ if ( !subTree )
+ return true;
+
+ if ( HasParent( subTree ) )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks to see if message is being subverted due to modal subtree logic
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+static bool ShouldHandleInputMessage( VPANEL p )
+{
+ // If there is not modal subtree, then always handle the msg
+ if ( !input()->GetModalSubTree() )
+ return true;
+
+ // What state are we in?
+ bool bChildOfModal = false;
+ VPANEL subTree = input()->GetModalSubTree();
+ if ( !subTree )
+ {
+ bChildOfModal = true;
+ }
+ else if ( ipanel()->HasParent( p, subTree ) )
+ {
+ bChildOfModal = true;
+ }
+
+ if ( input()->ShouldModalSubTreeReceiveMessages() )
+ return bChildOfModal;
+
+ return !bChildOfModal;
+}
+
+bool Panel::ShouldHandleInputMessage()
+{
+ return ::ShouldHandleInputMessage( GetVPanel() );
+}
+
+void Panel::InternalMousePressed(int code)
+{
+ long curtime = system()->GetTimeMillis();
+ if ( IsTriplePressAllowed() )
+ {
+ long elapsed = curtime - m_lLastDoublePressTime;
+ if ( elapsed < TRIPLE_PRESS_MSEC )
+ {
+ InternalMouseTriplePressed( code );
+ return;
+ }
+ }
+
+ // The menu system passively watches for mouse released messages so it
+ // can clear any open menus if the release is somewhere other than on a menu
+ Menu::OnInternalMousePressed( this, (MouseCode)code );
+
+ if ( !ShouldHandleInputMessage() )
+ return;
+
+ if ( IsCursorNone() )
+ return;
+
+ if ( !IsMouseInputEnabled())
+ {
+#if defined( VGUI_USEDRAGDROP )
+ DragDropStartDragging();
+#endif
+ return;
+ }
+
+ if (IsBuildGroupEnabled())
+ {
+ if ( _buildGroup->MousePressed((MouseCode)code, this) )
+ {
+ return;
+ }
+ }
+
+ Panel *pMouseHandler = m_hMouseEventHandler.Get();
+ if ( pMouseHandler )
+ {
+ pMouseHandler->OnMousePressed( (MouseCode)code );
+ }
+ else
+ {
+ OnMousePressed( (MouseCode)code );
+ }
+
+#if defined( VGUI_USEDRAGDROP )
+ DragDropStartDragging();
+#endif
+}
+
+void Panel::InternalMouseDoublePressed(int code)
+{
+ m_lLastDoublePressTime = system()->GetTimeMillis();
+
+ if ( !ShouldHandleInputMessage() )
+ return;
+
+ if ( IsCursorNone() )
+ return;
+
+ if ( !IsMouseInputEnabled())
+ {
+ return;
+ }
+
+ if (IsBuildGroupEnabled())
+ {
+ if ( _buildGroup->MouseDoublePressed((MouseCode)code, this) )
+ {
+ return;
+ }
+ }
+
+ Panel *pMouseHandler = m_hMouseEventHandler.Get();
+ if ( pMouseHandler )
+ {
+ pMouseHandler->OnMouseDoublePressed( (MouseCode)code );
+ }
+ else
+ {
+ OnMouseDoublePressed( (MouseCode)code );
+ }
+}
+
+#if defined( VGUI_USEDRAGDROP )
+void Panel::SetStartDragWhenMouseExitsPanel( bool state )
+{
+ _flags.SetFlag( DRAG_REQUIRES_PANEL_EXIT, state );
+}
+
+bool Panel::IsStartDragWhenMouseExitsPanel() const
+{
+ return _flags.IsFlagSet( DRAG_REQUIRES_PANEL_EXIT );
+}
+#endif // VGUI_USEDRAGDROP
+
+void Panel::SetTriplePressAllowed( bool state )
+{
+ _flags.SetFlag( TRIPLE_PRESS_ALLOWED, state );
+}
+
+bool Panel::IsTriplePressAllowed() const
+{
+ return _flags.IsFlagSet( TRIPLE_PRESS_ALLOWED );
+}
+
+void Panel::InternalMouseTriplePressed( int code )
+{
+ Assert( IsTriplePressAllowed() );
+ m_lLastDoublePressTime = 0L;
+
+ if ( !ShouldHandleInputMessage() )
+ return;
+
+ if ( IsCursorNone() )
+ return;
+
+ if ( !IsMouseInputEnabled())
+ {
+#if defined( VGUI_USEDRAGDROP )
+ DragDropStartDragging();
+#endif
+ return;
+ }
+
+ if (IsBuildGroupEnabled())
+ {
+ return;
+ }
+
+ OnMouseTriplePressed((MouseCode)code);
+#if defined( VGUI_USEDRAGDROP )
+ DragDropStartDragging();
+#endif
+}
+
+void Panel::InternalMouseReleased(int code)
+{
+#if defined( VGUI_USEDRAGDROP )
+ if ( g_DragDropCapture.Get() )
+ {
+ bool started = g_DragDropCapture->GetDragDropInfo()->m_bDragStarted;
+ g_DragDropCapture->OnFinishDragging( true, (MouseCode)code );
+ if ( started )
+ {
+ return;
+ }
+ }
+#endif
+
+ if ( !ShouldHandleInputMessage() )
+ return;
+
+ if ( IsCursorNone() )
+ return;
+
+ if ( !IsMouseInputEnabled())
+ {
+ return;
+ }
+
+ if (IsBuildGroupEnabled())
+ {
+ if ( _buildGroup->MouseReleased((MouseCode)code, this) )
+ {
+ return;
+ }
+ }
+
+ OnMouseReleased((MouseCode)code);
+}
+
+void Panel::InternalMouseWheeled(int delta)
+{
+ if (IsBuildGroupEnabled() || !IsMouseInputEnabled())
+ {
+ return;
+ }
+
+ if ( !ShouldHandleInputMessage() )
+ return;
+
+ OnMouseWheeled(delta);
+}
+
+void Panel::InternalKeyCodePressed(int code)
+{
+ if ( !ShouldHandleInputMessage() )
+ return;
+
+ if (IsKeyBoardInputEnabled())
+ {
+ OnKeyCodePressed((KeyCode)code);
+ }
+ else
+ {
+ CallParentFunction(new KeyValues("KeyCodePressed", "code", code));
+ }
+}
+
+#if defined( VGUI_USEKEYBINDINGMAPS )
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *bindingName -
+// keycode -
+// modifiers -
+//-----------------------------------------------------------------------------
+void Panel::AddKeyBinding( char const *bindingName, int keycode, int modifiers )
+{
+ PanelKeyBindingMap *map = LookupMapForBinding( bindingName );
+ if ( !map )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ BoundKey_t kb;
+ kb.isbuiltin = false;
+ kb.bindingname = CopyString( bindingName );
+ kb.keycode = keycode;
+ kb.modifiers = modifiers;
+
+ map->boundkeys.AddToTail( kb );
+}
+
+KeyBindingMap_t *Panel::LookupBinding( char const *bindingName )
+{
+ PanelKeyBindingMap *map = GetKBMap();
+ while( map )
+ {
+ int c = map->entries.Count();
+ for( int i = 0; i < c ; ++i )
+ {
+ KeyBindingMap_t *binding = &map->entries[ i ];
+ if ( !Q_stricmp( binding->bindingname, bindingName ) )
+ return binding;
+ }
+
+ map = map->baseMap;
+ }
+
+ return NULL;
+}
+
+PanelKeyBindingMap *Panel::LookupMapForBinding( char const *bindingName )
+{
+ PanelKeyBindingMap *map = GetKBMap();
+ while( map )
+ {
+ int c = map->entries.Count();
+ for( int i = 0; i < c ; ++i )
+ {
+ KeyBindingMap_t *binding = &map->entries[ i ];
+ if ( !Q_stricmp( binding->bindingname, bindingName ) )
+ return map;
+ }
+
+ map = map->baseMap;
+ }
+
+ return NULL;
+}
+
+KeyBindingMap_t *Panel::LookupBindingByKeyCode( KeyCode code, int modifiers )
+{
+ PanelKeyBindingMap *map = GetKBMap();
+ while( map )
+ {
+ int c = map->boundkeys.Count();
+ for( int i = 0; i < c ; ++i )
+ {
+ BoundKey_t *kb = &map->boundkeys[ i ];
+ if ( kb->keycode == code && kb->modifiers == modifiers )
+ {
+ KeyBindingMap_t *binding = LookupBinding( kb->bindingname );
+ Assert( binding );
+ if ( binding )
+ {
+ return binding;
+ }
+ }
+ }
+
+ map = map->baseMap;
+ }
+
+ return NULL;
+}
+
+BoundKey_t *Panel::LookupDefaultKey( char const *bindingName )
+{
+ PanelKeyBindingMap *map = GetKBMap();
+ while( map )
+ {
+ int c = map->defaultkeys.Count();
+ for( int i = 0; i < c ; ++i )
+ {
+ BoundKey_t *kb = &map->defaultkeys[ i ];
+ if ( !Q_stricmp( kb->bindingname, bindingName ) )
+ {
+ return kb;
+ }
+ }
+
+ map = map->baseMap;
+ }
+ return NULL;
+}
+
+void Panel::LookupBoundKeys( char const *bindingName, CUtlVector< BoundKey_t * >& list )
+{
+ PanelKeyBindingMap *map = GetKBMap();
+ while( map )
+ {
+ int c = map->boundkeys.Count();
+ for( int i = 0; i < c ; ++i )
+ {
+ BoundKey_t *kb = &map->boundkeys[ i ];
+ if ( !Q_stricmp( kb->bindingname, bindingName ) )
+ {
+ list.AddToTail( kb );
+ }
+ }
+
+ map = map->baseMap;
+ }
+}
+
+void Panel::RevertKeyBindingsToDefault()
+{
+ PanelKeyBindingMap *map = GetKBMap();
+ while( map )
+ {
+ map->boundkeys.RemoveAll();
+ map->boundkeys = map->defaultkeys;
+
+ map = map->baseMap;
+ }
+}
+
+void Panel::RemoveAllKeyBindings()
+{
+ PanelKeyBindingMap *map = GetKBMap();
+ while( map )
+ {
+ map->boundkeys.RemoveAll();
+ map = map->baseMap;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void Panel::ReloadKeyBindings()
+{
+ RevertKeyBindingsToDefault();
+ LoadKeyBindingsForOnePanel( GetKeyBindingsContext(), this );
+}
+
+#define MAKE_STRING( x ) #x
+#define KEY_NAME( str, disp ) { KEY_##str, MAKE_STRING( KEY_##str ), disp }
+
+struct KeyNames_t
+{
+ KeyCode code;
+ char const *string;
+ char const *displaystring;
+};
+
+static KeyNames_t g_KeyNames[] =
+{
+KEY_NAME( NONE, "None" ),
+KEY_NAME( 0, "0" ),
+KEY_NAME( 1, "1" ),
+KEY_NAME( 2, "2" ),
+KEY_NAME( 3, "3" ),
+KEY_NAME( 4, "4" ),
+KEY_NAME( 5, "5" ),
+KEY_NAME( 6, "6" ),
+KEY_NAME( 7, "7" ),
+KEY_NAME( 8, "8" ),
+KEY_NAME( 9, "9" ),
+KEY_NAME( A, "A" ),
+KEY_NAME( B, "B" ),
+KEY_NAME( C, "C" ),
+KEY_NAME( D, "D" ),
+KEY_NAME( E, "E" ),
+KEY_NAME( F, "F" ),
+KEY_NAME( G, "G" ),
+KEY_NAME( H, "H" ),
+KEY_NAME( I, "I" ),
+KEY_NAME( J, "J" ),
+KEY_NAME( K, "K" ),
+KEY_NAME( L, "L" ),
+KEY_NAME( M, "M" ),
+KEY_NAME( N, "N" ),
+KEY_NAME( O, "O" ),
+KEY_NAME( P, "P" ),
+KEY_NAME( Q, "Q" ),
+KEY_NAME( R, "R" ),
+KEY_NAME( S, "S" ),
+KEY_NAME( T, "T" ),
+KEY_NAME( U, "U" ),
+KEY_NAME( V, "V" ),
+KEY_NAME( W, "W" ),
+KEY_NAME( X, "X" ),
+KEY_NAME( Y, "Y" ),
+KEY_NAME( Z, "Z" ),
+KEY_NAME( PAD_0, "Key Pad 0" ),
+KEY_NAME( PAD_1, "Key Pad 1" ),
+KEY_NAME( PAD_2, "Key Pad 2" ),
+KEY_NAME( PAD_3, "Key Pad 3" ),
+KEY_NAME( PAD_4, "Key Pad 4" ),
+KEY_NAME( PAD_5, "Key Pad 5" ),
+KEY_NAME( PAD_6, "Key Pad 6" ),
+KEY_NAME( PAD_7, "Key Pad 7" ),
+KEY_NAME( PAD_8, "Key Pad 8" ),
+KEY_NAME( PAD_9, "Key Pad 9" ),
+KEY_NAME( PAD_DIVIDE, "Key Pad /" ),
+KEY_NAME( PAD_MULTIPLY, "Key Pad *" ),
+KEY_NAME( PAD_MINUS, "Key Pad -" ),
+KEY_NAME( PAD_PLUS, "Key Pad +" ),
+KEY_NAME( PAD_ENTER, "Key Pad Enter" ),
+KEY_NAME( PAD_DECIMAL, "Key Pad ." ),
+KEY_NAME( LBRACKET, "[" ),
+KEY_NAME( RBRACKET, "]" ),
+KEY_NAME( SEMICOLON, "," ),
+KEY_NAME( APOSTROPHE, "'" ),
+KEY_NAME( BACKQUOTE, "`" ),
+KEY_NAME( COMMA, "," ),
+KEY_NAME( PERIOD, "." ),
+KEY_NAME( SLASH, "/" ),
+KEY_NAME( BACKSLASH, "\\" ),
+KEY_NAME( MINUS, "-" ),
+KEY_NAME( EQUAL, "=" ),
+KEY_NAME( ENTER, "Enter" ),
+KEY_NAME( SPACE, "Space" ),
+KEY_NAME( BACKSPACE, "Backspace" ),
+KEY_NAME( TAB, "Tab" ),
+KEY_NAME( CAPSLOCK, "Caps Lock" ),
+KEY_NAME( NUMLOCK, "Num Lock" ),
+KEY_NAME( ESCAPE, "Escape" ),
+KEY_NAME( SCROLLLOCK, "Scroll Lock" ),
+KEY_NAME( INSERT, "Ins" ),
+KEY_NAME( DELETE, "Del" ),
+KEY_NAME( HOME, "Home" ),
+KEY_NAME( END, "End" ),
+KEY_NAME( PAGEUP, "PgUp" ),
+KEY_NAME( PAGEDOWN, "PgDn" ),
+KEY_NAME( BREAK, "Break" ),
+KEY_NAME( LSHIFT, "Shift" ),
+KEY_NAME( RSHIFT, "Shift" ),
+KEY_NAME( LALT, "Alt" ),
+KEY_NAME( RALT, "Alt" ),
+KEY_NAME( LCONTROL, "Ctrl" ),
+KEY_NAME( RCONTROL, "Ctrl" ),
+KEY_NAME( LWIN, "Windows" ),
+KEY_NAME( RWIN, "Windows" ),
+KEY_NAME( APP, "App" ),
+KEY_NAME( UP, "Up" ),
+KEY_NAME( LEFT, "Left" ),
+KEY_NAME( DOWN, "Down" ),
+KEY_NAME( RIGHT, "Right" ),
+KEY_NAME( F1, "F1" ),
+KEY_NAME( F2, "F2" ),
+KEY_NAME( F3, "F3" ),
+KEY_NAME( F4, "F4" ),
+KEY_NAME( F5, "F5" ),
+KEY_NAME( F6, "F6" ),
+KEY_NAME( F7, "F7" ),
+KEY_NAME( F8, "F8" ),
+KEY_NAME( F9, "F9" ),
+KEY_NAME( F10, "F10" ),
+KEY_NAME( F11, "F11" ),
+KEY_NAME( F12, "F12" ),
+KEY_NAME( CAPSLOCKTOGGLE, "Caps Lock Toggle" ),
+KEY_NAME( NUMLOCKTOGGLE, "Num Lock Toggle" ),
+KEY_NAME( SCROLLLOCKTOGGLE, "Scroll Lock Toggle" ),
+};
+
+char const *Panel::KeyCodeToString( KeyCode code )
+{
+ int c = ARRAYSIZE( g_KeyNames );
+ for ( int i = 0; i < c ; ++i )
+ {
+ if ( g_KeyNames[ i ].code == code )
+ return g_KeyNames[ i ].string;
+ }
+
+ return "";
+}
+
+wchar_t const *Panel::KeyCodeToDisplayString( KeyCode code )
+{
+ int c = ARRAYSIZE( g_KeyNames );
+ for ( int i = 0; i < c ; ++i )
+ {
+ if ( g_KeyNames[ i ].code == code )
+ {
+ char const *str = g_KeyNames[ i ].displaystring;
+ wchar_t *wstr = g_pVGuiLocalize->Find( str );
+ if ( wstr )
+ {
+ return wstr;
+ }
+
+ static wchar_t buf[ 64 ];
+ g_pVGuiLocalize->ConvertANSIToUnicode( str, buf, sizeof( buf ) );
+ return buf;
+ }
+ }
+
+ return L"";
+}
+
+static void AddModifierToString( char const *modifiername, char *buf, size_t bufsize )
+{
+ char add[ 32 ];
+ if ( Q_strlen( buf ) > 0 )
+ {
+ Q_snprintf( add, sizeof( add ), "+%s", modifiername );
+ }
+ else
+ {
+ Q_strncpy( add, modifiername, sizeof( add ) );
+ }
+
+ Q_strncat( buf, add, bufsize, COPY_ALL_CHARACTERS );
+
+}
+
+wchar_t const *Panel::KeyCodeModifiersToDisplayString( KeyCode code, int modifiers )
+{
+ char sz[ 256 ];
+ sz[ 0 ] = 0;
+
+ if ( modifiers & MODIFIER_SHIFT )
+ {
+ AddModifierToString( "Shift", sz, sizeof( sz ) );
+ }
+ if ( modifiers & MODIFIER_CONTROL )
+ {
+ AddModifierToString( "Ctrl", sz, sizeof( sz ) );
+ }
+ if ( modifiers & MODIFIER_ALT )
+ {
+ AddModifierToString( "Alt", sz, sizeof( sz ) );
+ }
+
+ if ( Q_strlen( sz ) > 0 )
+ {
+ Q_strncat( sz, "+", sizeof( sz ), COPY_ALL_CHARACTERS );
+ }
+
+ static wchar_t unicode[ 256 ];
+ V_swprintf_safe( unicode, L"%S%s", sz, Panel::KeyCodeToDisplayString( (KeyCode)code ) );
+ return unicode;
+}
+
+KeyCode Panel::StringToKeyCode( char const *str )
+{
+ int c = ARRAYSIZE( g_KeyNames );
+ for ( int i = 0; i < c ; ++i )
+ {
+ if ( !Q_stricmp( str, g_KeyNames[ i ].string ) )
+ return g_KeyNames[ i ].code;
+ }
+
+ return KEY_NONE;
+}
+
+static void WriteKeyBindingToBuffer( CUtlBuffer& buf, int level, const BoundKey_t& binding )
+{
+ BufPrint( buf, level, "\"keycode\"\t\"%s\"\n", Panel::KeyCodeToString( (KeyCode)binding.keycode ) );
+ if ( binding.modifiers & MODIFIER_SHIFT )
+ {
+ BufPrint( buf, level, "\"shift\"\t\"1\"\n" );
+ }
+ if ( binding.modifiers & MODIFIER_CONTROL )
+ {
+ BufPrint( buf, level, "\"ctrl\"\t\"1\"\n" );
+ }
+ if ( binding.modifiers & MODIFIER_ALT )
+ {
+ BufPrint( buf, level, "\"alt\"\t\"1\"\n" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+// *pathID -
+//-----------------------------------------------------------------------------
+void Panel::SaveKeyBindingsToBuffer( int level, CUtlBuffer& buf )
+{
+ Assert( IsValidKeyBindingsContext() );
+
+ Assert( buf.IsText() );
+
+ PanelKeyBindingMap *map = GetKBMap();
+ while( map )
+ {
+ int c = map->boundkeys.Count();
+ for( int i = 0; i < c ; ++i )
+ {
+ const BoundKey_t& binding = map->boundkeys[ i ];
+
+ // Spew to file
+ BufPrint( buf, level, "\"%s\"\n", binding.bindingname );
+ BufPrint( buf, level, "{\n" );
+
+ WriteKeyBindingToBuffer( buf, level + 1, binding );
+
+ BufPrint( buf, level, "}\n" );
+ }
+
+ map = map->baseMap;
+ }
+}
+
+bool Panel::ParseKeyBindings( KeyValues *kv )
+{
+ Assert( IsValidKeyBindingsContext() );
+ if ( !IsValidKeyBindingsContext() )
+ return false;
+
+ // To have KB the panel must have a name
+ Assert( GetName() && GetName()[ 0 ] );
+ if ( !GetName() || !GetName()[ 0 ] )
+ return false;
+
+ bool success = false;
+
+ g_KBMgr.AddPanelToContext( GetKeyBindingsContext(), this );
+
+ RemoveAllKeyBindings();
+
+ // Walk through bindings
+ for ( KeyValues *binding = kv->GetFirstSubKey(); binding != NULL; binding = binding->GetNextKey() )
+ {
+ char const *bindingName = binding->GetName();
+ if ( !bindingName || !bindingName[ 0 ] )
+ continue;
+
+ KeyBindingMap_t *b = LookupBinding( bindingName );
+ if ( b )
+ {
+ success = true;
+ const char *keycode = binding->GetString( "keycode", "" );
+ int modifiers = 0;
+ if ( binding->GetInt( "shift", 0 ) != 0 )
+ {
+ modifiers |= MODIFIER_SHIFT;
+ }
+ if ( binding->GetInt( "ctrl", 0 ) != 0 )
+ {
+ modifiers |= MODIFIER_CONTROL;
+ }
+ if ( binding->GetInt( "alt", 0 ) != 0 )
+ {
+ modifiers |= MODIFIER_ALT;
+ }
+
+ KeyBindingMap_t *bound = LookupBindingByKeyCode( StringToKeyCode( keycode ), modifiers );
+ if ( !bound )
+ {
+ AddKeyBinding( bindingName, StringToKeyCode( keycode ), modifiers );
+ }
+ }
+ else
+ {
+ Warning( "KeyBinding for panel '%s' contained unknown binding '%s'\n", GetName() ? GetName() : "???", bindingName );
+ }
+ }
+
+ // Now for each binding which is currently "unbound" to any key, use the default binding
+ PanelKeyBindingMap *map = GetKBMap();
+ while( map )
+ {
+ int c = map->entries.Count();
+ for( int i = 0; i < c ; ++i )
+ {
+ KeyBindingMap_t *binding = &map->entries[ i ];
+
+ // See if there is a bound key
+ CUtlVector< BoundKey_t * > list;
+ LookupBoundKeys( binding->bindingname, list );
+ if ( list.Count() == 0 )
+ {
+ // Assign the default binding to this key
+ BoundKey_t *defaultKey = LookupDefaultKey( binding->bindingname );
+ if ( defaultKey )
+ {
+ KeyBindingMap_t *alreadyBound = LookupBindingByKeyCode( (KeyCode)defaultKey->keycode, defaultKey->modifiers );
+ if ( alreadyBound )
+ {
+ Warning( "No binding for '%s', defautl key already bound to '%s'\n", binding->bindingname, alreadyBound->bindingname );
+ }
+ else
+ {
+ AddKeyBinding( defaultKey->bindingname, defaultKey->keycode, defaultKey->modifiers );
+ }
+ }
+ }
+ }
+
+ map = map->baseMap;
+ }
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : handle -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void Panel::SetKeyBindingsContext( KeyBindingContextHandle_t handle )
+{
+ Assert( !IsValidKeyBindingsContext() || handle == GetKeyBindingsContext() );
+ g_KBMgr.AddPanelToContext( handle, this );
+ m_hKeyBindingsContext = handle;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : KeyBindingContextHandle_t
+//-----------------------------------------------------------------------------
+KeyBindingContextHandle_t Panel::GetKeyBindingsContext() const
+{
+ return m_hKeyBindingsContext;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Panel::IsValidKeyBindingsContext() const
+{
+ return GetKeyBindingsContext() != INVALID_KEYBINDINGCONTEXT_HANDLE;
+}
+
+char const *Panel::GetKeyBindingsFile() const
+{
+ Assert( IsValidKeyBindingsContext() );
+ return g_KBMgr.GetKeyBindingsFile( GetKeyBindingsContext() );
+}
+
+char const *Panel::GetKeyBindingsFilePathID() const
+{
+ Assert( IsValidKeyBindingsContext() );
+ return g_KBMgr.GetKeyBindingsFilePathID( GetKeyBindingsContext() );
+}
+
+void Panel::EditKeyBindings()
+{
+ Assert( 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Set this to false to disallow IsKeyRebound chaining to GetParent() Panels...
+// Input : state -
+//-----------------------------------------------------------------------------
+void Panel::SetAllowKeyBindingChainToParent( bool state )
+{
+ _flags.SetFlag( ALLOW_CHAIN_KEYBINDING_TO_PARENT, state );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Panel::IsKeyBindingChainToParentAllowed() const
+{
+ return _flags.IsFlagSet( ALLOW_CHAIN_KEYBINDING_TO_PARENT );
+}
+
+bool Panel::IsKeyOverridden( KeyCode code, int modifiers )
+{
+ // By default assume all keys should pass through binding system
+ return false;
+}
+
+bool Panel::IsKeyRebound( KeyCode code, int modifiers )
+{
+ if ( IsKeyBoardInputEnabled() )
+ {
+ KeyBindingMap_t* binding = LookupBindingByKeyCode( code, modifiers );
+ // Only dispatch if we're part of the current modal subtree
+ if ( binding && IsChildOfSurfaceModalPanel() )
+ {
+ // Found match, post message to panel
+ if ( binding->func )
+ {
+ // dispatch the func
+ (this->*binding->func)();
+ }
+ else
+ {
+ Assert( 0 );
+ }
+
+ if ( !binding->passive )
+ {
+ // Exit this function...
+ return true;
+ }
+ }
+ }
+
+ // Chain to parent
+ Panel* pParent = GetParent();
+ if ( IsKeyBindingChainToParentAllowed() && pParent && !IsKeyOverridden( code, modifiers ) )
+ return pParent->IsKeyRebound( code, modifiers );
+
+ // No suitable binding found
+ return false;
+}
+
+static bool s_bSuppressRebindChecks = false;
+#endif // VGUI_USEKEYBINDINGMAPS
+
+void Panel::InternalKeyCodeTyped( int code )
+{
+ if ( !ShouldHandleInputMessage() )
+ {
+ input()->OnKeyCodeUnhandled( code );
+ return;
+ }
+
+ if (IsKeyBoardInputEnabled())
+ {
+ bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+ bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+ bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
+
+ int modifiers = 0;
+ if ( shift )
+ {
+ modifiers |= MODIFIER_SHIFT;
+ }
+ if ( ctrl )
+ {
+ modifiers |= MODIFIER_CONTROL;
+ }
+ if ( alt )
+ {
+ modifiers |= MODIFIER_ALT;
+ }
+
+ // Things in build mode don't have accelerators
+ if (IsBuildGroupEnabled())
+ {
+ _buildGroup->KeyCodeTyped((KeyCode)code, this);
+ return;
+ }
+
+ if ( !s_bSuppressRebindChecks && IsKeyRebound( (KeyCode)code, modifiers ) )
+ {
+ return;
+ }
+
+ bool oldVal = s_bSuppressRebindChecks;
+ s_bSuppressRebindChecks = true;
+ OnKeyCodeTyped((KeyCode)code);
+ s_bSuppressRebindChecks = oldVal;
+ }
+ else
+ {
+ if ( GetVPanel() == surface()->GetEmbeddedPanel() )
+ {
+ input()->OnKeyCodeUnhandled( code );
+ }
+ CallParentFunction(new KeyValues("KeyCodeTyped", "code", code));
+ }
+}
+
+void Panel::InternalKeyTyped(int unichar)
+{
+ if ( !ShouldHandleInputMessage() )
+ return;
+
+ if (IsKeyBoardInputEnabled())
+ {
+ if ( IsBuildGroupEnabled() )
+ {
+ if ( _buildGroup->KeyTyped( (wchar_t)unichar, this ) )
+ {
+ return;
+ }
+ }
+
+ OnKeyTyped((wchar_t)unichar);
+ }
+ else
+ {
+ CallParentFunction(new KeyValues("KeyTyped", "unichar", unichar));
+ }
+}
+
+void Panel::InternalKeyCodeReleased(int code)
+{
+ if ( !ShouldHandleInputMessage() )
+ return;
+
+ if (IsKeyBoardInputEnabled())
+ {
+ if (IsBuildGroupEnabled())
+ {
+ if ( _buildGroup->KeyCodeReleased((KeyCode)code, this) )
+ {
+ return;
+ }
+ }
+
+ OnKeyCodeReleased((KeyCode)code);
+ }
+ else
+ {
+ CallParentFunction(new KeyValues("KeyCodeReleased", "code", code));
+ }
+}
+
+void Panel::InternalKeyFocusTicked()
+{
+ if (IsBuildGroupEnabled())
+ return;
+
+ OnKeyFocusTicked();
+}
+
+void Panel::InternalMouseFocusTicked()
+{
+ if (IsBuildGroupEnabled())
+ {
+ // must repaint so the numbers will be accurate
+ if (_buildGroup->HasRulersOn())
+ {
+ PaintTraverse(true);
+ }
+ return;
+ }
+
+ // update cursor
+ InternalSetCursor();
+ OnMouseFocusTicked();
+}
+
+
+void Panel::InternalSetCursor()
+{
+ bool visible = IsVisible();
+
+ if (visible)
+ {
+#if defined( VGUI_USEDRAGDROP )
+ // Drag drop is overriding cursor?
+ if ( m_pDragDrop->m_bDragging ||
+ g_DragDropCapture.Get() != NULL )
+ return;
+#endif
+ // chain up and make sure all our parents are also visible
+ VPANEL p = GetVParent();
+ while (p)
+ {
+ visible &= ipanel()->IsVisible(p);
+ p = ipanel()->GetParent(p);
+ }
+
+ // only change the cursor if this panel is visible, and if its part of the main VGUI tree
+ if (visible && HasParent(surface()->GetEmbeddedPanel()))
+ {
+ HCursor cursor = GetCursor();
+
+ if (IsBuildGroupEnabled())
+ {
+ cursor = _buildGroup->GetCursor(this);
+ }
+
+ if (input()->GetCursorOveride())
+ {
+ cursor = input()->GetCursorOveride();
+ }
+
+ surface()->SetCursor(cursor);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called every frame the panel is visible, designed to be overridden
+//-----------------------------------------------------------------------------
+void Panel::OnThink()
+{
+#if defined( VGUI_USEDRAGDROP )
+ if ( IsPC() &&
+ m_pDragDrop->m_bDragEnabled &&
+ m_pDragDrop->m_bDragging &&
+ m_pDragDrop->m_bDragStarted )
+ {
+ bool isEscapeKeyDown = input()->IsKeyDown( KEY_ESCAPE );
+ if ( isEscapeKeyDown )
+ {
+ OnContinueDragging();
+ OnFinishDragging( true, (MouseCode)-1, true );
+ return;
+ }
+
+ if ( m_pDragDrop->m_hCurrentDrop != 0 )
+ {
+ if ( !input()->IsMouseDown( MOUSE_LEFT ) )
+ {
+ OnContinueDragging();
+ OnFinishDragging( true, (MouseCode)-1 );
+ return;
+ }
+
+ // allow the cursor to change based upon things like changing keystate, etc.
+ surface()->SetCursor( m_pDragDrop->m_hCurrentDrop->GetDropCursor( m_pDragDrop->m_DragData ) );
+
+ if ( !m_pDragDrop->m_bDropMenuShown )
+ {
+ // See if the hover time has gotten larger
+ float hoverSeconds = ( system()->GetTimeMillis() - m_pDragDrop->m_lDropHoverTime ) * 0.001f;
+ DragDrop_t *dropInfo = m_pDragDrop->m_hCurrentDrop->GetDragDropInfo();
+
+ if ( dropInfo->m_flHoverContextTime != 0.0f )
+ {
+ if ( hoverSeconds >= dropInfo->m_flHoverContextTime )
+ {
+ m_pDragDrop->m_bDropMenuShown = true;
+
+ CUtlVector< KeyValues * > data;
+
+ GetDragData( data );
+
+ int x, y;
+ input()->GetCursorPos( x, y );
+
+ if ( m_pDragDrop->m_hDropContextMenu.Get() )
+ {
+ delete m_pDragDrop->m_hDropContextMenu.Get();
+ }
+
+ Menu *menu = new Menu( m_pDragDrop->m_hCurrentDrop.Get(), "DropContext" );
+
+ bool useMenu = m_pDragDrop->m_hCurrentDrop->GetDropContextMenu( menu, data );
+ if ( useMenu )
+ {
+ m_pDragDrop->m_hDropContextMenu = menu;
+
+ menu->SetPos( x, y );
+ menu->SetVisible( true );
+ menu->MakePopup();
+ surface()->MovePopupToFront( menu->GetVPanel() );
+ if ( menu->GetItemCount() > 0 )
+ {
+ int id = menu->GetMenuID( 0 );
+ menu->SetCurrentlyHighlightedItem( id );
+ MenuItem *item = menu->GetMenuItem( id );
+ item->SetArmed( true );
+ }
+ }
+ else
+ {
+ delete menu;
+ }
+
+ m_pDragDrop->m_hCurrentDrop->OnDropContextHoverShow( data );
+ }
+ }
+ }
+ }
+ }
+#endif
+}
+
+// input messages handlers (designed for override)
+void Panel::OnCursorMoved(int x, int y)
+{
+ if( ParentNeedsCursorMoveEvents() )
+ {
+ // figure out x and y in parent space
+ int thisX, thisY;
+ ipanel()->GetPos( GetVPanel(), thisX, thisY );
+ CallParentFunction( new KeyValues( "OnCursorMoved", "x", x + thisX, "y", y + thisY ) );
+ }
+}
+
+void Panel::OnCursorEntered()
+{
+}
+
+void Panel::OnCursorExited()
+{
+}
+
+void Panel::OnMousePressed(MouseCode code)
+{
+}
+
+void Panel::OnMouseDoublePressed(MouseCode code)
+{
+}
+
+void Panel::OnMouseTriplePressed(MouseCode code)
+{
+}
+
+void Panel::OnMouseReleased(MouseCode code)
+{
+}
+
+void Panel::OnMouseWheeled(int delta)
+{
+ CallParentFunction(new KeyValues("MouseWheeled", "delta", delta));
+}
+
+// base implementation forwards Key messages to the Panel's parent - override to 'swallow' the input
+void Panel::OnKeyCodePressed(KeyCode code)
+{
+ static ConVarRef vgui_nav_lock( "vgui_nav_lock" );
+
+ bool handled = false;
+ switch( GetBaseButtonCode( code ) )
+ {
+ case KEY_XBUTTON_UP:
+ case KEY_XSTICK1_UP:
+ case KEY_XSTICK2_UP:
+ case KEY_UP:
+ if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateUp() )
+ {
+ vgui_nav_lock.SetValue( 1 );
+ vgui::surface()->PlaySound( "UI/menu_focus.wav" );
+ handled = true;
+ }
+ break;
+ case KEY_XBUTTON_DOWN:
+ case KEY_XSTICK1_DOWN:
+ case KEY_XSTICK2_DOWN:
+ case KEY_DOWN:
+ if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateDown() )
+ {
+ vgui_nav_lock.SetValue( 1 );
+ vgui::surface()->PlaySound( "UI/menu_focus.wav" );
+ handled = true;
+ }
+ break;
+ case KEY_XBUTTON_LEFT:
+ case KEY_XSTICK1_LEFT:
+ case KEY_XSTICK2_LEFT:
+ case KEY_LEFT:
+ if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateLeft() )
+ {
+ vgui_nav_lock.SetValue( 1 );
+ vgui::surface()->PlaySound( "UI/menu_focus.wav" );
+ handled = true;
+ }
+ break;
+ case KEY_XBUTTON_RIGHT:
+ case KEY_XSTICK1_RIGHT:
+ case KEY_XSTICK2_RIGHT:
+ case KEY_RIGHT:
+ if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateRight() )
+ {
+ vgui_nav_lock.SetValue( 1 );
+ vgui::surface()->PlaySound( "UI/menu_focus.wav" );
+ handled = true;
+ }
+ break;
+ case KEY_XBUTTON_B:
+ if ( ( !vgui_nav_lock.IsValid() || vgui_nav_lock.GetInt() == 0 ) && NavigateBack() )
+ {
+ vgui_nav_lock.SetValue( 1 );
+ vgui::surface()->PlaySound( "UI/menu_focus.wav" );
+ handled = true;
+ }
+ break;
+ }
+
+ if( !handled && !m_PassUnhandledInput )
+ return;
+
+ CallParentFunction(new KeyValues("KeyCodePressed", "code", code));
+}
+
+void Panel::OnKeyCodeTyped(KeyCode keycode)
+{
+ vgui::KeyCode code = GetBaseButtonCode( keycode );
+
+ // handle focus change
+ if ( IsX360() || IsConsoleStylePanel() )
+ {
+ // eat these typed codes, will get handled in OnKeyCodePressed
+ switch ( code )
+ {
+ case KEY_XBUTTON_UP:
+ case KEY_XSTICK1_UP:
+ case KEY_XSTICK2_UP:
+ case KEY_XBUTTON_DOWN:
+ case KEY_XSTICK1_DOWN:
+ case KEY_XSTICK2_DOWN:
+ case KEY_XBUTTON_LEFT:
+ case KEY_XSTICK1_LEFT:
+ case KEY_XSTICK2_LEFT:
+ case KEY_XBUTTON_RIGHT:
+ case KEY_XSTICK1_RIGHT:
+ case KEY_XSTICK2_RIGHT:
+ case KEY_XBUTTON_A:
+ case KEY_XBUTTON_B:
+ case KEY_XBUTTON_X:
+ case KEY_XBUTTON_Y:
+ case KEY_XBUTTON_LEFT_SHOULDER:
+ case KEY_XBUTTON_RIGHT_SHOULDER:
+ case KEY_XBUTTON_BACK:
+ case KEY_XBUTTON_START:
+ case KEY_XBUTTON_STICK1:
+ case KEY_XBUTTON_STICK2:
+ case KEY_XBUTTON_LTRIGGER:
+ case KEY_XBUTTON_RTRIGGER:
+
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ return;
+ }
+
+ // legacy handling - need to re-enable for older apps?
+ /*
+ if ( code == KEY_XSTICK1_RIGHT || code == KEY_XBUTTON_RIGHT )
+ {
+ RequestFocusNext();
+ return;
+ }
+ else if ( code == KEY_XSTICK1_LEFT || code == KEY_XBUTTON_LEFT )
+ {
+ RequestFocusPrev();
+ return;
+ }
+ */
+ }
+
+ if (code == KEY_TAB)
+ {
+ bool bShiftDown = input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT);
+
+ if ( IsConsoleStylePanel() )
+ {
+ if ( bShiftDown )
+ {
+ NavigateUp();
+ }
+ else
+ {
+ NavigateDown();
+ }
+ }
+ else
+ {
+ // if shift is down goto previous tab position, otherwise goto next
+ if ( bShiftDown )
+ {
+ RequestFocusPrev();
+ }
+ else
+ {
+ RequestFocusNext();
+ }
+ }
+ }
+ else
+ {
+ // forward up
+ if ( GetVPanel() == surface()->GetEmbeddedPanel() )
+ {
+ input()->OnKeyCodeUnhandled( keycode );
+ }
+ CallParentFunction(new KeyValues("KeyCodeTyped", "code", keycode));
+ }
+}
+
+void Panel::OnKeyTyped(wchar_t unichar)
+{
+ CallParentFunction(new KeyValues("KeyTyped", "unichar", unichar));
+}
+
+void Panel::OnKeyCodeReleased(KeyCode code)
+{
+ CallParentFunction(new KeyValues("KeyCodeReleased", "code", code));
+}
+
+void Panel::OnKeyFocusTicked()
+{
+ CallParentFunction(new KeyValues("KeyFocusTicked"));
+}
+
+void Panel::OnMouseFocusTicked()
+{
+ CallParentFunction(new KeyValues("OnMouseFocusTicked"));
+}
+
+bool Panel::IsWithin(int x,int y)
+{
+ // check against our clip rect
+ int clipRect[4];
+ ipanel()->GetClipRect(GetVPanel(), clipRect[0], clipRect[1], clipRect[2], clipRect[3]);
+
+ if (x < clipRect[0])
+ {
+ return false;
+ }
+
+ if (y < clipRect[1])
+ {
+ return false;
+ }
+
+ if (x >= clipRect[2])
+ {
+ return false;
+ }
+
+ if (y >= clipRect[3])
+ {
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: determines which is the topmost panel under the coordinates (x, y)
+//-----------------------------------------------------------------------------
+VPANEL Panel::IsWithinTraverse(int x, int y, bool traversePopups)
+{
+ // if this one is not visible, its children won't be either
+ // also if it doesn't want mouse input its children can't get it either
+ if (!IsVisible() || !IsMouseInputEnabled())
+ return NULL;
+
+ if (traversePopups)
+ {
+ // check popups first
+ int i;
+ CUtlVector< VPANEL > &children = ipanel()->GetChildren( GetVPanel() );
+ int childCount = children.Count();
+ for (i = childCount - 1; i >= 0; i--)
+ {
+ VPANEL panel = children[ i ];
+ if (ipanel()->IsPopup(panel))
+ {
+ panel = ipanel()->IsWithinTraverse(panel, x, y, true);
+ if (panel != null)
+ {
+ return panel;
+ }
+ }
+ }
+
+ // check children recursive, if you find one, just return first one
+ // this checks in backwards order so the last child drawn for this panel is chosen which
+ // coincides to how it would be visibly displayed
+ for (i = childCount - 1; i >= 0; i--)
+ {
+ VPANEL panel = children[ i ];
+ // we've already checked popups so ignore
+ if (!ipanel()->IsPopup(panel))
+ {
+ panel = ipanel()->IsWithinTraverse(panel, x, y, true);
+ if (panel != 0)
+ {
+ return panel;
+ }
+ }
+ }
+
+ // check ourself
+ if ( !IsMouseInputDisabledForThisPanel() && IsWithin(x, y) )
+ {
+ return GetVPanel();
+ }
+ }
+ else
+ {
+ // since we're not checking popups, it must be within us, so we can check ourself first
+ if (IsWithin(x, y))
+ {
+ // check children recursive, if you find one, just return first one
+ // this checks in backwards order so the last child drawn for this panel is chosen which
+ // coincides to how it would be visibly displayed
+ CUtlVector< VPANEL > &children = ipanel()->GetChildren( GetVPanel() );
+ int childCount = children.Count();
+ for (int i = childCount - 1; i >= 0; i--)
+ {
+ VPANEL panel = children[ i ];
+ // ignore popups
+ if (!ipanel()->IsPopup(panel))
+ {
+ panel = ipanel()->IsWithinTraverse(panel, x, y, false);
+ if (panel != 0)
+ {
+ return panel;
+ }
+ }
+ }
+
+ // not a child, must be us
+ if ( !IsMouseInputDisabledForThisPanel() )
+ return GetVPanel();
+ }
+ }
+
+ return NULL;
+}
+
+void Panel::LocalToScreen(int& x,int& y)
+{
+ int px, py;
+ ipanel()->GetAbsPos(GetVPanel(), px, py);
+
+ x = x + px;
+ y = y + py;
+}
+
+void Panel::ScreenToLocal(int& x,int& y)
+{
+ int px, py;
+ ipanel()->GetAbsPos(GetVPanel(), px, py);
+
+ x = x - px;
+ y = y - py;
+}
+
+void Panel::ParentLocalToScreen(int &x, int &y)
+{
+ int px, py;
+ ipanel()->GetAbsPos(GetVParent(), px, py);
+
+ x = x + px;
+ y = y + py;
+}
+
+void Panel::MakePopup(bool showTaskbarIcon,bool disabled)
+{
+ surface()->CreatePopup(GetVPanel(), false, showTaskbarIcon,disabled);
+}
+
+void Panel::SetCursor(HCursor cursor)
+{
+ _cursor = cursor;
+}
+
+HCursor Panel::GetCursor()
+{
+ return _cursor;
+}
+
+void Panel::SetCursorAlwaysVisible( bool visible )
+{
+ surface()->SetCursorAlwaysVisible( visible );
+}
+
+void Panel::SetMinimumSize(int wide,int tall)
+{
+ ipanel()->SetMinimumSize(GetVPanel(), wide, tall);
+}
+
+void Panel::GetMinimumSize(int& wide,int &tall)
+{
+ ipanel()->GetMinimumSize(GetVPanel(), wide, tall);
+}
+
+bool Panel::IsBuildModeEditable()
+{
+ return true;
+}
+
+void Panel::SetBuildModeEditable(bool state)
+{
+ if (state)
+ {
+ _buildModeFlags |= BUILDMODE_EDITABLE;
+ }
+ else
+ {
+ _buildModeFlags &= ~BUILDMODE_EDITABLE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+bool Panel::IsBuildModeDeletable()
+{
+ return (_buildModeFlags & BUILDMODE_DELETABLE);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void Panel::SetBuildModeDeletable(bool state)
+{
+ if (state)
+ {
+ _buildModeFlags |= BUILDMODE_DELETABLE;
+ }
+ else
+ {
+ _buildModeFlags &= ~BUILDMODE_DELETABLE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool Panel::IsBuildModeActive()
+{
+ return _buildGroup ? _buildGroup->IsEnabled() : false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::GetClipRect(int& x0,int& y0,int& x1,int& y1)
+{
+ ipanel()->GetClipRect(GetVPanel(), x0, y0, x1, y1);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int Panel::GetChildCount()
+{
+ if (ipanel())
+ {
+ return ipanel()->GetChildCount(GetVPanel());
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns a child by the specified index
+//-----------------------------------------------------------------------------
+Panel *Panel::GetChild(int index)
+{
+ // get the child and cast it to a panel
+ // this assumes that the child is from the same module as the this (precondition)
+ return ipanel()->GetPanel(ipanel()->GetChild(GetVPanel(), index), GetControlsModuleName());
+}
+
+CUtlVector< VPANEL > &Panel::GetChildren()
+{
+ return ipanel()->GetChildren(GetVPanel());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: moves the key focus back
+//-----------------------------------------------------------------------------
+bool Panel::RequestFocusPrev(VPANEL panel)
+{
+ // chain to parent
+ if (GetVParent())
+ {
+ return ipanel()->RequestFocusPrev(GetVParent(), GetVPanel());
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool Panel::RequestFocusNext(VPANEL panel)
+{
+ // chain to parent
+ if (GetVParent())
+ {
+ return ipanel()->RequestFocusNext(GetVParent(), GetVPanel());
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the panel to have the current sub focus
+// Input : direction - the direction in which focus travelled to arrive at this panel; forward = 1, back = -1
+//-----------------------------------------------------------------------------
+void Panel::RequestFocus(int direction)
+{
+ // NOTE: This doesn't make any sense if we don't have keyboard input enabled
+ Assert( ( IsX360() || IsConsoleStylePanel() ) || IsKeyBoardInputEnabled() );
+ // ivgui()->DPrintf2("RequestFocus(%s, %s)\n", GetName(), GetClassName());
+ OnRequestFocus(GetVPanel(), NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called after a panel requests focus to fix up the whole chain
+//-----------------------------------------------------------------------------
+void Panel::OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel)
+{
+ CallParentFunction(new KeyValues("OnRequestFocus", "subFocus", subFocus, "defaultPanel", defaultPanel));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+VPANEL Panel::GetCurrentKeyFocus()
+{
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the panel has focus
+//-----------------------------------------------------------------------------
+bool Panel::HasFocus()
+{
+ if (input()->GetFocus() == GetVPanel())
+ {
+ return true;
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetTabPosition(int position)
+{
+ _tabPosition = position;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int Panel::GetTabPosition()
+{
+ return _tabPosition;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::InternalFocusChanged(bool lost)
+{
+ /*
+ //if focus is gained tell the focusNavGroup about it so its current can be correct
+ if( (!lost) && (_focusNavGroup!=null) )
+ {
+ _focusNavGroup->setCurrentPanel(this);
+ }
+ */
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when a panel loses it's mouse capture
+//-----------------------------------------------------------------------------
+void Panel::OnMouseCaptureLost()
+{
+ if (m_pTooltips)
+ {
+ m_pTooltips->ResetDelay();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::AddActionSignalTarget(Panel *messageTarget)
+{
+ HPanel target = ivgui()->PanelToHandle(messageTarget->GetVPanel());
+ if (!_actionSignalTargetDar.HasElement(target))
+ {
+ _actionSignalTargetDar.AddElement(target);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::AddActionSignalTarget(VPANEL messageTarget)
+{
+ HPanel target = ivgui()->PanelToHandle(messageTarget);
+ if (!_actionSignalTargetDar.HasElement(target))
+ {
+ _actionSignalTargetDar.AddElement(target);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::RemoveActionSignalTarget(Panel *oldTarget)
+{
+ _actionSignalTargetDar.RemoveElement(ivgui()->PanelToHandle(oldTarget->GetVPanel()));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sends a message to all the panels that have requested action signals
+//-----------------------------------------------------------------------------
+void Panel::PostActionSignal( KeyValues *message )
+{
+ if ( m_bIsSilent != true )
+ {
+ // add who it was from the message
+ message->SetPtr("panel", this);
+ int i;
+ for (i = _actionSignalTargetDar.GetCount() - 1; i > 0; i--)
+ {
+ VPANEL panel = ivgui()->HandleToPanel(_actionSignalTargetDar[i]);
+ if (panel)
+ {
+ ivgui()->PostMessage(panel, message->MakeCopy(), GetVPanel());
+ }
+ }
+
+ // do this so we can save on one MakeCopy() call
+ if (i == 0)
+ {
+ VPANEL panel = ivgui()->HandleToPanel(_actionSignalTargetDar[i]);
+ if (panel)
+ {
+ ivgui()->PostMessage(panel, message, GetVPanel());
+ return;
+ }
+ }
+ }
+ message->deleteThis();
+}
+
+void Panel::SetBorder(IBorder *border)
+{
+ _border = border;
+
+ if (border)
+ {
+ int x, y, x2, y2;
+ border->GetInset(x, y, x2, y2);
+ ipanel()->SetInset(GetVPanel(), x, y, x2, y2);
+
+ // update our background type based on the bord
+ SetPaintBackgroundType(border->GetBackgroundType());
+ }
+ else
+ {
+ ipanel()->SetInset(GetVPanel(), 0, 0, 0, 0);
+ }
+}
+
+IBorder *Panel::GetBorder()
+{
+ return _border;
+}
+
+
+void Panel::SetPaintBorderEnabled(bool state)
+{
+ _flags.SetFlag( PAINT_BORDER_ENABLED, state );
+}
+
+void Panel::SetPaintBackgroundEnabled(bool state)
+{
+ _flags.SetFlag( PAINT_BACKGROUND_ENABLED, state );
+}
+
+void Panel::SetPaintBackgroundType( int type )
+{
+ // HACK only 0 through 2 supported for now
+ m_nPaintBackgroundType = clamp( type, 0, 2 );
+}
+
+void Panel::SetPaintEnabled(bool state)
+{
+ _flags.SetFlag( PAINT_ENABLED, state );
+}
+
+void Panel::SetPostChildPaintEnabled(bool state)
+{
+ _flags.SetFlag( POST_CHILD_PAINT_ENABLED, state );
+}
+
+void Panel::GetInset(int& left,int& top,int& right,int& bottom)
+{
+ ipanel()->GetInset(GetVPanel(), left, top, right, bottom);
+}
+
+void Panel::GetPaintSize(int& wide,int& tall)
+{
+ GetSize(wide, tall);
+ if (_border != null)
+ {
+ int left,top,right,bottom;
+ _border->GetInset(left,top,right,bottom);
+
+ wide -= (left+right);
+ tall -= (top+bottom);
+ }
+}
+
+int Panel::GetWide()
+{
+ int wide, tall;
+ ipanel()->GetSize(GetVPanel(), wide, tall);
+ return wide;
+}
+
+void Panel::SetWide(int wide)
+{
+ ipanel()->SetSize(GetVPanel(), wide, GetTall());
+}
+
+int Panel::GetTall()
+{
+ int wide, tall;
+ ipanel()->GetSize(GetVPanel(), wide, tall);
+ return tall;
+}
+
+void Panel::SetTall(int tall)
+{
+ ipanel()->SetSize(GetVPanel(), GetWide(), tall);
+}
+
+void Panel::SetBuildGroup(BuildGroup* buildGroup)
+{
+ //TODO: remove from old group
+
+ Assert(buildGroup != NULL);
+
+ _buildGroup = buildGroup;
+
+ _buildGroup->PanelAdded(this);
+}
+
+bool Panel::IsBuildGroupEnabled()
+{
+ if ( !_buildGroup.IsValid() )
+ return false;
+
+ bool enabled = _buildGroup->IsEnabled();
+ if ( enabled )
+ return enabled;
+
+ if ( GetParent() && GetParent()->IsBuildGroupEnabled() )
+ return true;
+
+ return false;
+}
+
+void Panel::SetBgColor(Color color)
+{
+ _bgColor = color;
+}
+
+void Panel::SetFgColor(Color color)
+{
+ _fgColor = color;
+}
+
+Color Panel::GetBgColor()
+{
+ return _bgColor;
+}
+
+Color Panel::GetFgColor()
+{
+ return _fgColor;
+}
+
+void Panel::InternalPerformLayout()
+{
+ // Don't layout if we're still waiting for our scheme to be applied.
+ // At worst, it leads to crashes, at best it does work that we'll redo as soon as the scheme has been applied.
+ if ( _flags.IsFlagSet( NEEDS_SCHEME_UPDATE ) )
+ return;
+
+ _flags.SetFlag( IN_PERFORM_LAYOUT );
+ // make sure the scheme has been applied
+ _flags.ClearFlag( NEEDS_LAYOUT );
+ PerformLayout();
+ _flags.ClearFlag( IN_PERFORM_LAYOUT );
+}
+
+void Panel::PerformLayout()
+{
+ // this should be overridden to relayout controls
+}
+
+void Panel::InvalidateLayout( bool layoutNow, bool reloadScheme )
+{
+ _flags.SetFlag( NEEDS_LAYOUT );
+
+ if (reloadScheme)
+ {
+ // make all our children reload the scheme
+ _flags.SetFlag( NEEDS_SCHEME_UPDATE );
+
+ for (int i = 0; i < GetChildCount(); i++)
+ {
+ vgui::Panel* panel = GetChild(i);
+ if( panel )
+ {
+ panel->InvalidateLayout(layoutNow, true);
+ }
+ }
+
+ PerformApplySchemeSettings();
+ }
+
+ if (layoutNow)
+ {
+ InternalPerformLayout();
+ Repaint();
+ }
+}
+
+bool Panel::IsCursorNone()
+{
+ HCursor cursor = GetCursor();
+
+ if (!cursor)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the cursor is currently over the panel
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Panel::IsCursorOver(void)
+{
+ int x, y;
+ input()->GetCursorPos(x, y);
+ return IsWithin(x, y);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when a panel receives a command message from another panel
+//-----------------------------------------------------------------------------
+void Panel::OnCommand(const char *command)
+{
+ if ( !Q_stricmp( "performlayout", command ) )
+ {
+ InvalidateLayout();
+ }
+ else if ( !Q_stricmp( "reloadscheme", command ) )
+ {
+ InvalidateLayout( false, true );
+ }
+ else
+ {
+ // if noone else caught this, pass along to the listeners
+ // (this is useful for generic dialogs - otherwise, commands just get ignored)
+ KeyValues *msg = new KeyValues( command );
+ PostActionSignal( msg );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: panel gained focus message
+//-----------------------------------------------------------------------------
+void Panel::OnSetFocus()
+{
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: panel lost focus message
+//-----------------------------------------------------------------------------
+void Panel::OnKillFocus()
+{
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the object up to be deleted next frame
+//-----------------------------------------------------------------------------
+void Panel::MarkForDeletion()
+{
+ if ( _flags.IsFlagSet( MARKED_FOR_DELETION ) )
+ return;
+
+ _flags.SetFlag( MARKED_FOR_DELETION );
+ _flags.ClearFlag( AUTODELETE_ENABLED );
+
+ if (ivgui()->IsRunning())
+ {
+ ivgui()->MarkPanelForDeletion(GetVPanel());
+ }
+ // direct delete is never safe because even if ivgui is shutdown we manually do RunFrame()
+ // and we can enter here in a think traverse and then delete from underneath ourselves
+ /*else
+ {
+ delete this;
+ }*/
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: return true if this object require a perform layout
+//-----------------------------------------------------------------------------
+bool Panel::IsLayoutInvalid()
+{
+ return _flags.IsFlagSet( NEEDS_LAYOUT );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the pin corner + resize mode for resizing panels
+//-----------------------------------------------------------------------------
+void Panel::SetAutoResize( PinCorner_e pinCorner, AutoResize_e resizeDir,
+ int nPinOffsetX, int nPinOffsetY, int nUnpinnedCornerOffsetX, int nUnpinnedCornerOffsetY )
+{
+ _pinCorner = pinCorner;
+ _autoResizeDirection = resizeDir;
+ m_nPinDeltaX = nPinOffsetX;
+ m_nPinDeltaY = nPinOffsetY;
+ m_nResizeDeltaX = nUnpinnedCornerOffsetX;
+ m_nResizeDeltaY = nUnpinnedCornerOffsetY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the pin corner for non-resizing panels
+//-----------------------------------------------------------------------------
+void Panel::SetPinCorner( PinCorner_e pinCorner, int nOffsetX, int nOffsetY )
+{
+ _pinCorner = pinCorner;
+ _autoResizeDirection = AUTORESIZE_NO;
+ m_nPinDeltaX = nOffsetX;
+ m_nPinDeltaY = nOffsetY;
+ m_nResizeDeltaX = 0;
+ m_nResizeDeltaY = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+Panel::PinCorner_e Panel::GetPinCorner()
+{
+ return (PinCorner_e)_pinCorner;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the relative offset of the control from the pin corner
+//-----------------------------------------------------------------------------
+void Panel::GetPinOffset( int &dx, int &dy )
+{
+ dx = m_nPinDeltaX;
+ dy = m_nPinDeltaY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+Panel::AutoResize_e Panel::GetAutoResize()
+{
+ return (AutoResize_e)_autoResizeDirection;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the relative offset of the control from the pin corner
+//-----------------------------------------------------------------------------
+void Panel::GetResizeOffset( int &dx, int &dy )
+{
+ dx = m_nResizeDeltaX;
+ dy = m_nResizeDeltaY;
+}
+
+//-----------------------------------------------------------------------------
+// Tells this panel that it should pin itself to the corner of a specified sibling panel
+//-----------------------------------------------------------------------------
+void Panel::PinToSibling( const char *pszSibling, PinCorner_e pinOurCorner, PinCorner_e pinSibling )
+{
+ _pinCornerToSibling = pinOurCorner;
+ _pinToSiblingCorner = pinSibling;
+
+ if ( _pinToSibling && pszSibling && !Q_strcmp( _pinToSibling, pszSibling ) )
+ return;
+
+ if (_pinToSibling)
+ {
+ delete [] _pinToSibling;
+ _pinToSibling = NULL;
+ }
+
+ if (pszSibling)
+ {
+ int len = Q_strlen(pszSibling) + 1;
+ _pinToSibling = new char[ len ];
+ Q_strncpy( _pinToSibling, pszSibling, len );
+ }
+ m_pinSibling = NULL;
+
+ UpdateSiblingPin();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::UpdateSiblingPin( void )
+{
+ if ( !_pinToSibling )
+ {
+ ipanel()->SetSiblingPin(GetVPanel(), NULL);
+ return;
+ }
+
+ if ( !m_pinSibling.Get() )
+ {
+ // Resolve our sibling now
+ m_pinSibling = FindSiblingByName( _pinToSibling );
+ }
+
+ if ( m_pinSibling.Get() )
+ {
+ ipanel()->SetSiblingPin( GetVPanel(), m_pinSibling->GetVPanel(), _pinCornerToSibling, _pinToSiblingCorner );
+ }
+ else
+ {
+ ipanel()->SetSiblingPin(GetVPanel(), NULL);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::ApplySchemeSettings(IScheme *pScheme)
+{
+ // get colors
+ SetFgColor(GetSchemeColor("Panel.FgColor", pScheme));
+ SetBgColor(GetSchemeColor("Panel.BgColor", pScheme));
+
+#if defined( VGUI_USEDRAGDROP )
+ m_clrDragFrame = pScheme->GetColor("DragDrop.DragFrame", Color(255, 255, 255, 192));
+ m_clrDropFrame = pScheme->GetColor("DragDrop.DropFrame", Color(150, 255, 150, 255));
+
+ m_infoFont = pScheme->GetFont( "DefaultVerySmall" );
+#endif
+ // mark us as no longer needing scheme settings applied
+ _flags.ClearFlag( NEEDS_SCHEME_UPDATE );
+
+ if ( IsBuildGroupEnabled() )
+ {
+ _buildGroup->ApplySchemeSettings(pScheme);
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks to see if the panel needs it's scheme info setup
+//-----------------------------------------------------------------------------
+void Panel::PerformApplySchemeSettings()
+{
+ if ( _flags.IsFlagSet( NEEDS_DEFAULT_SETTINGS_APPLIED ) )
+ {
+ InternalInitDefaultValues( GetAnimMap() );
+ }
+
+ if ( _flags.IsFlagSet( NEEDS_SCHEME_UPDATE ) )
+ {
+ VPROF( "ApplySchemeSettings" );
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+ AssertOnce( pScheme );
+ if ( pScheme ) // this should NEVER be null, but if it is bad things would happen in ApplySchemeSettings...
+ {
+ ApplySchemeSettings( pScheme );
+ //_needsSchemeUpdate = false;
+
+ ApplyOverridableColors();
+
+ UpdateSiblingPin();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads panel details related to autoresize from the resource info
+//-----------------------------------------------------------------------------
+#if defined( _DEBUG )
+static Panel *lastWarningParent = 0;
+#endif
+
+void Panel::ApplyAutoResizeSettings(KeyValues *inResourceData)
+{
+ int x, y;
+ GetPos(x, y);
+
+ int wide, tall;
+ GetSize( wide, tall );
+
+ AutoResize_e autoResize = (AutoResize_e)inResourceData->GetInt( "AutoResize", AUTORESIZE_NO );
+ PinCorner_e pinCorner = (PinCorner_e)inResourceData->GetInt( "PinCorner", PIN_TOPLEFT );
+
+ // By default, measure unpinned corner for the offset
+ int pw = wide, pt = tall;
+ if ( GetParent() )
+ {
+ GetParent()->GetSize( pw, pt );
+#if defined( _DEBUG )
+ if ( pw == 64 && pt == 24 )
+ {
+ if ( GetParent() != lastWarningParent )
+ {
+ lastWarningParent = GetParent();
+ Warning( "Resize parent (panel(%s) -> parent(%s)) not sized yet!!!\n", GetName(), GetParent()->GetName() );
+ }
+ }
+#endif
+ }
+
+ int nPinnedCornerOffsetX = 0, nPinnedCornerOffsetY = 0;
+ int nUnpinnedCornerOffsetX = 0, nUnpinnedCornerOffsetY = 0;
+ switch( pinCorner )
+ {
+ case PIN_TOPLEFT:
+ nPinnedCornerOffsetX = x;
+ nPinnedCornerOffsetY = y;
+ nUnpinnedCornerOffsetX = (x + wide) - pw;
+ nUnpinnedCornerOffsetY = (y + tall) - pt;
+ break;
+
+ case PIN_TOPRIGHT:
+ nPinnedCornerOffsetX = (x + wide) - pw;
+ nPinnedCornerOffsetY = y;
+ nUnpinnedCornerOffsetX = x;
+ nUnpinnedCornerOffsetY = (y + tall) - pt;
+ break;
+
+ case PIN_BOTTOMLEFT:
+ nPinnedCornerOffsetX = x;
+ nPinnedCornerOffsetY = (y + tall) - pt;
+ nUnpinnedCornerOffsetX = (x + wide) - pw;
+ nUnpinnedCornerOffsetY = y;
+ break;
+
+ case PIN_BOTTOMRIGHT:
+ nPinnedCornerOffsetX = (x + wide) - pw;
+ nPinnedCornerOffsetY = (y + tall) - pt;
+ nUnpinnedCornerOffsetX = x;
+ nUnpinnedCornerOffsetY = y;
+ break;
+ }
+
+ // Allow specific overrides in the resource file
+ if ( IsProportional() )
+ {
+ if ( inResourceData->FindKey( "PinnedCornerOffsetX" ) )
+ {
+ nPinnedCornerOffsetX = scheme()->GetProportionalScaledValueEx( GetScheme(), inResourceData->GetInt( "PinnedCornerOffsetX" ) );
+ }
+ if ( inResourceData->FindKey( "PinnedCornerOffsetY" ) )
+ {
+ nPinnedCornerOffsetY = scheme()->GetProportionalScaledValueEx( GetScheme(), inResourceData->GetInt( "PinnedCornerOffsetY" ) );
+ }
+ if ( inResourceData->FindKey( "UnpinnedCornerOffsetX" ) )
+ {
+ nUnpinnedCornerOffsetX = scheme()->GetProportionalScaledValueEx( GetScheme(), inResourceData->GetInt( "UnpinnedCornerOffsetX" ) );
+ }
+ if ( inResourceData->FindKey( "UnpinnedCornerOffsetY" ) )
+ {
+ nUnpinnedCornerOffsetY = scheme()->GetProportionalScaledValueEx( GetScheme(), inResourceData->GetInt( "UnpinnedCornerOffsetY" ) );
+ }
+ }
+ else
+ {
+ nPinnedCornerOffsetX = inResourceData->GetInt( "PinnedCornerOffsetX", nPinnedCornerOffsetX );
+ nPinnedCornerOffsetY = inResourceData->GetInt( "PinnedCornerOffsetY", nPinnedCornerOffsetY );
+ nUnpinnedCornerOffsetX = inResourceData->GetInt( "UnpinnedCornerOffsetX", nUnpinnedCornerOffsetX );
+ nUnpinnedCornerOffsetY = inResourceData->GetInt( "UnpinnedCornerOffsetY", nUnpinnedCornerOffsetY );
+ }
+
+ if ( autoResize == AUTORESIZE_NO )
+ {
+ nUnpinnedCornerOffsetX = nUnpinnedCornerOffsetY = 0;
+ }
+
+ SetAutoResize( pinCorner, autoResize, nPinnedCornerOffsetX, nPinnedCornerOffsetY, nUnpinnedCornerOffsetX, nUnpinnedCornerOffsetY );
+}
+
+ConVar panel_test_title_safe( "panel_test_title_safe", "0", FCVAR_CHEAT, "Test vgui panel positioning with title safe indentation" );
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads panel details from the resource info
+//-----------------------------------------------------------------------------
+void Panel::ApplySettings(KeyValues *inResourceData)
+{
+ // First restore to default values
+ if ( _flags.IsFlagSet( NEEDS_DEFAULT_SETTINGS_APPLIED ) )
+ {
+ InternalInitDefaultValues( GetAnimMap() );
+ }
+
+ // Let PanelAnimationVars auto-retrieve settings (we restore defaults above
+ // since a script might be missing certain values)
+ InternalApplySettings( GetAnimMap(), inResourceData );
+
+ // clear any alignment flags
+ _buildModeFlags &= ~(BUILDMODE_SAVE_XPOS_RIGHTALIGNED | BUILDMODE_SAVE_XPOS_CENTERALIGNED | BUILDMODE_SAVE_YPOS_BOTTOMALIGNED | BUILDMODE_SAVE_YPOS_CENTERALIGNED | BUILDMODE_SAVE_WIDE_FULL | BUILDMODE_SAVE_TALL_FULL | BUILDMODE_SAVE_PROPORTIONAL_TO_PARENT);
+
+ // get the position
+ int alignScreenWide, alignScreenTall; // screen dimensions used for pinning in splitscreen
+ surface()->GetScreenSize( alignScreenWide, alignScreenTall );
+
+ int screenWide = alignScreenWide;
+ int screenTall = alignScreenTall;
+
+ // temporarily remove the override to get the fullscreen dimensions
+ if ( surface()->IsScreenSizeOverrideActive() )
+ {
+ surface()->ForceScreenSizeOverride( false, 0, 0 );
+ surface()->GetScreenSize( screenWide, screenTall );
+
+ // restore the override
+ surface()->ForceScreenSizeOverride( true, alignScreenWide, alignScreenTall );
+ }
+
+ int parentX = 0;
+ int parentY = 0;
+
+ // flag to cause windows to get screenWide and screenTall from their parents,
+ // this allows children windows to use fill and right/bottom alignment even
+ // if their parent does not use the full screen.
+ if ( inResourceData->GetInt( "proportionalToParent", 0 ) == 1 )
+ {
+ _buildModeFlags |= BUILDMODE_SAVE_PROPORTIONAL_TO_PARENT;
+ if ( GetParent() != NULL )
+ {
+ GetParent()->GetBounds( parentX, parentY, alignScreenWide, alignScreenTall );
+ }
+ }
+
+ int x, y;
+ GetPos(x, y);
+ const char *xstr = inResourceData->GetString( "xpos", NULL );
+ const char *ystr = inResourceData->GetString( "ypos", NULL );
+
+ if (xstr)
+ {
+ // look for alignment flags
+ if (xstr[0] == 'r' || xstr[0] == 'R')
+ {
+ _buildModeFlags |= BUILDMODE_SAVE_XPOS_RIGHTALIGNED;
+ xstr++;
+ }
+ else if (xstr[0] == 'c' || xstr[0] == 'C')
+ {
+ _buildModeFlags |= BUILDMODE_SAVE_XPOS_CENTERALIGNED;
+ xstr++;
+ }
+
+ // get the value
+ x = atoi(xstr);
+ // scale the x up to our screen co-ords
+ if ( IsProportional() )
+ {
+ x = scheme()->GetProportionalScaledValueEx(GetScheme(), x);
+ }
+ // now correct the alignment
+ if (_buildModeFlags & BUILDMODE_SAVE_XPOS_RIGHTALIGNED)
+ {
+ x = alignScreenWide - x;
+ }
+ else if (_buildModeFlags & BUILDMODE_SAVE_XPOS_CENTERALIGNED)
+ {
+ x = (alignScreenWide / 2) + x;
+ }
+ }
+
+ if (ystr)
+ {
+ // look for alignment flags
+ if (ystr[0] == 'r' || ystr[0] == 'R')
+ {
+ _buildModeFlags |= BUILDMODE_SAVE_YPOS_BOTTOMALIGNED;
+ ystr++;
+ }
+ else if (ystr[0] == 'c' || ystr[0] == 'C')
+ {
+ _buildModeFlags |= BUILDMODE_SAVE_YPOS_CENTERALIGNED;
+ ystr++;
+ }
+ y = atoi(ystr);
+ if (IsProportional())
+ {
+ // scale the y up to our screen co-ords
+ y = scheme()->GetProportionalScaledValueEx(GetScheme(), y);
+ }
+ // now correct the alignment
+ if (_buildModeFlags & BUILDMODE_SAVE_YPOS_BOTTOMALIGNED)
+ {
+ y = alignScreenTall - y;
+ }
+ else if (_buildModeFlags & BUILDMODE_SAVE_YPOS_CENTERALIGNED)
+ {
+ y = (alignScreenTall / 2) + y;
+ }
+ }
+
+ bool bUsesTitleSafeArea = false;
+ int titleSafeWide = 0;
+ int titleSafeTall = 0;
+
+ Rect_t excludeEdgeFromTitleSafe; // if a side is set to != 0, don't title safe relative to that edge
+ excludeEdgeFromTitleSafe.x = 0;
+ excludeEdgeFromTitleSafe.y = 0;
+ excludeEdgeFromTitleSafe.width = 0;
+ excludeEdgeFromTitleSafe.height = 0;
+
+ if ( IsX360() || panel_test_title_safe.GetBool() )
+ {
+ // "usetitlesafe" "1" - required inner 90%
+ // "usetitlesafe" "2" - suggested inner 85%
+
+ int iUseTitleSafeValue = 0;
+ if ( inResourceData->FindKey( "usetitlesafe" ) )
+ {
+ iUseTitleSafeValue = inResourceData->GetInt( "usetitlesafe" );
+ bUsesTitleSafeArea = ( iUseTitleSafeValue > 0 );
+ }
+
+ if( bUsesTitleSafeArea )
+ {
+ titleSafeWide = screenWide * ( iUseTitleSafeValue == 1 ? 0.05f : 0.075f );
+ titleSafeTall = screenTall * ( iUseTitleSafeValue == 1 ? 0.05f : 0.075f );
+
+ // Don't title safe internal boundaries for split screen viewports
+ int splitX = 0;
+ int splitY = 0;
+ vgui::surface()->OffsetAbsPos( splitX, splitY );
+
+ bool bHorizontalSplit = ( alignScreenTall != screenTall );
+ bool bVerticalSplit = ( alignScreenWide != screenWide );
+
+ if ( bHorizontalSplit )
+ {
+ // top or bottom?
+ if ( splitY != parentY )
+ {
+ excludeEdgeFromTitleSafe.y = 1;
+ }
+ else
+ {
+ excludeEdgeFromTitleSafe.height = 1;
+ }
+ }
+
+ if ( bVerticalSplit )
+ {
+ // left or right
+ if ( splitX != parentX )
+ {
+ excludeEdgeFromTitleSafe.x = 1;
+ }
+ else
+ {
+ excludeEdgeFromTitleSafe.width = 1;
+ }
+ }
+
+ if ( _buildModeFlags & BUILDMODE_SAVE_XPOS_RIGHTALIGNED )
+ {
+ if ( !excludeEdgeFromTitleSafe.width )
+ {
+ x -= titleSafeWide; // right edge
+ }
+ }
+ else if (_buildModeFlags & BUILDMODE_SAVE_XPOS_CENTERALIGNED)
+ {
+ }
+ else if ( !excludeEdgeFromTitleSafe.x )
+ {
+ x += titleSafeWide; // left edge
+ }
+
+ if ( _buildModeFlags & BUILDMODE_SAVE_YPOS_BOTTOMALIGNED )
+ {
+ if ( !excludeEdgeFromTitleSafe.height )
+ {
+ y -= titleSafeTall; // bottom edge
+ }
+ }
+ else if (_buildModeFlags & BUILDMODE_SAVE_YPOS_CENTERALIGNED)
+ {
+ }
+ else if ( !excludeEdgeFromTitleSafe.y )
+ {
+ y += titleSafeTall; // top edge
+ }
+ }
+ }
+ SetNavUp( inResourceData->GetString("navUp") );
+ SetNavDown( inResourceData->GetString("navDown") );
+ SetNavLeft( inResourceData->GetString("navLeft") );
+ SetNavRight( inResourceData->GetString("navRight") );
+ SetNavToRelay( inResourceData->GetString("navToRelay") );
+ SetNavActivate( inResourceData->GetString("navActivate") );
+ SetNavBack( inResourceData->GetString("navBack") );
+
+ SetPos(x, y);
+
+ if (inResourceData->FindKey( "zpos" ))
+ {
+ SetZPos( inResourceData->GetInt( "zpos" ) );
+ }
+
+ // size
+ int wide, tall;
+ GetSize( wide, tall );
+
+ const char *wstr = inResourceData->GetString( "wide", NULL );
+ if ( wstr )
+ {
+ if (wstr[0] == 'f' || wstr[0] == 'F')
+ {
+ _buildModeFlags |= BUILDMODE_SAVE_WIDE_FULL;
+ wstr++;
+ }
+ wide = atoi(wstr);
+ if ( IsProportional() )
+ {
+ // scale the width up to our screen co-ords
+ wide = scheme()->GetProportionalScaledValueEx(GetScheme(), wide);
+ }
+ // now correct the alignment
+ if (_buildModeFlags & BUILDMODE_SAVE_WIDE_FULL)
+ {
+ wide = alignScreenWide - wide;
+ }
+ }
+
+ // allow tall to be use the "fill" option, set to the height of the parent/screen
+ wstr = inResourceData->GetString( "tall", NULL );
+ if ( wstr )
+ {
+ if (wstr[0] == 'f' || wstr[0] == 'F')
+ {
+ _buildModeFlags |= BUILDMODE_SAVE_TALL_FULL;
+ wstr++;
+ }
+ tall = atoi(wstr);
+ if ( IsProportional() )
+ {
+ // scale the height up to our screen co-ords
+ tall = scheme()->GetProportionalScaledValueEx(GetScheme(), tall);
+ }
+ // now correct the alignment
+ if (_buildModeFlags & BUILDMODE_SAVE_TALL_FULL)
+ {
+ tall = alignScreenTall - tall;
+ }
+ }
+
+ if( bUsesTitleSafeArea )
+ {
+ if ( _buildModeFlags & BUILDMODE_SAVE_WIDE_FULL )
+ {
+ if ( !excludeEdgeFromTitleSafe.x )
+ wide -= titleSafeWide;
+
+ if ( !excludeEdgeFromTitleSafe.width )
+ wide -= titleSafeWide;
+ }
+
+ if ( _buildModeFlags & BUILDMODE_SAVE_TALL_FULL )
+ {
+ if ( !excludeEdgeFromTitleSafe.y )
+ tall -= titleSafeTall;
+
+ if ( !excludeEdgeFromTitleSafe.height )
+ tall -= titleSafeTall;
+ }
+ }
+
+ SetSize( wide, tall );
+
+ // NOTE: This has to happen after pos + size is set
+ ApplyAutoResizeSettings( inResourceData );
+
+ // only get colors if we're ignoring the scheme
+ if (inResourceData->GetInt("IgnoreScheme", 0))
+ {
+ PerformApplySchemeSettings();
+ }
+
+ // state
+ int state = inResourceData->GetInt("visible", 1);
+ if (state == 0)
+ {
+ SetVisible(false);
+ }
+ else if (state == 1)
+ {
+ SetVisible(true);
+ }
+
+ SetEnabled( inResourceData->GetInt("enabled", true) );
+
+ bool bMouseEnabled = inResourceData->GetInt( "mouseinputenabled", true );
+ if ( !bMouseEnabled )
+ {
+ SetMouseInputEnabled( false );
+ }
+
+ // tab order
+ SetTabPosition(inResourceData->GetInt("tabPosition", 0));
+
+ const char *tooltip = inResourceData->GetString("tooltiptext", NULL);
+ if (tooltip && *tooltip)
+ {
+ GetTooltip()->SetText(tooltip);
+ }
+
+ // paint background?
+ int nPaintBackground = inResourceData->GetInt("paintbackground", -1);
+ if (nPaintBackground >= 0)
+ {
+ SetPaintBackgroundEnabled( nPaintBackground != 0 );
+ }
+
+ // paint border?
+ int nPaintBorder = inResourceData->GetInt("paintborder", -1);
+ if (nPaintBorder >= 0)
+ {
+ SetPaintBorderEnabled( nPaintBorder != 0 );
+ }
+
+ // border?
+ const char *pBorder = inResourceData->GetString( "border", "" );
+ if ( *pBorder )
+ {
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+ SetBorder( pScheme->GetBorder( pBorder ) );
+ }
+
+ // check to see if we have a new name assigned
+ const char *newName = inResourceData->GetString("fieldName", NULL);
+ if ( newName )
+ {
+ // Only slam the name if the new one differs...
+ SetName(newName);
+ }
+
+ // check to see if we need to render to the frame buffer even if
+ // stereo mode is trying to render all of the ui to a render target
+ m_bForceStereoRenderToFrameBuffer = inResourceData->GetBool( "ForceStereoRenderToFrameBuffer", false );
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [pfreese] Support for reading rounded corner flags
+ //=============================================================================
+ int roundedCorners = inResourceData->GetInt( "RoundedCorners", -1 );
+ if ( roundedCorners >= 0 )
+ {
+ m_roundedCorners = roundedCorners;
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ const char *pszSiblingName = inResourceData->GetString("pin_to_sibling", NULL);
+ PinCorner_e pinOurCornerToSibling = (PinCorner_e)inResourceData->GetInt( "pin_corner_to_sibling", PIN_TOPLEFT );
+ PinCorner_e pinSiblingCorner = (PinCorner_e)inResourceData->GetInt( "pin_to_sibling_corner", PIN_TOPLEFT );
+ PinToSibling( pszSiblingName, pinOurCornerToSibling, pinSiblingCorner );
+
+
+ // Allow overriding of colors. Used mostly by HUD elements, where scheme color usage is often undesired.
+ IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
+ for ( int i = 0; i < m_OverridableColorEntries.Count(); i++ )
+ {
+ // Need to ensure the key exists, so we don't overwrite existing colors when it's not set.
+ if ( inResourceData->FindKey( m_OverridableColorEntries[i].m_pszScriptName, false ) )
+ {
+ // Get the color as a string - test whether it is an actual color or a reference to a scheme color
+ const char *pColorStr = inResourceData->GetString( m_OverridableColorEntries[i].m_pszScriptName );
+ Color &clrDest = m_OverridableColorEntries[i].m_colFromScript;
+ if ( pColorStr[0] == '.' || isdigit( pColorStr[0] ) )
+ {
+ float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f;
+ sscanf( pColorStr, "%f %f %f %f", &r, &g, &b, &a );
+ clrDest[0] = (unsigned char)r;
+ clrDest[1] = (unsigned char)g;
+ clrDest[2] = (unsigned char)b;
+ clrDest[3] = (unsigned char)a;
+ }
+ else
+ {
+ // First character wasn't a digit or a decimal - do a scheme color lookup
+ clrDest = pScheme->GetColor( pColorStr, Color( 255, 255, 255, 255 ) );
+ }
+
+ (*m_OverridableColorEntries[i].m_pColor) = m_OverridableColorEntries[i].m_colFromScript;
+ m_OverridableColorEntries[i].m_bOverridden = true;
+ }
+ }
+
+ const char *pKeyboardInputEnabled = inResourceData->GetString( "keyboardinputenabled", NULL );
+ if ( pKeyboardInputEnabled && pKeyboardInputEnabled[0] )
+ {
+ SetKeyBoardInputEnabled( atoi( pKeyboardInputEnabled ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Saves out a resource description of this panel
+//-----------------------------------------------------------------------------
+void Panel::GetSettings( KeyValues *outResourceData )
+{
+ // control class name (so it can be recreated later if needed)
+ outResourceData->SetString( "ControlName", GetClassName() );
+
+ // name
+ outResourceData->SetString( "fieldName", _panelName );
+
+ // positioning
+ int screenWide, screenTall;
+ surface()->GetScreenSize(screenWide, screenTall);
+ int x, y;
+ GetPos( x, y );
+ if ( IsProportional() )
+ {
+ x = scheme()->GetProportionalNormalizedValueEx( GetScheme(), x );
+ y = scheme()->GetProportionalNormalizedValueEx( GetScheme(), y );
+ }
+ // correct for alignment
+ if (_buildModeFlags & BUILDMODE_SAVE_XPOS_RIGHTALIGNED)
+ {
+ x = screenWide - x;
+ char xstr[32];
+ Q_snprintf(xstr, sizeof( xstr ), "r%d", x);
+ outResourceData->SetString( "xpos", xstr );
+ }
+ else if (_buildModeFlags & BUILDMODE_SAVE_XPOS_CENTERALIGNED)
+ {
+ x = (screenWide / 2) + x;
+ char xstr[32];
+ Q_snprintf(xstr, sizeof( xstr ), "c%d", x);
+ outResourceData->SetString( "xpos", xstr );
+ }
+ else
+ {
+ outResourceData->SetInt( "xpos", x );
+ }
+ if (_buildModeFlags & BUILDMODE_SAVE_YPOS_BOTTOMALIGNED)
+ {
+ y = screenTall - y;
+ char ystr[32];
+ Q_snprintf(ystr, sizeof( ystr ), "r%d", y);
+ outResourceData->SetString( "ypos", ystr );
+ }
+ else if (_buildModeFlags & BUILDMODE_SAVE_YPOS_CENTERALIGNED)
+ {
+ y = (screenTall / 2) + y;
+ char ystr[32];
+ Q_snprintf(ystr, sizeof( ystr ), "c%d", y);
+ outResourceData->SetString( "ypos", ystr );
+ }
+ else
+ {
+ outResourceData->SetInt( "ypos", y );
+ }
+ if (m_pTooltips)
+ {
+ if (strlen(m_pTooltips->GetText()) > 0)
+ {
+ outResourceData->SetString("tooltiptext", m_pTooltips->GetText());
+ }
+ }
+ int wide, tall;
+ GetSize( wide, tall );
+ if ( IsProportional() )
+ {
+ wide = scheme()->GetProportionalNormalizedValueEx( GetScheme(), wide );
+ tall = scheme()->GetProportionalNormalizedValueEx( GetScheme(), tall );
+ }
+
+ int z = ipanel()->GetZPos(GetVPanel());
+ if (z)
+ {
+ outResourceData->SetInt("zpos", z);
+ }
+
+ // Correct for alignment
+ if (_buildModeFlags & BUILDMODE_SAVE_WIDE_FULL )
+ {
+ wide = screenWide - wide;
+ char wstr[32];
+ Q_snprintf(wstr, sizeof( wstr ), "f%d", wide);
+ outResourceData->SetString( "wide", wstr );
+ }
+ else
+ {
+ outResourceData->SetInt( "wide", wide );
+ }
+ outResourceData->SetInt( "tall", tall );
+
+ outResourceData->SetInt("AutoResize", GetAutoResize());
+ outResourceData->SetInt("PinCorner", GetPinCorner());
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [pfreese] Support for writing out rounded corner flags
+ //=============================================================================
+ outResourceData->SetInt("RoundedCorners", m_roundedCorners);
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ outResourceData->SetString( "pin_to_sibling", _pinToSibling );
+ outResourceData->SetInt("pin_corner_to_sibling", _pinCornerToSibling );
+ outResourceData->SetInt("pin_to_sibling_corner", _pinToSiblingCorner );
+
+
+ // state
+ outResourceData->SetInt( "visible", IsVisible() );
+ outResourceData->SetInt( "enabled", IsEnabled() );
+
+ outResourceData->SetInt( "tabPosition", GetTabPosition() );
+
+ for ( int i = 0; i < m_OverridableColorEntries.Count(); i++ )
+ {
+ if ( m_OverridableColorEntries[i].m_bOverridden )
+ {
+ outResourceData->SetColor( m_OverridableColorEntries[i].m_pszScriptName, m_OverridableColorEntries[i].m_colFromScript );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: After applying settings, apply overridable colors.
+// Done post apply settings, so that baseclass settings don't stomp
+// the script specified override colors.
+//-----------------------------------------------------------------------------
+void Panel::ApplyOverridableColors( void )
+{
+ for ( int i = 0; i < m_OverridableColorEntries.Count(); i++ )
+ {
+ if ( m_OverridableColorEntries[i].m_bOverridden )
+ {
+ (*m_OverridableColorEntries[i].m_pColor) = m_OverridableColorEntries[i].m_colFromScript;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetOverridableColor( Color *pColor, const Color &newColor )
+{
+ for ( int i = 0; i < m_OverridableColorEntries.Count(); i++ )
+ {
+ if ( m_OverridableColorEntries[i].m_bOverridden )
+ {
+ if ( m_OverridableColorEntries[i].m_pColor == pColor )
+ return;
+ }
+ }
+
+ // Didn't find it, or it's not been overridden.
+ *pColor = newColor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Color Panel::GetSchemeColor(const char *keyName, IScheme *pScheme)
+{
+ return pScheme->GetColor(keyName, Color(255, 255, 255, 255));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Color Panel::GetSchemeColor(const char *keyName, Color defaultColor, IScheme *pScheme)
+{
+ return pScheme->GetColor(keyName, defaultColor);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a string description of the panel fields for use in the UI
+//-----------------------------------------------------------------------------
+const char *Panel::GetDescription( void )
+{
+ static const char *panelDescription = "string fieldName, int xpos, int ypos, int wide, int tall, bool visible, bool enabled, int tabPosition, corner pinCorner, autoresize autoResize, string tooltiptext";
+ return panelDescription;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: user configuration settings
+// this is used for any control details the user wants saved between sessions
+// eg. dialog positions, last directory opened, list column width
+//-----------------------------------------------------------------------------
+void Panel::ApplyUserConfigSettings(KeyValues *userConfig)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns user config settings for this control
+//-----------------------------------------------------------------------------
+void Panel::GetUserConfigSettings(KeyValues *userConfig)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: optimization, return true if this control has any user config settings
+//-----------------------------------------------------------------------------
+bool Panel::HasUserConfigSettings()
+{
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::InternalInvalidateLayout()
+{
+ InvalidateLayout(false, false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called whenever the panel moves
+//-----------------------------------------------------------------------------
+void Panel::OnMove()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::InternalMove()
+{
+ OnMove();
+ for(int i=0;i<GetChildCount();i++)
+ {
+ // recursively apply to all children
+ GetChild(i)->OnMove();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: empty function
+//-----------------------------------------------------------------------------
+void Panel::OnTick()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: versioning
+//-----------------------------------------------------------------------------
+void *Panel::QueryInterface(EInterfaceID id)
+{
+ if (id == ICLIENTPANEL_STANDARD_INTERFACE)
+ {
+ return this;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Map all the base messages to functions
+// ordering from most -> least used improves speed
+//-----------------------------------------------------------------------------
+MessageMapItem_t Panel::m_MessageMap[] =
+{
+ MAP_MESSAGE_INT( Panel, "RequestFocus", RequestFocus, "direction" )
+};
+
+// IMPLEMENT_PANELMAP( Panel, NULL )
+PanelMap_t Panel::m_PanelMap = { Panel::m_MessageMap, ARRAYSIZE(Panel::m_MessageMap), "Panel", NULL };
+PanelMap_t *Panel::GetPanelMap( void ) { return &m_PanelMap; }
+
+//-----------------------------------------------------------------------------
+// Purpose: !! Soon to replace existing prepare panel map
+//-----------------------------------------------------------------------------
+void PreparePanelMessageMap(PanelMessageMap *panelMap)
+{
+ // iterate through the class hierarchy message maps
+ while ( panelMap != NULL && !panelMap->processed )
+ {
+ // hash message map strings into symbols
+ for (int i = 0; i < panelMap->entries.Count(); i++)
+ {
+ MessageMapItem_t *item = &panelMap->entries[i];
+
+ if (item->name)
+ {
+ item->nameSymbol = KeyValuesSystem()->GetSymbolForString(item->name);
+ }
+ else
+ {
+ item->nameSymbol = INVALID_KEY_SYMBOL;
+ }
+ if (item->firstParamName)
+ {
+ item->firstParamSymbol = KeyValuesSystem()->GetSymbolForString(item->firstParamName);
+ }
+ else
+ {
+ item->firstParamSymbol = INVALID_KEY_SYMBOL;
+ }
+ if (item->secondParamName)
+ {
+ item->secondParamSymbol = KeyValuesSystem()->GetSymbolForString(item->secondParamName);
+ }
+ else
+ {
+ item->secondParamSymbol = INVALID_KEY_SYMBOL;
+ }
+ }
+
+ panelMap->processed = true;
+ panelMap = panelMap->baseMap;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles a message
+// Dispatches the message to a set of message maps
+//-----------------------------------------------------------------------------
+void Panel::OnMessage(const KeyValues *params, VPANEL ifromPanel)
+{
+ PanelMessageMap *panelMap = GetMessageMap();
+ bool bFound = false;
+ int iMessageName = params->GetNameSymbol();
+
+ if ( !panelMap->processed )
+ {
+ PreparePanelMessageMap( panelMap );
+ }
+
+ // iterate through the class hierarchy message maps
+ for ( ; panelMap != NULL && !bFound; panelMap = panelMap->baseMap )
+ {
+#if defined( _DEBUG )
+// char const *className = panelMap->pfnClassName();
+// NOTE_UNUSED( className );
+#endif
+
+ // iterate all the entries in the panel map
+ for ( int i = 0; i < panelMap->entries.Count(); i++ )
+ {
+ MessageMapItem_t *pMap = &panelMap->entries[i];
+
+ if (iMessageName == pMap->nameSymbol)
+ {
+ bFound = true;
+
+ switch (pMap->numParams)
+ {
+ case 0:
+ {
+ (this->*(pMap->func))();
+ break;
+ }
+
+ case 1:
+ {
+ KeyValues *param1 = params->FindKey(pMap->firstParamSymbol);
+ if (!param1)
+ {
+ param1 = const_cast<KeyValues *>(params);
+ }
+
+ switch ( pMap->firstParamType )
+ {
+ case DATATYPE_INT:
+ typedef void (Panel::*MessageFunc_Int_t)(int);
+ (this->*((MessageFunc_Int_t)pMap->func))( param1->GetInt() );
+ break;
+
+ case DATATYPE_UINT64:
+ typedef void (Panel::*MessageFunc_Uin64_t)(uint64);
+ (this->*((MessageFunc_Uin64_t)pMap->func))( param1->GetUint64() );
+ break;
+
+ case DATATYPE_PTR:
+ typedef void (Panel::*MessageFunc_Ptr_t)( void * );
+ (this->*((MessageFunc_Ptr_t)pMap->func))( param1->GetPtr() );
+ break;
+
+ case DATATYPE_HANDLE:
+ {
+ typedef void (Panel::*MessageFunc_VPANEL_t)( VPANEL );
+ VPANEL vpanel = ivgui()->HandleToPanel( param1->GetInt() );
+ (this->*((MessageFunc_VPANEL_t)pMap->func))( vpanel );
+ }
+ break;
+
+ case DATATYPE_FLOAT:
+ typedef void (Panel::*MessageFunc_Float_t)( float );
+ (this->*((MessageFunc_Float_t)pMap->func))( param1->GetFloat() );
+ break;
+
+ case DATATYPE_CONSTCHARPTR:
+ typedef void (Panel::*MessageFunc_CharPtr_t)( const char * );
+ (this->*((MessageFunc_CharPtr_t)pMap->func))( param1->GetString() );
+ break;
+
+ case DATATYPE_CONSTWCHARPTR:
+ typedef void (Panel::*MessageFunc_WCharPtr_t)( const wchar_t * );
+ (this->*((MessageFunc_WCharPtr_t)pMap->func))( param1->GetWString() );
+ break;
+
+ case DATATYPE_KEYVALUES:
+ typedef void (Panel::*MessageFunc_KeyValues_t)(KeyValues *);
+ if ( pMap->firstParamName )
+ {
+ (this->*((MessageFunc_KeyValues_t)pMap->func))( (KeyValues *)param1->GetPtr() );
+ }
+ else
+ {
+ // no param set, so pass in the whole thing
+ (this->*((MessageFunc_KeyValues_t)pMap->func))( const_cast<KeyValues *>(params) );
+ }
+ break;
+
+ default:
+ Assert(!("No handler for vgui message function"));
+ break;
+ }
+ break;
+ }
+
+ case 2:
+ {
+ KeyValues *param1 = params->FindKey(pMap->firstParamSymbol);
+ if (!param1)
+ {
+ param1 = const_cast<KeyValues *>(params);
+ }
+ KeyValues *param2 = params->FindKey(pMap->secondParamSymbol);
+ if (!param2)
+ {
+ param2 = const_cast<KeyValues *>(params);
+ }
+
+ if ( (DATATYPE_INT == pMap->firstParamType) && (DATATYPE_INT == pMap->secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_IntInt_t)(int, int);
+ (this->*((MessageFunc_IntInt_t)pMap->func))( param1->GetInt(), param2->GetInt() );
+ }
+ else if ( (DATATYPE_PTR == pMap->firstParamType) && (DATATYPE_INT == pMap->secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_PtrInt_t)(void *, int);
+ (this->*((MessageFunc_PtrInt_t)pMap->func))( param1->GetPtr(), param2->GetInt() );
+ }
+ else if ( (DATATYPE_CONSTCHARPTR == pMap->firstParamType) && (DATATYPE_INT == pMap->secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_ConstCharPtrInt_t)(const char *, int);
+ (this->*((MessageFunc_ConstCharPtrInt_t)pMap->func))( param1->GetString(), param2->GetInt() );
+ }
+ else if ( (DATATYPE_CONSTCHARPTR == pMap->firstParamType) && (DATATYPE_CONSTCHARPTR == pMap->secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_ConstCharPtrConstCharPtr_t)(const char *, const char *);
+ (this->*((MessageFunc_ConstCharPtrConstCharPtr_t)pMap->func))( param1->GetString(), param2->GetString() );
+ }
+ else if ( (DATATYPE_INT == pMap->firstParamType) && (DATATYPE_CONSTCHARPTR == pMap->secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_IntConstCharPtr_t)(int, const char *);
+ (this->*((MessageFunc_IntConstCharPtr_t)pMap->func))( param1->GetInt(), param2->GetString() );
+ }
+ else if ( (DATATYPE_PTR == pMap->firstParamType) && (DATATYPE_CONSTCHARPTR == pMap->secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_PtrConstCharPtr_t)(void *, const char *);
+ (this->*((MessageFunc_PtrConstCharPtr_t)pMap->func))( param1->GetPtr(), param2->GetString() );
+ }
+ else if ( (DATATYPE_PTR == pMap->firstParamType) && (DATATYPE_CONSTWCHARPTR == pMap->secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_PtrConstCharPtr_t)(void *, const wchar_t *);
+ (this->*((MessageFunc_PtrConstCharPtr_t)pMap->func))( param1->GetPtr(), param2->GetWString() );
+ }
+ else if ( (DATATYPE_HANDLE == pMap->firstParamType) && (DATATYPE_CONSTCHARPTR == pMap->secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, const char *);
+ VPANEL vp = ivgui()->HandleToPanel( param1->GetInt() );
+ (this->*((MessageFunc_HandleConstCharPtr_t)pMap->func))( vp, param2->GetString() );
+ }
+ else if ( (DATATYPE_HANDLE == pMap->firstParamType) && (DATATYPE_CONSTWCHARPTR == pMap->secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, const wchar_t *);
+ VPANEL vp = ivgui()->HandleToPanel( param1->GetInt() );
+ (this->*((MessageFunc_HandleConstCharPtr_t)pMap->func))( vp, param2->GetWString() );
+ }
+ else
+ {
+ // the message isn't handled
+ ivgui()->DPrintf( "Message '%s', sent to '%s', has invalid parameter types\n", params->GetName(), GetName() );
+ }
+ break;
+ }
+
+ default:
+ Assert(!("Invalid number of parameters"));
+ break;
+ }
+
+ // break the loop
+ bFound = true;
+ break;
+ }
+ }
+ }
+
+ if (!bFound)
+ {
+ OnOldMessage(const_cast<KeyValues *>(params), ifromPanel);
+ }
+}
+
+void Panel::OnOldMessage(KeyValues *params, VPANEL ifromPanel)
+{
+ bool bFound = false;
+ // message map dispatch
+ int iMessageName = params->GetNameSymbol();
+
+ PanelMap_t *panelMap = GetPanelMap();
+ if ( !panelMap->processed )
+ {
+ PreparePanelMap( panelMap );
+ }
+
+ // iterate through the class hierarchy message maps
+ for ( ; panelMap != NULL && !bFound; panelMap = panelMap->baseMap )
+ {
+ MessageMapItem_t *pMessageMap = panelMap->dataDesc;
+
+ for ( int i = 0; i < panelMap->dataNumFields; i++ )
+ {
+ if (iMessageName == pMessageMap[i].nameSymbol)
+ {
+ // call the mapped function
+ switch ( pMessageMap[i].numParams )
+ {
+ case 2:
+ if ( (DATATYPE_INT == pMessageMap[i].firstParamType) && (DATATYPE_INT == pMessageMap[i].secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_IntInt_t)(int, int);
+ (this->*((MessageFunc_IntInt_t)pMessageMap[i].func))( params->GetInt(pMessageMap[i].firstParamName), params->GetInt(pMessageMap[i].secondParamName) );
+ }
+ else if ( (DATATYPE_PTR == pMessageMap[i].firstParamType) && (DATATYPE_INT == pMessageMap[i].secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_PtrInt_t)(void *, int);
+ (this->*((MessageFunc_PtrInt_t)pMessageMap[i].func))( params->GetPtr(pMessageMap[i].firstParamName), params->GetInt(pMessageMap[i].secondParamName) );
+ }
+ else if ( (DATATYPE_CONSTCHARPTR == pMessageMap[i].firstParamType) && (DATATYPE_INT == pMessageMap[i].secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_ConstCharPtrInt_t)(const char *, int);
+ (this->*((MessageFunc_ConstCharPtrInt_t)pMessageMap[i].func))( params->GetString(pMessageMap[i].firstParamName), params->GetInt(pMessageMap[i].secondParamName) );
+ }
+ else if ( (DATATYPE_CONSTCHARPTR == pMessageMap[i].firstParamType) && (DATATYPE_CONSTCHARPTR == pMessageMap[i].secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_ConstCharPtrConstCharPtr_t)(const char *, const char *);
+ (this->*((MessageFunc_ConstCharPtrConstCharPtr_t)pMessageMap[i].func))( params->GetString(pMessageMap[i].firstParamName), params->GetString(pMessageMap[i].secondParamName) );
+ }
+ else if ( (DATATYPE_INT == pMessageMap[i].firstParamType) && (DATATYPE_CONSTCHARPTR == pMessageMap[i].secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_IntConstCharPtr_t)(int, const char *);
+ (this->*((MessageFunc_IntConstCharPtr_t)pMessageMap[i].func))( params->GetInt(pMessageMap[i].firstParamName), params->GetString(pMessageMap[i].secondParamName) );
+ }
+ else if ( (DATATYPE_PTR == pMessageMap[i].firstParamType) && (DATATYPE_CONSTCHARPTR == pMessageMap[i].secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_PtrConstCharPtr_t)(void *, const char *);
+ (this->*((MessageFunc_PtrConstCharPtr_t)pMessageMap[i].func))( params->GetPtr(pMessageMap[i].firstParamName), params->GetString(pMessageMap[i].secondParamName) );
+ }
+ else if ( (DATATYPE_PTR == pMessageMap[i].firstParamType) && (DATATYPE_CONSTWCHARPTR == pMessageMap[i].secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_PtrConstCharPtr_t)(void *, const wchar_t *);
+ (this->*((MessageFunc_PtrConstCharPtr_t)pMessageMap[i].func))( params->GetPtr(pMessageMap[i].firstParamName), params->GetWString(pMessageMap[i].secondParamName) );
+ }
+ else if ( (DATATYPE_HANDLE == pMessageMap[i].firstParamType) && (DATATYPE_CONSTCHARPTR ==pMessageMap[i].secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, const char *);
+ VPANEL vp = ivgui()->HandleToPanel( params->GetInt( pMessageMap[i].firstParamName ) );
+ (this->*((MessageFunc_HandleConstCharPtr_t)pMessageMap[i].func))( vp, params->GetString(pMessageMap[i].secondParamName) );
+ }
+ else if ( (DATATYPE_HANDLE == pMessageMap[i].firstParamType) && (DATATYPE_CONSTWCHARPTR == pMessageMap[i].secondParamType) )
+ {
+ typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, const wchar_t *);
+ VPANEL vp = ivgui()->HandleToPanel( params->GetInt( pMessageMap[i].firstParamName ) );
+ (this->*((MessageFunc_HandleConstCharPtr_t)pMessageMap[i].func))( vp, params->GetWString(pMessageMap[i].secondParamName) );
+ }
+ else
+ {
+ // the message isn't handled
+ ivgui()->DPrintf( "Message '%s', sent to '%s', has invalid parameter types\n", params->GetName(), GetName() );
+ }
+ break;
+
+ case 1:
+ switch ( pMessageMap[i].firstParamType )
+ {
+ case DATATYPE_BOOL:
+ typedef void (Panel::*MessageFunc_Bool_t)(bool);
+ (this->*((MessageFunc_Bool_t)pMessageMap[i].func))( (bool)params->GetInt(pMessageMap[i].firstParamName) );
+ break;
+
+ case DATATYPE_CONSTCHARPTR:
+ typedef void (Panel::*MessageFunc_ConstCharPtr_t)(const char *);
+ (this->*((MessageFunc_ConstCharPtr_t)pMessageMap[i].func))( (const char *)params->GetString(pMessageMap[i].firstParamName) );
+ break;
+
+ case DATATYPE_CONSTWCHARPTR:
+ typedef void (Panel::*MessageFunc_ConstCharPtr_t)(const char *);
+ (this->*((MessageFunc_ConstCharPtr_t)pMessageMap[i].func))( (const char *)params->GetWString(pMessageMap[i].firstParamName) );
+ break;
+
+ case DATATYPE_INT:
+ typedef void (Panel::*MessageFunc_Int_t)(int);
+ (this->*((MessageFunc_Int_t)pMessageMap[i].func))( params->GetInt(pMessageMap[i].firstParamName) );
+ break;
+
+ case DATATYPE_FLOAT:
+ typedef void (Panel::*MessageFunc_Float_t)(float);
+ (this->*((MessageFunc_Float_t)pMessageMap[i].func))( params->GetFloat(pMessageMap[i].firstParamName) );
+ break;
+
+ case DATATYPE_PTR:
+ typedef void (Panel::*MessageFunc_Ptr_t)(void *);
+ (this->*((MessageFunc_Ptr_t)pMessageMap[i].func))( (void *)params->GetPtr(pMessageMap[i].firstParamName) );
+ break;
+
+ case DATATYPE_HANDLE:
+ {
+ typedef void (Panel::*MessageFunc_Ptr_t)(void *);
+ VPANEL vp = ivgui()->HandleToPanel( params->GetInt( pMessageMap[i].firstParamName ) );
+ Panel *panel = ipanel()->GetPanel( vp, GetModuleName() );
+ (this->*((MessageFunc_Ptr_t)pMessageMap[i].func))( (void *)panel );
+ }
+ break;
+
+ case DATATYPE_KEYVALUES:
+ typedef void (Panel::*MessageFunc_KeyValues_t)(KeyValues *);
+ if ( pMessageMap[i].firstParamName )
+ {
+ (this->*((MessageFunc_KeyValues_t)pMessageMap[i].func))( (KeyValues *)params->GetPtr(pMessageMap[i].firstParamName) );
+ }
+ else
+ {
+ (this->*((MessageFunc_KeyValues_t)pMessageMap[i].func))( params );
+ }
+ break;
+
+ default:
+ // the message isn't handled
+ ivgui()->DPrintf( "Message '%s', sent to '%s', has an invalid parameter type\n", params->GetName(), GetName() );
+ break;
+ }
+
+ break;
+
+ default:
+ (this->*(pMessageMap[i].func))();
+ break;
+ };
+
+ // break the loop
+ bFound = true;
+ break;
+ }
+ }
+ }
+
+ // message not handled
+ // debug code
+ if ( !bFound )
+ {
+ static int s_bDebugMessages = -1;
+ if ( s_bDebugMessages == -1 )
+ {
+ s_bDebugMessages = CommandLine()->FindParm( "-vguimessages" ) ? 1 : 0;
+ }
+ if ( s_bDebugMessages == 1 )
+ {
+ ivgui()->DPrintf( "Message '%s' not handled by panel '%s'\n", params->GetName(), GetName() );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Safe call to get info from child panel by name
+//-----------------------------------------------------------------------------
+bool Panel::RequestInfoFromChild(const char *childName, KeyValues *outputData)
+{
+ Panel *panel = FindChildByName(childName);
+ if (panel)
+ {
+ return panel->RequestInfo(outputData);
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Posts a message
+//-----------------------------------------------------------------------------
+void Panel::PostMessage(Panel *target, KeyValues *message, float delay)
+{
+ ivgui()->PostMessage(target->GetVPanel(), message, GetVPanel(), delay);
+}
+
+void Panel::PostMessage(VPANEL target, KeyValues *message, float delaySeconds)
+{
+ ivgui()->PostMessage(target, message, GetVPanel(), delaySeconds);
+}
+
+void Panel::PostMessageToAllSiblings( KeyValues *msg, float delaySeconds /*= 0.0f*/ )
+{
+ VPANEL parent = GetVParent();
+ if ( parent )
+ {
+ VPANEL vpanel = GetVPanel();
+
+ CUtlVector< VPANEL > &children = ipanel()->GetChildren( parent );
+ int nChildCount = children.Count();
+ for ( int i = 0; i < nChildCount; ++i )
+ {
+ VPANEL sibling = children[ i ];
+ if ( sibling == vpanel )
+ continue;
+
+ if ( sibling )
+ {
+ PostMessage( sibling, msg->MakeCopy(), delaySeconds );
+ }
+ }
+ }
+
+ msg->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Safe call to post a message to a child by name
+//-----------------------------------------------------------------------------
+void Panel::PostMessageToChild(const char *childName, KeyValues *message)
+{
+ Panel *panel = FindChildByName(childName);
+ if (panel)
+ {
+ ivgui()->PostMessage(panel->GetVPanel(), message, GetVPanel());
+ }
+ else
+ {
+ message->deleteThis();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Requests some information from the panel
+// Look through the message map for the handler
+//-----------------------------------------------------------------------------
+bool Panel::RequestInfo( KeyValues *outputData )
+{
+ if ( InternalRequestInfo( GetAnimMap(), outputData ) )
+ {
+ return true;
+ }
+
+ if (GetVParent())
+ {
+ return ipanel()->RequestInfo(GetVParent(), outputData);
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets a specified value in the control - inverse of RequestInfo
+//-----------------------------------------------------------------------------
+bool Panel::SetInfo(KeyValues *inputData)
+{
+ if ( InternalSetInfo( GetAnimMap(), inputData ) )
+ {
+ return true;
+ }
+
+ // doesn't chain to parent
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: change the panel's silent mode; if silent, the panel will not post
+// any action signals
+//-----------------------------------------------------------------------------
+
+void Panel::SetSilentMode( bool bSilent )
+{
+ m_bIsSilent = bSilent;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: mouse events will be send to handler panel instead of this panel
+//-----------------------------------------------------------------------------
+void Panel::InstallMouseHandler( Panel *pHandler )
+{
+ m_hMouseEventHandler = pHandler;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Prepares the hierarchy panel maps for use (with message maps etc)
+//-----------------------------------------------------------------------------
+void Panel::PreparePanelMap( PanelMap_t *panelMap )
+{
+ // iterate through the class hierarchy message maps
+ while ( panelMap != NULL && !panelMap->processed )
+ {
+ // fixup cross-dll boundary panel maps
+ if ( panelMap->baseMap == (PanelMap_t*)0x00000001 )
+ {
+ panelMap->baseMap = &Panel::m_PanelMap;
+ }
+
+ // hash message map strings into symbols
+ for (int i = 0; i < panelMap->dataNumFields; i++)
+ {
+ MessageMapItem_t *item = &panelMap->dataDesc[i];
+
+ if (item->name)
+ {
+ item->nameSymbol = KeyValuesSystem()->GetSymbolForString(item->name);
+ }
+ else
+ {
+ item->nameSymbol = INVALID_KEY_SYMBOL;
+ }
+ if (item->firstParamName)
+ {
+ item->firstParamSymbol = KeyValuesSystem()->GetSymbolForString(item->firstParamName);
+ }
+ else
+ {
+ item->firstParamSymbol = INVALID_KEY_SYMBOL;
+ }
+ if (item->secondParamName)
+ {
+ item->secondParamSymbol = KeyValuesSystem()->GetSymbolForString(item->secondParamName);
+ }
+ else
+ {
+ item->secondParamSymbol = INVALID_KEY_SYMBOL;
+ }
+ }
+
+ panelMap->processed = true;
+ panelMap = panelMap->baseMap;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called to delete the panel
+//-----------------------------------------------------------------------------
+void Panel::OnDelete()
+{
+#ifdef WIN32
+ Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) );
+#endif
+ delete this;
+#ifdef WIN32
+ Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Panel handle implementation
+// Returns a pointer to a valid panel, NULL if the panel has been deleted
+//-----------------------------------------------------------------------------
+Panel *PHandle::Get()
+{
+ if (m_iPanelID != INVALID_PANEL)
+ {
+ VPANEL panel = ivgui()->HandleToPanel(m_iPanelID);
+ if (panel)
+ {
+ Panel *vguiPanel = ipanel()->GetPanel(panel, GetControlsModuleName());
+ return vguiPanel;
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the smart pointer
+//-----------------------------------------------------------------------------
+Panel *PHandle::Set(Panel *pent)
+{
+ if (pent)
+ {
+ m_iPanelID = ivgui()->PanelToHandle(pent->GetVPanel());
+ }
+ else
+ {
+ m_iPanelID = INVALID_PANEL;
+ }
+ return pent;
+}
+
+Panel *PHandle::Set( HPanel hPanel )
+{
+ m_iPanelID = hPanel;
+ return Get();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a handle to a valid panel, NULL if the panel has been deleted
+//-----------------------------------------------------------------------------
+VPANEL VPanelHandle::Get()
+{
+ if (m_iPanelID != INVALID_PANEL)
+ {
+ if (ivgui())
+ {
+ return ivgui()->HandleToPanel(m_iPanelID);
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the smart pointer
+//-----------------------------------------------------------------------------
+VPANEL VPanelHandle::Set(VPANEL pent)
+{
+ if (pent)
+ {
+ m_iPanelID = ivgui()->PanelToHandle(pent);
+ }
+ else
+ {
+ m_iPanelID = INVALID_PANEL;
+ }
+ return pent;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns a pointer to the tooltip object associated with the panel
+//-----------------------------------------------------------------------------
+BaseTooltip *Panel::GetTooltip()
+{
+ if (!m_pTooltips)
+ {
+ m_pTooltips = new TextTooltip(this, NULL);
+ m_bToolTipOverridden = false;
+
+ if ( IsConsoleStylePanel() )
+ {
+ m_pTooltips->SetEnabled( false );
+ }
+ }
+
+ return m_pTooltips;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetTooltip( BaseTooltip *pToolTip, const char *pszText )
+{
+ if ( !m_bToolTipOverridden )
+ {
+ // Remove the one we made, we're being overridden.
+ delete m_pTooltips;
+ }
+
+ m_pTooltips = pToolTip;
+ m_bToolTipOverridden = true;
+
+ if ( _tooltipText )
+ {
+ delete [] _tooltipText;
+ _tooltipText = NULL;
+ }
+
+ if ( pszText )
+ {
+ int len = Q_strlen(pszText) + 1;
+ _tooltipText = new char[ len ];
+ Q_strncpy( _tooltipText, pszText, len );
+ }
+}
+
+//-----------------------------------------------------------------------------
+const char *Panel::GetEffectiveTooltipText() const
+{
+ if ( _tooltipText )
+ {
+ return _tooltipText;
+ }
+ if ( m_pTooltips )
+ {
+ const char *result = m_pTooltips->GetText();
+ if ( result )
+ {
+ return result;
+ }
+ }
+ return "";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the proportional flag on this panel and all it's children
+//-----------------------------------------------------------------------------
+void Panel::SetProportional(bool state)
+{
+ // only do something if the state changes
+ if( state != _flags.IsFlagSet( IS_PROPORTIONAL ) )
+ {
+ _flags.SetFlag( IS_PROPORTIONAL, state );
+
+ for(int i=0;i<GetChildCount();i++)
+ {
+ // recursively apply to all children
+ GetChild(i)->SetProportional( IsProportional() );
+ }
+ }
+ InvalidateLayout();
+}
+
+
+void Panel::SetKeyBoardInputEnabled( bool state )
+{
+ ipanel()->SetKeyBoardInputEnabled( GetVPanel(), state );
+ for ( int i = 0; i < GetChildCount(); i++ )
+ {
+ Panel *child = GetChild( i );
+ if ( !child )
+ {
+ continue;
+ }
+ child->SetKeyBoardInputEnabled( state );
+ }
+
+ // If turning off keyboard input enable, then make sure
+ // this panel is not the current key focus of a parent panel
+ if ( !state )
+ {
+ Panel *pParent = GetParent();
+ if ( pParent )
+ {
+ if ( pParent->GetCurrentKeyFocus() == GetVPanel() )
+ {
+ pParent->RequestFocusNext();
+ }
+ }
+ }
+}
+
+void Panel::SetMouseInputEnabled( bool state )
+{
+ ipanel()->SetMouseInputEnabled( GetVPanel(), state );
+ /* for(int i=0;i<GetChildCount();i++)
+ {
+ GetChild(i)->SetMouseInput(state);
+ }*/
+ vgui::surface()->CalculateMouseVisible();
+}
+
+bool Panel::IsKeyBoardInputEnabled()
+{
+ return ipanel()->IsKeyBoardInputEnabled( GetVPanel() );
+}
+
+bool Panel::IsMouseInputEnabled()
+{
+ return ipanel()->IsMouseInputEnabled( GetVPanel() );
+}
+
+class CFloatProperty : public vgui::IPanelAnimationPropertyConverter
+{
+public:
+ virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ kv->SetFloat( entry->name(), *(float *)data );
+ }
+
+ virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ *(float *)data = kv->GetFloat( entry->name() );
+ }
+
+ virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ *(float *)data = atof( entry->defaultvalue() );
+ }
+};
+
+class CProportionalFloatProperty : public vgui::IPanelAnimationPropertyConverter
+{
+public:
+ virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ float f = *(float *)data;
+ f = scheme()->GetProportionalNormalizedValueEx( panel->GetScheme(), f );
+ kv->SetFloat( entry->name(), f );
+ }
+
+ virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ float f = kv->GetFloat( entry->name() );
+ f = scheme()->GetProportionalScaledValueEx( panel->GetScheme(), f );
+ *(float *)data = f;
+ }
+
+ virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ float f = atof( entry->defaultvalue() );
+ f = scheme()->GetProportionalScaledValueEx( panel->GetScheme(), f );
+ *(float *)data = f;
+ }
+};
+
+class CIntProperty : public vgui::IPanelAnimationPropertyConverter
+{
+public:
+ virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ kv->SetInt( entry->name(), *(int *)data );
+ }
+
+ virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ *(int *)data = kv->GetInt( entry->name() );
+ }
+
+ virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ *(int *)data = atoi( entry->defaultvalue() );
+ }
+};
+
+class CProportionalIntProperty : public vgui::IPanelAnimationPropertyConverter
+{
+public:
+ virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ int i = *(int *)data;
+ i = scheme()->GetProportionalNormalizedValueEx( panel->GetScheme(), i );
+ kv->SetInt( entry->name(), i );
+ }
+
+ virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ int i = kv->GetInt( entry->name() );
+ i = scheme()->GetProportionalScaledValueEx( panel->GetScheme(), i );
+ *(int *)data = i;
+ }
+ virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ int i = atoi( entry->defaultvalue() );
+ i = scheme()->GetProportionalScaledValueEx( panel->GetScheme(), i );
+ *(int *)data = i;
+ }
+};
+
+class CProportionalIntWithScreenspacePropertyX : public vgui::IPanelAnimationPropertyConverter
+{
+public:
+ int ExtractValue( Panel *panel, const char *pszKey )
+ {
+ int iValue = 0;
+ bool bRightAlign = false;
+ bool bCenterAlign = false;
+ if (pszKey[0] == 'r' || pszKey[0] == 'R')
+ {
+ bRightAlign = true;
+ pszKey++;
+ }
+ else if (pszKey[0] == 'c' || pszKey[0] == 'C')
+ {
+ bCenterAlign = true;
+ pszKey++;
+ }
+
+ // get the value
+ iValue = atoi(pszKey);
+ iValue = scheme()->GetProportionalScaledValueEx(panel->GetScheme(), iValue);
+
+ int screenSize = GetScreenSize();
+ // now correct the alignment
+ if ( bRightAlign )
+ {
+ iValue = screenSize - iValue;
+ }
+ else if ( bCenterAlign )
+ {
+ iValue = (screenSize / 2) + iValue;
+ }
+
+ return iValue;
+ }
+
+ virtual int GetScreenSize( void )
+ {
+ int screenWide, screenTall;
+ surface()->GetScreenSize(screenWide, screenTall);
+ return screenWide;
+ }
+
+ virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ // Won't work with this, don't use it.
+ Assert(0);
+ }
+
+ virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ *(int *)data = ExtractValue( panel, kv->GetString( entry->name() ) );
+ }
+ virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ *(int *)data = ExtractValue( panel, entry->defaultvalue() );
+ }
+};
+
+class CProportionalIntWithScreenspacePropertyY : public CProportionalIntWithScreenspacePropertyX
+{
+public:
+ virtual int GetScreenSize( void )
+ {
+ int screenWide, screenTall;
+ surface()->GetScreenSize(screenWide, screenTall);
+ return screenTall;
+ }
+};
+
+class CColorProperty : public vgui::IPanelAnimationPropertyConverter
+{
+public:
+ virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ kv->SetColor( entry->name(), *(Color *)data );
+ }
+
+ virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() );
+ Assert( scheme );
+ if ( scheme )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+
+ char const *colorName = kv->GetString( entry->name() );
+ if ( !colorName || !colorName[0] )
+ {
+ *(Color *)data = kv->GetColor( entry->name() );
+ }
+ else
+ {
+ *(Color *)data = scheme->GetColor( colorName, Color( 0, 0, 0, 0 ) );
+ }
+ }
+ }
+
+ virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry )
+ {
+ vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() );
+ Assert( scheme );
+ if ( scheme )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ *(Color *)data = scheme->GetColor( entry->defaultvalue(), Color( 0, 0, 0, 0 ) );
+ }
+ }
+};
+
+class CBoolProperty : public vgui::IPanelAnimationPropertyConverter
+{
+public:
+ virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ kv->SetInt( entry->name(), *(bool *)data ? 1 : 0 );
+ }
+
+ virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ *(bool *)data = kv->GetInt( entry->name() ) ? true : false;
+ }
+
+ virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ bool b = false;
+ if ( !stricmp( entry->defaultvalue(), "true" )||
+ atoi( entry->defaultvalue() )!= 0 )
+ {
+ b = true;
+ }
+
+ *(bool *)data = b;
+ }
+};
+
+class CStringProperty : public vgui::IPanelAnimationPropertyConverter
+{
+public:
+ virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ kv->SetString( entry->name(), (char *)data );
+ }
+
+ virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ strcpy( (char *)data, kv->GetString( entry->name() ) );
+ }
+
+ virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ strcpy( ( char * )data, entry->defaultvalue() );
+ }
+};
+
+class CHFontProperty : public vgui::IPanelAnimationPropertyConverter
+{
+public:
+ virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() );
+ Assert( scheme );
+ if ( scheme )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ char const *fontName = scheme->GetFontName( *(HFont *)data );
+ kv->SetString( entry->name(), fontName );
+ }
+ }
+
+ virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() );
+ Assert( scheme );
+ if ( scheme )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ char const *fontName = kv->GetString( entry->name() );
+ *(HFont *)data = scheme->GetFont( fontName, panel->IsProportional() );
+ }
+ }
+
+ virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry )
+ {
+ vgui::IScheme *scheme = vgui::scheme()->GetIScheme( panel->GetScheme() );
+ Assert( scheme );
+ if ( scheme )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ *(HFont *)data = scheme->GetFont( entry->defaultvalue(), panel->IsProportional() );
+ }
+ }
+};
+
+class CTextureIdProperty : public vgui::IPanelAnimationPropertyConverter
+{
+public:
+ virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+ int currentId = *(int *)data;
+
+ // lookup texture name for id
+ char texturename[ 512 ];
+ if ( currentId != -1 &&
+ surface()->DrawGetTextureFile( currentId, texturename, sizeof( texturename ) ) )
+ {
+ kv->SetString( entry->name(), texturename );
+ }
+ else
+ {
+ kv->SetString( entry->name(), "" );
+ }
+ }
+
+ virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+
+ int currentId = -1;
+
+ char const *texturename = kv->GetString( entry->name() );
+ if ( texturename && texturename[ 0 ] )
+ {
+ currentId = surface()->DrawGetTextureId( texturename );
+ if ( currentId == -1 )
+ {
+ currentId = surface()->CreateNewTextureID();
+ }
+ surface()->DrawSetTextureFile( currentId, texturename, false, true );
+ }
+
+ *(int *)data = currentId;
+ }
+
+ virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry )
+ {
+ void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
+
+ int currentId = -1;
+
+ char const *texturename = entry->defaultvalue();
+ if ( texturename && texturename[ 0 ] )
+ {
+ currentId = surface()->DrawGetTextureId( texturename );
+ if ( currentId == -1 )
+ {
+ currentId = surface()->CreateNewTextureID();
+ }
+ surface()->DrawSetTextureFile( currentId, texturename, false, true );
+ }
+
+ *(int *)data = currentId;
+ }
+};
+
+static CFloatProperty floatconverter;
+static CProportionalFloatProperty p_floatconverter;
+static CIntProperty intconverter;
+static CProportionalIntProperty p_intconverter;
+static CProportionalIntWithScreenspacePropertyX p_screenspace_intconverter_X;
+static CProportionalIntWithScreenspacePropertyY p_screenspace_intconverter_Y;
+static CColorProperty colorconverter;
+static CBoolProperty boolconverter;
+static CStringProperty stringconverter;
+static CHFontProperty fontconverter;
+static CTextureIdProperty textureidconverter;
+//static CProportionalXPosProperty xposconverter;
+//static CProportionalYPosProperty yposconverter;
+
+static CUtlDict< IPanelAnimationPropertyConverter *, int > g_AnimationPropertyConverters;
+
+static IPanelAnimationPropertyConverter *FindConverter( char const *typeName )
+{
+ int lookup = g_AnimationPropertyConverters.Find( typeName );
+ if ( lookup == g_AnimationPropertyConverters.InvalidIndex() )
+ return NULL;
+
+ IPanelAnimationPropertyConverter *converter = g_AnimationPropertyConverters[ lookup ];
+ return converter;
+}
+
+void Panel::AddPropertyConverter( char const *typeName, IPanelAnimationPropertyConverter *converter )
+{
+ int lookup = g_AnimationPropertyConverters.Find( typeName );
+ if ( lookup != g_AnimationPropertyConverters.InvalidIndex() )
+ {
+ Msg( "Already have converter for type %s, ignoring...\n", typeName );
+ return;
+ }
+
+ g_AnimationPropertyConverters.Insert( typeName, converter );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Static method to initialize all needed converters
+//-----------------------------------------------------------------------------
+void Panel::InitPropertyConverters( void )
+{
+ static bool initialized = false;
+ if ( initialized )
+ return;
+ initialized = true;
+
+ AddPropertyConverter( "float", &floatconverter );
+ AddPropertyConverter( "int", &intconverter );
+ AddPropertyConverter( "Color", &colorconverter );
+ //AddPropertyConverter( "vgui::Color", &colorconverter );
+ AddPropertyConverter( "bool", &boolconverter );
+ AddPropertyConverter( "char", &stringconverter );
+ AddPropertyConverter( "string", &stringconverter );
+ AddPropertyConverter( "HFont", &fontconverter );
+ AddPropertyConverter( "vgui::HFont", &fontconverter );
+
+ // This is an aliased type for proportional float
+ AddPropertyConverter( "proportional_float", &p_floatconverter );
+ AddPropertyConverter( "proportional_int", &p_intconverter );
+
+ AddPropertyConverter( "proportional_xpos", &p_screenspace_intconverter_X );
+ AddPropertyConverter( "proportional_ypos", &p_screenspace_intconverter_Y );
+
+ AddPropertyConverter( "textureid", &textureidconverter );
+}
+
+bool Panel::InternalRequestInfo( PanelAnimationMap *map, KeyValues *outputData )
+{
+ if ( !map )
+ return false;
+
+ Assert( outputData );
+
+ char const *name = outputData->GetName();
+
+ PanelAnimationMapEntry *e = FindPanelAnimationEntry( name, map );
+ if ( e )
+ {
+ IPanelAnimationPropertyConverter *converter = FindConverter( e->type() );
+ if ( converter )
+ {
+ converter->GetData( this, outputData, e );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Panel::InternalSetInfo( PanelAnimationMap *map, KeyValues *inputData )
+{
+ if ( !map )
+ return false;
+
+ Assert( inputData );
+
+ char const *name = inputData->GetName();
+
+ PanelAnimationMapEntry *e = FindPanelAnimationEntry( name, map );
+ if ( e )
+ {
+ IPanelAnimationPropertyConverter *converter = FindConverter( e->type() );
+ if ( converter )
+ {
+ converter->SetData( this, inputData, e );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+PanelAnimationMapEntry *Panel::FindPanelAnimationEntry( char const *scriptname, PanelAnimationMap *map )
+{
+ if ( !map )
+ return NULL;
+
+ Assert( scriptname );
+
+ // Look through mapping for entry
+ int c = map->entries.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ PanelAnimationMapEntry *e = &map->entries[ i ];
+
+ if ( !stricmp( e->name(), scriptname ) )
+ {
+ return e;
+ }
+ }
+
+ // Recurse
+ if ( map->baseMap )
+ {
+ return FindPanelAnimationEntry( scriptname, map->baseMap );
+ }
+
+ return NULL;
+}
+
+// Recursively invoke settings for PanelAnimationVars
+void Panel::InternalApplySettings( PanelAnimationMap *map, KeyValues *inResourceData)
+{
+ // Loop through keys
+ KeyValues *kv;
+
+ for ( kv = inResourceData->GetFirstSubKey(); kv; kv = kv->GetNextKey() )
+ {
+ char const *varname = kv->GetName();
+
+ PanelAnimationMapEntry *entry = FindPanelAnimationEntry( varname, GetAnimMap() );
+ if ( entry )
+ {
+ // Set value to value from script
+ IPanelAnimationPropertyConverter *converter = FindConverter( entry->type() );
+ if ( converter )
+ {
+ converter->SetData( this, inResourceData, entry );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the default values of all CPanelAnimationVars
+//-----------------------------------------------------------------------------
+void Panel::InternalInitDefaultValues( PanelAnimationMap *map )
+{
+ _flags.ClearFlag( NEEDS_DEFAULT_SETTINGS_APPLIED );
+
+ // Look through mapping for entry
+ int c = map->entries.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ PanelAnimationMapEntry *e = &map->entries[ i ];
+ Assert( e );
+ IPanelAnimationPropertyConverter *converter = FindConverter( e->type() );
+ if ( !converter )
+ continue;
+
+ converter->InitFromDefault( this, e );
+ }
+
+ if ( map->baseMap )
+ {
+ InternalInitDefaultValues( map->baseMap );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+int Panel::GetPaintBackgroundType()
+{
+ return m_nPaintBackgroundType;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : w -
+// h -
+//-----------------------------------------------------------------------------
+void Panel::GetCornerTextureSize( int& w, int& h )
+{
+ if ( m_nBgTextureId1 == -1 )
+ {
+ w = h = 0;
+ return;
+ }
+ surface()->DrawGetTextureSize(m_nBgTextureId1, w, h);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: draws a selection box
+//-----------------------------------------------------------------------------
+void Panel::DrawBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha, bool hollow /*=false*/ )
+{
+ if ( m_nBgTextureId1 == -1 ||
+ m_nBgTextureId2 == -1 ||
+ m_nBgTextureId3 == -1 ||
+ m_nBgTextureId4 == -1 )
+ {
+ return;
+ }
+
+ color[3] *= normalizedAlpha;
+
+ // work out our bounds
+ int cornerWide, cornerTall;
+ GetCornerTextureSize( cornerWide, cornerTall );
+
+ // draw the background in the areas not occupied by the corners
+ // draw it in three horizontal strips
+ surface()->DrawSetColor(color);
+ surface()->DrawFilledRect(x + cornerWide, y, x + wide - cornerWide, y + cornerTall);
+ if ( !hollow )
+ {
+ surface()->DrawFilledRect(x, y + cornerTall, x + wide, y + tall - cornerTall);
+ }
+ else
+ {
+ surface()->DrawFilledRect(x, y + cornerTall, x + cornerWide, y + tall - cornerTall);
+ surface()->DrawFilledRect(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall);
+ }
+ surface()->DrawFilledRect(x + cornerWide, y + tall - cornerTall, x + wide - cornerWide, y + tall);
+
+ // draw the corners
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] We now check each individual corner and decide whether to draw it straight or rounded
+ //=============================================================================
+ //TOP-LEFT
+ if (ShouldDrawTopLeftCornerRounded())
+ {
+ surface()->DrawSetTexture(m_nBgTextureId1);
+ surface()->DrawTexturedRect(x, y, x + cornerWide, y + cornerTall);
+ }
+ else
+ {
+ surface()->DrawFilledRect(x, y, x + cornerWide, y + cornerTall);
+ }
+
+
+ //TOP-RIGHT
+ if (ShouldDrawTopRightCornerRounded())
+ {
+ surface()->DrawSetTexture(m_nBgTextureId2);
+ surface()->DrawTexturedRect(x + wide - cornerWide, y, x + wide, y + cornerTall);
+ }
+ else
+ {
+ surface()->DrawFilledRect(x + wide - cornerWide, y, x + wide, y + cornerTall);
+ }
+
+ //BOTTOM-LEFT
+ if (ShouldDrawBottomLeftCornerRounded())
+ {
+ surface()->DrawSetTexture(m_nBgTextureId4);
+ surface()->DrawTexturedRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall);
+ }
+ else
+ {
+ surface()->DrawFilledRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall);
+ }
+
+
+ //BOTTOM-RIGHT
+ if (ShouldDrawBottomRightCornerRounded())
+ {
+ surface()->DrawSetTexture(m_nBgTextureId3);
+ surface()->DrawTexturedRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall);
+ }
+ else
+ {
+ surface()->DrawFilledRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall);
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+}
+
+void Panel::DrawBoxFade(int x, int y, int wide, int tall, Color color, float normalizedAlpha, unsigned int alpha0, unsigned int alpha1, bool bHorizontal, bool hollow /*=false*/ )
+{
+ if ( m_nBgTextureId1 == -1 ||
+ m_nBgTextureId2 == -1 ||
+ m_nBgTextureId3 == -1 ||
+ m_nBgTextureId4 == -1 ||
+ surface()->DrawGetAlphaMultiplier() == 0 )
+ {
+ return;
+ }
+
+ color[3] *= normalizedAlpha;
+
+ // work out our bounds
+ int cornerWide, cornerTall;
+ GetCornerTextureSize( cornerWide, cornerTall );
+
+ if ( !bHorizontal )
+ {
+ // draw the background in the areas not occupied by the corners
+ // draw it in three horizontal strips
+ surface()->DrawSetColor(color);
+ surface()->DrawFilledRectFade(x + cornerWide, y, x + wide - cornerWide, y + cornerTall, alpha0, alpha0, bHorizontal );
+ if ( !hollow )
+ {
+ surface()->DrawFilledRectFade(x, y + cornerTall, x + wide, y + tall - cornerTall, alpha0, alpha1, bHorizontal);
+ }
+ else
+ {
+ surface()->DrawFilledRectFade(x, y + cornerTall, x + cornerWide, y + tall - cornerTall, alpha0, alpha1, bHorizontal);
+ surface()->DrawFilledRectFade(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall, alpha0, alpha1, bHorizontal);
+ }
+ surface()->DrawFilledRectFade(x + cornerWide, y + tall - cornerTall, x + wide - cornerWide, y + tall, alpha1, alpha1, bHorizontal);
+ }
+ else
+ {
+ // draw the background in the areas not occupied by the corners
+ // draw it in three horizontal strips
+ surface()->DrawSetColor(color);
+ surface()->DrawFilledRectFade(x, y + cornerTall, x + cornerWide, y + tall - cornerTall, alpha0, alpha0, bHorizontal );
+ if ( !hollow )
+ {
+ surface()->DrawFilledRectFade(x + cornerWide, y, x + wide - cornerWide, y + tall, alpha0, alpha1, bHorizontal);
+ }
+ else
+ {
+ // FIXME: Hollow horz version not implemented
+ //surface()->DrawFilledRectFade(x, y + cornerTall, x + cornerWide, y + tall - cornerTall, alpha0, alpha1, bHorizontal);
+ //surface()->DrawFilledRectFade(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall, alpha0, alpha1, bHorizontal);
+ }
+ surface()->DrawFilledRectFade(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall, alpha1, alpha1, bHorizontal);
+ }
+
+ float fOldAlpha = color[ 3 ];
+ int iAlpha0 = fOldAlpha * ( static_cast<float>( alpha0 ) / 255.0f );
+ int iAlpha1 = fOldAlpha * ( static_cast<float>( alpha1 ) / 255.0f );
+
+ // draw the corners
+ if ( !bHorizontal )
+ {
+ color[ 3 ] = iAlpha0;
+ surface()->DrawSetColor( color );
+ surface()->DrawSetTexture(m_nBgTextureId1);
+ surface()->DrawTexturedRect(x, y, x + cornerWide, y + cornerTall);
+ surface()->DrawSetTexture(m_nBgTextureId2);
+ surface()->DrawTexturedRect(x + wide - cornerWide, y, x + wide, y + cornerTall);
+
+ color[ 3 ] = iAlpha1;
+ surface()->DrawSetColor( color );
+ surface()->DrawSetTexture(m_nBgTextureId3);
+ surface()->DrawTexturedRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall);
+ surface()->DrawSetTexture(m_nBgTextureId4);
+ surface()->DrawTexturedRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall);
+ }
+ else
+ {
+ color[ 3 ] = iAlpha0;
+ surface()->DrawSetColor( color );
+ surface()->DrawSetTexture(m_nBgTextureId1);
+ surface()->DrawTexturedRect(x, y, x + cornerWide, y + cornerTall);
+ surface()->DrawSetTexture(m_nBgTextureId4);
+ surface()->DrawTexturedRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall);
+
+ color[ 3 ] = iAlpha1;
+ surface()->DrawSetColor( color );
+ surface()->DrawSetTexture(m_nBgTextureId2);
+ surface()->DrawTexturedRect(x + wide - cornerWide, y, x + wide, y + cornerTall);
+ surface()->DrawSetTexture(m_nBgTextureId3);
+ surface()->DrawTexturedRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : x -
+// y -
+// wide -
+// tall -
+// color -
+// normalizedAlpha -
+//-----------------------------------------------------------------------------
+void Panel::DrawHollowBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha )
+{
+ DrawBox( x, y, wide, tall, color, normalizedAlpha, true );
+}
+
+//=============================================================================
+// HPE_BEGIN:
+// [menglish] Draws a hollow box similar to the already existing draw hollow box function, but takes the indents as params
+//=============================================================================
+
+void Panel::DrawHollowBox( int x, int y, int wide, int tall, Color color, float normalizedAlpha, int cornerWide, int cornerTall )
+{
+ if ( m_nBgTextureId1 == -1 ||
+ m_nBgTextureId2 == -1 ||
+ m_nBgTextureId3 == -1 ||
+ m_nBgTextureId4 == -1 )
+ {
+ return;
+ }
+
+ color[3] *= normalizedAlpha;
+
+ // draw the background in the areas not occupied by the corners
+ // draw it in three horizontal strips
+ surface()->DrawSetColor(color);
+ surface()->DrawFilledRect(x + cornerWide, y, x + wide - cornerWide, y + cornerTall);
+ surface()->DrawFilledRect(x, y + cornerTall, x + cornerWide, y + tall - cornerTall);
+ surface()->DrawFilledRect(x + wide - cornerWide, y + cornerTall, x + wide, y + tall - cornerTall);
+ surface()->DrawFilledRect(x + cornerWide, y + tall - cornerTall, x + wide - cornerWide, y + tall);
+
+ // draw the corners
+ surface()->DrawSetTexture(m_nBgTextureId1);
+ surface()->DrawTexturedRect(x, y, x + cornerWide, y + cornerTall);
+ surface()->DrawSetTexture(m_nBgTextureId2);
+ surface()->DrawTexturedRect(x + wide - cornerWide, y, x + wide, y + cornerTall);
+ surface()->DrawSetTexture(m_nBgTextureId3);
+ surface()->DrawTexturedRect(x + wide - cornerWide, y + tall - cornerTall, x + wide, y + tall);
+ surface()->DrawSetTexture(m_nBgTextureId4);
+ surface()->DrawTexturedRect(x + 0, y + tall - cornerTall, x + cornerWide, y + tall);
+}
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Purpose: draws a selection box
+//-----------------------------------------------------------------------------
+void Panel::DrawTexturedBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha )
+{
+ if ( m_nBgTextureId1 == -1 )
+ return;
+
+ color[3] *= normalizedAlpha;
+
+ surface()->DrawSetColor( color );
+ surface()->DrawSetTexture(m_nBgTextureId1);
+ surface()->DrawTexturedRect(x, y, x + wide, y + tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Marks this panel as draggable (note that children will chain to their parents to see if any parent is draggable)
+// Input : enabled -
+//-----------------------------------------------------------------------------
+void Panel::SetDragEnabled( bool enabled )
+{
+#if defined( VGUI_USEDRAGDROP )
+ // If turning it off, quit dragging if mid-drag
+ if ( !enabled &&
+ m_pDragDrop->m_bDragging )
+ {
+ OnFinishDragging( false, (MouseCode)-1 );
+ }
+ m_pDragDrop->m_bDragEnabled = enabled;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Panel::IsDragEnabled() const
+{
+#if defined( VGUI_USEDRAGDROP )
+ return m_pDragDrop->m_bDragEnabled;
+#endif
+ return false;
+}
+
+void Panel::SetShowDragHelper( bool enabled )
+{
+#if defined( VGUI_USEDRAGDROP )
+ m_pDragDrop->m_bShowDragHelper = enabled;
+#endif
+}
+
+// Use this to prevent chaining up from a parent which can mess with mouse functionality if you don't want to chain up from a child panel to the best
+// draggable parent.
+void Panel::SetBlockDragChaining( bool block )
+{
+#if defined( VGUI_USEDRAGDROP )
+ m_pDragDrop->m_bPreventChaining = block;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Panel::IsBlockingDragChaining() const
+{
+#if defined( VGUI_USEDRAGDROP )
+ return m_pDragDrop->m_bPreventChaining;
+#endif
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// accessors for m_nDragStartTolerance
+//-----------------------------------------------------------------------------
+int Panel::GetDragStartTolerance() const
+{
+#if defined( VGUI_USEDRAGDROP )
+ return m_pDragDrop->m_nDragStartTolerance;
+#endif
+ return 0;
+}
+
+void Panel::SetDragSTartTolerance( int nTolerance )
+{
+#if defined( VGUI_USEDRAGDROP )
+ m_pDragDrop->m_nDragStartTolerance = nTolerance;
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Marks this panel as droppable ( note that children will chain to their parents to see if any parent is droppable)
+// Input : enabled -
+//-----------------------------------------------------------------------------
+void Panel::SetDropEnabled( bool enabled, float flHoverContextTime /* = 0.0f */ )
+{
+#if defined( VGUI_USEDRAGDROP )
+ m_pDragDrop->m_bDropEnabled = enabled;
+ m_pDragDrop->m_flHoverContextTime = flHoverContextTime;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Panel::IsDropEnabled() const
+{
+#if defined( VGUI_USEDRAGDROP )
+ return m_pDragDrop->m_bDropEnabled;
+#endif
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Chains up to any parent
+// 1) marked DropEnabled; and
+// 2) willing to accept the drop payload
+// Input : -
+// Output : Panel
+//-----------------------------------------------------------------------------
+Panel *Panel::GetDropTarget( CUtlVector< KeyValues * >& msglist )
+{
+#if defined( VGUI_USEDRAGDROP )
+ // Found one
+ if ( m_pDragDrop->m_bDropEnabled &&
+ IsDroppable( msglist ) )
+ {
+ return this;
+ }
+
+ // Chain up
+ if ( GetParent() )
+ {
+ return GetParent()->GetDropTarget( msglist );
+ }
+#endif
+ // No luck
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Chains up to first parent marked DragEnabled
+// Input : -
+// Output : Panel
+//-----------------------------------------------------------------------------
+Panel *Panel::GetDragPanel()
+{
+#if defined( VGUI_USEDRAGDROP )
+ // If we encounter a blocker, stop chaining
+ if ( m_pDragDrop->m_bPreventChaining )
+ return NULL;
+
+ if ( m_pDragDrop->m_bDragEnabled )
+ return this;
+
+ // Chain up
+ if ( GetParent() )
+ {
+ return GetParent()->GetDragPanel();
+ }
+#endif
+ // No luck
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void Panel::OnStartDragging()
+{
+#if defined( VGUI_USEDRAGDROP )
+ // Only left mouse initiates drag/drop.
+ // FIXME: Revisit?
+ if ( !input()->IsMouseDown( MOUSE_LEFT ) )
+ return;
+
+ if ( !m_pDragDrop->m_bDragEnabled )
+ return;
+
+ if ( m_pDragDrop->m_bDragging )
+ return;
+
+ g_DragDropCapture = this;
+
+ m_pDragDrop->m_bDragStarted = false;
+ m_pDragDrop->m_bDragging = true;
+ input()->GetCursorPos( m_pDragDrop->m_nStartPos[ 0 ], m_pDragDrop->m_nStartPos[ 1 ] );
+ m_pDragDrop->m_nLastPos[ 0 ] = m_pDragDrop->m_nStartPos[ 0 ];
+ m_pDragDrop->m_nLastPos[ 1 ] = m_pDragDrop->m_nStartPos[ 1 ];
+
+ OnContinueDragging();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called if drag drop is started but not dropped on top of droppable panel...
+// Input : -
+//-----------------------------------------------------------------------------
+void Panel::OnDragFailed( CUtlVector< KeyValues * >& msglist )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void Panel::OnFinishDragging( bool mousereleased, MouseCode code, bool abort /*= false*/ )
+{
+#if defined( VGUI_USEDRAGDROP )
+ g_DragDropCapture = NULL;
+
+ if ( !m_pDragDrop->m_bDragEnabled )
+ return;
+
+ Assert( m_pDragDrop->m_bDragging );
+
+ if ( !m_pDragDrop->m_bDragging )
+ return;
+
+ int x, y;
+ input()->GetCursorPos( x, y );
+
+ m_pDragDrop->m_nLastPos[ 0 ] = x;
+ m_pDragDrop->m_nLastPos[ 1 ] = y;
+
+ if ( s_DragDropHelper.Get() )
+ {
+ s_DragDropHelper->RemovePanel( this );
+ }
+
+ m_pDragDrop->m_bDragging = false;
+
+ CUtlVector< KeyValues * >& data = m_pDragDrop->m_DragData;
+ int c = data.Count();
+
+ Panel *target = NULL;
+ bool shouldDrop = false;
+
+ if ( m_pDragDrop->m_bDragStarted )
+ {
+ char cmd[ 256 ];
+ Q_strncpy( cmd, "default", sizeof( cmd ) );
+
+ if ( mousereleased &&
+ m_pDragDrop->m_hCurrentDrop != 0 &&
+ m_pDragDrop->m_hDropContextMenu.Get() )
+ {
+ Menu *menu = m_pDragDrop->m_hDropContextMenu;
+
+ VPANEL hover = menu->IsWithinTraverse( x, y, false );
+ if ( hover )
+ {
+ Panel *pHover = ipanel()->GetPanel( hover, GetModuleName() );
+ if ( pHover )
+ {
+ // Figure out if it's a menu item...
+ int c = menu->GetItemCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ int id = menu->GetMenuID( i );
+ MenuItem *item = menu->GetMenuItem( id );
+ if ( item == pHover )
+ {
+ KeyValues *command = item->GetCommand();
+ if ( command )
+ {
+ char const *p = command->GetString( "command", "" );
+ if ( p && p[ 0 ] )
+ {
+ Q_strncpy( cmd, p, sizeof( cmd ) );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ delete menu;
+ m_pDragDrop->m_hDropContextMenu = NULL;
+ }
+
+ for ( int i = 0 ; i < c; ++i )
+ {
+ KeyValues *msg = data[ i ];
+
+ msg->SetString( "command", cmd );
+
+ msg->SetInt( "screenx", x );
+ msg->SetInt( "screeny", y );
+ }
+
+ target = m_pDragDrop->m_hCurrentDrop.Get();
+ if ( target && !abort )
+ {
+ int localmousex = x, localmousey = y;
+ // Convert screen space coordintes to coordinates relative to drop window
+ target->ScreenToLocal( localmousex, localmousey );
+
+ for ( int i = 0 ; i < c; ++i )
+ {
+ KeyValues *msg = data[ i ];
+
+ msg->SetInt( "x", localmousex );
+ msg->SetInt( "y", localmousey );
+ }
+
+ shouldDrop = true;
+ }
+
+ if ( !shouldDrop )
+ {
+ OnDragFailed( data );
+ }
+ }
+
+ m_pDragDrop->m_bDragStarted = false;
+ m_pDragDrop->m_DragPanels.RemoveAll();
+ m_pDragDrop->m_hCurrentDrop = NULL;
+
+ // Copy data ptrs out of data because OnPanelDropped might cause this panel to be deleted
+ // and our this ptr will be hosed...
+ CUtlVector< KeyValues * > temp;
+ for ( int i = 0 ; i < c; ++i )
+ {
+ temp.AddToTail( data[ i ] );
+ }
+ data.RemoveAll();
+
+ if ( shouldDrop && target )
+ {
+ target->OnPanelDropped( temp );
+ }
+ for ( int i = 0 ; i < c ; ++i )
+ {
+ temp[ i ]->deleteThis();
+ }
+#endif
+}
+
+void Panel::OnDropContextHoverShow( CUtlVector< KeyValues * >& msglist )
+{
+}
+
+void Panel::OnDropContextHoverHide( CUtlVector< KeyValues * >& msglist )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *msg -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Panel::IsDroppable( CUtlVector< KeyValues * >& msglist )
+{
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : startx -
+// starty -
+// mx -
+// my -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Panel::CanStartDragging( int startx, int starty, int mx, int my )
+{
+#if defined( VGUI_USEDRAGDROP )
+ if ( IsStartDragWhenMouseExitsPanel() )
+ {
+ ScreenToLocal( mx, my );
+ if ( mx < 0 || my < 0 )
+ return true;
+ if ( mx > GetWide() || my > GetTall() )
+ return true;
+
+ return false;
+ }
+
+ int deltax = abs( mx - startx );
+ int deltay = abs( my - starty );
+ if ( deltax > m_pDragDrop->m_nDragStartTolerance ||
+ deltay > m_pDragDrop->m_nDragStartTolerance )
+ {
+ return true;
+ }
+#endif
+ return false;
+}
+
+HCursor Panel::GetDropCursor( CUtlVector< KeyValues * >& msglist )
+{
+ return dc_arrow;
+}
+
+bool IsSelfDroppable( CUtlVector< KeyValues * > &dragData )
+{
+ if ( dragData.Count() == 0 )
+ return false;
+
+ KeyValues *pKeyValues( dragData[ 0 ] );
+ if ( !pKeyValues )
+ return false;
+
+ return pKeyValues->GetInt( "selfDroppable" ) != 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void Panel::OnContinueDragging()
+{
+#if defined( VGUI_USEDRAGDROP )
+ if ( !m_pDragDrop->m_bDragEnabled )
+ return;
+
+ if ( !m_pDragDrop->m_bDragging )
+ return;
+
+ int x, y;
+ input()->GetCursorPos( x, y );
+
+ // Update last position
+ m_pDragDrop->m_nLastPos[ 0 ] = x;
+ m_pDragDrop->m_nLastPos[ 1 ] = y;
+
+ if ( !m_pDragDrop->m_bDragStarted )
+ {
+ if ( CanStartDragging( m_pDragDrop->m_nStartPos[ 0 ], m_pDragDrop->m_nStartPos[ 1 ], x, y ) )
+ {
+ m_pDragDrop->m_bDragStarted = true;
+ CreateDragData();
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ if ( !s_DragDropHelper.Get() && m_pDragDrop->m_bShowDragHelper )
+ {
+ s_DragDropHelper = new CDragDropHelperPanel();
+ s_DragDropHelper->SetKeyBoardInputEnabled( false );
+ s_DragDropHelper->SetMouseInputEnabled( false );
+ Assert( s_DragDropHelper.Get() );
+ }
+
+ if ( !s_DragDropHelper.Get() )
+ return;
+
+ s_DragDropHelper->AddPanel( this );
+
+ Assert( m_pDragDrop->m_DragData.Count() );
+
+ vgui::PHandle oldDrop = m_pDragDrop->m_hCurrentDrop;
+
+ // See what's under that
+ m_pDragDrop->m_hCurrentDrop = NULL;
+
+ // Search under mouse pos...
+ Panel *dropTarget = FindDropTargetPanel();
+ if ( dropTarget )
+ {
+ dropTarget = dropTarget->GetDropTarget( m_pDragDrop->m_DragData );
+ }
+
+ // it's not okay until we find a droppable panel
+ surface()->SetCursor( dc_no );
+
+ if ( dropTarget )
+ {
+ if ( dropTarget != this || IsSelfDroppable( m_pDragDrop->m_DragData ) )
+ {
+ m_pDragDrop->m_hCurrentDrop = dropTarget;
+ surface()->SetCursor( dropTarget->GetDropCursor( m_pDragDrop->m_DragData ) );
+ }
+ }
+
+ if ( m_pDragDrop->m_hCurrentDrop.Get() != oldDrop.Get() )
+ {
+ if ( oldDrop.Get() )
+ {
+ oldDrop->OnPanelExitedDroppablePanel( m_pDragDrop->m_DragData );
+ }
+
+ if ( m_pDragDrop->m_hCurrentDrop.Get() )
+ {
+ m_pDragDrop->m_hCurrentDrop->OnPanelEnteredDroppablePanel( m_pDragDrop->m_DragData );
+ m_pDragDrop->m_hCurrentDrop->OnDropContextHoverHide( m_pDragDrop->m_DragData );
+
+ // Reset hover time
+ m_pDragDrop->m_lDropHoverTime = system()->GetTimeMillis();
+ m_pDragDrop->m_bDropMenuShown = false;
+ }
+
+ // Discard any stale context menu...
+ if ( m_pDragDrop->m_hDropContextMenu.Get() )
+ {
+ delete m_pDragDrop->m_hDropContextMenu.Get();
+ }
+ }
+
+ if ( m_pDragDrop->m_hCurrentDrop != 0 &&
+ m_pDragDrop->m_hDropContextMenu.Get() )
+ {
+ Menu *menu = m_pDragDrop->m_hDropContextMenu;
+
+ VPANEL hover = menu->IsWithinTraverse( x, y, false );
+ if ( hover )
+ {
+ Panel *pHover = ipanel()->GetPanel( hover, GetModuleName() );
+ if ( pHover )
+ {
+ // Figure out if it's a menu item...
+ int c = menu->GetItemCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ int id = menu->GetMenuID( i );
+ MenuItem *item = menu->GetMenuItem( id );
+ if ( item == pHover )
+ {
+ menu->SetCurrentlyHighlightedItem( id );
+ }
+ }
+ }
+ }
+ else
+ {
+ menu->ClearCurrentlyHighlightedItem();
+ }
+ }
+#endif
+}
+
+#if defined( VGUI_USEDRAGDROP )
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : DragDrop_t
+//-----------------------------------------------------------------------------
+DragDrop_t *Panel::GetDragDropInfo()
+{
+ Assert( m_pDragDrop );
+ return m_pDragDrop;
+}
+#endif
+
+void Panel::OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles )
+{
+ // Nothing here
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Virtual method to allow panels to add to the default values
+// Input : *msg -
+//-----------------------------------------------------------------------------
+void Panel::OnCreateDragData( KeyValues *msg )
+{
+ // These values are filled in for you:
+ // "panel" ptr to panel being dropped
+ // "screenx", "screeny" - drop cursor pos in screen space
+ // "x", "y" - drop coordinates relative to this window (the window being dropped upon)
+}
+
+// Called if m_flHoverContextTime was non-zero, allows droppee to preview the drop data and show an appropriate menu
+bool Panel::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist )
+{
+ return false;
+}
+
+void Panel::CreateDragData()
+{
+#if defined( VGUI_USEDRAGDROP )
+ int i, c;
+
+ if ( m_pDragDrop->m_DragData.Count() )
+ {
+ return;
+ }
+
+ PHandle h;
+ h = this;
+ m_pDragDrop->m_DragPanels.AddToTail( h );
+
+ CUtlVector< Panel * > temp;
+ OnGetAdditionalDragPanels( temp );
+ c = temp.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ h = temp[ i ];
+ m_pDragDrop->m_DragPanels.AddToTail( h );
+ }
+
+ c = m_pDragDrop->m_DragPanels.Count();
+ for ( i = 0 ; i < c; ++i )
+ {
+ Panel *sibling = m_pDragDrop->m_DragPanels[ i ].Get();
+ if ( !sibling )
+ {
+ continue;
+ }
+
+ KeyValues *msg = new KeyValues( "DragDrop" );
+ msg->SetPtr( "panel", sibling );
+
+ sibling->OnCreateDragData( msg );
+
+ m_pDragDrop->m_DragData.AddToTail( msg );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : KeyValues
+//-----------------------------------------------------------------------------
+void Panel::GetDragData( CUtlVector< KeyValues * >& list )
+{
+#if defined( VGUI_USEDRAGDROP )
+ int i, c;
+
+ list.RemoveAll();
+
+ c = m_pDragDrop->m_DragData.Count();
+ for ( i = 0 ; i < c; ++i )
+ {
+ list.AddToTail( m_pDragDrop->m_DragData[ i ] );
+ }
+#endif
+}
+
+#if defined( VGUI_USEDRAGDROP )
+CDragDropHelperPanel::CDragDropHelperPanel() : BaseClass( NULL, "DragDropHelper" )
+{
+ SetVisible( true );
+ SetPaintEnabled( false );
+ SetPaintBackgroundEnabled( false );
+ SetMouseInputEnabled( false );
+ SetKeyBoardInputEnabled( false );
+ // SetCursor( dc_none );
+ ipanel()->SetTopmostPopup( GetVPanel(), true );
+ int w, h;
+ surface()->GetScreenSize( w, h );
+ SetBounds( 0, 0, w, h );
+
+ SetPostChildPaintEnabled( true );
+
+ MakePopup( false );
+}
+
+VPANEL CDragDropHelperPanel::IsWithinTraverse(int x, int y, bool traversePopups)
+{
+ return (VPANEL)0;
+}
+
+void CDragDropHelperPanel::PostChildPaint()
+{
+ int c = m_PaintList.Count();
+ for ( int i = c - 1; i >= 0 ; --i )
+ {
+ DragHelperPanel_t& data = m_PaintList[ i ];
+
+ Panel *panel = data.m_hPanel.Get();
+ if ( !panel )
+ {
+ m_PaintList.Remove( i );
+ continue;
+ }
+
+ Panel *dropPanel = panel->GetDragDropInfo()->m_hCurrentDrop.Get();
+ if ( panel )
+ {
+ if ( !dropPanel )
+ {
+ panel->OnDraggablePanelPaint();
+ }
+ else
+ {
+ CUtlVector< Panel * > temp;
+ CUtlVector< PHandle >& data = panel->GetDragDropInfo()->m_DragPanels;
+ CUtlVector< KeyValues * >& msglist = panel->GetDragDropInfo()->m_DragData;
+ int i, c;
+ c = data.Count();
+ for ( i = 0; i < c ; ++i )
+ {
+ Panel *pPanel = data[ i ].Get();
+ if ( pPanel )
+ {
+ temp.AddToTail( pPanel );
+ }
+ }
+
+ dropPanel->OnDroppablePanelPaint( msglist, temp );
+ }
+ }
+ }
+
+ if ( c == 0 )
+ {
+ MarkForDeletion();
+ }
+}
+
+void CDragDropHelperPanel::AddPanel( Panel *current )
+{
+ if ( !current )
+ return;
+
+ Menu *hover = current->GetDragDropInfo()->m_hDropContextMenu.Get();
+
+ surface()->MovePopupToFront( GetVPanel() );
+ if ( hover && hover->IsPopup() )
+ {
+ surface()->MovePopupToFront( hover->GetVPanel() );
+ }
+
+ int c = m_PaintList.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ if ( m_PaintList[ i ].m_hPanel.Get() == current )
+ return;
+ }
+
+ DragHelperPanel_t data;
+ data.m_hPanel = current;
+ m_PaintList.AddToTail( data );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *search -
+//-----------------------------------------------------------------------------
+void CDragDropHelperPanel::RemovePanel( Panel *search )
+{
+ int c = m_PaintList.Count();
+ for ( int i = c - 1 ; i >= 0; --i )
+ {
+ if ( m_PaintList[ i ].m_hPanel.Get() == search )
+ {
+ m_PaintList.Remove( i );
+ return;
+ }
+ }
+}
+#endif
+//-----------------------------------------------------------------------------
+// Purpose: Enumerates panels under mouse x,y
+// Input : panelList -
+// x -
+// y -
+// check -
+//-----------------------------------------------------------------------------
+void Panel::FindDropTargetPanel_R( CUtlVector< VPANEL >& panelList, int x, int y, VPANEL check )
+{
+#if defined( VGUI_USEDRAGDROP )
+ if ( !ipanel()->IsFullyVisible( check ) )
+ return;
+
+ if ( ::ShouldHandleInputMessage( check ) && ipanel()->IsWithinTraverse( check, x, y, false ) )
+ {
+ panelList.AddToTail( check );
+ }
+
+ CUtlVector< VPANEL > &children = ipanel()->GetChildren( check );
+ int childCount = children.Count();
+ for ( int i = 0; i < childCount; i++ )
+ {
+ VPANEL child = children[ i ];
+ FindDropTargetPanel_R( panelList, x, y, child );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Panel
+//-----------------------------------------------------------------------------
+Panel *Panel::FindDropTargetPanel()
+{
+#if defined( VGUI_USEDRAGDROP )
+ if ( !s_DragDropHelper.Get() )
+ return NULL;
+
+ CUtlVector< VPANEL > hits;
+
+ int x, y;
+ input()->GetCursorPos( x, y );
+
+ VPANEL embedded = surface()->GetEmbeddedPanel();
+ VPANEL helper = s_DragDropHelper.Get()->GetVPanel();
+
+ if ( surface()->IsCursorVisible() && surface()->IsWithin(x, y) )
+ {
+ // faster version of code below
+ // checks through each popup in order, top to bottom windows
+ int c = surface()->GetPopupCount();
+ for (int i = c - 1; i >= 0 && hits.Count() == 0; i--)
+ {
+ VPANEL popup = surface()->GetPopup(i);
+ if ( popup == embedded )
+ continue;
+
+ // Don't return helper panel!!!
+ if ( popup == helper )
+ continue;
+
+ if ( !ipanel()->IsFullyVisible( popup ) )
+ continue;
+
+ FindDropTargetPanel_R( hits, x, y, popup );
+ }
+
+ // Check embedded
+ if ( !hits.Count() )
+ {
+ FindDropTargetPanel_R( hits, x, y, embedded );
+ }
+ }
+
+ // Nothing under mouse...
+ if ( !hits.Count() )
+ return NULL;
+
+ // Return topmost panel under mouse, if it's visible to this .dll
+ Panel *panel = NULL;
+ int nCount = hits.Count();
+ while ( --nCount >= 0 )
+ {
+ panel = ipanel()->GetPanel( hits[ nCount ], GetModuleName() );
+ if ( panel )
+ return panel;
+ }
+#endif
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Mouse is on draggable panel and has started moving, but is not over a droppable panel yet
+// Input : -
+//-----------------------------------------------------------------------------
+void Panel::OnDraggablePanelPaint()
+{
+#if defined( VGUI_USEDRAGDROP )
+ int sw, sh;
+ GetSize( sw, sh );
+
+ int x, y;
+ input()->GetCursorPos( x, y );
+ int w, h;
+
+ w = min( sw, 80 );
+ h = min( sh, 80 );
+ x -= ( w >> 1 );
+ y -= ( h >> 1 );
+
+ surface()->DrawSetColor( m_clrDragFrame );
+ surface()->DrawOutlinedRect( x, y, x + w, y + h );
+
+ if ( m_pDragDrop->m_DragPanels.Count() > 1 )
+ {
+ surface()->DrawSetTextColor( m_clrDragFrame );
+ surface()->DrawSetTextFont( m_infoFont );
+ surface()->DrawSetTextPos( x + 5, y + 2 );
+
+ wchar_t sz[ 64 ];
+ V_swprintf_safe( sz, L"[ %i ]", m_pDragDrop->m_DragPanels.Count() );
+
+ surface()->DrawPrintText( sz, wcslen( sz ) );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Mouse is now over a droppable panel
+// Input : *dragPanel -
+//-----------------------------------------------------------------------------
+void Panel::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels )
+{
+#if defined( VGUI_USEDRAGDROP )
+ if ( !dragPanels.Count() )
+ return;
+
+ // Convert this panel's bounds to screen space
+ int w, h;
+ GetSize( w, h );
+
+ int x, y;
+ x = y = 0;
+ LocalToScreen( x, y );
+
+ surface()->DrawSetColor( m_clrDropFrame );
+ // Draw 2 pixel frame
+ surface()->DrawOutlinedRect( x, y, x + w, y + h );
+ surface()->DrawOutlinedRect( x+1, y+1, x + w-1, y + h-1 );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Color
+//-----------------------------------------------------------------------------
+Color Panel::GetDropFrameColor()
+{
+#if defined( VGUI_USEDRAGDROP )
+ return m_clrDropFrame;
+#endif
+ return Color(0, 0, 0, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Color
+//-----------------------------------------------------------------------------
+Color Panel::GetDragFrameColor()
+{
+#if defined( VGUI_USEDRAGDROP )
+ return m_clrDragFrame;
+#endif
+ return Color(0, 0, 0, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *data -
+//-----------------------------------------------------------------------------
+void Panel::OnPanelDropped( CUtlVector< KeyValues * >& data )
+{
+ // Empty. Derived classes would implement handlers here
+}
+
+//-----------------------------------------------------------------------------
+// called on droptarget when draggable panel enters droptarget
+//-----------------------------------------------------------------------------
+void Panel::OnPanelEnteredDroppablePanel( CUtlVector< KeyValues * >& msglist )
+{
+ // Empty. Derived classes would implement handlers here
+}
+
+//-----------------------------------------------------------------------------
+// called on droptarget when draggable panel exits droptarget
+//-----------------------------------------------------------------------------
+void Panel::OnPanelExitedDroppablePanel ( CUtlVector< KeyValues * >& msglist )
+{
+ // Empty. Derived classes would implement handlers here
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void Panel::DragDropStartDragging()
+{
+#if defined( VGUI_USEDRAGDROP )
+ // We somehow missed a mouse release, cancel the previous drag
+ if ( g_DragDropCapture.Get() )
+ {
+ if ( HasParent( g_DragDropCapture.Get()->GetVPanel() ) )
+ return;
+
+ bool started = g_DragDropCapture->GetDragDropInfo()->m_bDragStarted;
+ g_DragDropCapture->OnFinishDragging( true, (MouseCode)-1 );
+ if ( started )
+ {
+ return;
+ }
+ }
+
+ // Find actual target panel
+ Panel *panel = GetDragPanel();
+ if ( !panel )
+ return;
+
+ DragDrop_t *data = panel->GetDragDropInfo();
+ if ( !data )
+ return;
+
+ if ( !panel->IsDragEnabled() )
+ return;
+
+ if ( data->m_bDragging )
+ return;
+
+ panel->OnStartDragging();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool Panel::IsBeingDragged()
+{
+#if defined( VGUI_USEDRAGDROP )
+ if ( !g_DragDropCapture.Get() )
+ return false;
+
+ if ( g_DragDropCapture.Get() == this )
+ return true;
+
+ // If we encounter a blocker, stop chaining
+ if ( m_pDragDrop->m_bPreventChaining )
+ return false;
+
+ // Chain up
+ if ( GetParent() )
+ {
+ return GetParent()->IsBeingDragged();
+ }
+#endif
+ // No luck
+ return false;
+}
+
+struct srect_t
+{
+ int x0, y0;
+ int x1, y1;
+
+ bool IsDegenerate()
+ {
+ if ( x1 - x0 <= 0 )
+ return true;
+ if ( y1 - y0 <= 0 )
+ return true;
+ return false;
+ }
+};
+
+// Draws a filled rect of specified bounds, but omits the bounds of the skip panel from those bounds
+void Panel::FillRectSkippingPanel( const Color &clr, int x, int y, int w, int h, Panel *skipPanel )
+{
+ int sx = 0, sy = 0, sw, sh;
+ skipPanel->GetSize( sw, sh );
+ skipPanel->LocalToScreen( sx, sy );
+ ScreenToLocal( sx, sy );
+
+ surface()->DrawSetColor( clr );
+
+ srect_t r1;
+ r1.x0 = x;
+ r1.y0 = y;
+ r1.x1 = x + w;
+ r1.y1 = y + h;
+
+ srect_t r2;
+ r2.x0 = sx;
+ r2.y0 = sy;
+ r2.x1 = sx + sw;
+ r2.y1 = sy + sh;
+
+ int topy = r1.y0;
+ int bottomy = r1.y1;
+
+ // We'll descend vertically and draw:
+ // 1 a possible bar across the top
+ // 2 a possible bar across the bottom
+ // 3 possible left bar
+ // 4 possible right bar
+
+ // Room at top?
+ if ( r2.y0 > r1.y0 )
+ {
+ topy = r2.y0;
+
+ surface()->DrawFilledRect( r1.x0, r1.y0, r1.x1, topy );
+ }
+
+ // Room at bottom?
+ if ( r2.y1 < r1.y1 )
+ {
+ bottomy = r2.y1;
+
+ surface()->DrawFilledRect( r1.x0, bottomy, r1.x1, r1.y1 );
+ }
+
+ // Room on left side?
+ if ( r2.x0 > r1.x0 )
+ {
+ int left = r2.x0;
+
+ surface()->DrawFilledRect( r1.x0, topy, left, bottomy );
+ }
+
+ // Room on right side
+ if ( r2.x1 < r1.x1 )
+ {
+ int right = r2.x1;
+
+ surface()->DrawFilledRect( right, topy, r1.x1, bottomy );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *child -
+//-----------------------------------------------------------------------------
+void Panel::SetSkipChildDuringPainting( Panel *child )
+{
+ m_SkipChild = child;
+}
+
+HPanel Panel::ToHandle() const
+{
+ return ivgui()->PanelToHandle( _vpanel );
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Panel* Panel::NavigateUp()
+{
+ Panel *target = GetNavUp();
+ if ( target )
+ {
+ NavigateFrom();
+ target->m_LastNavDirection = ND_UP;
+ target->NavigateTo();
+ }
+
+ return target;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Panel* Panel::NavigateDown()
+{
+ Panel *target = GetNavDown();
+ if ( target )
+ {
+ NavigateFrom();
+ target->m_LastNavDirection = ND_DOWN;
+ target->NavigateTo();
+ }
+
+ return target;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Panel* Panel::NavigateLeft()
+{
+ Panel *target = GetNavLeft();
+ if ( target )
+ {
+ NavigateFrom();
+ target->m_LastNavDirection = ND_LEFT;
+ target->NavigateTo();
+ }
+ return target;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Panel* Panel::NavigateRight()
+{
+ Panel *target = GetNavRight();
+ if ( target )
+ {
+ NavigateFrom();
+ target->m_LastNavDirection = ND_RIGHT;
+ target->NavigateTo();
+ }
+ return target;
+}
+
+Panel* Panel::NavigateActivate()
+{
+ Panel *target = GetNavActivate();
+ if ( target )
+ {
+ NavigateFrom();
+ target->m_LastNavDirection = ND_NONE;
+ target->NavigateTo();
+ }
+ return target;
+}
+
+Panel* Panel::NavigateBack()
+{
+ Panel *target = GetNavBack();
+ if ( target )
+ {
+ NavigateFrom();
+ target->m_LastNavDirection = ND_NONE;
+ target->NavigateTo();
+ }
+ return target;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::NavigateTo()
+{
+ if ( IsX360() )
+ {
+ RequestFocus( 0 );
+ }
+
+ CallParentFunction( new KeyValues( "OnNavigateTo", "panelName", GetName() ) );
+
+ Panel *target = GetNavToRelay();
+ if ( target )
+ {
+ NavigateFrom();
+ target->m_LastNavDirection = ND_NONE;
+ NavigateToChild( target );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::NavigateFrom()
+{
+ for ( int i = 0; i < GetChildCount(); ++i )
+ {
+ Panel* currentNav = GetChild(i);
+ if ( currentNav != 0 )
+ {
+ currentNav->NavigateFrom();
+ }
+ }
+
+ CallParentFunction( new KeyValues( "OnNavigateFrom", "panelName", GetName() ) );
+
+ if ( m_pTooltips )
+ {
+ m_pTooltips->HideTooltip();
+ }
+
+ m_LastNavDirection = ND_NONE;
+}
+
+void Panel::NavigateToChild( Panel *pNavigateTo )
+{
+ for( int i = 0; i != GetChildCount(); ++i )
+ {
+ vgui::Panel *pChild = GetChild(i);
+ if( pChild )
+ pChild->NavigateFrom();
+ }
+ pNavigateTo->NavigateTo();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Panel* Panel::SetNavUp( Panel* navUp )
+{
+ Panel* lastNav = m_NavUp;
+ m_NavUp = navUp;
+
+ if( navUp )
+ m_sNavUpName = navUp->GetName();
+ else
+ m_sNavUpName.Clear();
+
+ return lastNav;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Panel* Panel::SetNavDown( Panel* navDown )
+{
+ Panel* lastNav = m_NavDown;
+ m_NavDown = navDown;
+
+ if( navDown )
+ m_sNavDownName = navDown->GetName();
+ else
+ m_sNavDownName.Clear();
+
+ return lastNav;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Panel* Panel::SetNavLeft( Panel* navLeft )
+{
+ Panel* lastNav = m_NavLeft;
+ m_NavLeft = navLeft;
+
+ if( navLeft )
+ m_sNavLeftName = navLeft->GetName();
+ else
+ m_sNavLeftName.Clear();
+
+ return lastNav;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Panel* Panel::SetNavRight( Panel* navRight )
+{
+ Panel* lastNav = m_NavRight;
+ m_NavRight = navRight;
+
+ if( navRight )
+ m_sNavRightName = navRight->GetName();
+ else
+ m_sNavRightName.Clear();
+
+ return lastNav;
+}
+
+Panel* Panel::SetNavToRelay( Panel* navToRelay )
+{
+ Panel* lastNav = m_NavToRelay;
+ m_NavToRelay = navToRelay;
+
+ return lastNav;
+}
+
+Panel* Panel::SetNavActivate( Panel* navActivate )
+{
+ Panel* lastNav = m_NavActivate;
+ m_NavActivate = navActivate;
+
+ return lastNav;
+}
+
+Panel* Panel::SetNavBack( Panel* navBack )
+{
+ Panel* lastNav = m_NavBack;
+ m_NavBack = navBack;
+
+ return lastNav;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Panel::NAV_DIRECTION Panel::GetLastNavDirection()
+{
+ return m_LastNavDirection;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::OnNavigateTo( const char* panelName )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::OnNavigateFrom( const char* panelName )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetNavUp( const char* controlName )
+{
+ if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 )
+ {
+ m_NavUp = NULL;
+ m_sNavUpName = controlName;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetNavDown( const char* controlName )
+{
+ if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 )
+ {
+ m_NavDown = NULL;
+ m_sNavDownName = controlName;
+ }
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetNavLeft( const char* controlName )
+{
+ if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 )
+ {
+ m_NavLeft = NULL;
+ m_sNavLeftName = controlName;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Panel::SetNavRight( const char* controlName )
+{
+ if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 )
+ {
+ m_NavRight = NULL;
+ m_sNavRightName = controlName;
+ }
+}
+
+void Panel::SetNavToRelay( const char* controlName )
+{
+ if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 )
+ {
+ m_NavToRelay = NULL;
+ m_sNavToRelayName = controlName;
+ }
+}
+
+void Panel::SetNavActivate( const char* controlName )
+{
+ if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 )
+ {
+ m_NavActivate = NULL;
+ m_sNavActivateName = controlName;
+ }
+}
+
+void Panel::SetNavBack( const char* controlName )
+{
+ if ( controlName && 0 < Q_strlen( controlName ) && GetParent() != 0 )
+ {
+ m_NavBack = NULL;
+ m_sNavBackName = controlName;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+vgui::Panel* Panel::GetNavUp( Panel *first )
+{
+ if ( !m_NavUp && m_sNavUpName.Length() > 0 )
+ {
+ Panel *pParent = GetParent();
+ const char *pName = m_sNavUpName.String();
+ while ( pParent && pName[ 0 ] == '<' )
+ {
+ pParent = pParent->GetParent();
+ pName++;
+ }
+
+ if ( !pParent )
+ {
+ return NULL;
+ }
+
+ Panel *foundPanel = pParent->FindChildByName( pName, true );
+ if ( foundPanel != 0 )
+ {
+ m_NavUp = foundPanel;
+ }
+ }
+
+ vgui::Panel* nextPanel = m_NavUp;
+ if( m_NavUp && m_NavUp != first && !m_NavUp->IsVisible() )
+ {
+ Panel *firstPanel = first == NULL ? this : first;
+ nextPanel = nextPanel->GetNavUp( firstPanel );
+ }
+
+ return nextPanel;
+}
+
+vgui::Panel* Panel::GetNavDown( Panel *first )
+{
+ if ( !m_NavDown && m_sNavDownName.Length() > 0 )
+ {
+ Panel *pParent = GetParent();
+ const char *pName = m_sNavDownName.String();
+ while ( pParent && pName[ 0 ] == '<' )
+ {
+ pParent = pParent->GetParent();
+ pName++;
+ }
+
+ if ( !pParent )
+ {
+ return NULL;
+ }
+
+ Panel* foundPanel = pParent->FindChildByName( pName, true );
+ if ( foundPanel != 0 )
+ {
+ m_NavDown = foundPanel->GetPanel();
+ }
+ }
+
+ vgui::Panel* nextPanel = m_NavDown;
+ if( m_NavDown && m_NavDown != first && !m_NavDown->IsVisible() )
+ {
+ Panel *firstPanel = first == NULL ? this : first;
+ nextPanel = nextPanel->GetNavDown( firstPanel );
+ }
+
+ return nextPanel;
+}
+
+vgui::Panel* Panel::GetNavLeft( Panel *first )
+{
+ if ( !m_NavLeft && m_sNavLeftName.Length() > 0 )
+ {
+ Panel *pParent = GetParent();
+ const char *pName = m_sNavLeftName.String();
+ while ( pParent && pName[ 0 ] == '<' )
+ {
+ pParent = pParent->GetParent();
+ pName++;
+ }
+
+ if ( !pParent )
+ {
+ return NULL;
+ }
+
+ Panel* foundPanel = pParent->FindChildByName( pName, true );
+ if ( foundPanel != 0 )
+ {
+ m_NavLeft = foundPanel->GetPanel();
+ }
+ }
+
+ vgui::Panel* nextPanel = m_NavLeft;
+ if( m_NavLeft && m_NavLeft != first && !m_NavLeft->IsVisible() )
+ {
+ Panel *firstPanel = first == NULL ? this : first;
+ nextPanel = nextPanel->GetNavLeft( firstPanel );
+ }
+
+ return nextPanel;
+}
+
+vgui::Panel* Panel::GetNavRight( Panel *first )
+{
+ if ( !m_NavRight && m_sNavRightName.Length() > 0 )
+ {
+ Panel *pParent = GetParent();
+ const char *pName = m_sNavRightName.String();
+ while ( pParent && pName[ 0 ] == '<' )
+ {
+ pParent = pParent->GetParent();
+ pName++;
+ }
+
+ if ( !pParent )
+ {
+ return NULL;
+ }
+
+ Panel* foundPanel = pParent->FindChildByName( pName, true );
+ if ( foundPanel != 0 )
+ {
+ m_NavRight = foundPanel->GetPanel();
+ }
+ }
+
+ vgui::Panel* nextPanel = m_NavRight;
+ if( m_NavRight && m_NavRight != first && !m_NavRight->IsVisible() )
+ {
+ Panel *firstPanel = first == NULL ? this : first;
+ nextPanel = nextPanel->GetNavRight( firstPanel );
+ }
+
+ return nextPanel;
+}
+
+vgui::Panel* Panel::GetNavToRelay( Panel *first )
+{
+ if ( !m_NavToRelay && m_sNavToRelayName.Length() > 0 )
+ {
+ Panel *pParent = this;
+ const char *pName = m_sNavToRelayName.String();
+ while ( pParent && pName[ 0 ] == '<' )
+ {
+ pParent = pParent->GetParent();
+ pName++;
+ }
+
+ if ( !pParent )
+ {
+ return NULL;
+ }
+
+ Panel* foundPanel = pParent->FindChildByName( pName, true );
+ if ( foundPanel != 0 )
+ {
+ m_NavToRelay = foundPanel->GetPanel();
+ }
+ }
+
+ vgui::Panel* nextPanel = m_NavToRelay;
+ if ( m_NavToRelay && m_NavToRelay != first && !m_NavToRelay->IsVisible() )
+ {
+ Panel *firstPanel = first == NULL ? this : first;
+ nextPanel = nextPanel->GetNavToRelay( firstPanel );
+ }
+
+ return nextPanel;
+}
+
+vgui::Panel* Panel::GetNavActivate( Panel *first )
+{
+ if ( !m_NavActivate && m_sNavActivateName.Length() > 0 )
+ {
+ Panel *pParent = GetParent();
+ const char *pName = m_sNavActivateName.String();
+ while ( pParent && pName[ 0 ] == '<' )
+ {
+ pParent = pParent->GetParent();
+ pName++;
+ }
+
+ if ( !pParent )
+ {
+ return NULL;
+ }
+
+ Panel* foundPanel = pParent->FindChildByName( pName, true );
+ if ( foundPanel != 0 )
+ {
+ m_NavActivate = foundPanel->GetPanel();
+ }
+ }
+
+ vgui::Panel* nextPanel = m_NavActivate;
+ if ( m_NavActivate && m_NavActivate != first && !m_NavActivate->IsVisible() )
+ {
+ Panel *firstPanel = first == NULL ? this : first;
+ nextPanel = nextPanel->GetNavActivate( firstPanel );
+ }
+
+ return nextPanel;
+}
+
+vgui::Panel* Panel::GetNavBack( Panel *first )
+{
+ if ( !m_NavBack && m_sNavBackName.Length() > 0 )
+ {
+ Panel *pParent = GetParent();
+ const char *pName = m_sNavBackName.String();
+ while ( pParent && pName[ 0 ] == '<' )
+ {
+ pParent = pParent->GetParent();
+ pName++;
+ }
+
+ if ( !pParent )
+ {
+ return NULL;
+ }
+
+ Panel *foundPanel = pParent->FindChildByName( pName );
+ if ( foundPanel )
+ {
+ m_NavBack = foundPanel;
+ }
+ }
+
+ vgui::Panel* nextPanel = m_NavBack;
+ if ( m_NavBack && m_NavBack != first && !m_NavBack->IsVisible() )
+ {
+ Panel *firstPanel = first == NULL ? this : first;
+ nextPanel = nextPanel->GetNavBack( firstPanel );
+ }
+
+ return nextPanel;
+}
+
+vgui::Panel* Panel::GetNavUpPanel()
+{
+ return m_NavUp;
+}
+
+vgui::Panel* Panel::GetNavDownPanel()
+{
+ return m_NavDown;
+}
+
+vgui::Panel* Panel::GetNavLeftPanel()
+{
+ return m_NavLeft;
+}
+
+vgui::Panel* Panel::GetNavRightPanel()
+{
+ return m_NavRight;
+}
+
+vgui::Panel* Panel::GetNavToRelayPanel()
+{
+ return m_NavToRelay;
+}
+
+vgui::Panel* Panel::GetNavActivatePanel()
+{
+ return m_NavActivate;
+}
+
+vgui::Panel* Panel::GetNavBackPanel()
+{
+ return m_NavBack;
+}
+
+void Panel::SetConsoleStylePanel( bool bConsoleStyle )
+{
+ m_bIsConsoleStylePanel = bConsoleStyle;
+}
+
+bool Panel::IsConsoleStylePanel() const
+{
+ return m_bIsConsoleStylePanel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Utility class for handling message map allocation
+//-----------------------------------------------------------------------------
+class CPanelMessageMapDictionary
+{
+public:
+ CPanelMessageMapDictionary() : m_PanelMessageMapPool( sizeof(PanelMessageMap), 32, CUtlMemoryPool::GROW_FAST, "CPanelMessageMapDictionary::m_PanelMessageMapPool" )
+ {
+ m_MessageMaps.RemoveAll();
+ }
+
+ PanelMessageMap *FindOrAddPanelMessageMap( char const *className );
+ PanelMessageMap *FindPanelMessageMap( char const *className );
+private:
+
+ struct PanelMessageMapDictionaryEntry
+ {
+ PanelMessageMap *map;
+ };
+
+ char const *StripNamespace( char const *className );
+
+ CUtlDict< PanelMessageMapDictionaryEntry, int > m_MessageMaps;
+ CUtlMemoryPool m_PanelMessageMapPool;
+};
+
+
+char const *CPanelMessageMapDictionary::StripNamespace( char const *className )
+{
+ if ( !strnicmp( className, "vgui::", 6 ) )
+ {
+ return className + 6;
+ }
+ return className;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find but don't add mapping
+//-----------------------------------------------------------------------------
+PanelMessageMap *CPanelMessageMapDictionary::FindPanelMessageMap( char const *className )
+{
+ int lookup = m_MessageMaps.Find( StripNamespace( className ) );
+ if ( lookup != m_MessageMaps.InvalidIndex() )
+ {
+ return m_MessageMaps[ lookup ].map;
+ }
+ return NULL;
+}
+
+#include <tier0/memdbgoff.h>
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+PanelMessageMap *CPanelMessageMapDictionary::FindOrAddPanelMessageMap( char const *className )
+{
+ PanelMessageMap *map = FindPanelMessageMap( className );
+ if ( map )
+ return map;
+
+ PanelMessageMapDictionaryEntry entry;
+ // use the alloc in place method of new
+ entry.map = new (m_PanelMessageMapPool.Alloc(sizeof(PanelMessageMap))) PanelMessageMap;
+ Construct(entry.map);
+ m_MessageMaps.Insert( StripNamespace( className ), entry );
+ return entry.map;
+}
+#include <tier0/memdbgon.h>
+
+#if defined( VGUI_USEKEYBINDINGMAPS )
+//-----------------------------------------------------------------------------
+// Purpose: Utility class for handling keybinding map allocation
+//-----------------------------------------------------------------------------
+class CPanelKeyBindingMapDictionary
+{
+public:
+ CPanelKeyBindingMapDictionary() : m_PanelKeyBindingMapPool( sizeof(PanelKeyBindingMap), 32, CUtlMemoryPool::GROW_FAST, "CPanelKeyBindingMapDictionary::m_PanelKeyBindingMapPool" )
+ {
+ m_MessageMaps.RemoveAll();
+ }
+
+ PanelKeyBindingMap *FindOrAddPanelKeyBindingMap( char const *className );
+ PanelKeyBindingMap *FindPanelKeyBindingMap( char const *className );
+private:
+
+ struct PanelKeyBindingMapDictionaryEntry
+ {
+ PanelKeyBindingMap *map;
+ };
+
+ char const *StripNamespace( char const *className );
+
+ CUtlDict< PanelKeyBindingMapDictionaryEntry, int > m_MessageMaps;
+ CUtlMemoryPool m_PanelKeyBindingMapPool;
+};
+
+
+char const *CPanelKeyBindingMapDictionary::StripNamespace( char const *className )
+{
+ if ( !strnicmp( className, "vgui::", 6 ) )
+ {
+ return className + 6;
+ }
+ return className;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find but don't add mapping
+//-----------------------------------------------------------------------------
+PanelKeyBindingMap *CPanelKeyBindingMapDictionary::FindPanelKeyBindingMap( char const *className )
+{
+ int lookup = m_MessageMaps.Find( StripNamespace( className ) );
+ if ( lookup != m_MessageMaps.InvalidIndex() )
+ {
+ return m_MessageMaps[ lookup ].map;
+ }
+ return NULL;
+}
+
+#include <tier0/memdbgoff.h>
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+PanelKeyBindingMap *CPanelKeyBindingMapDictionary::FindOrAddPanelKeyBindingMap( char const *className )
+{
+ PanelKeyBindingMap *map = FindPanelKeyBindingMap( className );
+ if ( map )
+ return map;
+
+ PanelKeyBindingMapDictionaryEntry entry;
+ // use the alloc in place method of new
+ entry.map = new (m_PanelKeyBindingMapPool.Alloc(sizeof(PanelKeyBindingMap))) PanelKeyBindingMap;
+ Construct(entry.map);
+ m_MessageMaps.Insert( StripNamespace( className ), entry );
+ return entry.map;
+}
+
+#include <tier0/memdbgon.h>
+
+CPanelKeyBindingMapDictionary& GetPanelKeyBindingMapDictionary()
+{
+ static CPanelKeyBindingMapDictionary dictionary;
+ return dictionary;
+}
+
+#endif // VGUI_USEKEYBINDINGMAPS
+
+CPanelMessageMapDictionary& GetPanelMessageMapDictionary()
+{
+ static CPanelMessageMapDictionary dictionary;
+ return dictionary;
+}
+
+namespace vgui
+{
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ PanelMessageMap *FindOrAddPanelMessageMap( char const *className )
+ {
+ return GetPanelMessageMapDictionary().FindOrAddPanelMessageMap( className );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Find but don't add mapping
+ //-----------------------------------------------------------------------------
+ PanelMessageMap *FindPanelMessageMap( char const *className )
+ {
+ return GetPanelMessageMapDictionary().FindPanelMessageMap( className );
+ }
+
+#if defined( VGUI_USEKEYBINDINGMAPS )
+ CPanelKeyBindingMapDictionary& GetPanelKeyBindingMapDictionary()
+ {
+ static CPanelKeyBindingMapDictionary dictionary;
+ return dictionary;
+ }
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ PanelKeyBindingMap *FindOrAddPanelKeyBindingMap( char const *className )
+ {
+ return GetPanelKeyBindingMapDictionary().FindOrAddPanelKeyBindingMap( className );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Find but don't add mapping
+ //-----------------------------------------------------------------------------
+ PanelKeyBindingMap *FindPanelKeyBindingMap( char const *className )
+ {
+ return GetPanelKeyBindingMapDictionary().FindPanelKeyBindingMap( className );
+ }
+#endif // VGUI_USEKEYBINDINGMAPS
+
+SortedPanel_t::SortedPanel_t( Panel *panel )
+{
+ pPanel = panel; pButton = dynamic_cast< Button* >( panel );
+}
+
+
+void VguiPanelGetSortedChildPanelList( Panel *pParentPanel, void *pSortedPanels )
+{
+ CUtlSortVector< SortedPanel_t, CSortedPanelYLess > *pList = reinterpret_cast< CUtlSortVector< SortedPanel_t, CSortedPanelYLess >* >( pSortedPanels );
+
+ for ( int i = 0; i < pParentPanel->GetChildCount(); i++ )
+ {
+ // perform auto-layout on the child panel
+ Panel *pPanel = pParentPanel->GetChild( i );
+ if ( !pPanel || !pPanel->IsVisible() )
+ continue;
+
+ pList->Insert( SortedPanel_t( static_cast< Panel* >( pPanel ) ) );
+ }
+}
+
+void VguiPanelGetSortedChildButtonList( Panel *pParentPanel, void *pSortedPanels, char *pchFilter /*= NULL*/, int nFilterType /*= 0*/ )
+{
+ CUtlSortVector< SortedPanel_t, CSortedPanelYLess > *pList = reinterpret_cast< CUtlSortVector< SortedPanel_t, CSortedPanelYLess >* >( pSortedPanels );
+
+ for ( int i = 0; i < pParentPanel->GetChildCount(); i++ )
+ {
+ // perform auto-layout on the child panel
+ Button *pPanel = dynamic_cast< Button* >( pParentPanel->GetChild( i ) );
+ if ( !pPanel || !pPanel->IsVisible() )
+ continue;
+
+ if ( pchFilter && pchFilter[ 0 ] != '\0' )
+ {
+ char szBuff[ 128 ];
+ pPanel->GetText( szBuff, sizeof( szBuff ) );
+
+ // Prefix
+ if ( nFilterType == 0 )
+ {
+ if ( !StringHasPrefix( szBuff, pchFilter ) )
+ {
+ continue;
+ }
+ }
+ // Substring
+ else if ( nFilterType == 1 )
+ {
+ if ( V_strstr( szBuff, pchFilter ) == NULL )
+ {
+ continue;
+ }
+ }
+ }
+
+ pList->Insert( SortedPanel_t( pPanel ) );
+ }
+}
+
+int VguiPanelNavigateSortedChildButtonList( void *pSortedPanels, int nDir )
+{
+ CUtlSortVector< SortedPanel_t, CSortedPanelYLess > *pList = reinterpret_cast< CUtlSortVector< SortedPanel_t, CSortedPanelYLess >* >( pSortedPanels );
+
+ if ( pList->Count() <= 0 )
+ return -1;
+
+ if ( nDir != 0 )
+ {
+ int nArmed = -1;
+ for ( int i = 0; i < pList->Count(); i++ )
+ {
+ if ( (*pList)[ i ].pButton->IsArmed() )
+ {
+ nArmed = i;
+ break;
+ }
+ }
+
+ if ( nArmed == -1 )
+ {
+ (*pList)[ 0 ].pButton->SetArmed( true );
+ return 0;
+ }
+ else
+ {
+ int nNewArmed = clamp( nArmed + nDir, 0, pList->Count() - 1 );
+ if ( nNewArmed != nArmed )
+ {
+ (*pList)[ nArmed ].pButton->SetArmed( false );
+ }
+
+ (*pList)[ nNewArmed ].pButton->RequestFocus();
+ (*pList)[ nNewArmed ].pButton->SetArmed( true );
+
+ return nNewArmed;
+ }
+ }
+
+ return -1;
+}
+
+}
diff --git a/mp/src/vgui2/vgui_controls/PanelListPanel.cpp b/mp/src/vgui2/vgui_controls/PanelListPanel.cpp
new file mode 100644
index 00000000..5ef40966
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/PanelListPanel.cpp
@@ -0,0 +1,473 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vgui/MouseCode.h"
+#include "vgui/IInput.h"
+#include "vgui/IScheme.h"
+#include "vgui/ISurface.h"
+
+#include "vgui_controls/EditablePanel.h"
+#include "vgui_controls/ScrollBar.h"
+#include "vgui_controls/Label.h"
+#include "vgui_controls/Button.h"
+#include "vgui_controls/Controls.h"
+#include "vgui_controls/PanelListPanel.h"
+
+#include "KeyValues.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+PanelListPanel::PanelListPanel( vgui::Panel *parent, char const *panelName ) : EditablePanel( parent, panelName )
+{
+ SetBounds( 0, 0, 100, 100 );
+
+ m_vbar = new ScrollBar(this, "PanelListPanelVScroll", true);
+ m_vbar->SetVisible(false);
+ m_vbar->AddActionSignalTarget( this );
+
+ m_pPanelEmbedded = new EditablePanel(this, "PanelListEmbedded");
+ m_pPanelEmbedded->SetBounds(0, 0, 20, 20);
+ m_pPanelEmbedded->SetPaintBackgroundEnabled( false );
+ m_pPanelEmbedded->SetPaintBorderEnabled(false);
+
+ m_iFirstColumnWidth = 100; // default width
+ m_iNumColumns = 1; // 1 column by default
+
+ if ( IsProportional() )
+ {
+ m_iDefaultHeight = scheme()->GetProportionalScaledValueEx( GetScheme(), DEFAULT_HEIGHT );
+ m_iPanelBuffer = scheme()->GetProportionalScaledValueEx( GetScheme(), PANELBUFFER );
+ }
+ else
+ {
+ m_iDefaultHeight = DEFAULT_HEIGHT;
+ m_iPanelBuffer = PANELBUFFER;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+PanelListPanel::~PanelListPanel()
+{
+ // free data from table
+ DeleteAllItems();
+}
+
+void PanelListPanel::SetVerticalBufferPixels( int buffer )
+{
+ m_iPanelBuffer = buffer;
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: counts the total vertical pixels
+//-----------------------------------------------------------------------------
+int PanelListPanel::ComputeVPixelsNeeded()
+{
+ int iCurrentItem = 0;
+ int iLargestH = 0;
+
+ int pixels = 0;
+ for ( int i = 0; i < m_SortedItems.Count(); i++ )
+ {
+ Panel *panel = m_DataItems[ m_SortedItems[i] ].panel;
+ if ( !panel )
+ continue;
+
+ if ( panel->IsLayoutInvalid() )
+ {
+ panel->InvalidateLayout( true );
+ }
+
+ int iCurrentColumn = iCurrentItem % m_iNumColumns;
+
+ int w, h;
+ panel->GetSize( w, h );
+
+ if ( iLargestH < h )
+ iLargestH = h;
+
+ if ( iCurrentColumn == 0 )
+ pixels += m_iPanelBuffer; // add in buffer. between rows.
+
+ if ( iCurrentColumn >= m_iNumColumns - 1 )
+ {
+ pixels += iLargestH;
+ iLargestH = 0;
+ }
+
+ iCurrentItem++;
+ }
+
+ // Add in remaining largest height
+ pixels += iLargestH;
+
+ pixels += m_iPanelBuffer; // add in buffer below last item
+
+ return pixels;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the panel to use to render a cell
+//-----------------------------------------------------------------------------
+Panel *PanelListPanel::GetCellRenderer( int row )
+{
+ if ( !m_SortedItems.IsValidIndex(row) )
+ return NULL;
+
+ Panel *panel = m_DataItems[ m_SortedItems[row] ].panel;
+ return panel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds an item to the view
+// data->GetName() is used to uniquely identify an item
+// data sub items are matched against column header name to be used in the table
+//-----------------------------------------------------------------------------
+int PanelListPanel::AddItem( Panel *labelPanel, Panel *panel)
+{
+ Assert(panel);
+
+ if ( labelPanel )
+ {
+ labelPanel->SetParent( m_pPanelEmbedded );
+ }
+
+ panel->SetParent( m_pPanelEmbedded );
+
+ int itemID = m_DataItems.AddToTail();
+ DATAITEM &newitem = m_DataItems[itemID];
+ newitem.labelPanel = labelPanel;
+ newitem.panel = panel;
+ m_SortedItems.AddToTail(itemID);
+
+ InvalidateLayout();
+ return itemID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: iteration accessor
+//-----------------------------------------------------------------------------
+int PanelListPanel::GetItemCount() const
+{
+ return m_DataItems.Count();
+}
+
+int PanelListPanel::GetItemIDFromRow( int nRow ) const
+{
+ if ( nRow < 0 || nRow >= GetItemCount() )
+ return m_DataItems.InvalidIndex();
+ return m_SortedItems[ nRow ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Iteration. Use these until they return InvalidItemID to iterate all the items.
+//-----------------------------------------------------------------------------
+int PanelListPanel::FirstItem() const
+{
+ return m_DataItems.Head();
+}
+
+int PanelListPanel::NextItem( int nItemID ) const
+{
+ return m_DataItems.Next( nItemID );
+}
+
+int PanelListPanel::InvalidItemID() const
+{
+ return m_DataItems.InvalidIndex( );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns label panel for this itemID
+//-----------------------------------------------------------------------------
+Panel *PanelListPanel::GetItemLabel(int itemID)
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return NULL;
+
+ return m_DataItems[itemID].labelPanel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns label panel for this itemID
+//-----------------------------------------------------------------------------
+Panel *PanelListPanel::GetItemPanel(int itemID)
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return NULL;
+
+ return m_DataItems[itemID].panel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PanelListPanel::RemoveItem(int itemID)
+{
+ if ( !m_DataItems.IsValidIndex(itemID) )
+ return;
+
+ DATAITEM &item = m_DataItems[itemID];
+ if ( item.panel )
+ {
+ item.panel->MarkForDeletion();
+ }
+ if ( item.labelPanel )
+ {
+ item.labelPanel->MarkForDeletion();
+ }
+
+ m_DataItems.Remove(itemID);
+ m_SortedItems.FindAndRemove(itemID);
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: clears and deletes all the memory used by the data items
+//-----------------------------------------------------------------------------
+void PanelListPanel::DeleteAllItems()
+{
+ FOR_EACH_LL( m_DataItems, i )
+ {
+ if ( m_DataItems[i].panel )
+ {
+ delete m_DataItems[i].panel;
+ }
+ }
+
+ m_DataItems.RemoveAll();
+ m_SortedItems.RemoveAll();
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: clears and deletes all the memory used by the data items
+//-----------------------------------------------------------------------------
+void PanelListPanel::RemoveAll()
+{
+ m_DataItems.RemoveAll();
+ m_SortedItems.RemoveAll();
+
+ // move the scrollbar to the top of the list
+ m_vbar->SetValue(0);
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PanelListPanel::OnSizeChanged(int wide, int tall)
+{
+ BaseClass::OnSizeChanged(wide, tall);
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: relayouts out the panel after any internal changes
+//-----------------------------------------------------------------------------
+void PanelListPanel::PerformLayout()
+{
+ int wide, tall;
+ GetSize( wide, tall );
+
+ int vpixels = ComputeVPixelsNeeded();
+
+ m_vbar->SetRange( 0, vpixels );
+ m_vbar->SetRangeWindow( tall );
+ m_vbar->SetButtonPressedScrollValue( tall / 4 ); // standard height of labels/buttons etc.
+
+ m_vbar->SetPos( wide - m_vbar->GetWide() - 2, 0 );
+ m_vbar->SetSize( m_vbar->GetWide(), tall - 2 );
+
+ int top = m_vbar->GetValue();
+
+ m_pPanelEmbedded->SetPos( 0, -top );
+ m_pPanelEmbedded->SetSize( wide - m_vbar->GetWide(), vpixels ); // scrollbar will sit on top (zpos set explicitly)
+
+ bool bScrollbarVisible = true;
+ // If we're supposed to automatically hide the scrollbar when unnecessary, check it now
+ if ( m_bAutoHideScrollbar )
+ {
+ bScrollbarVisible = (m_pPanelEmbedded->GetTall() > tall);
+ }
+ m_vbar->SetVisible( bScrollbarVisible );
+
+ // Now lay out the controls on the embedded panel
+ int y = 0;
+ int h = 0;
+ int totalh = 0;
+
+ int xpos = m_iFirstColumnWidth + m_iPanelBuffer;
+ int iColumnWidth = ( wide - xpos - m_vbar->GetWide() - 12 ) / m_iNumColumns;
+
+ for ( int i = 0; i < m_SortedItems.Count(); i++ )
+ {
+ int iCurrentColumn = i % m_iNumColumns;
+
+ // add in a little buffer between panels
+ if ( iCurrentColumn == 0 )
+ y += m_iPanelBuffer;
+
+ DATAITEM &item = m_DataItems[ m_SortedItems[i] ];
+
+ if ( h < item.panel->GetTall() )
+ h = item.panel->GetTall();
+
+ if ( item.labelPanel )
+ {
+ item.labelPanel->SetBounds( 0, y, m_iFirstColumnWidth, item.panel->GetTall() );
+ }
+
+ item.panel->SetBounds( xpos + iCurrentColumn * iColumnWidth, y, iColumnWidth, item.panel->GetTall() );
+
+ if ( iCurrentColumn >= m_iNumColumns - 1 )
+ {
+ y += h;
+ totalh += h;
+
+ h = 0;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: scheme settings
+//-----------------------------------------------------------------------------
+void PanelListPanel::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
+ SetBgColor(GetSchemeColor("ListPanel.BgColor", GetBgColor(), pScheme));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PanelListPanel::OnSliderMoved( int position )
+{
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PanelListPanel::MoveScrollBarToTop()
+{
+ m_vbar->SetValue(0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PanelListPanel::SetFirstColumnWidth( int width )
+{
+ m_iFirstColumnWidth = width;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+int PanelListPanel::GetFirstColumnWidth()
+{
+ return m_iFirstColumnWidth;
+}
+
+void PanelListPanel::SetNumColumns( int iNumColumns )
+{
+ m_iNumColumns = iNumColumns;
+}
+
+int PanelListPanel::GetNumColumns( void )
+{
+ return m_iNumColumns;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: moves the scrollbar with the mousewheel
+//-----------------------------------------------------------------------------
+void PanelListPanel::OnMouseWheeled(int delta)
+{
+ int val = m_vbar->GetValue();
+ val -= (delta * DEFAULT_HEIGHT);
+ m_vbar->SetValue(val);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: selection handler
+//-----------------------------------------------------------------------------
+void PanelListPanel::SetSelectedPanel( Panel *panel )
+{
+ if ( panel != m_hSelectedItem )
+ {
+ // notify the panels of the selection change
+ if ( m_hSelectedItem )
+ {
+ PostMessage( m_hSelectedItem.Get(), new KeyValues("PanelSelected", "state", 0) );
+ }
+ if ( panel )
+ {
+ PostMessage( panel, new KeyValues("PanelSelected", "state", 1) );
+ }
+ m_hSelectedItem = panel;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+Panel *PanelListPanel::GetSelectedPanel()
+{
+ return m_hSelectedItem;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PanelListPanel::ScrollToItem( int itemNumber )
+{
+ if (!m_vbar->IsVisible())
+ {
+ return;
+ }
+
+ DATAITEM& item = m_DataItems[ m_SortedItems[ itemNumber ] ];
+ if ( !item.panel )
+ return;
+
+ int x, y;
+ item.panel->GetPos( x, y );
+ int lx, ly;
+ lx = x;
+ ly = y;
+ m_pPanelEmbedded->LocalToScreen( lx, ly );
+ ScreenToLocal( lx, ly );
+
+ int h = item.panel->GetTall();
+
+ if ( ly >= 0 && ly + h < GetTall() )
+ return;
+
+ m_vbar->SetValue( y );
+ InvalidateLayout();
+}
+
+
diff --git a/mp/src/vgui2/vgui_controls/PerforceFileExplorer.cpp b/mp/src/vgui2/vgui_controls/PerforceFileExplorer.cpp
new file mode 100644
index 00000000..de5cf9d1
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/PerforceFileExplorer.cpp
@@ -0,0 +1,278 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Contains a list of files, determines their perforce status
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include <vgui_controls/PerforceFileExplorer.h>
+#include <vgui_controls/PerforceFileList.h>
+#include <vgui_controls/ComboBox.h>
+#include <vgui_controls/Button.h>
+#include <vgui_controls/Tooltip.h>
+#include "tier1/KeyValues.h"
+#include "vgui/ISystem.h"
+#include "filesystem.h"
+#include <ctype.h>
+#include "p4lib/ip4.h"
+#include "tier2/tier2.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+PerforceFileExplorer::PerforceFileExplorer( Panel *pParent, const char *pPanelName ) :
+ BaseClass( pParent, pPanelName )
+{
+ m_pFileList = new PerforceFileList( this, "PerforceFileList" );
+
+ // Get the list of available drives and put them in a menu here.
+ // Start with the directory we are in.
+ m_pFullPathCombo = new ComboBox( this, "FullPathCombo", 8, false );
+ m_pFullPathCombo->GetTooltip()->SetTooltipFormatToSingleLine();
+
+ char pFullPath[MAX_PATH];
+ g_pFullFileSystem->GetCurrentDirectory( pFullPath, sizeof(pFullPath) );
+ SetCurrentDirectory( pFullPath );
+
+ m_pFullPathCombo->AddActionSignalTarget( this );
+
+ m_pFolderUpButton = new Button(this, "FolderUpButton", "", this);
+ m_pFolderUpButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_Up" );
+ m_pFolderUpButton->SetCommand( new KeyValues( "FolderUp" ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+PerforceFileExplorer::~PerforceFileExplorer()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Inherited from Frame
+//-----------------------------------------------------------------------------
+void PerforceFileExplorer::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+ m_pFolderUpButton->AddImage( scheme()->GetImage( "resource/icon_folderup", false), -3 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Inherited from Frame
+//-----------------------------------------------------------------------------
+void PerforceFileExplorer::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int x, y, w, h;
+ GetClientArea( x, y, w, h );
+
+ m_pFullPathCombo->SetBounds( x, y + 6, w - 30, 24 );
+ m_pFolderUpButton->SetBounds( x + w - 24, y + 6, 24, 24 );
+
+ m_pFileList->SetBounds( x, y + 36, w, h - 36 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the current directory
+//-----------------------------------------------------------------------------
+void PerforceFileExplorer::SetCurrentDirectory( const char *pFullPath )
+{
+ if ( !pFullPath )
+ return;
+
+ while ( isspace( *pFullPath ) )
+ {
+ ++pFullPath;
+ }
+
+ if ( !pFullPath[0] )
+ return;
+
+ m_CurrentDirectory = pFullPath;
+ m_CurrentDirectory.StripTrailingSlash();
+ Q_FixSlashes( m_CurrentDirectory.Get() );
+
+ PopulateFileList();
+ PopulateDriveList();
+
+ char pCurrentDirectory[ MAX_PATH ];
+ m_pFullPathCombo->GetText( pCurrentDirectory, sizeof(pCurrentDirectory) );
+ if ( Q_stricmp( m_CurrentDirectory.Get(), pCurrentDirectory ) )
+ {
+ char pNewDirectory[ MAX_PATH ];
+ Q_snprintf( pNewDirectory, sizeof(pNewDirectory), "%s\\", m_CurrentDirectory.Get() );
+ m_pFullPathCombo->SetText( pNewDirectory );
+ m_pFullPathCombo->GetTooltip()->SetText( pNewDirectory );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PerforceFileExplorer::PopulateDriveList()
+{
+ char pFullPath[MAX_PATH * 4];
+ char pSubDirPath[MAX_PATH * 4];
+ Q_strncpy( pFullPath, m_CurrentDirectory.Get(), sizeof( pFullPath ) );
+ Q_strncpy( pSubDirPath, m_CurrentDirectory.Get(), sizeof( pSubDirPath ) );
+
+ m_pFullPathCombo->DeleteAllItems();
+
+ // populate the drive list
+ char buf[512];
+ int len = system()->GetAvailableDrives(buf, 512);
+ char *pBuf = buf;
+ for (int i=0; i < len / 4; i++)
+ {
+ m_pFullPathCombo->AddItem(pBuf, NULL);
+
+ // is this our drive - add all subdirectories
+ if ( !_strnicmp( pBuf, pFullPath, 2 ) )
+ {
+ int indent = 0;
+ char *pData = pFullPath;
+ while (*pData)
+ {
+ if (*pData == '\\')
+ {
+ if (indent > 0)
+ {
+ memset(pSubDirPath, ' ', indent);
+ memcpy(pSubDirPath+indent, pFullPath, pData-pFullPath+1);
+ pSubDirPath[indent+pData-pFullPath+1] = 0;
+
+ m_pFullPathCombo->AddItem( pSubDirPath, NULL );
+ }
+ indent += 2;
+ }
+ pData++;
+ }
+ }
+ pBuf += 4;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Fill the filelist with the names of all the files in the current directory
+//-----------------------------------------------------------------------------
+void PerforceFileExplorer::PopulateFileList()
+{
+ // clear the current list
+ m_pFileList->RemoveAllFiles();
+
+ // Create filter string
+ char pFullFoundPath[MAX_PATH];
+ char pFilter[MAX_PATH+3];
+ Q_snprintf( pFilter, sizeof(pFilter), "%s\\*.*", m_CurrentDirectory.Get() );
+
+ // Find all files on disk
+ FileFindHandle_t h;
+ const char *pFileName = g_pFullFileSystem->FindFirstEx( pFilter, NULL, &h );
+ for ( ; pFileName; pFileName = g_pFullFileSystem->FindNext( h ) )
+ {
+ if ( !Q_stricmp( pFileName, ".." ) || !Q_stricmp( pFileName, "." ) )
+ continue;
+
+ if ( !Q_IsAbsolutePath( pFileName ) )
+ {
+ Q_snprintf( pFullFoundPath, sizeof(pFullFoundPath), "%s\\%s", m_CurrentDirectory.Get(), pFileName );
+ pFileName = pFullFoundPath;
+ }
+
+ int nItemID = m_pFileList->AddFile( pFileName, true );
+ m_pFileList->RefreshPerforceState( nItemID, true, NULL );
+ }
+ g_pFullFileSystem->FindClose( h );
+
+ // Now find all files in perforce
+ CUtlVector<P4File_t> &fileList = p4->GetFileList( m_CurrentDirectory );
+ int nCount = fileList.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ const char *pFileName = p4->String( fileList[i].m_sLocalFile );
+ if ( !pFileName[0] )
+ continue;
+
+ int nItemID = m_pFileList->FindFile( pFileName );
+ bool bFileExists = true;
+ if ( nItemID == m_pFileList->InvalidItemID() )
+ {
+ // If it didn't find it, the file must not exist
+ // since it already would have added it above
+ bFileExists = false;
+ nItemID = m_pFileList->AddFile( pFileName, false, fileList[i].m_bDir );
+ }
+ m_pFileList->RefreshPerforceState( nItemID, bFileExists, &fileList[i] );
+ }
+
+ m_pFileList->SortList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle an item in the Drive combo box being selected
+//-----------------------------------------------------------------------------
+void PerforceFileExplorer::OnTextChanged( KeyValues *kv )
+{
+ Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL );
+
+ // first check which control had its text changed!
+ if ( pPanel == m_pFullPathCombo )
+ {
+ char pCurrentDirectory[ MAX_PATH ];
+ m_pFullPathCombo->GetText( pCurrentDirectory, sizeof(pCurrentDirectory) );
+ SetCurrentDirectory( pCurrentDirectory );
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Called when the file list was doubleclicked
+//-----------------------------------------------------------------------------
+void PerforceFileExplorer::OnItemDoubleClicked()
+{
+ if ( m_pFileList->GetSelectedItemsCount() != 1 )
+ return;
+
+ int nItemID = m_pFileList->GetSelectedItem( 0 );
+ if ( m_pFileList->IsDirectoryItem( nItemID ) )
+ {
+ const char *pDirectoryName = m_pFileList->GetFile( nItemID );
+ SetCurrentDirectory( pDirectoryName );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Called when the folder up button was hit
+//-----------------------------------------------------------------------------
+void PerforceFileExplorer::OnFolderUp()
+{
+ char pUpDirectory[MAX_PATH];
+ Q_strncpy( pUpDirectory, m_CurrentDirectory.Get(), sizeof(pUpDirectory) );
+ Q_StripLastDir( pUpDirectory, sizeof(pUpDirectory) );
+ Q_StripTrailingSlash( pUpDirectory );
+
+ // This occurs at the root directory
+ if ( !Q_stricmp( pUpDirectory, "." ) )
+ return;
+ SetCurrentDirectory( pUpDirectory );
+}
+
+
+
diff --git a/mp/src/vgui2/vgui_controls/PerforceFileList.cpp b/mp/src/vgui2/vgui_controls/PerforceFileList.cpp
new file mode 100644
index 00000000..618f4443
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/PerforceFileList.cpp
@@ -0,0 +1,570 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Contains a list of files, determines their perforce status
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include <vgui_controls/PerforceFileList.h>
+#include <vgui_controls/ListPanel.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/ImageList.h>
+#include "tier1/KeyValues.h"
+#include <vgui/ISurface.h>
+#include "filesystem.h"
+#include "p4lib/ip4.h"
+#include "tier2/tier2.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+
+using namespace vgui;
+
+
+static int ListFileNameSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ NOTE_UNUSED( pPanel );
+
+ bool dir1 = item1.kv->GetInt("directory") == 1;
+ bool dir2 = item2.kv->GetInt("directory") == 1;
+
+ // if they're both not directories of files, return if dir1 is a directory (before files)
+ if ( dir1 != dir2 )
+ {
+ return dir1 ? -1 : 1;
+ }
+
+ const char *string1 = item1.kv->GetString("text");
+ const char *string2 = item2.kv->GetString("text");
+
+ // YWB: Mimic windows behavior where filenames starting with numbers are sorted based on numeric part
+ int num1 = Q_atoi( string1 );
+ int num2 = Q_atoi( string2 );
+
+ if ( num1 != 0 &&
+ num2 != 0 )
+ {
+ if ( num1 < num2 )
+ return -1;
+ else if ( num1 > num2 )
+ return 1;
+ }
+
+ // Push numbers before everything else
+ if ( num1 != 0 )
+ {
+ return -1;
+ }
+
+ // Push numbers before everything else
+ if ( num2 != 0 )
+ {
+ return 1;
+ }
+
+ return Q_stricmp( string1, string2 );
+}
+
+static int ListBaseStringSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName )
+{
+ bool dir1 = item1.kv->GetInt("directory") == 1;
+ bool dir2 = item2.kv->GetInt("directory") == 1;
+
+ // if they're both not directories of files, return if dir1 is a directory (before files)
+ if (dir1 != dir2)
+ {
+ return -1;
+ }
+
+ const char *string1 = item1.kv->GetString(fieldName);
+ const char *string2 = item2.kv->GetString(fieldName);
+ int cval = Q_stricmp(string1, string2);
+ if ( cval == 0 )
+ {
+ // Use filename to break ties
+ return ListFileNameSortFunc( pPanel, item1, item2 );
+ }
+
+ return cval;
+}
+
+static int ListBaseIntegerSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName )
+{
+ bool dir1 = item1.kv->GetInt("directory") == 1;
+ bool dir2 = item2.kv->GetInt("directory") == 1;
+
+ // if they're both not directories of files, return if dir1 is a directory (before files)
+ if (dir1 != dir2)
+ {
+ return -1;
+ }
+
+ int i1 = item1.kv->GetInt(fieldName);
+ int i2 = item2.kv->GetInt(fieldName);
+ if ( i1 == i2 )
+ {
+ // Use filename to break ties
+ return ListFileNameSortFunc( pPanel, item1, item2 );
+ }
+
+ return ( i1 < i2 ) ? -1 : 1;
+}
+
+static int ListFileSizeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ return ListBaseIntegerSortFunc( pPanel, item1, item2, "filesizeint" );
+}
+
+static int ListFileAttributesSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ return ListBaseStringSortFunc( pPanel, item1, item2, "attributes" );
+}
+
+static int ListFileTypeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
+{
+ return ListBaseStringSortFunc( pPanel, item1, item2, "type" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Dictionary of start dir contexts
+//-----------------------------------------------------------------------------
+struct ColumnInfo_t
+{
+ char const *columnName;
+ char const *columnText;
+ int startingWidth;
+ int minWidth;
+ int maxWidth;
+ int flags;
+ SortFunc *pfnSort;
+ Label::Alignment alignment;
+};
+
+static ColumnInfo_t g_ColInfo[] =
+{
+ { "text", "#PerforceFileList_Col_Name", 175, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileNameSortFunc , Label::a_west },
+ { "type", "#PerforceFileList_Col_Type", 150, 20, 10000, 0, &ListFileTypeSortFunc , Label::a_west },
+ { "in_perforce", "#PerforceFileList_Col_InPerforce", 50, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileAttributesSortFunc , Label::a_west },
+ { "synched", "#PerforceFileList_Col_Synched", 50, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileAttributesSortFunc , Label::a_west },
+ { "checked_out", "#PerforceFileList_Col_Checked_Out", 50, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileAttributesSortFunc , Label::a_west },
+ { "attributes", "#PerforceFileList_Col_Attributes", 50, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileAttributesSortFunc , Label::a_west },
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+PerforceFileList::PerforceFileList( Panel *pParent, const char *pPanelName ) :
+ BaseClass( pParent, pPanelName )
+{
+ SetMultiselectEnabled( false );
+ m_bShowDeletedFiles = false;
+
+ // list panel
+ for ( int i = 0; i < ARRAYSIZE( g_ColInfo ); ++i )
+ {
+ const ColumnInfo_t& info = g_ColInfo[ i ];
+
+ AddColumnHeader( i, info.columnName, info.columnText, info.startingWidth, info.minWidth, info.maxWidth, info.flags );
+ SetSortFunc( i, info.pfnSort );
+ SetColumnTextAlignment( i, info.alignment );
+ }
+
+ SetSortColumn( 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+PerforceFileList::~PerforceFileList()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply scheme settings
+//-----------------------------------------------------------------------------
+void PerforceFileList::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ ImageList *pImageList = new ImageList( false );
+ pImageList->AddImage( scheme()->GetImage( "resource/icon_file", false ) );
+ pImageList->AddImage( scheme()->GetImage( "resource/icon_folder", false ) );
+ pImageList->AddImage( scheme()->GetImage( "resource/icon_folder_selected", false ) );
+
+ SetImageList( pImageList, true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Toggle showing deleted files or not
+//-----------------------------------------------------------------------------
+void PerforceFileList::ShowDeletedFiles( bool bShowDeletedFiles )
+{
+ if ( m_bShowDeletedFiles != bShowDeletedFiles )
+ {
+ m_bShowDeletedFiles = bShowDeletedFiles;
+
+ for ( int i = FirstItem(); i != InvalidItemID(); i = NextItem( i ) )
+ {
+ KeyValues *pKeyValues = GetItem( i );
+ if ( !pKeyValues->GetInt( "deleted", 0 ) )
+ continue;
+
+ SetItemVisible( i, m_bShowDeletedFiles );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Add a directory to the directory list, returns client spec
+//-----------------------------------------------------------------------------
+void PerforceFileList::AddItemToDirectoryList( const char *pFullPath, int nItemID, bool bIsDirectory )
+{
+ char pDirectoryBuf[MAX_PATH];
+ Q_ExtractFilePath( pFullPath, pDirectoryBuf, sizeof(pDirectoryBuf) );
+ Q_StripTrailingSlash( pDirectoryBuf );
+ pFullPath = pDirectoryBuf;
+
+ DirectoryInfo_t *pInfo;
+ UtlSymId_t i = m_Directories.Find( pFullPath );
+ if ( i != m_Directories.InvalidIndex() )
+ {
+ pInfo = &m_Directories[i];
+ }
+ else
+ {
+ char pClientSpec[MAX_PATH];
+ if ( !p4->GetClientSpecForDirectory( pFullPath, pClientSpec, sizeof(pClientSpec) ) )
+ {
+ pClientSpec[0] = 0;
+ }
+
+ pInfo = &m_Directories[ pFullPath ];
+ pInfo->m_ClientSpec = pClientSpec;
+ }
+
+ pInfo->m_ItemIDs.AddToTail( nItemID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Add a file to the file list.
+//-----------------------------------------------------------------------------
+int PerforceFileList::AddFileToFileList( const char *pFullPath, bool bExistsOnDisk )
+{
+ bool bIsFileWriteable = bExistsOnDisk ? g_pFullFileSystem->IsFileWritable( pFullPath, NULL ) : true;
+
+ // add the file to the list
+ KeyValues *kv = new KeyValues("item");
+
+ const char *pRelativePath = Q_UnqualifiedFileName( pFullPath );
+ kv->SetString( "text", pRelativePath );
+ kv->SetString( "fullpath", pFullPath );
+ kv->SetInt( "image", 1 );
+
+ IImage *pImage = surface()->GetIconImageForFullPath( pFullPath );
+ if ( pImage )
+ {
+ kv->SetPtr( "iconImage", (void *)pImage );
+ }
+
+ kv->SetInt( "imageSelected", 1 );
+ kv->SetInt( "directory", 0 );
+
+ // These are computed by Refresh
+ kv->SetInt( "in_perforce", 0 );
+ kv->SetInt( "synched", 0 );
+ kv->SetInt( "checked_out", 0 );
+ kv->SetInt( "deleted", 0 );
+
+ wchar_t pFileType[ 80 ];
+ g_pFullFileSystem->GetFileTypeForFullPath( pFullPath, pFileType, sizeof( pFileType ) );
+
+ kv->SetWString( "type", pFileType );
+ kv->SetString( "attributes", bIsFileWriteable ? "" : "R" );
+
+ int nItemID = AddItem( kv, 0, false, false );
+ kv->deleteThis();
+
+ AddItemToDirectoryList( pFullPath, nItemID, false );
+ return nItemID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add a directory to the file list.
+//-----------------------------------------------------------------------------
+int PerforceFileList::AddDirectoryToFileList( const char *pFullPath, bool bExistsOnDisk )
+{
+ KeyValues *kv = new KeyValues("item");
+
+ const char *pRelativePath = Q_UnqualifiedFileName( pFullPath );
+ kv->SetString( "text", pRelativePath );
+ kv->SetString( "fullpath", pFullPath );
+ kv->SetPtr( "iconImage", (void *)NULL );
+ kv->SetInt( "image", 2 );
+ kv->SetInt( "imageSelected", 3 );
+ kv->SetInt( "directory", 1 );
+
+ // These are computed by Refresh
+ kv->SetInt( "in_perforce", 0 );
+ kv->SetInt( "synched", 0 );
+ kv->SetInt( "checked_out", 0 );
+ kv->SetInt( "deleted", 0 );
+
+ kv->SetString( "type", "#PerforceFileList_FileType_Folder" );
+ kv->SetString( "attributes", "D" );
+
+ int nItemID = AddItem(kv, 0, false, false);
+ kv->deleteThis();
+
+ AddItemToDirectoryList( pFullPath, nItemID, true );
+ return nItemID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add a file or directory to the file list.
+//-----------------------------------------------------------------------------
+int PerforceFileList::AddFile( const char *pFullPath, int nFileExists, int nIsDirectory )
+{
+ if ( !pFullPath )
+ return InvalidItemID();
+
+ if ( !Q_IsAbsolutePath( pFullPath ) )
+ {
+ Warning( "Absolute paths required for PerforceFileList::AddFile!\n"
+ "\"%s\" is not an abolute path", pFullPath );
+ return InvalidItemID();
+ }
+
+ char pFixedPath[MAX_PATH];
+ Q_strncpy( pFixedPath, pFullPath, sizeof(pFixedPath) );
+ Q_FixSlashes( pFixedPath );
+
+ // Check to see if the file is on disk
+ int nItemID = -1;
+ bool bFileExists, bIsDirectory;
+ if ( nFileExists < 0 )
+ {
+ bFileExists = g_pFullFileSystem->FileExists( pFixedPath ) ;
+ }
+ else
+ {
+ bFileExists = ( nFileExists != 0 );
+ }
+
+ if ( nIsDirectory < 0 )
+ {
+ if ( bFileExists )
+ {
+ bIsDirectory = g_pFullFileSystem->IsDirectory( pFixedPath );
+ }
+ else
+ {
+ int nLen = Q_strlen( pFixedPath );
+ bIsDirectory = ( pFixedPath[nLen-1] == CORRECT_PATH_SEPARATOR );
+ }
+ }
+ else
+ {
+ bIsDirectory = ( nIsDirectory != 0 );
+ }
+
+ if ( bIsDirectory )
+ {
+ nItemID = AddDirectoryToFileList( pFixedPath, bFileExists );
+ }
+ else
+ {
+ nItemID = AddFileToFileList( pFixedPath, bFileExists );
+ }
+
+ return nItemID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Remove all files from the list
+//-----------------------------------------------------------------------------
+void PerforceFileList::RemoveAllFiles()
+{
+ RemoveAll();
+ m_Directories.Clear();
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a file in the p4 list
+//-----------------------------------------------------------------------------
+static P4File_t *FindFileInPerforceList( const char *pFileName, CUtlVector<P4File_t> &fileList, bool *pFound )
+{
+ int nCount = fileList.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( pFound[i] )
+ continue;
+
+ const char *pPerforceFileName = p4->String( fileList[i].m_sLocalFile );
+ if ( !Q_stricmp( pPerforceFileName, pFileName ) )
+ {
+ pFound[i] = true;
+ return &fileList[i];
+ }
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Refresh perforce information
+//-----------------------------------------------------------------------------
+void PerforceFileList::RefreshPerforceState( int nItemID, bool bFileExists, P4File_t *pFileInfo )
+{
+ KeyValues *kv = GetItem( nItemID );
+
+ bool bIsSynched = false;
+ bool bIsFileInPerforce = (pFileInfo != NULL);
+ if ( bIsFileInPerforce )
+ {
+ if ( pFileInfo->m_bDeleted != bFileExists )
+ {
+ bIsSynched = ( pFileInfo->m_bDeleted || ( pFileInfo->m_iHeadRevision == pFileInfo->m_iHaveRevision ) );
+ }
+ }
+ else
+ {
+ bIsSynched = !bFileExists;
+ }
+
+ bool bIsDeleted = bIsFileInPerforce && !bFileExists && pFileInfo->m_bDeleted;
+
+ kv->SetInt( "in_perforce", bIsFileInPerforce );
+ kv->SetInt( "synched", bIsSynched );
+ kv->SetInt( "checked_out", bIsFileInPerforce && ( pFileInfo->m_eOpenState != P4FILE_UNOPENED ) );
+ kv->SetInt( "deleted", bIsDeleted );
+
+ if ( bIsDeleted )
+ {
+ SetItemVisible( nItemID, m_bShowDeletedFiles );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Refresh perforce information
+//-----------------------------------------------------------------------------
+void PerforceFileList::Refresh()
+{
+ /*
+ // Slow method.. does too many perforce operations
+ for ( int i = FirstItem(); i != InvalidItemID(); i = NextItem( i ) )
+ {
+ const char *pFile = GetFile( i );
+
+ P4File_t fileInfo;
+ bool bIsFileInPerforce = p4->GetFileInfo( pFile, &fileInfo );
+ bool bFileExists = g_pFullFileSystem->FileExists( pFile );
+ RefreshPerforceState( i, bFileExists, bIsFileInPerforce ? &fileInfo : NULL );
+ }
+ */
+
+ // NOTE: Reducing the # of perforce calls is important for performance
+ int nCount = m_Directories.GetNumStrings();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ const char *pDirectory = m_Directories.String(i);
+ DirectoryInfo_t *pInfo = &m_Directories[i];
+
+ // Retrives files, uses faster method to avoid finding clientspec
+ CUtlVector<P4File_t> &fileList = p4->GetFileListUsingClientSpec( pDirectory, pInfo->m_ClientSpec );
+ int nFileCount = fileList.Count();
+ bool *pFound = (bool*)_alloca( nFileCount * sizeof(bool) );
+ memset( pFound, 0, nFileCount * sizeof(bool) );
+
+ int nItemCount = pInfo->m_ItemIDs.Count();
+ for ( int j = 0; j < nItemCount; ++j )
+ {
+ int nItemID = pInfo->m_ItemIDs[j];
+ const char *pFileName = GetFile( nItemID );
+ bool bFileExists = g_pFullFileSystem->FileExists( pFileName );
+ P4File_t *pFileInfo = FindFileInPerforceList( pFileName, fileList, pFound );
+ RefreshPerforceState( nItemID, bFileExists, pFileInfo );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Is a particular list item a directory?
+//-----------------------------------------------------------------------------
+bool PerforceFileList::IsDirectoryItem( int nItemID )
+{
+ KeyValues *kv = GetItem( nItemID );
+ return kv->GetInt( "directory", 0 ) != 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the file associated with a particular item ID
+//-----------------------------------------------------------------------------
+const char *PerforceFileList::GetFile( int nItemID )
+{
+ KeyValues *kv = GetItem( nItemID );
+ Assert( kv );
+ return kv->GetString( "fullpath", "<no file>" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Find the item ID associated with a particular file
+//-----------------------------------------------------------------------------
+int PerforceFileList::FindFile( const char *pFullPath )
+{
+ for ( int i = FirstItem(); i != InvalidItemID(); i = NextItem( i ) )
+ {
+ const char *pFile = GetFile( i );
+ if ( !Q_stricmp( pFile, pFullPath ) )
+ return i;
+ }
+ return InvalidItemID();
+}
+
+
+//-----------------------------------------------------------------------------
+// Is a file already in the list?
+//-----------------------------------------------------------------------------
+bool PerforceFileList::IsFileInList( const char *pFullPath )
+{
+ return ( FindFile( pFullPath ) != InvalidItemID() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Double-click: expand folders
+//-----------------------------------------------------------------------------
+void PerforceFileList::OnMouseDoublePressed( MouseCode code )
+{
+ if ( code == MOUSE_LEFT )
+ {
+ // select the item
+ OnMousePressed(code);
+
+ // post a special message
+ if ( GetSelectedItemsCount() > 0 )
+ {
+ PostActionSignal( new KeyValues("ItemDoubleClicked" ) );
+ }
+ return;
+ }
+
+ BaseClass::OnMouseDoublePressed( code );
+}
+
+
diff --git a/mp/src/vgui2/vgui_controls/ProgressBar.cpp b/mp/src/vgui2/vgui_controls/ProgressBar.cpp
new file mode 100644
index 00000000..8fe77d2f
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ProgressBar.cpp
@@ -0,0 +1,427 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+
+#include <vgui_controls/ProgressBar.h>
+#include <vgui_controls/Controls.h>
+
+#include <vgui/ILocalize.h>
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <KeyValues.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( ProgressBar );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+ProgressBar::ProgressBar(Panel *parent, const char *panelName) : Panel(parent, panelName)
+{
+ _progress = 0.0f;
+ m_pszDialogVar = NULL;
+ SetSegmentInfo( 4, 8 );
+ SetBarInset( 4 );
+ SetMargin( 0 );
+ m_iProgressDirection = PROGRESS_EAST;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+ProgressBar::~ProgressBar()
+{
+ delete [] m_pszDialogVar;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void ProgressBar::SetSegmentInfo( int gap, int width )
+{
+ _segmentGap = gap;
+ _segmentWide = width;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the number of segment blocks drawn
+//-----------------------------------------------------------------------------
+int ProgressBar::GetDrawnSegmentCount()
+{
+ int wide, tall;
+ GetSize(wide, tall);
+ int segmentTotal = wide / (_segmentGap + _segmentWide);
+ return (int)(segmentTotal * _progress);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ProgressBar::PaintBackground()
+{
+ int wide, tall;
+ GetSize(wide, tall);
+
+ surface()->DrawSetColor(GetBgColor());
+ surface()->DrawFilledRect(0, 0, wide, tall);
+}
+
+void ProgressBar::PaintSegment( int &x, int &y, int tall, int wide )
+{
+ switch( m_iProgressDirection )
+ {
+ case PROGRESS_EAST:
+ x += _segmentGap;
+ surface()->DrawFilledRect(x, y, x + _segmentWide, y + tall - (y * 2));
+ x += _segmentWide;
+ break;
+
+ case PROGRESS_WEST:
+ x -= _segmentGap + _segmentWide;
+ surface()->DrawFilledRect(x, y, x + _segmentWide, y + tall - (y * 2));
+ break;
+
+ case PROGRESS_NORTH:
+ y -= _segmentGap + _segmentWide;
+ surface()->DrawFilledRect(x, y, x + wide - (x * 2), y + _segmentWide );
+ break;
+
+ case PROGRESS_SOUTH:
+ y += _segmentGap;
+ surface()->DrawFilledRect(x, y, x + wide - (x * 2), y + _segmentWide );
+ y += _segmentWide;
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ProgressBar::Paint()
+{
+ int wide, tall;
+ GetSize(wide, tall);
+
+ // gaps
+ int segmentTotal = 0, segmentsDrawn = 0;
+ int x = 0, y = 0;
+
+ switch( m_iProgressDirection )
+ {
+ case PROGRESS_WEST:
+ wide -= 2 * m_iBarMargin;
+ x = wide - m_iBarMargin;
+ y = m_iBarInset;
+ segmentTotal = wide / (_segmentGap + _segmentWide);
+ segmentsDrawn = (int)(segmentTotal * _progress);
+ break;
+
+ case PROGRESS_EAST:
+ wide -= 2 * m_iBarMargin;
+ x = m_iBarMargin;
+ y = m_iBarInset;
+ segmentTotal = wide / (_segmentGap + _segmentWide);
+ segmentsDrawn = (int)(segmentTotal * _progress);
+ break;
+
+ case PROGRESS_NORTH:
+ tall -= 2 * m_iBarMargin;
+ x = m_iBarInset;
+ y = tall - m_iBarMargin;
+ segmentTotal = tall / (_segmentGap + _segmentWide);
+ segmentsDrawn = (int)(segmentTotal * _progress);
+ break;
+
+ case PROGRESS_SOUTH:
+ tall -= 2 * m_iBarMargin;
+ x = m_iBarInset;
+ y = m_iBarMargin;
+ segmentTotal = tall / (_segmentGap + _segmentWide);
+ segmentsDrawn = (int)(segmentTotal * _progress);
+ break;
+ }
+
+ surface()->DrawSetColor(GetFgColor());
+ for (int i = 0; i < segmentsDrawn; i++)
+ {
+ PaintSegment( x, y, tall, wide );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ProgressBar::SetProgress(float progress)
+{
+ if (progress != _progress)
+ {
+ // clamp the progress value within the range
+ if (progress < 0.0f)
+ {
+ progress = 0.0f;
+ }
+ else if (progress > 1.0f)
+ {
+ progress = 1.0f;
+ }
+
+ _progress = progress;
+ Repaint();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+float ProgressBar::GetProgress()
+{
+ return _progress;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ProgressBar::ApplySchemeSettings(IScheme *pScheme)
+{
+ Panel::ApplySchemeSettings(pScheme);
+
+ SetFgColor(GetSchemeColor("ProgressBar.FgColor", pScheme));
+ SetBgColor(GetSchemeColor("ProgressBar.BgColor", pScheme));
+ SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: utility function for calculating a time remaining string
+//-----------------------------------------------------------------------------
+bool ProgressBar::ConstructTimeRemainingString(wchar_t *output, int outputBufferSizeInBytes, float startTime, float currentTime, float currentProgress, float lastProgressUpdateTime, bool addRemainingSuffix)
+{
+ Assert(lastProgressUpdateTime <= currentTime);
+ output[0] = 0;
+
+ // calculate pre-extrapolation values
+ float timeElapsed = lastProgressUpdateTime - startTime;
+ float totalTime = timeElapsed / currentProgress;
+
+ // calculate seconds
+ int secondsRemaining = (int)(totalTime - timeElapsed);
+ if (lastProgressUpdateTime < currentTime)
+ {
+ // old update, extrapolate
+ float progressRate = currentProgress / timeElapsed;
+ float extrapolatedProgress = progressRate * (currentTime - startTime);
+ float extrapolatedTotalTime = (currentTime - startTime) / extrapolatedProgress;
+ secondsRemaining = (int)(extrapolatedTotalTime - timeElapsed);
+ }
+ // if there's some time, make sure it's at least one second left
+ if ( secondsRemaining == 0 && ( ( totalTime - timeElapsed ) > 0 ) )
+ {
+ secondsRemaining = 1;
+ }
+
+ // calculate minutes
+ int minutesRemaining = 0;
+ while (secondsRemaining >= 60)
+ {
+ minutesRemaining++;
+ secondsRemaining -= 60;
+ }
+
+ char minutesBuf[16];
+ Q_snprintf(minutesBuf, sizeof( minutesBuf ), "%d", minutesRemaining);
+ char secondsBuf[16];
+ Q_snprintf(secondsBuf, sizeof( secondsBuf ), "%d", secondsRemaining);
+
+ if (minutesRemaining > 0)
+ {
+ wchar_t unicodeMinutes[16];
+ g_pVGuiLocalize->ConvertANSIToUnicode(minutesBuf, unicodeMinutes, sizeof( unicodeMinutes ));
+ wchar_t unicodeSeconds[16];
+ g_pVGuiLocalize->ConvertANSIToUnicode(secondsBuf, unicodeSeconds, sizeof( unicodeSeconds ));
+
+ const char *unlocalizedString = "#vgui_TimeLeftMinutesSeconds";
+ if (minutesRemaining == 1 && secondsRemaining == 1)
+ {
+ unlocalizedString = "#vgui_TimeLeftMinuteSecond";
+ }
+ else if (minutesRemaining == 1)
+ {
+ unlocalizedString = "#vgui_TimeLeftMinuteSeconds";
+ }
+ else if (secondsRemaining == 1)
+ {
+ unlocalizedString = "#vgui_TimeLeftMinutesSecond";
+ }
+
+ char unlocString[64];
+ Q_strncpy(unlocString, unlocalizedString,sizeof( unlocString ));
+ if (addRemainingSuffix)
+ {
+ Q_strncat(unlocString, "Remaining", sizeof(unlocString ), COPY_ALL_CHARACTERS);
+ }
+ g_pVGuiLocalize->ConstructString(output, outputBufferSizeInBytes, g_pVGuiLocalize->Find(unlocString), 2, unicodeMinutes, unicodeSeconds);
+
+ }
+ else if (secondsRemaining > 0)
+ {
+ wchar_t unicodeSeconds[16];
+ g_pVGuiLocalize->ConvertANSIToUnicode(secondsBuf, unicodeSeconds, sizeof( unicodeSeconds ));
+
+ const char *unlocalizedString = "#vgui_TimeLeftSeconds";
+ if (secondsRemaining == 1)
+ {
+ unlocalizedString = "#vgui_TimeLeftSecond";
+ }
+ char unlocString[64];
+ Q_strncpy(unlocString, unlocalizedString,sizeof(unlocString));
+ if (addRemainingSuffix)
+ {
+ Q_strncat(unlocString, "Remaining",sizeof(unlocString), COPY_ALL_CHARACTERS);
+ }
+ g_pVGuiLocalize->ConstructString(output, outputBufferSizeInBytes, g_pVGuiLocalize->Find(unlocString), 1, unicodeSeconds);
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void ProgressBar::SetBarInset( int pixels )
+{
+ m_iBarInset = pixels;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+int ProgressBar::GetBarInset( void )
+{
+ return m_iBarInset;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void ProgressBar::SetMargin( int pixels )
+{
+ m_iBarMargin = pixels;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+int ProgressBar::GetMargin()
+{
+ return m_iBarMargin;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ProgressBar::ApplySettings(KeyValues *inResourceData)
+{
+ _progress = inResourceData->GetFloat("progress", 0.0f);
+
+ const char *dialogVar = inResourceData->GetString("variable", "");
+ if (dialogVar && *dialogVar)
+ {
+ m_pszDialogVar = new char[strlen(dialogVar) + 1];
+ strcpy(m_pszDialogVar, dialogVar);
+ }
+
+ BaseClass::ApplySettings(inResourceData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ProgressBar::GetSettings(KeyValues *outResourceData)
+{
+ BaseClass::GetSettings(outResourceData);
+ outResourceData->SetFloat("progress", _progress );
+
+ if (m_pszDialogVar)
+ {
+ outResourceData->SetString("variable", m_pszDialogVar);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a string description of the panel fields for use in the UI
+//-----------------------------------------------------------------------------
+const char *ProgressBar::GetDescription( void )
+{
+ static char buf[1024];
+ _snprintf(buf, sizeof(buf), "%s, string progress, string variable", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: updates progress bar bases on values
+//-----------------------------------------------------------------------------
+void ProgressBar::OnDialogVariablesChanged(KeyValues *dialogVariables)
+{
+ if (m_pszDialogVar)
+ {
+ int val = dialogVariables->GetInt(m_pszDialogVar, -1);
+ if (val >= 0.0f)
+ {
+ SetProgress(val / 100.0f);
+ }
+ }
+}
+
+
+DECLARE_BUILD_FACTORY( ContinuousProgressBar );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+ContinuousProgressBar::ContinuousProgressBar(Panel *parent, const char *panelName) : ProgressBar(parent, panelName)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ContinuousProgressBar::Paint()
+{
+ int x = 0, y = 0;
+ int wide, tall;
+ GetSize(wide, tall);
+
+ surface()->DrawSetColor(GetFgColor());
+
+ switch( m_iProgressDirection )
+ {
+ case PROGRESS_EAST:
+ surface()->DrawFilledRect( x, y, x + (int)( wide * _progress ), y + tall );
+ break;
+
+ case PROGRESS_WEST:
+ surface()->DrawFilledRect( x + (int)( wide * ( 1.0f - _progress ) ), y, x + wide, y + tall );
+ break;
+
+ case PROGRESS_NORTH:
+ surface()->DrawFilledRect( x, y + (int)( tall * ( 1.0f - _progress ) ), x + wide, y + tall );
+ break;
+
+ case PROGRESS_SOUTH:
+ surface()->DrawFilledRect( x, y, x + wide, y + (int)( tall * _progress ) );
+ break;
+ }
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/ProgressBox.cpp b/mp/src/vgui2/vgui_controls/ProgressBox.cpp
new file mode 100644
index 00000000..56624380
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ProgressBox.cpp
@@ -0,0 +1,360 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <vgui/IInput.h>
+#include <vgui/ILocalize.h>
+#include <vgui/ISurface.h>
+#include <vgui/ISystem.h>
+#include <vgui/IVGui.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/Button.h>
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/ProgressBar.h>
+#include <vgui_controls/ProgressBox.h>
+
+#include <stdio.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+ProgressBox::ProgressBox(const char *title, const char *text, const char *pszUnknownTimeString, Panel *parent) : Frame(parent, NULL, parent ? false : true)
+{
+ // save off the non-localized title, since we may need to dynamically localize it (on progress updates)
+ const wchar_t *ws = g_pVGuiLocalize->Find(title);
+ if (ws)
+ {
+ wcsncpy(m_wszTitleString, ws, sizeof(m_wszTitleString) / sizeof(wchar_t));
+ }
+ else
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode(title, m_wszTitleString, sizeof(m_wszTitleString));
+ }
+
+ m_pMessageLabel = new Label(this, NULL, pszUnknownTimeString);
+
+ ws = g_pVGuiLocalize->Find(text);
+ if (ws)
+ {
+ wcsncpy(m_wcsInfoString, ws, sizeof(m_wcsInfoString) / sizeof(wchar_t));
+ }
+ else
+ {
+ m_wcsInfoString[0] = 0;
+ }
+
+ ws = g_pVGuiLocalize->Find(pszUnknownTimeString);
+ if (ws)
+ {
+ wcsncpy(m_wszUnknownTimeString, ws, sizeof(m_wszUnknownTimeString) / sizeof(wchar_t));
+ }
+ else
+ {
+ m_wszUnknownTimeString[0] = 0;
+ }
+ Init();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+ProgressBox::ProgressBox(const wchar_t *wszTitle, const wchar_t *wszText, const wchar_t *wszUnknownTimeString, Panel *parent) : Frame(parent, NULL, parent ? false : true)
+{
+ wcsncpy(m_wszTitleString, wszTitle, sizeof(m_wszTitleString) / sizeof(wchar_t));
+ m_pMessageLabel = new Label(this, NULL, wszUnknownTimeString);
+ wcsncpy(m_wcsInfoString, wszText, sizeof(m_wcsInfoString) / sizeof(wchar_t));
+ wcsncpy(m_wszUnknownTimeString, wszUnknownTimeString, sizeof(m_wszUnknownTimeString) / sizeof(wchar_t));
+ Init();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor Helper
+//-----------------------------------------------------------------------------
+void ProgressBox::Init()
+{
+ m_pProgressBar = new ProgressBar(this, NULL);
+ m_pProgressBar->SetVisible(false);
+
+ m_pCancelButton = new Button(this, NULL, "#VGui_Cancel");
+ m_pCancelButton->SetSize(72, 24);
+ m_pCancelButton->SetCommand("Cancel");
+
+ SetMenuButtonResponsive(false);
+ SetMinimizeButtonVisible(false);
+ SetCancelButtonVisible(false);
+ SetSizeable(false);
+ SetSize(384, 128);
+ m_flCurrentProgress = 0.0f;
+ m_flFirstProgressUpdate = -0.1f;
+ m_flLastProgressUpdate = 0.0f;
+
+ // mark ourselves as needed ticked once a second, to force us to repaint
+ ivgui()->AddTickSignal(GetVPanel(), 1000);
+
+ UpdateTitle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+ProgressBox::~ProgressBox()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: resize the message label
+//-----------------------------------------------------------------------------
+void ProgressBox::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ int wide, tall;
+ m_pMessageLabel->GetContentSize(wide, tall);
+ SetSize(384, tall + 92);
+ m_pMessageLabel->SetSize(344, tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Put the message box into a modal state
+// Does not suspend execution - use addActionSignal to get return value
+//-----------------------------------------------------------------------------
+void ProgressBox::DoModal(Frame *pFrameOver)
+{
+ ShowWindow(pFrameOver);
+ input()->SetAppModalSurface(GetVPanel());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Activates the window
+//-----------------------------------------------------------------------------
+void ProgressBox::ShowWindow(Frame *pFrameOver)
+{
+ // move to the middle of the screen
+ // get the screen size
+ int wide, tall;
+ // get our dialog size
+ GetSize(wide, tall);
+
+ if (pFrameOver)
+ {
+ int frameX, frameY;
+ int frameWide, frameTall;
+ pFrameOver->GetPos(frameX, frameY);
+ pFrameOver->GetSize(frameWide, frameTall);
+
+ SetPos((frameWide - wide) / 2 + frameX, (frameTall - tall) / 2 + frameY);
+ }
+ else
+ {
+ int swide, stall;
+ surface()->GetScreenSize(swide, stall);
+ // put the dialog in the middle of the screen
+ SetPos((swide - wide) / 2, (stall - tall) / 2);
+ }
+
+ BaseClass::Activate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Put the text and OK buttons in correct place
+//-----------------------------------------------------------------------------
+void ProgressBox::PerformLayout()
+{
+ int x, y, wide, tall;
+ GetClientArea(x, y, wide, tall);
+ wide += x;
+ tall += y;
+
+ int leftEdge = x + 16;
+ m_pMessageLabel->SetPos(leftEdge, y + 12);
+ m_pProgressBar->SetPos(leftEdge, y + 14 + m_pMessageLabel->GetTall() + 2);
+ m_pProgressBar->SetSize(wide - 44, 24);
+
+ if (m_pCancelButton->IsVisible())
+ {
+ // make room for cancel
+ int px, py, pw, pt;
+ int offs = m_pCancelButton->GetWide();
+ m_pProgressBar->GetBounds(px, py, pw, pt);
+ m_pCancelButton->SetPos(px + pw - offs, py);
+ m_pProgressBar->SetSize(pw - offs - 10, pt);
+ }
+
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: updates progress bar, range [0, 1]
+//-----------------------------------------------------------------------------
+void ProgressBox::SetProgress(float progress)
+{
+ Assert(progress >= 0.0f && progress <= 1.0f);
+ m_pProgressBar->SetProgress(progress);
+ m_pProgressBar->SetVisible(true);
+
+ // only update progress timings if the progress has actually changed
+ if (progress != m_flCurrentProgress)
+ {
+ // store off timings for calculating time remaining
+ if (m_flFirstProgressUpdate < 0.0f)
+ {
+ m_flFirstProgressUpdate = (float)system()->GetFrameTime();
+ }
+ m_flCurrentProgress = progress;
+ m_flLastProgressUpdate = (float)system()->GetFrameTime();
+
+ UpdateTitle();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the info text
+//-----------------------------------------------------------------------------
+void ProgressBox::SetText(const char *text)
+{
+ m_pMessageLabel->SetText(text);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the dialog title text
+//-----------------------------------------------------------------------------
+void ProgressBox::UpdateTitle()
+{
+ // update progress text
+ wchar_t unicode[256];
+ wchar_t completion[64];
+ if ((int)(m_flCurrentProgress * 100.0f) > 0)
+ {
+ _snwprintf(completion, sizeof(completion) / sizeof(wchar_t), L"- %d%% complete", (int)(m_flCurrentProgress * 100.0f));
+ }
+ else
+ {
+ completion[0] = 0;
+ }
+ g_pVGuiLocalize->ConstructString(unicode, sizeof(unicode), m_wszTitleString, 1, completion);
+ SetTitle(unicode, true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called every render
+//-----------------------------------------------------------------------------
+void ProgressBox::OnThink()
+{
+ // calculate the progress made
+ if (m_flFirstProgressUpdate >= 0.0f && m_wcsInfoString[0])
+ {
+ wchar_t timeRemaining[128];
+ if (ProgressBar::ConstructTimeRemainingString(timeRemaining, sizeof(timeRemaining), m_flFirstProgressUpdate, (float)system()->GetFrameTime(), m_flCurrentProgress, m_flLastProgressUpdate, true))
+ {
+ wchar_t unicode[256];
+ g_pVGuiLocalize->ConstructString(unicode, sizeof(unicode), m_wcsInfoString, 1, timeRemaining);
+ m_pMessageLabel->SetText(unicode);
+ }
+ else
+ {
+ m_pMessageLabel->SetText(m_wszUnknownTimeString);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Forces us to repaint once per second
+//-----------------------------------------------------------------------------
+void ProgressBox::OnTick()
+{
+ if (m_flFirstProgressUpdate >= 0.0f)
+ {
+ Repaint();
+ }
+
+ BaseClass::OnTick();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles ESC closing dialog
+//-----------------------------------------------------------------------------
+void ProgressBox::OnCommand(const char *command)
+{
+ if (!stricmp(command, "Cancel"))
+ {
+ OnCancel();
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: close button pressed
+//-----------------------------------------------------------------------------
+void ProgressBox::OnCloseFrameButtonPressed()
+{
+ OnCancel();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deletes self when closed
+//-----------------------------------------------------------------------------
+void ProgressBox::OnClose()
+{
+ BaseClass::OnClose();
+ // modal surface is released on deletion
+ MarkForDeletion();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ProgressBox::OnShutdownRequest()
+{
+ // Shutdown the dialog
+ PostMessage(this, new KeyValues("Command", "command", "Cancel"));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: On update cancelled
+//-----------------------------------------------------------------------------
+void ProgressBox::OnCancel()
+{
+ // post a message that we've been cancelled
+ PostActionSignal(new KeyValues("ProgressBoxCancelled"));
+
+ // close this dialog
+ Close();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggles visibility of the close box.
+//-----------------------------------------------------------------------------
+void ProgressBox::SetCancelButtonVisible(bool state)
+{
+ BaseClass::SetCloseButtonVisible(state);
+ m_pCancelButton->SetVisible(state);
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ProgressBox::SetCancelButtonEnabled(bool state)
+{
+ m_pCancelButton->SetEnabled(state);
+ BaseClass::SetCloseButtonVisible(state);
+ InvalidateLayout();
+ Repaint();
+}
diff --git a/mp/src/vgui2/vgui_controls/PropertyDialog.cpp b/mp/src/vgui2/vgui_controls/PropertyDialog.cpp
new file mode 100644
index 00000000..3f81dbd3
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/PropertyDialog.cpp
@@ -0,0 +1,303 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <vgui/KeyCode.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/Button.h>
+#include <vgui_controls/PropertyDialog.h>
+#include <vgui_controls/PropertySheet.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+PropertyDialog::PropertyDialog(Panel *parent, const char *panelName) : Frame(parent, panelName)
+{
+ // create the property sheet
+ _propertySheet = new PropertySheet(this, "Sheet");
+ _propertySheet->AddActionSignalTarget(this);
+ _propertySheet->SetTabPosition(1);
+
+ // add the buttons
+ _okButton = new Button(this, "OKButton", "#PropertyDialog_OK");
+ _okButton->AddActionSignalTarget(this);
+ _okButton->SetTabPosition(2);
+ _okButton->SetCommand("OK");
+ GetFocusNavGroup().SetDefaultButton(_okButton);
+
+ _cancelButton = new Button(this, "CancelButton", "#PropertyDialog_Cancel");
+ _cancelButton->AddActionSignalTarget(this);
+ _cancelButton->SetTabPosition(3);
+ _cancelButton->SetCommand("Cancel");
+
+ _applyButton = new Button(this, "ApplyButton", "#PropertyDialog_Apply");
+ _applyButton->AddActionSignalTarget(this);
+ _applyButton->SetTabPosition(4);
+ _applyButton->SetVisible(false); // default to not visible
+ _applyButton->SetEnabled(false); // default to not enabled
+ _applyButton->SetCommand("Apply");
+
+ SetSizeable(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+PropertyDialog::~PropertyDialog()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a pointer to the PropertySheet this dialog encapsulates
+// Output : PropertySheet *
+//-----------------------------------------------------------------------------
+PropertySheet *PropertyDialog::GetPropertySheet()
+{
+ return _propertySheet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets a pointer to the currently active page.
+// Output : Panel
+//-----------------------------------------------------------------------------
+Panel *PropertyDialog::GetActivePage()
+{
+ return _propertySheet->GetActivePage();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Wrapped function
+//-----------------------------------------------------------------------------
+void PropertyDialog::AddPage(Panel *page, const char *title)
+{
+ _propertySheet->AddPage(page, title);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: reloads the data in all the property page
+//-----------------------------------------------------------------------------
+void PropertyDialog::ResetAllData()
+{
+ _propertySheet->ResetAllData();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies any changes
+//-----------------------------------------------------------------------------
+void PropertyDialog::ApplyChanges()
+{
+ OnCommand("Apply");
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets up the sheet
+//-----------------------------------------------------------------------------
+void PropertyDialog::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int iBottom = m_iSheetInsetBottom;
+ if ( IsProportional() )
+ {
+ iBottom = scheme()->GetProportionalScaledValueEx( GetScheme(), iBottom );
+ }
+
+ int x, y, wide, tall;
+ GetClientArea(x, y, wide, tall);
+ _propertySheet->SetBounds(x, y, wide, tall - iBottom);
+
+
+ // move the buttons to the bottom-right corner
+ int xpos = x + wide - 80;
+ int ypos = tall + y - 28;
+
+ if (_applyButton->IsVisible())
+ {
+ _applyButton->SetBounds(xpos, ypos, 72, 24);
+ xpos -= 80;
+ }
+
+ if (_cancelButton->IsVisible())
+ {
+ _cancelButton->SetBounds(xpos, ypos, 72, 24);
+ xpos -= 80;
+ }
+
+ _okButton->SetBounds(xpos, ypos, 72, 24);
+
+ _propertySheet->InvalidateLayout(); // tell the propertysheet to redraw!
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles command text from the buttons
+//-----------------------------------------------------------------------------
+void PropertyDialog::OnCommand(const char *command)
+{
+ if (!stricmp(command, "OK"))
+ {
+ if ( OnOK(false) )
+ {
+ OnCommand("Close");
+ }
+ _applyButton->SetEnabled(false);
+ }
+ else if (!stricmp(command, "Cancel"))
+ {
+ OnCancel();
+ Close();
+ }
+ else if (!stricmp(command, "Apply"))
+ {
+ OnOK(true);
+ _applyButton->SetEnabled(false);
+ InvalidateLayout();
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the Cancel button is pressed
+//-----------------------------------------------------------------------------
+void PropertyDialog::OnCancel()
+{
+ // designed to be overridden
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : code -
+//-----------------------------------------------------------------------------
+void PropertyDialog::OnKeyCodeTyped(KeyCode code)
+{
+ // this has been removed, since it conflicts with how we use the escape key in the game
+// if (code == KEY_ESCAPE)
+// {
+// OnCommand("Cancel");
+// }
+// else
+ {
+ BaseClass::OnKeyCodeTyped(code);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Command handler
+//-----------------------------------------------------------------------------
+bool PropertyDialog::OnOK(bool applyOnly)
+{
+ // the sheet should have the pages apply changes before we tell the world
+ _propertySheet->ApplyChanges();
+
+ // this should tell anybody who's watching us that we're done
+ PostActionSignal(new KeyValues("ApplyChanges"));
+
+ // default to closing
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Overrides build mode so it edits the sub panel
+//-----------------------------------------------------------------------------
+void PropertyDialog::ActivateBuildMode()
+{
+ // no subpanel, no build mode
+ EditablePanel *panel = dynamic_cast<EditablePanel *>(GetActivePage());
+ if (!panel)
+ return;
+
+ panel->ActivateBuildMode();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the text on the OK/Cancel buttons, overriding the default
+//-----------------------------------------------------------------------------
+void PropertyDialog::SetOKButtonText(const char *text)
+{
+ _okButton->SetText(text);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the text on the OK/Cancel buttons, overriding the default
+//-----------------------------------------------------------------------------
+void PropertyDialog::SetCancelButtonText(const char *text)
+{
+ _cancelButton->SetText(text);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the text on the apply buttons, overriding the default
+//-----------------------------------------------------------------------------
+void PropertyDialog::SetApplyButtonText(const char *text)
+{
+ _applyButton->SetText(text);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: changes the visibility of the buttons
+//-----------------------------------------------------------------------------
+void PropertyDialog::SetOKButtonVisible(bool state)
+{
+ _okButton->SetVisible(state);
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: changes the visibility of the buttons
+//-----------------------------------------------------------------------------
+void PropertyDialog::SetCancelButtonVisible(bool state)
+{
+ _cancelButton->SetVisible(state);
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: changes the visibility of the buttons
+//-----------------------------------------------------------------------------
+void PropertyDialog::SetApplyButtonVisible(bool state)
+{
+ _applyButton->SetVisible(state);
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: when a sheet changes, enable the apply button
+//-----------------------------------------------------------------------------
+void PropertyDialog::OnApplyButtonEnable()
+{
+ if (_applyButton->IsEnabled())
+ return;
+
+ EnableApplyButton(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: enable/disable the apply button
+//-----------------------------------------------------------------------------
+void PropertyDialog::EnableApplyButton(bool bEnable)
+{
+ _applyButton->SetEnabled(bEnable);
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PropertyDialog::RequestFocus(int direction)
+{
+ _propertySheet->RequestFocus(direction);
+}
diff --git a/mp/src/vgui2/vgui_controls/PropertyPage.cpp b/mp/src/vgui2/vgui_controls/PropertyPage.cpp
new file mode 100644
index 00000000..2a97e7c0
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/PropertyPage.cpp
@@ -0,0 +1,114 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vgui/IScheme.h"
+#include "vgui/KeyCode.h"
+#include "vgui/ISurface.h"
+#include "KeyValues.h"
+
+#include "vgui_controls/PropertyPage.h"
+#include "vgui_controls/Controls.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+PropertyPage::PropertyPage(Panel *parent, const char *panelName) : EditablePanel(parent, panelName)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+PropertyPage::~PropertyPage()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when page is loaded. Data should be reloaded from document into controls.
+//-----------------------------------------------------------------------------
+void PropertyPage::OnResetData()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the OK / Apply button is pressed. Changed data should be written into document.
+//-----------------------------------------------------------------------------
+void PropertyPage::OnApplyChanges()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Designed to be overriden
+//-----------------------------------------------------------------------------
+void PropertyPage::OnPageShow()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Designed to be overriden
+//-----------------------------------------------------------------------------
+void PropertyPage::OnPageHide()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pageTab -
+//-----------------------------------------------------------------------------
+void PropertyPage::OnPageTabActivated(Panel *pageTab)
+{
+ _pageTab = pageTab;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PropertyPage::OnKeyCodeTyped(KeyCode code)
+{
+ switch (code)
+ {
+ // left and right only get propogated to parents if our tab has focus
+ case KEY_RIGHT:
+ {
+ if (_pageTab != 0 && _pageTab->HasFocus())
+ BaseClass::OnKeyCodeTyped(code);
+ break;
+ }
+ case KEY_LEFT:
+ {
+ if (_pageTab != 0 && _pageTab->HasFocus())
+ BaseClass::OnKeyCodeTyped(code);
+ break;
+ }
+ default:
+ BaseClass::OnKeyCodeTyped(code);
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PropertyPage::SetVisible(bool state)
+{
+ if (IsVisible() && !state)
+ {
+ // if we're going away and we have a current button, get rid of it
+ if (GetFocusNavGroup().GetCurrentDefaultButton())
+ {
+ GetFocusNavGroup().SetCurrentDefaultButton(NULL);
+ }
+ }
+
+ BaseClass::SetVisible(state);
+}
+
diff --git a/mp/src/vgui2/vgui_controls/PropertySheet.cpp b/mp/src/vgui2/vgui_controls/PropertySheet.cpp
new file mode 100644
index 00000000..b6705b55
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/PropertySheet.cpp
@@ -0,0 +1,1674 @@
+//========= 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" ) );
+ PropertySheet *sheet = reinterpret_cast< PropertySheet * >( data->GetPtr( "propertysheet" ) );
+ char const *title = data->GetString( "tabname", "" );
+ if ( !page || !sheet )
+ return;
+
+ // Can only create if sheet was part of a ToolWindow derived object
+ ToolWindow *tw = dynamic_cast< ToolWindow * >( sheet->GetParent() );
+ if ( tw )
+ {
+ IToolWindowFactory *factory = tw->GetToolWindowFactory();
+ if ( factory )
+ {
+ bool hasContextMenu = sheet->PageHasContextMenu( page );
+ sheet->RemovePage( page );
+ factory->InstanceToolWindow( tw->GetParent(), sheet->ShouldShowContextButtons(), page, title, hasContextMenu );
+
+ if ( sheet->GetNumPages() == 0 )
+ {
+ tw->MarkForDeletion();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ virtual void OnCreateDragData( KeyValues *msg )
+ {
+ Assert( m_pParent->IsDraggableTab() );
+
+ msg->SetPtr( "propertypage", m_pPage );
+ msg->SetPtr( "propertysheet", m_pParent );
+ char sz[ 256 ];
+ GetText( sz, sizeof( sz ) );
+ msg->SetString( "tabname", sz );
+ msg->SetString( "text", sz );
+ }
+
+ virtual void ApplySchemeSettings(IScheme *pScheme)
+ {
+ // set up the scheme settings
+ Button::ApplySchemeSettings(pScheme);
+
+ _textColor = GetSchemeColor("PropertySheet.SelectedTextColor", GetFgColor(), pScheme);
+ _dimTextColor = GetSchemeColor("PropertySheet.TextColor", GetFgColor(), pScheme);
+ m_pActiveBorder = pScheme->GetBorder("TabActiveBorder");
+ m_pNormalBorder = pScheme->GetBorder("TabBorder");
+
+ if ( m_pImage )
+ {
+ ClearImages();
+ m_pImage->SetImage(scheme()->GetImage(m_pszImageName, false));
+ AddImage( m_pImage->GetImage(), 2 );
+ int w, h;
+ m_pImage->GetSize( w, h );
+ w += m_pContextLabel ? 10 : 0;
+ if ( m_pContextLabel )
+ {
+ m_pImage->SetPos( 10, 0 );
+ }
+ SetSize( w + 4, h + 2 );
+ }
+ else
+ {
+ int wide, tall;
+ int contentWide, contentTall;
+ GetSize(wide, tall);
+ GetContentSize(contentWide, contentTall);
+
+ wide = max(m_bMaxTabWidth, contentWide + 10); // 10 = 5 pixels margin on each side
+ wide += m_pContextLabel ? 10 : 0;
+ SetSize(wide, tall);
+ }
+
+ if ( m_pContextLabel )
+ {
+ SetTextInset( 12, 0 );
+ }
+ }
+
+ virtual void ApplySettings( KeyValues *inResourceData )
+ {
+ const char *pBorder = inResourceData->GetString("activeborder_override", "");
+ if (*pBorder)
+ {
+ m_pActiveBorder = scheme()->GetIScheme(GetScheme())->GetBorder( pBorder );
+ }
+ pBorder = inResourceData->GetString("normalborder_override", "");
+ if (*pBorder)
+ {
+ m_pNormalBorder = scheme()->GetIScheme(GetScheme())->GetBorder( pBorder );
+ }
+ BaseClass::ApplySettings(inResourceData);
+ }
+
+ virtual void OnCommand( char const *cmd )
+ {
+ if ( !Q_stricmp( cmd, "ShowContextMenu" ) )
+ {
+ KeyValues *kv = new KeyValues("OpenContextMenu");
+ kv->SetPtr( "page", m_pPage );
+ kv->SetPtr( "contextlabel", m_pContextLabel );
+ PostActionSignal( kv );
+ return;
+ }
+ BaseClass::OnCommand( cmd );
+ }
+
+ IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
+ {
+ if (_active)
+ {
+ return m_pActiveBorder;
+ }
+ return m_pNormalBorder;
+ }
+
+ virtual Color GetButtonFgColor()
+ {
+ if (_active)
+ {
+ return _textColor;
+ }
+ else
+ {
+ return _dimTextColor;
+ }
+ }
+
+ virtual void SetActive(bool state)
+ {
+ _active = state;
+ SetZPos( state ? 100 : 0 );
+ InvalidateLayout();
+ Repaint();
+ }
+
+ virtual void SetTabWidth( int iWidth )
+ {
+ m_bMaxTabWidth = iWidth;
+ InvalidateLayout();
+ }
+
+ virtual bool CanBeDefaultButton(void)
+ {
+ return false;
+ }
+
+ //Fire action signal when mouse is pressed down instead of on release.
+ virtual void OnMousePressed(MouseCode code)
+ {
+ // check for context menu open
+ if (!IsEnabled())
+ return;
+
+ if (!IsMouseClickEnabled(code))
+ return;
+
+ if (IsUseCaptureMouseEnabled())
+ {
+ {
+ RequestFocus();
+ FireActionSignal();
+ SetSelected(true);
+ Repaint();
+ }
+
+ // lock mouse input to going to this button
+ input()->SetMouseCapture(GetVPanel());
+ }
+ }
+
+ virtual void OnMouseReleased(MouseCode code)
+ {
+ // ensure mouse capture gets released
+ if (IsUseCaptureMouseEnabled())
+ {
+ input()->SetMouseCapture(NULL);
+ }
+
+ // make sure the button gets unselected
+ SetSelected(false);
+ Repaint();
+
+ if (code == MOUSE_RIGHT)
+ {
+ KeyValues *kv = new KeyValues("OpenContextMenu");
+ kv->SetPtr( "page", m_pPage );
+ kv->SetPtr( "contextlabel", m_pContextLabel );
+ PostActionSignal( kv );
+ }
+ }
+
+ virtual void PerformLayout()
+ {
+ BaseClass::PerformLayout();
+
+ if ( m_pContextLabel )
+ {
+ int w, h;
+ GetSize( w, h );
+ m_pContextLabel->SetBounds( 0, 0, 10, h );
+ }
+ }
+};
+
+
+}; // namespace vgui
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+PropertySheet::PropertySheet(
+ Panel *parent,
+ const char *panelName,
+ bool draggableTabs /*= false*/ ) : BaseClass(parent, panelName)
+{
+ _activePage = NULL;
+ _activeTab = NULL;
+ _tabWidth = 64;
+ _activeTabIndex = 0;
+ _showTabs = true;
+ _combo = NULL;
+ _tabFocus = false;
+ m_flPageTransitionEffectTime = 0.0f;
+ m_bSmallTabs = false;
+ m_tabFont = 0;
+ m_bDraggableTabs = draggableTabs;
+ m_pTabKV = NULL;
+ m_iTabHeight = 0;
+ m_iTabHeightSmall = 0;
+
+ if ( m_bDraggableTabs )
+ {
+ SetDropEnabled( true );
+ }
+
+ m_bKBNavigationEnabled = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor, associates pages with a combo box
+//-----------------------------------------------------------------------------
+PropertySheet::PropertySheet(Panel *parent, const char *panelName, ComboBox *combo) : BaseClass(parent, panelName)
+{
+ _activePage = NULL;
+ _activeTab = NULL;
+ _tabWidth = 64;
+ _activeTabIndex = 0;
+ _combo=combo;
+ _combo->AddActionSignalTarget(this);
+ _showTabs = false;
+ _tabFocus = false;
+ m_flPageTransitionEffectTime = 0.0f;
+ m_bSmallTabs = false;
+ m_tabFont = 0;
+ m_bDraggableTabs = false;
+ m_pTabKV = NULL;
+ m_iTabHeight = 0;
+ m_iTabHeightSmall = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+PropertySheet::~PropertySheet()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: ToolWindow uses this to drag tools from container to container by dragging the tab
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PropertySheet::IsDraggableTab() const
+{
+ return m_bDraggableTabs;
+}
+
+void PropertySheet::SetDraggableTabs( bool state )
+{
+ m_bDraggableTabs = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Lower profile tabs
+// Input : state -
+//-----------------------------------------------------------------------------
+void PropertySheet::SetSmallTabs( bool state )
+{
+ m_bSmallTabs = state;
+ m_tabFont = scheme()->GetIScheme( GetScheme() )->GetFont( m_bSmallTabs ? "DefaultVerySmall" : "Default" );
+ int c = m_PageTabs.Count();
+ for ( int i = 0; i < c ; ++i )
+ {
+ PageTab *tab = m_PageTabs[ i ];
+ Assert( tab );
+ tab->SetFont( m_tabFont );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PropertySheet::IsSmallTabs() const
+{
+ return m_bSmallTabs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : state -
+//-----------------------------------------------------------------------------
+void PropertySheet::ShowContextButtons( bool state )
+{
+ m_bContextButton = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PropertySheet::ShouldShowContextButtons() const
+{
+ return m_bContextButton;
+}
+
+int PropertySheet::FindPage( Panel *page ) const
+{
+ int c = m_Pages.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ if ( m_Pages[ i ].page == page )
+ return i;
+ }
+
+ return m_Pages.InvalidIndex();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds a page to the sheet
+//-----------------------------------------------------------------------------
+void PropertySheet::AddPage(Panel *page, const char *title, char const *imageName /*= NULL*/, bool bHasContextMenu /*= false*/ )
+{
+ if (!page)
+ return;
+
+ // don't add the page if we already have it
+ if ( FindPage( page ) != m_Pages.InvalidIndex() )
+ return;
+
+ long hoverActivatePageTime = 250;
+ PageTab *tab = new PageTab(this, "tab", title, imageName, _tabWidth, page, m_bContextButton && bHasContextMenu, hoverActivatePageTime );
+ if ( m_bDraggableTabs )
+ {
+ tab->SetDragEnabled( true );
+ }
+
+ tab->SetFont( m_tabFont );
+ if(_showTabs)
+ {
+ tab->AddActionSignalTarget(this);
+ }
+ else if (_combo)
+ {
+ _combo->AddItem(title, NULL);
+ }
+
+ if ( m_pTabKV )
+ {
+ tab->ApplySettings( m_pTabKV );
+ }
+
+ m_PageTabs.AddToTail(tab);
+
+ Page_t info;
+ info.page = page;
+ info.contextMenu = m_bContextButton && bHasContextMenu;
+
+ m_Pages.AddToTail( info );
+
+ page->SetParent(this);
+ page->AddActionSignalTarget(this);
+ PostMessage(page, new KeyValues("ResetData"));
+
+ page->SetVisible(false);
+ InvalidateLayout();
+
+ if (!_activePage)
+ {
+ // first page becomes the active page
+ ChangeActiveTab( 0 );
+ if ( _activePage )
+ {
+ _activePage->RequestFocus( 0 );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PropertySheet::SetActivePage(Panel *page)
+{
+ // walk the list looking for this page
+ int index = FindPage( page );
+ if (!m_Pages.IsValidIndex(index))
+ return;
+
+ ChangeActiveTab(index);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PropertySheet::SetTabWidth(int pixels)
+{
+ if ( pixels < 0 )
+ {
+ if( !_activeTab )
+ return;
+
+ int nTall;
+ _activeTab->GetContentSize( pixels, nTall );
+ }
+
+ if ( _tabWidth == pixels )
+ return;
+
+ _tabWidth = pixels;
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: reloads the data in all the property page
+//-----------------------------------------------------------------------------
+void PropertySheet::ResetAllData()
+{
+ // iterate all the dialogs resetting them
+ for (int i = 0; i < m_Pages.Count(); i++)
+ {
+ ipanel()->SendMessage(m_Pages[i].page->GetVPanel(), new KeyValues("ResetData"), GetVPanel());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies any changes made by the dialog
+//-----------------------------------------------------------------------------
+void PropertySheet::ApplyChanges()
+{
+ // iterate all the dialogs resetting them
+ for (int i = 0; i < m_Pages.Count(); i++)
+ {
+ ipanel()->SendMessage(m_Pages[i].page->GetVPanel(), new KeyValues("ApplyChanges"), GetVPanel());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets a pointer to the currently active page
+//-----------------------------------------------------------------------------
+Panel *PropertySheet::GetActivePage()
+{
+ return _activePage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets a pointer to the currently active tab
+//-----------------------------------------------------------------------------
+Panel *PropertySheet::GetActiveTab()
+{
+ return _activeTab;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the number of panels in the sheet
+//-----------------------------------------------------------------------------
+int PropertySheet::GetNumPages()
+{
+ return m_Pages.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the name contained in the active tab
+// Input : a text buffer to contain the output
+//-----------------------------------------------------------------------------
+void PropertySheet::GetActiveTabTitle (char *textOut, int bufferLen )
+{
+ if(_activeTab) _activeTab->GetText(textOut, bufferLen);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the name contained in the active tab
+// Input : a text buffer to contain the output
+//-----------------------------------------------------------------------------
+bool PropertySheet::GetTabTitle( int i, char *textOut, int bufferLen )
+{
+ if ( i < 0 || i >= m_PageTabs.Count() )
+ {
+ return false;
+ }
+
+ m_PageTabs[i]->GetText(textOut, bufferLen);
+ return true;
+}
+
+bool PropertySheet::SetTabTitle( int i, char *pchTitle )
+{
+ if ( i < 0 || i >= m_PageTabs.Count() )
+ {
+ return false;
+ }
+
+ m_PageTabs[ i ]->SetText( pchTitle );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the index of the currently active page
+//-----------------------------------------------------------------------------
+int PropertySheet::GetActivePageNum()
+{
+ for (int i = 0; i < m_Pages.Count(); i++)
+ {
+ if (m_Pages[i].page == _activePage)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Forwards focus requests to current active page
+//-----------------------------------------------------------------------------
+void PropertySheet::RequestFocus(int direction)
+{
+ if (direction == -1 || direction == 0)
+ {
+ if (_activePage)
+ {
+ _activePage->RequestFocus(direction);
+ _tabFocus = false;
+ }
+ }
+ else
+ {
+ if (_showTabs && _activeTab)
+ {
+ _activeTab->RequestFocus(direction);
+ _tabFocus = true;
+ }
+ else if (_activePage)
+ {
+ _activePage->RequestFocus(direction);
+ _tabFocus = false;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: moves focus back
+//-----------------------------------------------------------------------------
+bool PropertySheet::RequestFocusPrev(VPANEL panel)
+{
+ if (_tabFocus || !_showTabs || !_activeTab)
+ {
+ _tabFocus = false;
+ return BaseClass::RequestFocusPrev(panel);
+ }
+ else
+ {
+ if (GetVParent())
+ {
+ PostMessage(GetVParent(), new KeyValues("FindDefaultButton"));
+ }
+ _activeTab->RequestFocus(-1);
+ _tabFocus = true;
+ return true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: moves focus forward
+//-----------------------------------------------------------------------------
+bool PropertySheet::RequestFocusNext(VPANEL panel)
+{
+ if (!_tabFocus || !_activePage)
+ {
+ return BaseClass::RequestFocusNext(panel);
+ }
+ else
+ {
+ if (!_activeTab)
+ {
+ return BaseClass::RequestFocusNext(panel);
+ }
+ else
+ {
+ _activePage->RequestFocus(1);
+ _tabFocus = false;
+ return true;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets scheme settings
+//-----------------------------------------------------------------------------
+void PropertySheet::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ // a little backwards-compatibility with old scheme files
+ IBorder *pBorder = pScheme->GetBorder("PropertySheetBorder");
+ if (pBorder == pScheme->GetBorder("Default"))
+ {
+ // get the old name
+ pBorder = pScheme->GetBorder("RaisedBorder");
+ }
+
+ SetBorder(pBorder);
+ m_flPageTransitionEffectTime = atof(pScheme->GetResourceString("PropertySheet.TransitionEffectTime"));
+
+ m_tabFont = pScheme->GetFont( m_bSmallTabs ? "DefaultVerySmall" : "Default" );
+
+ if ( m_pTabKV )
+ {
+ for (int i = 0; i < m_PageTabs.Count(); i++)
+ {
+ m_PageTabs[i]->ApplySettings( m_pTabKV );
+ }
+ }
+
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Here, we used to use a single size variable and overwrite it when we scaled.
+ // This led to problems when we changes resolutions, so now we recalcuate the absolute
+ // size from the relative size each time (based on proportionality)
+ //=============================================================================
+ if ( IsProportional() )
+ {
+ m_iTabHeight = scheme()->GetProportionalScaledValueEx( GetScheme(), m_iSpecifiedTabHeight );
+ m_iTabHeightSmall = scheme()->GetProportionalScaledValueEx( GetScheme(), m_iSpecifiedTabHeightSmall );
+ }
+ else
+ {
+ m_iTabHeight = m_iSpecifiedTabHeight;
+ m_iTabHeightSmall = m_iSpecifiedTabHeightSmall;
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PropertySheet::ApplySettings(KeyValues *inResourceData)
+{
+ BaseClass::ApplySettings(inResourceData);
+
+ KeyValues *pTabKV = inResourceData->FindKey( "tabskv" );
+ if ( pTabKV )
+ {
+ if ( m_pTabKV )
+ {
+ m_pTabKV->deleteThis();
+ }
+ m_pTabKV = new KeyValues("tabkv");
+ pTabKV->CopySubkeys( m_pTabKV );
+ }
+
+ KeyValues *pTabWidthKV = inResourceData->FindKey( "tabwidth" );
+ if ( pTabWidthKV )
+ {
+ _tabWidth = scheme()->GetProportionalScaledValueEx(GetScheme(), pTabWidthKV->GetInt());
+ for (int i = 0; i < m_PageTabs.Count(); i++)
+ {
+ m_PageTabs[i]->SetTabWidth( _tabWidth );
+ }
+ }
+
+ KeyValues *pTransitionKV = inResourceData->FindKey( "transition_time" );
+ if ( pTransitionKV )
+ {
+ m_flPageTransitionEffectTime = pTransitionKV->GetFloat();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Paint our border specially, with the tabs in mind
+//-----------------------------------------------------------------------------
+void PropertySheet::PaintBorder()
+{
+ IBorder *border = GetBorder();
+ if (!border)
+ return;
+
+ // draw the border, but with a break at the active tab
+ int px = 0, py = 0, pwide = 0, ptall = 0;
+ if (_activeTab)
+ {
+ _activeTab->GetBounds(px, py, pwide, ptall);
+ ptall -= 1;
+ }
+
+ // draw the border underneath the buttons, with a break
+ int wide, tall;
+ GetSize(wide, tall);
+ border->Paint(0, py + ptall, wide, tall, IBorder::SIDE_TOP, px + 1, px + pwide - 1);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Lays out the dialog
+//-----------------------------------------------------------------------------
+void PropertySheet::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int x, y, wide, tall;
+ GetBounds(x, y, wide, tall);
+ if (_activePage)
+ {
+ int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight;
+
+ if(_showTabs)
+ {
+ _activePage->SetBounds(0, tabHeight, wide, tall - tabHeight);
+ }
+ else
+ {
+ _activePage->SetBounds(0, 0, wide, tall );
+ }
+ _activePage->InvalidateLayout();
+ }
+
+
+ int xtab;
+ int limit = m_PageTabs.Count();
+
+ xtab = m_iTabXIndent;
+
+ // draw the visible tabs
+ if (_showTabs)
+ {
+ for (int i = 0; i < limit; i++)
+ {
+ int tabHeight = IsSmallTabs() ? (m_iTabHeightSmall-1) : (m_iTabHeight-1);
+
+ int width, tall;
+ m_PageTabs[i]->GetSize(width, tall);
+
+ if ( m_bTabFitText )
+ {
+ m_PageTabs[i]->SizeToContents();
+ width = m_PageTabs[i]->GetWide();
+
+ int iXInset, iYInset;
+ m_PageTabs[i]->GetTextInset( &iXInset, &iYInset );
+ width += (iXInset * 2);
+ }
+
+ if (m_PageTabs[i] == _activeTab)
+ {
+ // active tab is taller
+ _activeTab->SetBounds(xtab, 2, width, tabHeight);
+ }
+ else
+ {
+ m_PageTabs[i]->SetBounds(xtab, 4, width, tabHeight - 2);
+ }
+ m_PageTabs[i]->SetVisible(true);
+ xtab += (width + 1) + m_iTabXDelta;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < limit; i++)
+ {
+ m_PageTabs[i]->SetVisible(false);
+ }
+ }
+
+ // ensure draw order (page drawing over all the tabs except one)
+ if (_activePage)
+ {
+ _activePage->MoveToFront();
+ _activePage->Repaint();
+ }
+ if (_activeTab)
+ {
+ _activeTab->MoveToFront();
+ _activeTab->Repaint();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Switches the active panel
+//-----------------------------------------------------------------------------
+void PropertySheet::OnTabPressed(Panel *panel)
+{
+ // look for the tab in the list
+ for (int i = 0; i < m_PageTabs.Count(); i++)
+ {
+ if (m_PageTabs[i] == panel)
+ {
+ // flip to the new tab
+ ChangeActiveTab(i);
+ return;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the panel associated with index i
+// Input : the index of the panel to return
+//-----------------------------------------------------------------------------
+Panel *PropertySheet::GetPage(int i)
+{
+ if(i<0 || i>=m_Pages.Count())
+ {
+ return NULL;
+ }
+
+ return m_Pages[i].page;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: disables page by name
+//-----------------------------------------------------------------------------
+void PropertySheet::DisablePage(const char *title)
+{
+ SetPageEnabled(title, false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: enables page by name
+//-----------------------------------------------------------------------------
+void PropertySheet::EnablePage(const char *title)
+{
+ SetPageEnabled(title, true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: enabled or disables page by name
+//-----------------------------------------------------------------------------
+void PropertySheet::SetPageEnabled(const char *title, bool state)
+{
+ for (int i = 0; i < m_PageTabs.Count(); i++)
+ {
+ if (_showTabs)
+ {
+ char tmp[50];
+ m_PageTabs[i]->GetText(tmp,50);
+ if (!strnicmp(title,tmp,strlen(tmp)))
+ {
+ m_PageTabs[i]->SetEnabled(state);
+ }
+ }
+ else
+ {
+ _combo->SetItemEnabled(title,state);
+ }
+ }
+}
+
+void PropertySheet::RemoveAllPages()
+{
+ int c = m_Pages.Count();
+ for ( int i = c - 1; i >= 0 ; --i )
+ {
+ RemovePage( m_Pages[ i ].page );
+ }
+}
+
+void PropertySheet::DeleteAllPages()
+{
+ int c = m_Pages.Count();
+ for ( int i = c - 1; i >= 0 ; --i )
+ {
+ DeletePage( m_Pages[ i ].page );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: deletes the page associated with panel
+// Input : *panel - the panel of the page to remove
+//-----------------------------------------------------------------------------
+void PropertySheet::RemovePage(Panel *panel)
+{
+ int location = FindPage( panel );
+ if ( location == m_Pages.InvalidIndex() )
+ return;
+
+ // Since it's being deleted, don't animate!!!
+ m_hPreviouslyActivePage = NULL;
+ _activeTab = NULL;
+
+ // ASSUMPTION = that the number of pages equals number of tabs
+ if( _showTabs )
+ {
+ m_PageTabs[location]->RemoveActionSignalTarget( this );
+ }
+ // now remove the tab
+ PageTab *tab = m_PageTabs[ location ];
+ m_PageTabs.Remove( location );
+ tab->MarkForDeletion();
+
+ // Remove from page list
+ m_Pages.Remove( location );
+
+ // Unparent
+ panel->SetParent( (Panel *)NULL );
+
+ if ( _activePage == panel )
+ {
+ _activePage = NULL;
+ // if this page is currently active, backup to the page before this.
+ ChangeActiveTab( max( location - 1, 0 ) );
+ }
+
+ PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: deletes the page associated with panel
+// Input : *panel - the panel of the page to remove
+//-----------------------------------------------------------------------------
+void PropertySheet::DeletePage(Panel *panel)
+{
+ Assert( panel );
+ RemovePage( panel );
+ panel->MarkForDeletion();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: flips to the new tab, sending out all the right notifications
+// flipping to a tab activates the tab.
+//-----------------------------------------------------------------------------
+void PropertySheet::ChangeActiveTab( int index )
+{
+ if ( !m_Pages.IsValidIndex( index ) )
+ {
+ _activeTab = NULL;
+ if ( m_Pages.Count() > 0 )
+ {
+ _activePage = NULL;
+
+ if ( index < 0 )
+ {
+ ChangeActiveTab( m_Pages.Count() - 1 );
+ }
+ else
+ {
+ ChangeActiveTab( 0 );
+ }
+ }
+ return;
+ }
+
+ if ( m_Pages[index].page == _activePage )
+ {
+ if ( _activeTab )
+ {
+ _activeTab->RequestFocus();
+ }
+ _tabFocus = true;
+ return;
+ }
+
+ int c = m_Pages.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ m_Pages[ i ].page->SetVisible( false );
+ }
+
+ m_hPreviouslyActivePage = _activePage;
+ // notify old page
+ if (_activePage)
+ {
+ ivgui()->PostMessage(_activePage->GetVPanel(), new KeyValues("PageHide"), GetVPanel());
+ KeyValues *msg = new KeyValues("PageTabActivated");
+ msg->SetPtr("panel", (Panel *)NULL);
+ ivgui()->PostMessage(_activePage->GetVPanel(), msg, GetVPanel());
+ }
+ if (_activeTab)
+ {
+ //_activeTabIndex=index;
+ _activeTab->SetActive(false);
+
+ // does the old tab have the focus?
+ _tabFocus = _activeTab->HasFocus();
+ }
+ else
+ {
+ _tabFocus = false;
+ }
+
+ // flip page
+ _activePage = m_Pages[index].page;
+ _activeTab = m_PageTabs[index];
+ _activeTabIndex = index;
+
+ _activePage->SetVisible(true);
+ _activePage->MoveToFront();
+
+ _activeTab->SetVisible(true);
+ _activeTab->MoveToFront();
+ _activeTab->SetActive(true);
+
+ if (_tabFocus)
+ {
+ // if a tab already has focused,give the new tab the focus
+ _activeTab->RequestFocus();
+ }
+ else
+ {
+ // otherwise, give the focus to the page
+ _activePage->RequestFocus();
+ }
+
+ if (!_showTabs)
+ {
+ _combo->ActivateItemByRow(index);
+ }
+
+ _activePage->MakeReadyForUse();
+
+ // transition effect
+ if (m_flPageTransitionEffectTime)
+ {
+ if (m_hPreviouslyActivePage.Get())
+ {
+ // fade out the previous page
+ GetAnimationController()->RunAnimationCommand(m_hPreviouslyActivePage, "Alpha", 0.0f, 0.0f, m_flPageTransitionEffectTime / 2, AnimationController::INTERPOLATOR_LINEAR);
+ }
+
+ // fade in the new page
+ _activePage->SetAlpha(0);
+ GetAnimationController()->RunAnimationCommand(_activePage, "Alpha", 255.0f, m_flPageTransitionEffectTime / 2, m_flPageTransitionEffectTime / 2, AnimationController::INTERPOLATOR_LINEAR);
+ }
+ else
+ {
+ if (m_hPreviouslyActivePage.Get())
+ {
+ // no transition, just hide the previous page
+ m_hPreviouslyActivePage->SetVisible(false);
+ }
+ _activePage->SetAlpha( 255 );
+ }
+
+ // notify
+ ivgui()->PostMessage(_activePage->GetVPanel(), new KeyValues("PageShow"), GetVPanel());
+
+ KeyValues *msg = new KeyValues("PageTabActivated");
+ msg->SetPtr("panel", (Panel *)_activeTab);
+ ivgui()->PostMessage(_activePage->GetVPanel(), msg, GetVPanel());
+
+ // tell parent
+ PostActionSignal(new KeyValues("PageChanged"));
+
+ // Repaint
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the panel with the specified hotkey, from the current page
+//-----------------------------------------------------------------------------
+Panel *PropertySheet::HasHotkey(wchar_t key)
+{
+ if (!_activePage)
+ return NULL;
+
+ for (int i = 0; i < _activePage->GetChildCount(); i++)
+ {
+ Panel *hot = _activePage->GetChild(i)->HasHotkey(key);
+ if (hot)
+ {
+ return hot;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: catches the opencontextmenu event
+//-----------------------------------------------------------------------------
+void PropertySheet::OnOpenContextMenu( KeyValues *params )
+{
+ // tell parent
+ KeyValues *kv = params->MakeCopy();
+ PostActionSignal( kv );
+ Panel *page = reinterpret_cast< Panel * >( params->GetPtr( "page" ) );
+ if ( page )
+ {
+ PostMessage( page->GetVPanel(), params->MakeCopy() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle key presses, through tabs.
+//-----------------------------------------------------------------------------
+void PropertySheet::OnKeyCodePressed(KeyCode code)
+{
+ bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+ bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+ bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
+
+ if ( ctrl && shift && alt && code == KEY_B )
+ {
+ // enable build mode
+ EditablePanel *ep = dynamic_cast< EditablePanel * >( GetActivePage() );
+ if ( ep )
+ {
+ ep->ActivateBuildMode();
+ return;
+ }
+ }
+
+ if ( IsKBNavigationEnabled() )
+ {
+ ButtonCode_t nButtonCode = GetBaseButtonCode( code );
+
+ switch ( nButtonCode )
+ {
+ // for now left and right arrows just open or close submenus if they are there.
+ case KEY_RIGHT:
+ case KEY_XBUTTON_RIGHT:
+ case KEY_XSTICK1_RIGHT:
+ case KEY_XSTICK2_RIGHT:
+ {
+ ChangeActiveTab(_activeTabIndex+1);
+ break;
+ }
+ case KEY_LEFT:
+ case KEY_XBUTTON_LEFT:
+ case KEY_XSTICK1_LEFT:
+ case KEY_XSTICK2_LEFT:
+ {
+ ChangeActiveTab(_activeTabIndex-1);
+ break;
+ }
+ default:
+ BaseClass::OnKeyCodePressed(code);
+ break;
+ }
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed(code);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called by the associated combo box (if in that mode), changes the current panel
+//-----------------------------------------------------------------------------
+void PropertySheet::OnTextChanged(Panel *panel,const wchar_t *wszText)
+{
+ if ( panel == _combo )
+ {
+ wchar_t tabText[30];
+ for(int i = 0 ; i < m_PageTabs.Count() ; i++ )
+ {
+ tabText[0] = 0;
+ m_PageTabs[i]->GetText(tabText,30);
+ if ( !wcsicmp(wszText,tabText) )
+ {
+ ChangeActiveTab(i);
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PropertySheet::OnCommand(const char *command)
+{
+ // propogate the close command to our parent
+ if (!stricmp(command, "Close") && GetVParent())
+ {
+ CallParentFunction(new KeyValues("Command", "command", command));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PropertySheet::OnApplyButtonEnable()
+{
+ // tell parent
+ PostActionSignal(new KeyValues("ApplyButtonEnable"));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PropertySheet::OnCurrentDefaultButtonSet( vgui::VPANEL defaultButton )
+{
+ // forward the message up
+ if (GetVParent())
+ {
+ KeyValues *msg = new KeyValues("CurrentDefaultButtonSet");
+ msg->SetInt("button", ivgui()->PanelToHandle( defaultButton ) );
+ PostMessage(GetVParent(), msg);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PropertySheet::OnDefaultButtonSet( VPANEL defaultButton )
+{
+ // forward the message up
+ if (GetVParent())
+ {
+ KeyValues *msg = new KeyValues("DefaultButtonSet");
+ msg->SetInt("button", ivgui()->PanelToHandle( defaultButton ) );
+ PostMessage(GetVParent(), msg);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PropertySheet::OnFindDefaultButton()
+{
+ if (GetVParent())
+ {
+ PostMessage(GetVParent(), new KeyValues("FindDefaultButton"));
+ }
+}
+
+bool PropertySheet::PageHasContextMenu( Panel *page ) const
+{
+ int pageNum = FindPage( page );
+ if ( pageNum == m_Pages.InvalidIndex() )
+ return false;
+
+ return m_Pages[ pageNum ].contextMenu;
+}
+
+void PropertySheet::OnPanelDropped( CUtlVector< KeyValues * >& msglist )
+{
+ if ( msglist.Count() != 1 )
+ {
+ return;
+ }
+
+ PropertySheet *sheet = IsDroppingSheet( msglist );
+ if ( !sheet )
+ {
+ // Defer to active page
+ if ( _activePage && _activePage->IsDropEnabled() )
+ {
+ return _activePage->OnPanelDropped( msglist );
+ }
+ return;
+ }
+
+ KeyValues *data = msglist[ 0 ];
+
+ Panel *page = reinterpret_cast< Panel * >( data->GetPtr( "propertypage" ) );
+ char const *title = data->GetString( "tabname", "" );
+ if ( !page || !sheet )
+ return;
+
+ // Can only create if sheet was part of a ToolWindow derived object
+ ToolWindow *tw = dynamic_cast< ToolWindow * >( sheet->GetParent() );
+ if ( tw )
+ {
+ IToolWindowFactory *factory = tw->GetToolWindowFactory();
+ if ( factory )
+ {
+ bool showContext = sheet->PageHasContextMenu( page );
+ sheet->RemovePage( page );
+ if ( sheet->GetNumPages() == 0 )
+ {
+ tw->MarkForDeletion();
+ }
+
+ AddPage( page, title, NULL, showContext );
+ }
+ }
+}
+
+bool PropertySheet::IsDroppable( CUtlVector< KeyValues * >& msglist )
+{
+ if ( !m_bDraggableTabs )
+ return false;
+
+ if ( msglist.Count() != 1 )
+ {
+ return false;
+ }
+
+ int mx, my;
+ input()->GetCursorPos( mx, my );
+ ScreenToLocal( mx, my );
+
+ int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight;
+ if ( my > tabHeight )
+ return false;
+
+ PropertySheet *sheet = IsDroppingSheet( msglist );
+ if ( !sheet )
+ {
+ return false;
+ }
+
+ if ( sheet == this )
+ return false;
+
+ return true;
+}
+
+// Mouse is now over a droppable panel
+void PropertySheet::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels )
+{
+ // Convert this panel's bounds to screen space
+ int x, y, w, h;
+
+ GetSize( w, h );
+
+ int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight;
+ h = tabHeight + 4;
+
+ x = y = 0;
+ LocalToScreen( x, y );
+
+ surface()->DrawSetColor( GetDropFrameColor() );
+ // Draw 2 pixel frame
+ surface()->DrawOutlinedRect( x, y, x + w, y + h );
+ surface()->DrawOutlinedRect( x+1, y+1, x + w-1, y + h-1 );
+
+ if ( !IsDroppable( msglist ) )
+ {
+ return;
+ }
+
+ if ( !_showTabs )
+ {
+ return;
+ }
+
+ // Draw a fake new tab...
+
+ x = 0;
+ y = 2;
+ w = 1;
+ h = tabHeight;
+
+ int last = m_PageTabs.Count();
+ if ( last != 0 )
+ {
+ m_PageTabs[ last - 1 ]->GetBounds( x, y, w, h );
+ }
+
+ // Compute left edge of "fake" tab
+
+ x += ( w + 1 );
+
+ // Compute size of new panel
+ KeyValues *data = msglist[ 0 ];
+ char const *text = data->GetString( "tabname", "" );
+ Assert( text );
+
+ PageTab *fakeTab = new PageTab( this, "FakeTab", text, NULL, _tabWidth, NULL, false );
+ fakeTab->SetBounds( x, 4, w, tabHeight - 4 );
+ fakeTab->SetFont( m_tabFont );
+ SETUP_PANEL( fakeTab );
+ fakeTab->Repaint();
+ surface()->SolveTraverse( fakeTab->GetVPanel(), true );
+ surface()->PaintTraverse( fakeTab->GetVPanel() );
+ delete fakeTab;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : state -
+//-----------------------------------------------------------------------------
+void PropertySheet::SetKBNavigationEnabled( bool state )
+{
+ m_bKBNavigationEnabled = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PropertySheet::IsKBNavigationEnabled() const
+{
+ return m_bKBNavigationEnabled;
+}
diff --git a/mp/src/vgui2/vgui_controls/QueryBox.cpp b/mp/src/vgui2/vgui_controls/QueryBox.cpp
new file mode 100644
index 00000000..71da25e2
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/QueryBox.cpp
@@ -0,0 +1,222 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+// This class is a message box that has two buttons, ok and cancel instead of
+// just the ok button of a message box. We use a message box class for the ok button
+// and implement another button here.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <vgui/KeyCode.h>
+
+#include <vgui_controls/QueryBox.h>
+#include <vgui_controls/TextImage.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+QueryBox::QueryBox(const char *title, const char *queryText, vgui::Panel *parent) : MessageBox(title, queryText,parent)
+{
+ SetDeleteSelfOnClose(true);
+ m_pCancelButton = new Button(this, "CancelButton", "#QueryBox_Cancel");
+ m_pCancelButton->SetCommand("Cancel");
+ m_pOkButton->SetCommand("OK");
+ m_pCancelCommand = NULL;
+ m_pOkCommand = NULL;
+
+ m_pOkButton->SetTabPosition(1);
+ m_pCancelButton->SetTabPosition(2);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+QueryBox::QueryBox(const wchar_t *wszTitle, const wchar_t *wszQueryText,vgui::Panel *parent) : MessageBox(wszTitle, wszQueryText,parent)
+{
+ SetDeleteSelfOnClose(true);
+ m_pCancelButton = new Button(this, "CancelButton", "#QueryBox_Cancel");
+ m_pCancelButton->SetCommand("Cancel");
+ m_pOkButton->SetCommand("OK");
+ m_pCancelCommand = NULL;
+ m_pOkCommand = NULL;
+
+ m_pOkButton->SetTabPosition(1);
+ m_pCancelButton->SetTabPosition(2);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+QueryBox::~QueryBox()
+{
+ delete m_pCancelButton;
+
+ if ( m_pOkCommand )
+ {
+ m_pOkCommand->deleteThis();
+ }
+ if ( m_pCancelCommand )
+ {
+ m_pCancelCommand->deleteThis();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Layout the window for drawing
+//-----------------------------------------------------------------------------
+void QueryBox::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int boxWidth, boxTall;
+ GetSize(boxWidth, boxTall);
+
+ int x, y, wide, tall;
+ GetClientArea(x, y, wide, tall);
+ wide += x;
+ tall += y;
+
+ int oldWide, oldTall;
+ m_pCancelButton->GetSize(oldWide, oldTall);
+
+ int btnWide, btnTall;
+ m_pCancelButton->GetContentSize(btnWide, btnTall);
+ btnWide = max(oldWide, btnWide + 10);
+ btnTall = max(oldTall, btnTall + 10);
+ m_pCancelButton->SetSize(btnWide, btnTall);
+
+//nt boxWidth, boxTall;
+ GetSize(boxWidth, boxTall);
+// wide = max(wide, btnWide * 2 + 100);
+// SetSize(wide, tall);
+
+ m_pOkButton->SetPos((wide/2)-(m_pOkButton->GetWide())-1 + x, tall - m_pOkButton->GetTall() - 15);
+ m_pCancelButton->SetPos((wide/2) + x+16, tall - m_pCancelButton->GetTall() - 15);
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles command text from the buttons
+// Deletes self when closed
+//-----------------------------------------------------------------------------
+void QueryBox::OnCommand(const char *command)
+{
+ if (!stricmp(command, "OK"))
+ {
+ OnCommand("Close");
+
+ if ( m_pOkCommand )
+ {
+ PostActionSignal(m_pOkCommand->MakeCopy());
+ }
+ }
+ else if (!stricmp(command, "Cancel"))
+ {
+ OnCommand("Close");
+
+ if (m_pCancelCommand)
+ {
+ PostActionSignal(m_pCancelCommand->MakeCopy());
+ }
+ }
+
+ BaseClass::OnCommand(command);
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the keyvalues to send when ok button is hit
+//-----------------------------------------------------------------------------
+void QueryBox::SetOKCommand(KeyValues *keyValues)
+{
+ if ( m_pOkCommand )
+ {
+ m_pOkCommand->deleteThis();
+ }
+
+ m_pOkCommand = keyValues;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set a value of the ok command
+//-----------------------------------------------------------------------------
+void QueryBox::SetOKCommandValue(const char *keyName, int value)
+{
+ if ( !m_pOkCommand )
+ {
+ m_pOkCommand = new KeyValues("Command");
+ }
+
+ m_pOkCommand->SetInt(keyName, value);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the keyvalues to send when the cancel button is hit
+//-----------------------------------------------------------------------------
+void QueryBox::SetCancelCommand(KeyValues *keyValues)
+{
+ if ( m_pCancelCommand )
+ {
+ m_pCancelCommand->deleteThis();
+ }
+
+ m_pCancelCommand = keyValues;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the cancel button text
+//-----------------------------------------------------------------------------
+void QueryBox::SetCancelButtonText(const char* buttonText)
+{
+ m_pCancelButton->SetText(buttonText);
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the cancel button text
+//-----------------------------------------------------------------------------
+void QueryBox::SetCancelButtonText(const wchar_t* wszButtonText)
+{
+ m_pCancelButton->SetText(wszButtonText);
+ InvalidateLayout();
+}
+
+void QueryBox::OnKeyCodeTyped( KeyCode code )
+{
+ if ( code == KEY_ESCAPE )
+ {
+ OnCommand("Cancel");
+ }
+ else
+ {
+ Frame::OnKeyCodeTyped(code);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void QueryBox::OnKeyCodePressed( KeyCode code )
+{
+ if ( code == KEY_XBUTTON_B )
+ {
+ OnCommand("Cancel");
+ }
+ else
+ {
+ Frame::OnKeyCodePressed(code);
+ }
+}
+
+
+
diff --git a/mp/src/vgui2/vgui_controls/RadioButton.cpp b/mp/src/vgui2/vgui_controls/RadioButton.cpp
new file mode 100644
index 00000000..f6f49034
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/RadioButton.cpp
@@ -0,0 +1,419 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdarg.h>
+#include <stdio.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_controls/FocusNavGroup.h>
+#include <vgui_controls/Image.h>
+#include <vgui_controls/RadioButton.h>
+#include <vgui_controls/TextImage.h>
+#include <vgui_controls/Controls.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+enum direction
+{
+ UP = -1,
+ DOWN = 1,
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Check box image
+//-----------------------------------------------------------------------------
+void RadioImage::Paint()
+{
+ DrawSetTextFont(GetFont());
+
+ // draw background
+ if (_radioButton->IsEnabled())
+ {
+ DrawSetTextColor(_bgColor);
+ }
+ else
+ {
+ DrawSetTextColor(_radioButton->GetBgColor());
+ }
+ DrawPrintChar(0, 1, 'n');
+
+ // draw border circl
+ DrawSetTextColor(_borderColor1);
+ DrawPrintChar(0, 1, 'j');
+ DrawSetTextColor(_borderColor2);
+ DrawPrintChar(0, 1, 'k');
+
+ // draw selected check
+ if (_radioButton->IsSelected())
+ {
+ DrawSetTextColor(_checkColor);
+ DrawPrintChar(0, 1, 'h');
+ }
+}
+
+DECLARE_BUILD_FACTORY_DEFAULT_TEXT( RadioButton, RadioButton );
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a radio button.
+//-----------------------------------------------------------------------------
+RadioButton::RadioButton(Panel *parent, const char *panelName, const char *text) : ToggleButton(parent, panelName, text)
+{
+ SetContentAlignment(a_west);
+
+ // create the image
+ _radioBoxImage = new RadioImage(this);
+
+ _oldTabPosition = 0;
+ _subTabPosition = 0;
+
+ SetTextImageIndex(1);
+ SetImageAtIndex(0, _radioBoxImage, 0);
+
+ SetButtonActivationType(ACTIVATE_ONPRESSED);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+RadioButton::~RadioButton()
+{
+ delete _radioBoxImage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply resource file scheme.
+//-----------------------------------------------------------------------------
+void RadioButton::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+ _radioBoxImage->_bgColor = GetSchemeColor("CheckButton.BgColor", Color(150, 150, 150, 0), pScheme);
+ _radioBoxImage->_borderColor1 = GetSchemeColor("CheckButton.Border1", Color(20, 20, 20, 0), pScheme);
+ _radioBoxImage->_borderColor2 = GetSchemeColor("CheckButton.Border2", Color(90, 90, 90, 0), pScheme);
+ _radioBoxImage->_checkColor = GetSchemeColor("CheckButton.Check", Color(20, 20, 20, 0), pScheme);
+
+ SetFgColor(GetSchemeColor("RadioButton.TextColor", pScheme));
+ _selectedFgColor = GetSchemeColor("RadioButton.SelectedTextColor", GetSchemeColor("ControlText", pScheme), pScheme);
+
+ SetDefaultColor( GetFgColor(), GetBgColor() );
+
+ SetArmedColor( GetSchemeColor("RadioButton.ArmedTextColor", pScheme), GetButtonArmedBgColor() );
+
+ SetContentAlignment(a_west);
+
+ // reloading the scheme wipes out lists of images
+ _radioBoxImage->SetFont( pScheme->GetFont("Marlett", IsProportional()) );
+ _radioBoxImage->ResizeImageToContent();
+ SetImageAtIndex(0, _radioBoxImage, 0);
+
+ // don't draw a background
+ SetPaintBackgroundEnabled(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the border style of the button, Radio buttons have no border
+//-----------------------------------------------------------------------------
+IBorder *RadioButton::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
+{
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the tab position of the radio button with the set of radio buttons
+// A group of RadioButtons must have the same TabPosition, with [1, n] subtabpositions
+//-----------------------------------------------------------------------------
+int RadioButton::GetSubTabPosition()
+{
+ return _subTabPosition;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the tab position of the radio button with the set of radio buttons
+// A group of RadioButtons must have the same TabPosition, with [1, n] subtabpositions
+//-----------------------------------------------------------------------------
+void RadioButton::SetSubTabPosition(int position)
+{
+ _subTabPosition = position;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the RadioButton's real tab position (its Panel one changes)
+//-----------------------------------------------------------------------------
+int RadioButton::GetRadioTabPosition()
+{
+ return _oldTabPosition;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the radio button checked. When a radio button is checked, a
+// message is sent to all other radio buttons in the same group so
+// they will become unchecked.
+//-----------------------------------------------------------------------------
+void RadioButton::SetSelected(bool state)
+{
+ InternalSetSelected( state, true );
+}
+
+void RadioButton::InternalSetSelected(bool state, bool bFireEvents)
+{
+ if (state == true)
+ {
+ if (!IsEnabled())
+ return;
+
+ // restore our tab position
+ SetTabPosition(_oldTabPosition);
+
+ // Should we send notifications?
+ if ( bFireEvents )
+ {
+ // send a message
+ KeyValues *msg = new KeyValues("RadioButtonChecked");
+ msg->SetPtr("panel", this);
+ msg->SetInt("tabposition", _oldTabPosition);
+
+ // send a message to all other panels on the same level as heirarchy,
+ // so that other radio buttons know to shut off
+ VPANEL radioParent = GetVParent();
+ if (radioParent)
+ {
+ for (int i = 0; i < ipanel()->GetChildCount(radioParent); i++)
+ {
+ VPANEL child = ipanel()->GetChild(radioParent, i);
+ if (child != GetVPanel())
+ {
+ ivgui()->PostMessage(child, msg->MakeCopy(), GetVPanel());
+ }
+ }
+ }
+
+ RequestFocus();
+ PostActionSignal(msg);
+ }
+ }
+ else
+ {
+ // remove ourselves from the tab order
+ if (GetTabPosition())
+ {
+ _oldTabPosition = GetTabPosition();
+ }
+ SetTabPosition(0);
+ }
+
+ InvalidateLayout();
+ Repaint();
+
+ ToggleButton::SetSelected(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the selection state without firing any events
+//-----------------------------------------------------------------------------
+void RadioButton::SilentSetSelected(bool state)
+{
+ InternalSetSelected( state, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set up the text color before doing normal layout
+//-----------------------------------------------------------------------------
+void RadioButton::PerformLayout()
+{
+ if (IsSelected())
+ {
+ SetFgColor(_selectedFgColor);
+ }
+ else
+ {
+ SetFgColor(GetButtonFgColor());
+ }
+
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply resource settings including button state and text color.
+//-----------------------------------------------------------------------------
+void RadioButton::ApplySettings(KeyValues *inResourceData)
+{
+ ToggleButton::ApplySettings(inResourceData);
+ SetTextColorState(CS_NORMAL);
+
+ _subTabPosition = inResourceData->GetInt("SubTabPosition");
+ _oldTabPosition = inResourceData->GetInt("TabPosition");
+ SetImageAtIndex(0, _radioBoxImage, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get resource settings including button state, text color, and subTabPosition
+//-----------------------------------------------------------------------------
+void RadioButton::GetSettings(KeyValues *outResourceData)
+{
+ ToggleButton::GetSettings(outResourceData);
+ outResourceData->SetInt("SubTabPosition", _subTabPosition);
+ outResourceData->SetInt("TabPosition", GetRadioTabPosition());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Describe editing details
+//-----------------------------------------------------------------------------
+const char *RadioButton::GetDescription( void )
+{
+ static char buf[1024];
+ Q_snprintf(buf, sizeof(buf), "%s, int SubTabPosition", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: When a radio button is checked, all other radio buttons
+// in the same group become unchecked.
+//-----------------------------------------------------------------------------
+void RadioButton::OnRadioButtonChecked(int tabPosition)
+{
+ // make sure we're in the same tab group
+ if (tabPosition != _oldTabPosition)
+ return;
+
+ // wouldn't be sent to us from ourselves, so another radio button has taken over
+ SetSelected(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws the selection rectangle
+//-----------------------------------------------------------------------------
+void RadioButton::Paint()
+{
+ BaseClass::Paint();
+
+ /*
+ if (HasFocus())
+ {
+ int tx0, ty0, tx1, ty1;
+ _textImage->GetPos(tx0, ty0);
+ _textImage->GetSize(tx1, ty1);
+ DrawFocusBorder(tx0, ty0, tx0 + tx1, ty0 + ty1);
+ }
+ */
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RadioButton::DoClick()
+{
+ SetSelected(true);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle arrow key movement
+//-----------------------------------------------------------------------------
+void RadioButton::OnKeyCodeTyped(KeyCode code)
+{
+ switch (code)
+ {
+ case KEY_ENTER:
+ case KEY_SPACE:
+ {
+ if (!IsSelected())
+ {
+ SetSelected(true);
+ }
+ else
+ {
+ Panel::OnKeyCodeTyped(code);
+ }
+ }
+ break;
+
+ case KEY_DOWN:
+ case KEY_RIGHT:
+ {
+ RadioButton *bestRadio = FindBestRadioButton( DOWN );
+ if (bestRadio)
+ {
+ bestRadio->SetSelected(true);
+ }
+ }
+ break;
+ case KEY_UP:
+ case KEY_LEFT:
+ {
+ RadioButton *bestRadio = FindBestRadioButton( UP );
+ if (bestRadio)
+ {
+ bestRadio->SetSelected(true);
+ }
+ }
+ break;
+
+ default:
+ BaseClass::OnKeyCodeTyped(code);
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find the correct radio button to move to.
+// Input : direction - the direction we are moving, up or down.
+//-----------------------------------------------------------------------------
+RadioButton *RadioButton::FindBestRadioButton(int direction)
+{
+ RadioButton *bestRadio = NULL;
+ int highestRadio = 0;
+ Panel *pr = GetParent();
+ if (pr)
+ {
+ // find the radio button to go to next
+ for (int i = 0; i < pr->GetChildCount(); i++)
+ {
+ RadioButton *child = dynamic_cast<RadioButton *>(pr->GetChild(i));
+ if (child && child->GetRadioTabPosition() == _oldTabPosition)
+ {
+ if (child->GetSubTabPosition() == _subTabPosition + direction)
+ {
+ bestRadio = child;
+ break;
+ }
+ if ( (child->GetSubTabPosition() == 0) && (direction == DOWN) )
+ {
+ bestRadio = child;
+ continue;
+ }
+ else if ( (child->GetSubTabPosition() > highestRadio) && (direction == UP) )
+ {
+ bestRadio = child;
+ highestRadio = bestRadio->GetSubTabPosition();
+ continue;
+ }
+ if (!bestRadio)
+ {
+ bestRadio = child;
+ }
+ }
+ }
+
+ if (bestRadio)
+ {
+ bestRadio->RequestFocus();
+ }
+
+ InvalidateLayout();
+ Repaint();
+ }
+
+ return bestRadio;
+}
diff --git a/mp/src/vgui2/vgui_controls/RichText.cpp b/mp/src/vgui2/vgui_controls/RichText.cpp
new file mode 100644
index 00000000..c4c76682
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/RichText.cpp
@@ -0,0 +1,2744 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vgui_controls/pch_vgui_controls.h"
+#include "vgui/ILocalize.h"
+
+// memdbgon must be the last include file in a .cpp file
+#include "tier0/memdbgon.h"
+
+enum
+{
+ MAX_BUFFER_SIZE = 999999, // maximum size of text buffer
+ DRAW_OFFSET_X = 3,
+ DRAW_OFFSET_Y = 1,
+};
+
+using namespace vgui;
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+namespace vgui
+{
+
+//#define DRAW_CLICK_PANELS
+
+//-----------------------------------------------------------------------------
+// Purpose: Panel used for clickable URL's
+//-----------------------------------------------------------------------------
+class ClickPanel : public Panel
+{
+ DECLARE_CLASS_SIMPLE( ClickPanel, Panel );
+
+public:
+ ClickPanel(Panel *parent)
+ {
+ _viewIndex = 0;
+ _textIndex = 0;
+ SetParent(parent);
+ AddActionSignalTarget(parent);
+
+ SetCursor(dc_hand);
+
+ SetPaintBackgroundEnabled(false);
+ SetPaintEnabled(false);
+// SetPaintAppearanceEnabled(false);
+
+#if defined( DRAW_CLICK_PANELS )
+ SetPaintEnabled(true);
+#endif
+ }
+
+ void SetTextIndex( int linkStartIndex, int viewStartIndex )
+ {
+ _textIndex = linkStartIndex;
+ _viewIndex = viewStartIndex;
+ }
+
+#if defined( DRAW_CLICK_PANELS )
+ virtual void Paint()
+ {
+ surface()->DrawSetColor( Color( 255, 0, 0, 255 ) );
+ surface()->DrawOutlinedRect( 0, 0, GetWide(), GetTall() );
+ }
+#endif
+
+ int GetTextIndex()
+ {
+ return _textIndex;
+ }
+
+ int GetViewTextIndex()
+ {
+ return _viewIndex;
+ }
+
+ void OnMousePressed(MouseCode code)
+ {
+ if (code == MOUSE_LEFT)
+ {
+ PostActionSignal(new KeyValues("ClickPanel", "index", _textIndex));
+ }
+ else
+ {
+ GetParent()->OnMousePressed( code );
+ }
+ }
+
+private:
+ int _textIndex;
+ int _viewIndex;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Panel used only to draw the interior border region
+//-----------------------------------------------------------------------------
+class RichTextInterior : public Panel
+{
+ DECLARE_CLASS_SIMPLE( RichTextInterior, Panel );
+
+public:
+ RichTextInterior( RichText *pParent, const char *pchName ) : BaseClass( pParent, pchName )
+ {
+ SetKeyBoardInputEnabled( false );
+ SetMouseInputEnabled( false );
+ SetPaintBackgroundEnabled( false );
+ SetPaintEnabled( false );
+ m_pRichText = pParent;
+ }
+
+/* virtual IAppearance *GetAppearance()
+ {
+ if ( m_pRichText->IsScrollbarVisible() )
+ return m_pAppearanceScrollbar;
+
+ return BaseClass::GetAppearance();
+ }*/
+
+ virtual void ApplySchemeSettings( IScheme *pScheme )
+ {
+ BaseClass::ApplySchemeSettings( pScheme );
+// m_pAppearanceScrollbar = FindSchemeAppearance( pScheme, "scrollbar_visible" );
+ }
+
+private:
+ RichText *m_pRichText;
+// IAppearance *m_pAppearanceScrollbar;
+};
+
+}; // namespace vgui
+
+DECLARE_BUILD_FACTORY( RichText );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+RichText::RichText(Panel *parent, const char *panelName) : BaseClass(parent, panelName)
+{
+ m_bAllTextAlphaIsZero = false;
+ _font = INVALID_FONT;
+ m_hFontUnderline = INVALID_FONT;
+
+ m_bRecalcLineBreaks = true;
+ m_pszInitialText = NULL;
+ _cursorPos = 0;
+ _mouseSelection = false;
+ _mouseDragSelection = false;
+ _vertScrollBar = new ScrollBar(this, "ScrollBar", true);
+ _vertScrollBar->AddActionSignalTarget(this);
+ _recalcSavedRenderState = true;
+ _maxCharCount = (64 * 1024);
+ AddActionSignalTarget(this);
+ m_pInterior = new RichTextInterior( this, NULL );
+
+ //a -1 for _select[0] means that the selection is empty
+ _select[0] = -1;
+ _select[1] = -1;
+ m_pEditMenu = NULL;
+
+ SetCursor(dc_ibeam);
+
+ //position the cursor so it is at the end of the text
+ GotoTextEnd();
+
+ // set default foreground color to black
+ _defaultTextColor = Color(0, 0, 0, 0);
+
+ // initialize the line break array
+ InvalidateLineBreakStream();
+
+ if ( IsProportional() )
+ {
+ int width, height;
+ int sw,sh;
+ surface()->GetProportionalBase( width, height );
+ surface()->GetScreenSize(sw, sh);
+
+ _drawOffsetX = static_cast<int>( static_cast<float>( DRAW_OFFSET_X )*( static_cast<float>( sw )/ static_cast<float>( width )));
+ _drawOffsetY = static_cast<int>( static_cast<float>( DRAW_OFFSET_Y )*( static_cast<float>( sw )/ static_cast<float>( width )));
+ }
+ else
+ {
+ _drawOffsetX = DRAW_OFFSET_X;
+ _drawOffsetY = DRAW_OFFSET_Y;
+ }
+
+ // add a basic format string
+ TFormatStream stream;
+ stream.color = _defaultTextColor;
+ stream.fade.flFadeStartTime = 0.0f;
+ stream.fade.flFadeLength = -1.0f;
+ stream.pixelsIndent = 0;
+ stream.textStreamIndex = 0;
+ stream.textClickable = false;
+ m_FormatStream.AddToTail(stream);
+
+ m_bResetFades = false;
+ m_bInteractive = true;
+ m_bUnusedScrollbarInvis = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+RichText::~RichText()
+{
+ delete [] m_pszInitialText;
+ delete m_pEditMenu;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RichText::SetDrawOffsets( int ofsx, int ofsy )
+{
+ _drawOffsetX = ofsx;
+ _drawOffsetY = ofsy;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets it as drawing text only - used for embedded RichText control into other text drawing situations
+//-----------------------------------------------------------------------------
+void RichText::SetDrawTextOnly()
+{
+ SetDrawOffsets( 0, 0 );
+ SetPaintBackgroundEnabled( false );
+// SetPaintAppearanceEnabled( false );
+ SetPostChildPaintEnabled( false );
+ m_pInterior->SetVisible( false );
+ SetVerticalScrollbar( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: configures colors
+//-----------------------------------------------------------------------------
+void RichText::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ _font = pScheme->GetFont("Default", IsProportional() );
+ m_hFontUnderline = pScheme->GetFont("DefaultUnderline", IsProportional() );
+
+ SetFgColor(GetSchemeColor("RichText.TextColor", pScheme));
+ SetBgColor(GetSchemeColor("RichText.BgColor", pScheme));
+
+ _selectionTextColor = GetSchemeColor("RichText.SelectedTextColor", GetFgColor(), pScheme);
+ _selectionColor = GetSchemeColor("RichText.SelectedBgColor", pScheme);
+
+ if ( Q_strlen( pScheme->GetResourceString( "RichText.InsetX" ) ) )
+ {
+ SetDrawOffsets( atoi( pScheme->GetResourceString( "RichText.InsetX" ) ), atoi( pScheme->GetResourceString( "RichText.InsetY" ) ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: if the default format color isn't set then set it
+//-----------------------------------------------------------------------------
+void RichText::SetFgColor( Color color )
+{
+ // Replace default format color if
+ // the stream is empty and the color is the default ( or the previous FgColor )
+ if ( m_FormatStream.Size() == 1 &&
+ ( m_FormatStream[0].color == _defaultTextColor || m_FormatStream[0].color == GetFgColor() ) )
+ {
+ m_FormatStream[0].color = color;
+ }
+
+ BaseClass::SetFgColor( color );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sends a message if the data has changed
+// Turns off any selected text in the window if we are not using the edit menu
+//-----------------------------------------------------------------------------
+void RichText::OnKillFocus()
+{
+ // check if we clicked the right mouse button or if it is down
+ bool mouseRightClicked = input()->WasMousePressed(MOUSE_RIGHT);
+ bool mouseRightUp = input()->WasMouseReleased(MOUSE_RIGHT);
+ bool mouseRightDown = input()->IsMouseDown(MOUSE_RIGHT);
+
+ if (mouseRightClicked || mouseRightDown || mouseRightUp )
+ {
+ // get the start and ends of the selection area
+ int start, end;
+ if (GetSelectedRange(start, end)) // we have selected text
+ {
+ // see if we clicked in the selection area
+ int startX, startY;
+ CursorToPixelSpace(start, startX, startY);
+ int endX, endY;
+ CursorToPixelSpace(end, endX, endY);
+ int cursorX, cursorY;
+ input()->GetCursorPos(cursorX, cursorY);
+ ScreenToLocal(cursorX, cursorY);
+
+ // check the area vertically
+ // we need to handle the horizontal edge cases eventually
+ int fontTall = GetLineHeight();
+ endY = endY + fontTall;
+ if ((startY < cursorY) && (endY > cursorY))
+ {
+ // if we clicked in the selection area, leave the text highlighted
+ return;
+ }
+ }
+ }
+
+ // clear any selection
+ SelectNone();
+
+ // chain
+ BaseClass::OnKillFocus();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Wipe line breaks after the size of a panel has been changed
+//-----------------------------------------------------------------------------
+void RichText::OnSizeChanged( int wide, int tall )
+{
+ BaseClass::OnSizeChanged( wide, tall );
+
+ // blow away the line breaks list
+ _invalidateVerticalScrollbarSlider = true;
+ InvalidateLineBreakStream();
+ InvalidateLayout();
+
+ if ( _vertScrollBar->IsVisible() )
+ {
+ _vertScrollBar->MakeReadyForUse();
+ m_pInterior->SetBounds( 0, 0, wide - _vertScrollBar->GetWide(), tall );
+ }
+ else
+ {
+ m_pInterior->SetBounds( 0, 0, wide, tall );
+ }
+}
+
+
+const wchar_t *RichText::ResolveLocalizedTextAndVariables( char const *pchLookup, wchar_t *outbuf, size_t outbufsizeinbytes )
+{
+ if ( pchLookup[ 0 ] == '#' )
+ {
+ // try lookup in localization tables
+ StringIndex_t index = g_pVGuiLocalize->FindIndex( pchLookup + 1 );
+ if ( index == INVALID_LOCALIZE_STRING_INDEX )
+ {
+/* // if it's not found, maybe it's a special expanded variable - look for an expansion
+ char rgchT[MAX_PATH];
+
+ // get the variables
+ KeyValues *variables = GetDialogVariables_R();
+ if ( variables )
+ {
+ // see if any are any special vars to put in
+ for ( KeyValues *pkv = variables->GetFirstSubKey(); pkv != NULL; pkv = pkv->GetNextKey() )
+ {
+ if ( !Q_strncmp( pkv->GetName(), "$", 1 ) )
+ {
+ // make a new lookup, with this key appended
+ Q_snprintf( rgchT, sizeof( rgchT ), "%s%s=%s", pchLookup, pkv->GetName(), pkv->GetString() );
+ index = localize()->FindIndex( rgchT );
+ break;
+ }
+ }
+ }
+ */
+ }
+
+ // see if we have a valid string
+ if ( index != INVALID_LOCALIZE_STRING_INDEX )
+ {
+ wchar_t *format = g_pVGuiLocalize->GetValueByIndex( index );
+ Assert( format );
+ if ( format )
+ {
+ /*// Try and substitute variables if any
+ KeyValues *variables = GetDialogVariables_R();
+ if ( variables )
+ {
+ localize()->ConstructString( outbuf, outbufsizeinbytes, index, variables );
+ return outbuf;
+ }*/
+ }
+ V_wcsncpy( outbuf, format, outbufsizeinbytes );
+ return outbuf;
+ }
+ }
+
+ Q_UTF8ToUnicode( pchLookup, outbuf, outbufsizeinbytes );
+ return outbuf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the text array
+// Using this function will cause all lineBreaks to be discarded.
+// This is because this fxn replaces the contents of the text buffer.
+// For modifying large buffers use insert functions.
+//-----------------------------------------------------------------------------
+void RichText::SetText(const char *text)
+{
+ if (!text)
+ {
+ text = "";
+ }
+
+ wchar_t unicode[1024];
+
+ if (text[0] == '#')
+ {
+ ResolveLocalizedTextAndVariables( text, unicode, sizeof( unicode ) );
+ SetText( unicode );
+ return;
+ }
+
+ // convert to unicode
+ Q_UTF8ToUnicode(text, unicode, sizeof(unicode));
+ SetText(unicode);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RichText::SetText(const wchar_t *text)
+{
+ // reset the formatting stream
+ m_FormatStream.RemoveAll();
+ TFormatStream stream;
+ stream.color = GetFgColor();
+ stream.fade.flFadeLength = -1.0f;
+ stream.fade.flFadeStartTime = 0.0f;
+ stream.pixelsIndent = 0;
+ stream.textStreamIndex = 0;
+ stream.textClickable = false;
+ m_FormatStream.AddToTail(stream);
+
+ // set the new text stream
+ m_TextStream.RemoveAll();
+ if ( text && *text )
+ {
+ int textLen = wcslen(text) + 1;
+ m_TextStream.EnsureCapacity(textLen);
+ for(int i = 0; i < textLen; i++)
+ {
+ m_TextStream.AddToTail(text[i]);
+ }
+ }
+ GotoTextStart();
+ SelectNone();
+
+ // blow away the line breaks list
+ InvalidateLineBreakStream();
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Given cursor's position in the text buffer, convert it to
+// the local window's x and y pixel coordinates
+// Input: cursorPos: cursor index
+// Output: cx, cy, the corresponding coords in the local window
+//-----------------------------------------------------------------------------
+void RichText::CursorToPixelSpace(int cursorPos, int &cx, int &cy)
+{
+ int yStart = _drawOffsetY;
+ int x = _drawOffsetX, y = yStart;
+ _pixelsIndent = 0;
+ int lineBreakIndexIndex = 0;
+
+ for (int i = GetStartDrawIndex(lineBreakIndexIndex); i < m_TextStream.Count(); i++)
+ {
+ wchar_t ch = m_TextStream[i];
+
+ // if we've found the position, break
+ if (cursorPos == i)
+ {
+ // if we've passed a line break go to that
+ if (m_LineBreaks[lineBreakIndexIndex] == i)
+ {
+ // add another line
+ AddAnotherLine(x, y);
+ lineBreakIndexIndex++;
+ }
+ break;
+ }
+
+ // if we've passed a line break go to that
+ if (m_LineBreaks[lineBreakIndexIndex] == i)
+ {
+ // add another line
+ AddAnotherLine(x, y);
+ lineBreakIndexIndex++;
+ }
+
+ // add to the current position
+ x += surface()->GetCharacterWidth(_font, ch);
+ }
+
+ cx = x;
+ cy = y;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Converts local pixel coordinates to an index in the text buffer
+//-----------------------------------------------------------------------------
+int RichText::PixelToCursorSpace(int cx, int cy)
+{
+ int fontTall = GetLineHeight();
+
+ // where to start reading
+ int yStart = _drawOffsetY;
+ int x = _drawOffsetX, y = yStart;
+ _pixelsIndent = 0;
+ int lineBreakIndexIndex = 0;
+
+ int startIndex = GetStartDrawIndex(lineBreakIndexIndex);
+ if (_recalcSavedRenderState)
+ {
+ RecalculateDefaultState(startIndex);
+ }
+
+ _pixelsIndent = m_CachedRenderState.pixelsIndent;
+ _currentTextClickable = m_CachedRenderState.textClickable;
+ TRenderState renderState = m_CachedRenderState;
+
+ bool onRightLine = false;
+ int i;
+ for (i = startIndex; i < m_TextStream.Count(); i++)
+ {
+ wchar_t ch = m_TextStream[i];
+
+ renderState.x = x;
+ if ( UpdateRenderState( i, renderState ) )
+ {
+ x = renderState.x;
+ }
+
+ // if we are on the right line but off the end of if put the cursor at the end of the line
+ if (m_LineBreaks[lineBreakIndexIndex] == i)
+ {
+ // add another line
+ AddAnotherLine(x, y);
+ lineBreakIndexIndex++;
+
+ if (onRightLine)
+ break;
+ }
+
+ // check to see if we're on the right line
+ if (cy < yStart)
+ {
+ // cursor is above panel
+ onRightLine = true;
+ }
+ else if (cy >= y && (cy < (y + fontTall + _drawOffsetY)))
+ {
+ onRightLine = true;
+ }
+
+ int wide = surface()->GetCharacterWidth(_font, ch);
+
+ // if we've found the position, break
+ if (onRightLine)
+ {
+ if (cx > GetWide()) // off right side of window
+ {
+ }
+ else if (cx < (_drawOffsetX + renderState.pixelsIndent) || cy < yStart) // off left side of window
+ {
+ // Msg( "PixelToCursorSpace() off left size, returning %d '%c'\n", i, m_TextStream[i] );
+ return i; // move cursor one to left
+ }
+
+ if (cx >= x && cx < (x + wide))
+ {
+ // check which side of the letter they're on
+ if (cx < (x + (wide * 0.5))) // left side
+ {
+ // Msg( "PixelToCursorSpace() on the left size, returning %d '%c'\n", i, m_TextStream[i] );
+ return i;
+ }
+ else // right side
+ {
+ // Msg( "PixelToCursorSpace() on the right size, returning %d '%c'\n", i + 1, m_TextStream[i + 1] );
+ return i + 1;
+ }
+ }
+ }
+ x += wide;
+ }
+
+ // Msg( "PixelToCursorSpace() never hit, returning %d\n", i );
+ return i;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws a string of characters in the panel
+// Input: iFirst - Index of the first character to draw
+// iLast - Index of the last character to draw
+// renderState - Render state to use
+// font- font to use
+// Output: returns the width of the character drawn
+//-----------------------------------------------------------------------------
+int RichText::DrawString(int iFirst, int iLast, TRenderState &renderState, HFont font)
+{
+// VPROF( "RichText::DrawString" );
+
+ // Calculate the render size
+ int fontTall = surface()->GetFontTall(font);
+ // BUGBUG John: This won't exactly match the rendered size
+ int charWide = 0;
+ for ( int i = iFirst; i <= iLast; i++ )
+ {
+ wchar_t ch = m_TextStream[i];
+#if USE_GETKERNEDCHARWIDTH
+ wchar_t chBefore = 0;
+ wchar_t chAfter = 0;
+ if ( i > 0 )
+ chBefore = m_TextStream[i-1];
+ if ( i < iLast )
+ chAfter = m_TextStream[i+1];
+ float flWide = 0.0f, flabcA = 0.0f;
+ surface()->GetKernedCharWidth(font, ch, chBefore, chAfter, flWide, flabcA);
+ if ( ch == L' ' )
+ flWide = ceil( flWide );
+ charWide += floor( flWide + 0.6 );
+#else
+ charWide += surface()->GetCharacterWidth(font, ch);
+#endif
+ }
+
+ // draw selection, if any
+ int selection0 = -1, selection1 = -1;
+ GetSelectedRange(selection0, selection1);
+
+ if (iFirst >= selection0 && iFirst < selection1)
+ {
+ // draw background selection color
+ surface()->DrawSetColor(_selectionColor);
+ surface()->DrawFilledRect(renderState.x, renderState.y, renderState.x + charWide, renderState.y + 1 + fontTall);
+
+ // reset text color
+ surface()->DrawSetTextColor(_selectionTextColor);
+ m_bAllTextAlphaIsZero = false;
+ }
+ else
+ {
+ surface()->DrawSetTextColor(renderState.textColor);
+ }
+
+ if ( renderState.textColor.a() != 0 )
+ {
+ m_bAllTextAlphaIsZero = false;
+ surface()->DrawSetTextPos(renderState.x, renderState.y);
+ surface()->DrawPrintText(&m_TextStream[iFirst], iLast - iFirst + 1);
+ }
+
+ return charWide;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finish drawing url
+//-----------------------------------------------------------------------------
+void RichText::FinishingURL(int x, int y)
+{
+ // finishing URL
+ if ( _clickableTextPanels.IsValidIndex( _clickableTextIndex ) )
+ {
+ ClickPanel *clickPanel = _clickableTextPanels[ _clickableTextIndex ];
+ int px, py;
+ clickPanel->GetPos(px, py);
+ int fontTall = GetLineHeight();
+ clickPanel->SetSize( MAX( x - px, 6 ), y - py + fontTall );
+ clickPanel->SetVisible(true);
+
+ // if we haven't actually advanced any, step back and ignore this one
+ // this is probably a data input problem though, need to find root cause
+ if ( x - px <= 0 )
+ {
+ --_clickableTextIndex;
+ clickPanel->SetVisible(false);
+ }
+ }
+}
+
+void RichText::CalculateFade( TRenderState &renderState )
+{
+ if ( m_FormatStream.IsValidIndex( renderState.formatStreamIndex ) )
+ {
+ if ( m_bResetFades == false )
+ {
+ if ( m_FormatStream[renderState.formatStreamIndex].fade.flFadeLength != -1.0f )
+ {
+ float frac = ( m_FormatStream[renderState.formatStreamIndex].fade.flFadeStartTime - system()->GetCurrentTime() ) / m_FormatStream[renderState.formatStreamIndex].fade.flFadeLength;
+
+ int alpha = frac * m_FormatStream[renderState.formatStreamIndex].fade.iOriginalAlpha;
+ alpha = clamp( alpha, 0, m_FormatStream[renderState.formatStreamIndex].fade.iOriginalAlpha );
+
+ renderState.textColor.SetColor( renderState.textColor.r(), renderState.textColor.g(), renderState.textColor.b(), alpha );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws the text in the panel
+//-----------------------------------------------------------------------------
+void RichText::Paint()
+{
+ // Assume the worst
+ m_bAllTextAlphaIsZero = true;
+
+ HFont hFontCurrent = _font;
+
+ // hide all the clickable panels until we know where they are to reside
+ for (int j = 0; j < _clickableTextPanels.Count(); j++)
+ {
+ _clickableTextPanels[j]->SetVisible(false);
+ }
+
+ if ( !HasText() )
+ return;
+
+ int wide, tall;
+ GetSize( wide, tall );
+
+ int lineBreakIndexIndex = 0;
+ int startIndex = GetStartDrawIndex(lineBreakIndexIndex);
+ _currentTextClickable = false;
+
+ _clickableTextIndex = GetClickableTextIndexStart(startIndex);
+
+ // recalculate and cache the render state at the render start
+ if (_recalcSavedRenderState)
+ {
+ RecalculateDefaultState(startIndex);
+ }
+ // copy off the cached render state
+ TRenderState renderState = m_CachedRenderState;
+
+ _pixelsIndent = m_CachedRenderState.pixelsIndent;
+ _currentTextClickable = m_CachedRenderState.textClickable;
+
+ renderState.textClickable = _currentTextClickable;
+ renderState.textColor = m_FormatStream[renderState.formatStreamIndex].color;
+ CalculateFade( renderState );
+
+ renderState.formatStreamIndex++;
+
+ if ( _currentTextClickable )
+ {
+ _clickableTextIndex = startIndex;
+ }
+
+ // where to start drawing
+ renderState.x = _drawOffsetX + _pixelsIndent;
+ renderState.y = _drawOffsetY;
+
+ // draw the text
+ int selection0 = -1, selection1 = -1;
+ GetSelectedRange(selection0, selection1);
+
+ surface()->DrawSetTextFont( hFontCurrent );
+
+ for (int i = startIndex; i < m_TextStream.Count() && renderState.y < tall; )
+ {
+ // 1.
+ // Update our current render state based on the formatting and color streams,
+ // this has to happen if it's our very first iteration, or if we are actually changing
+ // state.
+ int nXBeforeStateChange = renderState.x;
+ if ( UpdateRenderState(i, renderState) || i == startIndex )
+ {
+ // check for url state change
+ if (renderState.textClickable != _currentTextClickable)
+ {
+ if (renderState.textClickable)
+ {
+ // entering new URL
+ _clickableTextIndex++;
+ hFontCurrent = m_hFontUnderline;
+ surface()->DrawSetTextFont( hFontCurrent );
+
+ // set up the panel
+ ClickPanel *clickPanel = _clickableTextPanels.IsValidIndex( _clickableTextIndex ) ? _clickableTextPanels[_clickableTextIndex] : NULL;
+
+ if (clickPanel)
+ {
+ clickPanel->SetPos(renderState.x, renderState.y);
+ }
+ }
+ else
+ {
+ FinishingURL(nXBeforeStateChange, renderState.y);
+ hFontCurrent = _font;
+ surface()->DrawSetTextFont( hFontCurrent );
+ }
+ _currentTextClickable = renderState.textClickable;
+ }
+ }
+
+ // 2.
+ // if we've passed a line break go to that
+ if ( m_LineBreaks.IsValidIndex( lineBreakIndexIndex ) && m_LineBreaks[lineBreakIndexIndex] <= i )
+ {
+ if (_currentTextClickable)
+ {
+ FinishingURL(renderState.x, renderState.y);
+ }
+
+ // add another line
+ AddAnotherLine(renderState.x, renderState.y);
+ lineBreakIndexIndex++;
+
+ // Skip white space unless the previous line ended from the hard carriage return
+ if ( i && ( m_TextStream[i-1] != '\n' ) && ( m_TextStream[i-1] != '\r') )
+ {
+ while ( m_TextStream[i] == L' ' )
+ {
+ if ( i+1 < m_TextStream.Count() )
+ ++i;
+ else
+ break;
+ }
+ }
+
+ if (renderState.textClickable)
+ {
+ // move to the next URL
+ _clickableTextIndex++;
+ ClickPanel *clickPanel = _clickableTextPanels.IsValidIndex( _clickableTextIndex ) ? _clickableTextPanels[_clickableTextIndex] : NULL;
+ if (clickPanel)
+ {
+ clickPanel->SetPos(renderState.x, renderState.y);
+ }
+ }
+ }
+
+ // 3.
+ // Calculate the range of text to draw all at once
+ int iLim = m_TextStream.Count();
+
+ // Stop at the next format change
+ if ( m_FormatStream.IsValidIndex(renderState.formatStreamIndex) &&
+ m_FormatStream[renderState.formatStreamIndex].textStreamIndex < iLim &&
+ m_FormatStream[renderState.formatStreamIndex].textStreamIndex >= i &&
+ m_FormatStream[renderState.formatStreamIndex].textStreamIndex )
+ {
+ iLim = m_FormatStream[renderState.formatStreamIndex].textStreamIndex;
+ }
+
+ // Stop at the next line break
+ if ( m_LineBreaks.IsValidIndex( lineBreakIndexIndex ) && m_LineBreaks[lineBreakIndexIndex] < iLim )
+ iLim = m_LineBreaks[lineBreakIndexIndex];
+
+ // Handle non-drawing characters specially
+ for ( int iT = i; iT < iLim; iT++ )
+ {
+ if ( iswcntrl(m_TextStream[iT]) )
+ {
+ iLim = iT;
+ break;
+ }
+ }
+
+ // 4.
+ // Draw the current text range
+ if ( iLim <= i )
+ {
+ if ( m_TextStream[i] == '\t' )
+ {
+ int dxTabWidth = 8 * surface()->GetCharacterWidth(hFontCurrent, ' ');
+ dxTabWidth = MAX( 1, dxTabWidth );
+
+ renderState.x = ( dxTabWidth * ( 1 + ( renderState.x / dxTabWidth ) ) );
+ }
+ i++;
+ }
+ else
+ {
+ renderState.x += DrawString(i, iLim - 1, renderState, hFontCurrent );
+ i = iLim;
+ }
+ }
+
+ if (renderState.textClickable)
+ {
+ FinishingURL(renderState.x, renderState.y);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int RichText::GetClickableTextIndexStart(int startIndex)
+{
+ // cycle to the right url panel for what is visible after the startIndex.
+ for (int i = 0; i < _clickableTextPanels.Count(); i++)
+ {
+ if (_clickableTextPanels[i]->GetViewTextIndex() >= startIndex)
+ {
+ return i - 1;
+ }
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recalcultes the formatting state from the specified index
+//-----------------------------------------------------------------------------
+void RichText::RecalculateDefaultState(int startIndex)
+{
+ if (!HasText() )
+ return;
+
+ Assert(startIndex < m_TextStream.Count());
+
+ m_CachedRenderState.textColor = GetFgColor();
+ _pixelsIndent = 0;
+ _currentTextClickable = false;
+ _clickableTextIndex = GetClickableTextIndexStart(startIndex);
+
+ // find where in the formatting stream we need to be
+ GenerateRenderStateForTextStreamIndex(startIndex, m_CachedRenderState);
+ _recalcSavedRenderState = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: updates a render state based on the formatting and color streams
+// Output: true if we changed the render state
+//-----------------------------------------------------------------------------
+bool RichText::UpdateRenderState(int textStreamPos, TRenderState &renderState)
+{
+ // check the color stream
+ if (m_FormatStream.IsValidIndex(renderState.formatStreamIndex) &&
+ m_FormatStream[renderState.formatStreamIndex].textStreamIndex == textStreamPos)
+ {
+ // set the current formatting
+ renderState.textColor = m_FormatStream[renderState.formatStreamIndex].color;
+ renderState.textClickable = m_FormatStream[renderState.formatStreamIndex].textClickable;
+
+ CalculateFade( renderState );
+
+ int indentChange = m_FormatStream[renderState.formatStreamIndex].pixelsIndent - renderState.pixelsIndent;
+ renderState.pixelsIndent = m_FormatStream[renderState.formatStreamIndex].pixelsIndent;
+
+ if (indentChange)
+ {
+ renderState.x = renderState.pixelsIndent + _drawOffsetX;
+ }
+
+ //!! for supporting old functionality, store off state in globals
+ _pixelsIndent = renderState.pixelsIndent;
+
+ // move to the next position in the color stream
+ renderState.formatStreamIndex++;
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the index in the format stream for the specified text stream index
+//-----------------------------------------------------------------------------
+int RichText::FindFormatStreamIndexForTextStreamPos(int textStreamIndex)
+{
+ int formatStreamIndex = 0;
+ for (; m_FormatStream.IsValidIndex(formatStreamIndex); formatStreamIndex++)
+ {
+ if (m_FormatStream[formatStreamIndex].textStreamIndex > textStreamIndex)
+ break;
+ }
+
+ // step back to the color change before the new line
+ formatStreamIndex--;
+ if (!m_FormatStream.IsValidIndex(formatStreamIndex))
+ {
+ formatStreamIndex = 0;
+ }
+ return formatStreamIndex;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generates a base renderstate given a index into the text stream
+//-----------------------------------------------------------------------------
+void RichText::GenerateRenderStateForTextStreamIndex(int textStreamIndex, TRenderState &renderState)
+{
+ // find where in the format stream we need to be given the specified place in the text stream
+ renderState.formatStreamIndex = FindFormatStreamIndexForTextStreamPos(textStreamIndex);
+
+ // copy the state data
+ renderState.textColor = m_FormatStream[renderState.formatStreamIndex].color;
+ renderState.pixelsIndent = m_FormatStream[renderState.formatStreamIndex].pixelsIndent;
+ renderState.textClickable = m_FormatStream[renderState.formatStreamIndex].textClickable;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called pre render
+//-----------------------------------------------------------------------------
+void RichText::OnThink()
+{
+ if (m_bRecalcLineBreaks)
+ {
+ _recalcSavedRenderState = true;
+ RecalculateLineBreaks();
+
+ // recalculate scrollbar position
+ if (_invalidateVerticalScrollbarSlider)
+ {
+ LayoutVerticalScrollBarSlider();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when data changes or panel size changes
+//-----------------------------------------------------------------------------
+void RichText::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ // force a Repaint
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: inserts a color change into the formatting stream
+//-----------------------------------------------------------------------------
+void RichText::InsertColorChange(Color col)
+{
+ // see if color already exists in text stream
+ TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1];
+ if (prevItem.color == col)
+ {
+ // inserting same color into stream, just ignore
+ }
+ else if (prevItem.textStreamIndex == m_TextStream.Count())
+ {
+ // this item is in the same place; update values
+ prevItem.color = col;
+ }
+ else
+ {
+ // add to text stream, based off existing item
+ TFormatStream streamItem = prevItem;
+ streamItem.color = col;
+ streamItem.textStreamIndex = m_TextStream.Count();
+ m_FormatStream.AddToTail(streamItem);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: inserts a fade into the formatting stream
+//-----------------------------------------------------------------------------
+void RichText::InsertFade( float flSustain, float flLength )
+{
+ // see if color already exists in text stream
+ TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1];
+ if (prevItem.textStreamIndex == m_TextStream.Count())
+ {
+ // this item is in the same place; update values
+ prevItem.fade.flFadeStartTime = system()->GetCurrentTime() + flSustain;
+ prevItem.fade.flFadeSustain = flSustain;
+ prevItem.fade.flFadeLength = flLength;
+ prevItem.fade.iOriginalAlpha = prevItem.color.a();
+ }
+ else
+ {
+ // add to text stream, based off existing item
+ TFormatStream streamItem = prevItem;
+
+ prevItem.fade.flFadeStartTime = system()->GetCurrentTime() + flSustain;
+ prevItem.fade.flFadeLength = flLength;
+ prevItem.fade.flFadeSustain = flSustain;
+ prevItem.fade.iOriginalAlpha = prevItem.color.a();
+
+ streamItem.textStreamIndex = m_TextStream.Count();
+ m_FormatStream.AddToTail(streamItem);
+ }
+}
+
+void RichText::ResetAllFades( bool bHold, bool bOnlyExpired, float flNewSustain )
+{
+ m_bResetFades = bHold;
+
+ if ( m_bResetFades == false )
+ {
+ for (int i = 1; i < m_FormatStream.Count(); i++)
+ {
+ if ( bOnlyExpired == true )
+ {
+ if ( m_FormatStream[i].fade.flFadeStartTime >= system()->GetCurrentTime() )
+ continue;
+ }
+
+ if ( flNewSustain == -1.0f )
+ {
+ flNewSustain = m_FormatStream[i].fade.flFadeSustain;
+ }
+
+ m_FormatStream[i].fade.flFadeStartTime = system()->GetCurrentTime() + flNewSustain;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: inserts an indent change into the formatting stream
+//-----------------------------------------------------------------------------
+void RichText::InsertIndentChange(int pixelsIndent)
+{
+ if (pixelsIndent < 0)
+ {
+ pixelsIndent = 0;
+ }
+ else if (pixelsIndent > 255)
+ {
+ pixelsIndent = 255;
+ }
+
+ // see if indent change already exists in text stream
+ TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1];
+ if (prevItem.pixelsIndent == pixelsIndent)
+ {
+ // inserting same indent into stream, just ignore
+ }
+ else if (prevItem.textStreamIndex == m_TextStream.Count())
+ {
+ // this item is in the same place; update
+ prevItem.pixelsIndent = pixelsIndent;
+ }
+ else
+ {
+ // add to text stream, based off existing item
+ TFormatStream streamItem = prevItem;
+ streamItem.pixelsIndent = pixelsIndent;
+ streamItem.textStreamIndex = m_TextStream.Count();
+ m_FormatStream.AddToTail(streamItem);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Inserts character Start for clickable text, eg. URLS
+//-----------------------------------------------------------------------------
+void RichText::InsertClickableTextStart( const char *pchClickAction )
+{
+ // see if indent change already exists in text stream
+ TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1];
+ TFormatStream *pFormatStream = &prevItem;
+ if (prevItem.textStreamIndex == m_TextStream.Count())
+ {
+ // this item is in the same place; update
+ prevItem.textClickable = true;
+ pFormatStream->m_sClickableTextAction = pchClickAction;
+ }
+ else
+ {
+ // add to text stream, based off existing item
+ TFormatStream formatStreamCopy = prevItem;
+ int iFormatStream = m_FormatStream.AddToTail( formatStreamCopy );
+
+ // set the new params
+ pFormatStream = &m_FormatStream[iFormatStream];
+ pFormatStream->textStreamIndex = m_TextStream.Count();
+ pFormatStream->textClickable = true;
+ pFormatStream->m_sClickableTextAction = pchClickAction;
+ }
+
+ // invalidate the layout to recalculate where the click panels should go
+ InvalidateLineBreakStream();
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Inserts character end for clickable text, eg. URLS
+//-----------------------------------------------------------------------------
+void RichText::InsertClickableTextEnd()
+{
+ // see if indent change already exists in text stream
+ TFormatStream &prevItem = m_FormatStream[m_FormatStream.Count() - 1];
+ if (!prevItem.textClickable)
+ {
+ // inserting same indent into stream, just ignore
+ }
+ else if (prevItem.textStreamIndex == m_TextStream.Count())
+ {
+ // this item is in the same place; update
+ prevItem.textClickable = false;
+ }
+ else
+ {
+ // add to text stream, based off existing item
+ TFormatStream streamItem = prevItem;
+ streamItem.textClickable = false;
+ streamItem.textStreamIndex = m_TextStream.Count();
+ m_FormatStream.AddToTail(streamItem);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: moves x,y to the Start of the next line of text
+//-----------------------------------------------------------------------------
+void RichText::AddAnotherLine(int &cx, int &cy)
+{
+ cx = _drawOffsetX + _pixelsIndent;
+ cy += (GetLineHeight() + _drawOffsetY);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recalculates line breaks
+//-----------------------------------------------------------------------------
+void RichText::RecalculateLineBreaks()
+{
+ if ( !m_bRecalcLineBreaks )
+ return;
+
+ int wide = GetWide();
+ if (!wide)
+ return;
+
+ wide -= _drawOffsetX;
+
+ m_bRecalcLineBreaks = false;
+ _recalcSavedRenderState = true;
+ if (!HasText())
+ return;
+
+ int selection0 = -1, selection1 = -1;
+
+ // subtract the scrollbar width
+ if (_vertScrollBar->IsVisible())
+ {
+ wide -= _vertScrollBar->GetWide();
+ }
+
+ int x = _drawOffsetX, y = _drawOffsetY;
+
+ HFont fontWordStart = INVALID_FONT;
+ int wordStartIndex = 0;
+ int lineStartIndex = 0;
+ bool hasWord = false;
+ bool justStartedNewLine = true;
+ bool wordStartedOnNewLine = true;
+
+ int startChar = 0;
+ if (_recalculateBreaksIndex <= 0)
+ {
+ m_LineBreaks.RemoveAll();
+ }
+ else
+ {
+ // remove the rest of the linebreaks list since its out of date.
+ for (int i = _recalculateBreaksIndex + 1; i < m_LineBreaks.Count(); ++i)
+ {
+ m_LineBreaks.Remove(i);
+ --i; // removing shrinks the list!
+ }
+ startChar = m_LineBreaks[_recalculateBreaksIndex];
+ lineStartIndex = m_LineBreaks[_recalculateBreaksIndex];
+ wordStartIndex = lineStartIndex;
+ }
+
+ // handle the case where this char is a new line, in that case
+ // we have already taken its break index into account above so skip it.
+ if (m_TextStream[startChar] == '\r' || m_TextStream[startChar] == '\n')
+ {
+ startChar++;
+ lineStartIndex = startChar;
+ }
+
+ // cycle to the right url panel for what is visible after the startIndex.
+ int clickableTextNum = GetClickableTextIndexStart(startChar);
+ clickableTextNum++;
+
+ // initialize the renderstate with the start
+ TRenderState renderState;
+ GenerateRenderStateForTextStreamIndex(startChar, renderState);
+ _currentTextClickable = false;
+
+ HFont font = _font;
+
+ bool bForceBreak = false;
+ float flLineWidthSoFar = 0;
+
+ // loop through all the characters
+ for (int i = startChar; i < m_TextStream.Count(); ++i)
+ {
+ wchar_t ch = m_TextStream[i];
+ renderState.x = x;
+ if (UpdateRenderState(i, renderState))
+ {
+ x = renderState.x;
+ int preI = i;
+
+ // check for clickable text
+ if (renderState.textClickable != _currentTextClickable)
+ {
+ if (renderState.textClickable)
+ {
+ // make a new clickable text panel
+ if (clickableTextNum >= _clickableTextPanels.Count())
+ {
+ _clickableTextPanels.AddToTail(new ClickPanel(this));
+ }
+
+ ClickPanel *clickPanel = _clickableTextPanels[clickableTextNum++];
+ clickPanel->SetTextIndex(preI, preI);
+ }
+
+ // url state change
+ _currentTextClickable = renderState.textClickable;
+ }
+ }
+
+ bool bIsWSpace = iswspace( ch ) ? true : false;
+
+ bool bPreviousWordStartedOnNewLine = wordStartedOnNewLine;
+ int iPreviousWordStartIndex = wordStartIndex;
+ if ( !bIsWSpace && ch != L'\t' && ch != L'\n' && ch != L'\r' )
+ {
+ if (!hasWord)
+ {
+ // Start a new word
+ wordStartIndex = i;
+ hasWord = true;
+ wordStartedOnNewLine = justStartedNewLine;
+ fontWordStart = font;
+ }
+ // else append to the current word
+ }
+ else
+ {
+ // whitespace/punctuation character
+ // end the word
+ hasWord = false;
+ }
+
+ float w = 0;
+ wchar_t wchBefore = 0;
+ wchar_t wchAfter = 0;
+
+ if ( i > 0 && i > lineStartIndex && i != selection0 && i-1 != selection1 )
+ wchBefore = m_TextStream[i-1];
+ if ( i < m_TextStream.Count() - 1 && i+1 != selection0 && i != selection1 )
+ wchAfter = m_TextStream[i+1];
+
+ float flabcA;
+ surface()->GetKernedCharWidth( font, ch, wchBefore, wchAfter, w, flabcA );
+ flLineWidthSoFar += w;
+
+ // See if we've exceeded the width we have available, with
+ if ( floor(flLineWidthSoFar + 0.6) + x > wide )
+ {
+ bForceBreak = true;
+ }
+
+ if (!iswcntrl(ch))
+ {
+ justStartedNewLine = false;
+ }
+
+ if ( bForceBreak || ch == '\r' || ch == '\n' )
+ {
+ bForceBreak = false;
+ // add another line
+ AddAnotherLine(x, y);
+
+ if ( ch == '\r' || ch == '\n' )
+ {
+ // skip the newline so it's not at the beginning of the new line
+ lineStartIndex = i + 1;
+ m_LineBreaks.AddToTail(i + 1);
+ }
+ else if ( bPreviousWordStartedOnNewLine || iPreviousWordStartIndex <= lineStartIndex )
+ {
+ lineStartIndex = i;
+ m_LineBreaks.AddToTail( i );
+
+ if (renderState.textClickable)
+ {
+ // need to split the url into two panels
+ int oldIndex = _clickableTextPanels[clickableTextNum - 1]->GetTextIndex();
+
+ // make a new clickable text panel
+ if (clickableTextNum >= _clickableTextPanels.Count())
+ {
+ _clickableTextPanels.AddToTail(new ClickPanel(this));
+ }
+
+ ClickPanel *clickPanel = _clickableTextPanels[clickableTextNum++];
+ clickPanel->SetTextIndex(oldIndex, i);
+ }
+ }
+ else
+ {
+ m_LineBreaks.AddToTail( iPreviousWordStartIndex );
+ lineStartIndex = iPreviousWordStartIndex;
+ i = iPreviousWordStartIndex;
+
+ TRenderState renderStateAtLastWord;
+ GenerateRenderStateForTextStreamIndex( i, renderStateAtLastWord );
+
+ // If the word is clickable, and that started prior to the beginning of the word, then we must split the click panel
+ if ( renderStateAtLastWord.textClickable && m_FormatStream[ renderStateAtLastWord.formatStreamIndex ].textStreamIndex < i )
+ {
+ // need to split the url into two panels
+ int oldIndex = _clickableTextPanels[clickableTextNum - 1]->GetTextIndex();
+
+ // make a new clickable text panel
+ if (clickableTextNum >= _clickableTextPanels.Count())
+ {
+ _clickableTextPanels.AddToTail(new ClickPanel(this));
+ }
+
+ ClickPanel *clickPanel = _clickableTextPanels[clickableTextNum++];
+ clickPanel->SetTextIndex(oldIndex, i);
+ }
+ }
+
+ flLineWidthSoFar = 0;
+ justStartedNewLine = true;
+ hasWord = false;
+ wordStartedOnNewLine = false;
+ _currentTextClickable = false;
+ continue;
+ }
+ }
+
+ // end the list
+ m_LineBreaks.AddToTail(MAX_BUFFER_SIZE);
+
+ // set up the scrollbar
+ _invalidateVerticalScrollbarSlider = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recalculate where the vertical scroll bar slider should be
+// based on the current cursor line we are on.
+//-----------------------------------------------------------------------------
+void RichText::LayoutVerticalScrollBarSlider()
+{
+ _invalidateVerticalScrollbarSlider = false;
+
+ // set up the scrollbar
+ //if (!_vertScrollBar->IsVisible())
+ // return;
+
+
+ // see where the scrollbar currently is
+ int previousValue = _vertScrollBar->GetValue();
+ bool bCurrentlyAtEnd = false;
+ int rmin, rmax;
+ _vertScrollBar->GetRange(rmin, rmax);
+ if (rmax && (previousValue + rmin + _vertScrollBar->GetRangeWindow() == rmax))
+ {
+ bCurrentlyAtEnd = true;
+ }
+
+ // work out position to put scrollbar, factoring in insets
+ int wide, tall;
+ GetSize( wide, tall );
+
+ _vertScrollBar->SetPos( wide - _vertScrollBar->GetWide(), 0 );
+ // scrollbar is inside the borders.
+ _vertScrollBar->SetSize( _vertScrollBar->GetWide(), tall );
+
+ // calculate how many lines we can fully display
+ int displayLines = tall / (GetLineHeight() + _drawOffsetY);
+ int numLines = m_LineBreaks.Count();
+
+ if (numLines <= displayLines)
+ {
+ // disable the scrollbar
+ _vertScrollBar->SetEnabled(false);
+ _vertScrollBar->SetRange(0, numLines);
+ _vertScrollBar->SetRangeWindow(numLines);
+ _vertScrollBar->SetValue(0);
+
+ if ( m_bUnusedScrollbarInvis )
+ {
+ SetVerticalScrollbar( false );
+ }
+ }
+ else
+ {
+ if ( m_bUnusedScrollbarInvis )
+ {
+ SetVerticalScrollbar( true );
+ }
+
+ // set the scrollbars range
+ _vertScrollBar->SetRange(0, numLines);
+ _vertScrollBar->SetRangeWindow(displayLines);
+ _vertScrollBar->SetEnabled(true);
+
+ // this should make it scroll one line at a time
+ _vertScrollBar->SetButtonPressedScrollValue(1);
+ if (bCurrentlyAtEnd)
+ {
+ _vertScrollBar->SetValue(numLines - displayLines);
+ }
+ _vertScrollBar->InvalidateLayout();
+ _vertScrollBar->Repaint();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets whether a vertical scrollbar is visible
+//-----------------------------------------------------------------------------
+void RichText::SetVerticalScrollbar(bool state)
+{
+ if (_vertScrollBar->IsVisible() != state)
+ {
+ _vertScrollBar->SetVisible(state);
+ InvalidateLineBreakStream();
+ InvalidateLayout();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create cut/copy/paste dropdown menu
+//-----------------------------------------------------------------------------
+void RichText::CreateEditMenu()
+{
+ // create a drop down cut/copy/paste menu appropriate for this object's states
+ if (m_pEditMenu)
+ delete m_pEditMenu;
+ m_pEditMenu = new Menu(this, "EditMenu");
+
+
+ // add cut/copy/paste drop down options if its editable, just copy if it is not
+ m_pEditMenu->AddMenuItem("C&opy", new KeyValues("DoCopySelected"), this);
+
+ m_pEditMenu->SetVisible(false);
+ m_pEditMenu->SetParent(this);
+ m_pEditMenu->AddActionSignalTarget(this);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: We want single line windows to scroll horizontally and select text
+// in response to clicking and holding outside window
+//-----------------------------------------------------------------------------
+void RichText::OnMouseFocusTicked()
+{
+ // if a button is down move the scrollbar slider the appropriate direction
+ if (_mouseDragSelection) // text is being selected via mouse clicking and dragging
+ {
+ OnCursorMoved(0,0); // we want the text to scroll as if we were dragging
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If a cursor enters the window, we are not elegible for
+// MouseFocusTicked events
+//-----------------------------------------------------------------------------
+void RichText::OnCursorEntered()
+{
+ _mouseDragSelection = false; // outside of window dont recieve drag scrolling ticks
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: When the cursor is outside the window, if we are holding the mouse
+// button down, then we want the window to scroll the text one char at a time using Ticks
+//-----------------------------------------------------------------------------
+void RichText::OnCursorExited()
+{
+ // outside of window recieve drag scrolling ticks
+ if (_mouseSelection)
+ {
+ _mouseDragSelection = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle selection of text by mouse
+//-----------------------------------------------------------------------------
+void RichText::OnCursorMoved(int x, int y)
+{
+ if (_mouseSelection)
+ {
+ // update the cursor position
+ int x, y;
+ input()->GetCursorPos(x, y);
+ ScreenToLocal(x, y);
+ _cursorPos = PixelToCursorSpace(x, y);
+
+ if (_cursorPos != _select[1])
+ {
+ _select[1] = _cursorPos;
+ Repaint();
+ }
+ // Msg( "selecting range [%d..%d]\n", _select[0], _select[1] );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle mouse button down events.
+//-----------------------------------------------------------------------------
+void RichText::OnMousePressed(MouseCode code)
+{
+ if (code == MOUSE_LEFT)
+ {
+ // clear current selection
+ SelectNone();
+
+ // move the cursor to where the mouse was pressed
+ int x, y;
+ input()->GetCursorPos(x, y);
+ ScreenToLocal(x, y);
+
+ _cursorPos = PixelToCursorSpace(x, y);
+
+ if ( m_bInteractive )
+ {
+ // enter selection mode
+ input()->SetMouseCapture(GetVPanel());
+ _mouseSelection = true;
+
+ if (_select[0] < 0)
+ {
+ // if no initial selection position, Start selection position at cursor
+ _select[0] = _cursorPos;
+ }
+ _select[1] = _cursorPos;
+ }
+
+ RequestFocus();
+ Repaint();
+ }
+ else if (code == MOUSE_RIGHT) // check for context menu open
+ {
+ if ( m_bInteractive )
+ {
+ CreateEditMenu();
+ Assert(m_pEditMenu);
+
+ OpenEditMenu();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle mouse button up events
+//-----------------------------------------------------------------------------
+void RichText::OnMouseReleased(MouseCode code)
+{
+ _mouseSelection = false;
+ input()->SetMouseCapture(NULL);
+
+ // make sure something has been selected
+ int cx0, cx1;
+ if (GetSelectedRange(cx0, cx1))
+ {
+ if (cx1 - cx0 == 0)
+ {
+ // nullify selection
+ _select[0] = -1;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle mouse double clicks
+//-----------------------------------------------------------------------------
+void RichText::OnMouseDoublePressed(MouseCode code)
+{
+ if ( !m_bInteractive )
+ return;
+
+ // left double clicking on a word selects the word
+ if (code == MOUSE_LEFT)
+ {
+ // move the cursor just as if you single clicked.
+ OnMousePressed(code);
+ // then find the start and end of the word we are in to highlight it.
+ int selectSpot[2];
+ GotoWordLeft();
+ selectSpot[0] = _cursorPos;
+ GotoWordRight();
+ selectSpot[1] = _cursorPos;
+
+ if ( _cursorPos > 0 && (_cursorPos-1) < m_TextStream.Count() )
+ {
+ if (iswspace(m_TextStream[_cursorPos-1]))
+ {
+ selectSpot[1]--;
+ _cursorPos--;
+ }
+ }
+
+ _select[0] = selectSpot[0];
+ _select[1] = selectSpot[1];
+ _mouseSelection = true;
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turn off text selection code when mouse button is not down
+//-----------------------------------------------------------------------------
+void RichText::OnMouseCaptureLost()
+{
+ _mouseSelection = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Masks which keys get chained up
+// Maps keyboard input to text window functions.
+//-----------------------------------------------------------------------------
+void RichText::OnKeyCodeTyped(KeyCode code)
+{
+ bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+ bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+ bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
+ bool winkey = (input()->IsKeyDown(KEY_LWIN) || input()->IsKeyDown(KEY_RWIN));
+ bool fallThrough = false;
+
+ if ( ctrl || ( winkey && IsOSX() ) )
+ {
+ switch(code)
+ {
+ case KEY_INSERT:
+ case KEY_C:
+ case KEY_X:
+ {
+ CopySelected();
+ break;
+ }
+ case KEY_PAGEUP:
+ case KEY_HOME:
+ {
+ GotoTextStart();
+ break;
+ }
+ case KEY_PAGEDOWN:
+ case KEY_END:
+ {
+ GotoTextEnd();
+ break;
+ }
+ default:
+ {
+ fallThrough = true;
+ break;
+ }
+ }
+ }
+ else if (alt)
+ {
+ // do nothing with ALT-x keys
+ fallThrough = true;
+ }
+ else
+ {
+ switch(code)
+ {
+ case KEY_TAB:
+ case KEY_LSHIFT:
+ case KEY_RSHIFT:
+ case KEY_ESCAPE:
+ case KEY_ENTER:
+ {
+ fallThrough = true;
+ break;
+ }
+ case KEY_DELETE:
+ {
+ if (shift)
+ {
+ // shift-delete is cut
+ CopySelected();
+ }
+ break;
+ }
+ case KEY_HOME:
+ {
+ GotoTextStart();
+ break;
+ }
+ case KEY_END:
+ {
+ GotoTextEnd();
+ break;
+ }
+ case KEY_PAGEUP:
+ {
+ // if there is a scroll bar scroll down one rangewindow
+ if (_vertScrollBar->IsVisible())
+ {
+ int window = _vertScrollBar->GetRangeWindow();
+ int newval = _vertScrollBar->GetValue();
+ _vertScrollBar->SetValue(newval - window - 1);
+ }
+ break;
+
+ }
+ case KEY_PAGEDOWN:
+ {
+ // if there is a scroll bar scroll down one rangewindow
+ if (_vertScrollBar->IsVisible())
+ {
+ int window = _vertScrollBar->GetRangeWindow();
+ int newval = _vertScrollBar->GetValue();
+ _vertScrollBar->SetValue(newval + window + 1);
+ }
+ break;
+ }
+ default:
+ {
+ // return if any other char is pressed.
+ // as it will be a unicode char.
+ // and we don't want select[1] changed unless a char was pressed that this fxn handles
+ return;
+ }
+ }
+ }
+
+ // select[1] is the location in the line where the blinking cursor started
+ _select[1] = _cursorPos;
+
+ // chain back on some keys
+ if (fallThrough)
+ {
+ BaseClass::OnKeyCodeTyped(code);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Scrolls the list according to the mouse wheel movement
+//-----------------------------------------------------------------------------
+void RichText::OnMouseWheeled(int delta)
+{
+ MoveScrollBar(delta);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Scrolls the list
+// Input : delta - amount to move scrollbar up
+//-----------------------------------------------------------------------------
+void RichText::MoveScrollBar(int delta)
+{
+ MoveScrollBarDirect( delta * 3 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Scrolls the list
+// Input : delta - amount to move scrollbar up
+//-----------------------------------------------------------------------------
+void RichText::MoveScrollBarDirect(int delta)
+{
+ if (_vertScrollBar->IsVisible())
+ {
+ int val = _vertScrollBar->GetValue();
+ val -= delta;
+ _vertScrollBar->SetValue(val);
+ _recalcSavedRenderState = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set the maximum number of chars in the text buffer
+//-----------------------------------------------------------------------------
+void RichText::SetMaximumCharCount(int maxChars)
+{
+ _maxCharCount = maxChars;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find out what line the cursor is on
+//-----------------------------------------------------------------------------
+int RichText::GetCursorLine()
+{
+ // always returns the last place
+ int pos = m_LineBreaks[m_LineBreaks.Count() - 1];
+ Assert(pos == MAX_BUFFER_SIZE);
+ return pos;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the cursor over to the Start of the next word to the right
+//-----------------------------------------------------------------------------
+void RichText::GotoWordRight()
+{
+ // search right until we hit a whitespace character or a newline
+ while (++_cursorPos < m_TextStream.Count())
+ {
+ if (iswspace(m_TextStream[_cursorPos]))
+ break;
+ }
+
+ // search right until we hit an nonspace character
+ while (++_cursorPos < m_TextStream.Count())
+ {
+ if (!iswspace(m_TextStream[_cursorPos]))
+ break;
+ }
+
+ if (_cursorPos > m_TextStream.Count())
+ {
+ _cursorPos = m_TextStream.Count();
+ }
+
+ // now we are at the start of the next word
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the cursor over to the Start of the next word to the left
+//-----------------------------------------------------------------------------
+void RichText::GotoWordLeft()
+{
+ if (_cursorPos < 1)
+ return;
+
+ // search left until we hit an nonspace character
+ while (--_cursorPos >= 0)
+ {
+ if (!iswspace(m_TextStream[_cursorPos]))
+ break;
+ }
+
+ // search left until we hit a whitespace character
+ while (--_cursorPos >= 0)
+ {
+ if (iswspace(m_TextStream[_cursorPos]))
+ {
+ break;
+ }
+ }
+
+ // we end one character off
+ _cursorPos++;
+
+ // now we are at the start of the previous word
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move cursor to the Start of the text buffer
+//-----------------------------------------------------------------------------
+void RichText::GotoTextStart()
+{
+ _cursorPos = 0; // set cursor to start
+ _invalidateVerticalScrollbarSlider = true;
+ // force scrollbar to the top
+ _vertScrollBar->SetValue(0);
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move cursor to the end of the text buffer
+//-----------------------------------------------------------------------------
+void RichText::GotoTextEnd()
+{
+ _cursorPos = m_TextStream.Count(); // set cursor to end of buffer
+ _invalidateVerticalScrollbarSlider = true;
+
+ // force the scrollbar to the bottom
+ int min, max;
+ _vertScrollBar->GetRange(min, max);
+ _vertScrollBar->SetValue(max);
+
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Culls the text stream down to a managable size
+//-----------------------------------------------------------------------------
+void RichText::TruncateTextStream()
+{
+ if (_maxCharCount < 1)
+ return;
+
+ // choose a point to cull at
+ int cullPos = _maxCharCount / 2;
+
+ // kill half the buffer
+ m_TextStream.RemoveMultiple(0, cullPos);
+
+ // work out where in the format stream we can start
+ int formatIndex = FindFormatStreamIndexForTextStreamPos(cullPos);
+ if (formatIndex > 0)
+ {
+ // take a copy, make it first
+ m_FormatStream[0] = m_FormatStream[formatIndex];
+ m_FormatStream[0].textStreamIndex = 0;
+ // kill the others
+ m_FormatStream.RemoveMultiple(1, formatIndex);
+ }
+
+ // renormalize the remainder of the format stream
+ for (int i = 1; i < m_FormatStream.Count(); i++)
+ {
+ Assert(m_FormatStream[i].textStreamIndex > cullPos);
+ m_FormatStream[i].textStreamIndex -= cullPos;
+ }
+
+ // mark everything to be recalculated
+ InvalidateLineBreakStream();
+ InvalidateLayout();
+ _invalidateVerticalScrollbarSlider = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Insert a character into the text buffer
+//-----------------------------------------------------------------------------
+void RichText::InsertChar(wchar_t wch)
+{
+ // throw away redundant linefeed characters
+ if ( wch == '\r' )
+ return;
+
+ if (_maxCharCount > 0 && m_TextStream.Count() > _maxCharCount)
+ {
+ TruncateTextStream();
+ }
+
+ // insert the new char at the end of the buffer
+ m_TextStream.AddToTail(wch);
+
+ // mark the linebreak steam as needing recalculating from that point
+ _recalculateBreaksIndex = m_LineBreaks.Count() - 2;
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Insert a string into the text buffer, this is just a series
+// of char inserts because we have to check each char is ok to insert
+//-----------------------------------------------------------------------------
+void RichText::InsertString(const char *text)
+{
+ if (text[0] == '#')
+ {
+ wchar_t unicode[ 1024 ];
+ ResolveLocalizedTextAndVariables( text, unicode, sizeof( unicode ) );
+ InsertString( unicode );
+ return;
+ }
+
+ // upgrade the ansi text to unicode to display it
+ int len = strlen(text);
+ wchar_t *unicode = (wchar_t *)_alloca((len + 1) * sizeof(wchar_t));
+ Q_UTF8ToUnicode(text, unicode, ((len + 1) * sizeof(wchar_t)));
+ InsertString(unicode);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Insertsa a unicode string into the buffer
+//-----------------------------------------------------------------------------
+void RichText::InsertString(const wchar_t *wszText)
+{
+ // insert the whole string
+ for (const wchar_t *ch = wszText; *ch != 0; ++ch)
+ {
+ InsertChar(*ch);
+ }
+ InvalidateLayout();
+ m_bRecalcLineBreaks = true;
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Declare a selection empty
+//-----------------------------------------------------------------------------
+void RichText::SelectNone()
+{
+ // tag the selection as empty
+ _select[0] = -1;
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Load in the selection range so cx0 is the Start and cx1 is the end
+// from smallest to highest (right to left)
+//-----------------------------------------------------------------------------
+bool RichText::GetSelectedRange(int &cx0, int &cx1)
+{
+ // if there is nothing selected return false
+ if (_select[0] == -1)
+ return false;
+
+ // sort the two position so cx0 is the smallest
+ cx0 = _select[0];
+ cx1 = _select[1];
+ if (cx1 < cx0)
+ {
+ int temp = cx0;
+ cx0 = cx1;
+ cx1 = temp;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Opens the cut/copy/paste dropdown menu
+//-----------------------------------------------------------------------------
+void RichText::OpenEditMenu()
+{
+ // get cursor position, this is local to this text edit window
+ // so we need to adjust it relative to the parent
+ int cursorX, cursorY;
+ input()->GetCursorPos(cursorX, cursorY);
+
+ /* !! disabled since it recursively gets panel pointers, potentially across dll boundaries,
+ and doesn't need to be necessary (it's just for handling windowed mode)
+
+ // find the frame that has no parent (the one on the desktop)
+ Panel *panel = this;
+ while ( panel->GetParent() != NULL)
+ {
+ panel = panel->GetParent();
+ }
+ panel->ScreenToLocal(cursorX, cursorY);
+ int x, y;
+ // get base panel's postition
+ panel->GetPos(x, y);
+
+ // adjust our cursor position accordingly
+ cursorX += x;
+ cursorY += y;
+ */
+
+ int x0, x1;
+ if (GetSelectedRange(x0, x1)) // there is something selected
+ {
+ m_pEditMenu->SetItemEnabled("&Cut", true);
+ m_pEditMenu->SetItemEnabled("C&opy", true);
+ }
+ else // there is nothing selected, disable cut/copy options
+ {
+ m_pEditMenu->SetItemEnabled("&Cut", false);
+ m_pEditMenu->SetItemEnabled("C&opy", false);
+ }
+ m_pEditMenu->SetVisible(true);
+ m_pEditMenu->RequestFocus();
+
+ // relayout the menu immediately so that we know it's size
+ m_pEditMenu->InvalidateLayout(true);
+ int menuWide, menuTall;
+ m_pEditMenu->GetSize(menuWide, menuTall);
+
+ // work out where the cursor is and therefore the best place to put the menu
+ int wide, tall;
+ surface()->GetScreenSize(wide, tall);
+
+ if (wide - menuWide > cursorX)
+ {
+ // menu hanging right
+ if (tall - menuTall > cursorY)
+ {
+ // menu hanging down
+ m_pEditMenu->SetPos(cursorX, cursorY);
+ }
+ else
+ {
+ // menu hanging up
+ m_pEditMenu->SetPos(cursorX, cursorY - menuTall);
+ }
+ }
+ else
+ {
+ // menu hanging left
+ if (tall - menuTall > cursorY)
+ {
+ // menu hanging down
+ m_pEditMenu->SetPos(cursorX - menuWide, cursorY);
+ }
+ else
+ {
+ // menu hanging up
+ m_pEditMenu->SetPos(cursorX - menuWide, cursorY - menuTall);
+ }
+ }
+
+ m_pEditMenu->RequestFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Cuts the selected chars from the buffer and
+// copies them into the clipboard
+//-----------------------------------------------------------------------------
+void RichText::CutSelected()
+{
+ CopySelected();
+ // have to request focus if we used the menu
+ RequestFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Copies the selected chars into the clipboard
+//-----------------------------------------------------------------------------
+void RichText::CopySelected()
+{
+ int x0, x1;
+ if (GetSelectedRange(x0, x1))
+ {
+ CUtlVector<wchar_t> buf;
+ for (int i = x0; i <= x1; i++)
+ {
+ if ( m_TextStream.IsValidIndex(i) == false )
+ continue;
+
+ if (m_TextStream[i] == '\n')
+ {
+ buf.AddToTail( '\r' );
+ }
+ // remove any rich edit commands
+ buf.AddToTail(m_TextStream[i]);
+ }
+ buf.AddToTail('\0');
+ system()->SetClipboardText(buf.Base(), buf.Count() - 1);
+ }
+
+ // have to request focus if we used the menu
+ RequestFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the index in the text buffer of the
+// character the drawing should Start at
+//-----------------------------------------------------------------------------
+int RichText::GetStartDrawIndex(int &lineBreakIndexIndex)
+{
+ int startIndex = 0;
+ int startLine = _vertScrollBar->GetValue();
+
+ if ( startLine >= m_LineBreaks.Count() ) // incase the line breaks got reset and the scroll bar hasn't
+ {
+ startLine = m_LineBreaks.Count() - 1;
+ }
+
+ lineBreakIndexIndex = startLine;
+ if (startLine && startLine < m_LineBreaks.Count())
+ {
+ startIndex = m_LineBreaks[startLine - 1];
+ }
+
+ return startIndex;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a string from text buffer
+// Input: offset - index to Start reading from
+// bufLen - length of string
+//-----------------------------------------------------------------------------
+void RichText::GetText(int offset, wchar_t *buf, int bufLenInBytes)
+{
+ if (!buf)
+ return;
+
+ Assert( bufLenInBytes >= sizeof(buf[0]) );
+ int bufLen = bufLenInBytes / sizeof(wchar_t);
+ int i;
+ for (i = offset; i < (offset + bufLen - 1); i++)
+ {
+ if (i >= m_TextStream.Count())
+ break;
+
+ buf[i-offset] = m_TextStream[i];
+ }
+ buf[(i-offset)] = 0;
+ buf[bufLen-1] = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets text from the buffer
+//-----------------------------------------------------------------------------
+void RichText::GetText(int offset, char *pch, int bufLenInBytes)
+{
+ wchar_t rgwchT[4096];
+ GetText(offset, rgwchT, sizeof(rgwchT));
+ Q_UnicodeToUTF8(rgwchT, pch, bufLenInBytes);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the font of the buffer text
+//-----------------------------------------------------------------------------
+void RichText::SetFont(HFont font)
+{
+ _font = font;
+ InvalidateLayout();
+ m_bRecalcLineBreaks = true;
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the scrollbar slider is moved
+//-----------------------------------------------------------------------------
+void RichText::OnSliderMoved()
+{
+ _recalcSavedRenderState = true;
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool RichText::RequestInfo(KeyValues *outputData)
+{
+ if (!stricmp(outputData->GetName(), "GetText"))
+ {
+ wchar_t wbuf[512];
+ GetText(0, wbuf, sizeof(wbuf));
+ outputData->SetWString("text", wbuf);
+ return true;
+ }
+
+ return BaseClass::RequestInfo(outputData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RichText::OnSetText(const wchar_t *text)
+{
+ SetText(text);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when a URL, etc has been clicked on
+//-----------------------------------------------------------------------------
+void RichText::OnClickPanel(int index)
+{
+ wchar_t wBuf[512];
+ int outIndex = 0;
+
+ // parse out the clickable text, and send it to our listeners
+ _currentTextClickable = true;
+ TRenderState renderState;
+ GenerateRenderStateForTextStreamIndex(index, renderState);
+ for (int i = index; i < (sizeof(wBuf) - 1) && i < m_TextStream.Count(); i++)
+ {
+ // stop getting characters when text is no longer clickable
+ UpdateRenderState(i, renderState);
+ if (!renderState.textClickable)
+ break;
+
+ // copy out the character
+ wBuf[outIndex++] = m_TextStream[i];
+ }
+
+ wBuf[outIndex] = 0;
+
+ int iFormatSteam = FindFormatStreamIndexForTextStreamPos( index );
+ if ( m_FormatStream[iFormatSteam].m_sClickableTextAction )
+ {
+ Q_UTF8ToUnicode( m_FormatStream[iFormatSteam].m_sClickableTextAction.String(), wBuf, sizeof( wBuf ) );
+ }
+
+ PostActionSignal(new KeyValues("TextClicked", "text", wBuf));
+ OnTextClicked(wBuf);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RichText::ApplySettings(KeyValues *inResourceData)
+{
+ BaseClass::ApplySettings(inResourceData);
+ SetMaximumCharCount(inResourceData->GetInt("maxchars", -1));
+ SetVerticalScrollbar(inResourceData->GetInt("scrollbar", 1));
+
+ // get the starting text, if any
+ const char *text = inResourceData->GetString("text", "");
+ if (*text)
+ {
+ delete [] m_pszInitialText;
+ int len = Q_strlen(text) + 1;
+ m_pszInitialText = new char[ len ];
+ Q_strncpy( m_pszInitialText, text, len );
+ SetText(text);
+ }
+ else
+ {
+ const char *textfilename = inResourceData->GetString("textfile", NULL);
+ if ( textfilename )
+ {
+ FileHandle_t f = g_pFullFileSystem->Open( textfilename, "rt" );
+ if (!f)
+ {
+ Warning( "RichText: textfile parameter '%s' not found.\n", textfilename );
+ return;
+ }
+
+ int len = g_pFullFileSystem->Size( f );
+ delete [] m_pszInitialText;
+ m_pszInitialText = new char[ len + 1 ];
+ g_pFullFileSystem->Read( m_pszInitialText, len, f );
+ m_pszInitialText[len - 1] = 0;
+ SetText( m_pszInitialText );
+
+ g_pFullFileSystem->Close( f );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RichText::GetSettings(KeyValues *outResourceData)
+{
+ BaseClass::GetSettings(outResourceData);
+ outResourceData->SetInt("maxchars", _maxCharCount);
+ outResourceData->SetInt("scrollbar", _vertScrollBar->IsVisible() );
+ if (m_pszInitialText)
+ {
+ outResourceData->SetString("text", m_pszInitialText);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *RichText::GetDescription()
+{
+ static char buf[1024];
+ Q_snprintf(buf, sizeof(buf), "%s, string text, bool scrollbar", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the number of lines in the window
+//-----------------------------------------------------------------------------
+int RichText::GetNumLines()
+{
+ return m_LineBreaks.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the height of the text entry window so all text will fit inside
+//-----------------------------------------------------------------------------
+void RichText::SetToFullHeight()
+{
+ PerformLayout();
+ int wide, tall;
+ GetSize(wide, tall);
+
+ tall = GetNumLines() * (GetLineHeight() + _drawOffsetY) + _drawOffsetY + 2;
+ SetSize (wide, tall);
+ PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Select all the text.
+//-----------------------------------------------------------------------------
+void RichText::SelectAllText()
+{
+ _cursorPos = 0;
+ _select[0] = 0;
+ _select[1] = m_TextStream.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Select all the text.
+//-----------------------------------------------------------------------------
+void RichText::SelectNoText()
+{
+ _select[0] = 0;
+ _select[1] = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RichText::OnSetFocus()
+{
+ BaseClass::OnSetFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Invalidates the current linebreak stream
+//-----------------------------------------------------------------------------
+void RichText::InvalidateLineBreakStream()
+{
+ // clear the buffer
+ m_LineBreaks.RemoveAll();
+ m_LineBreaks.AddToTail(MAX_BUFFER_SIZE);
+ _recalculateBreaksIndex = 0;
+ m_bRecalcLineBreaks = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Inserts a text string while making URLs clickable/different color
+// Input : *text - string that may contain URLs to make clickable/color coded
+// URLTextColor - color for URL text
+// normalTextColor - color for normal text
+//-----------------------------------------------------------------------------
+void RichText::InsertPossibleURLString(const char* text, Color URLTextColor, Color normalTextColor)
+{
+ InsertColorChange(normalTextColor);
+
+ // parse out the string for URL's
+ int len = Q_strlen(text), pos = 0;
+ bool clickable = false;
+ char *pchURLText = (char *)stackalloc( len + 1 );
+ char *pchURL = (char *)stackalloc( len + 1 );
+
+ while (pos < len)
+ {
+ pos = ParseTextStringForUrls( text, pos, pchURLText, len, pchURL, len, clickable );
+
+ if ( clickable )
+ {
+ InsertClickableTextStart( pchURL );
+ InsertColorChange( URLTextColor );
+ }
+
+ InsertString( pchURLText );
+
+ if ( clickable )
+ {
+ InsertColorChange(normalTextColor);
+ InsertClickableTextEnd();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: looks for URLs in the string and returns information about the URL
+//-----------------------------------------------------------------------------
+int RichText::ParseTextStringForUrls( const char *text, int startPos, char *pchURLText, int cchURLText, char *pchURL, int cchURL, bool &clickable )
+{
+ // scan for text that looks like a URL
+ int i = startPos;
+ while (text[i] != 0)
+ {
+ bool bURLFound = false;
+
+ if ( !Q_strnicmp(text + i, "<a href=", 8) )
+ {
+ if (i > startPos)
+ break;
+
+ // embedded link
+ bURLFound = true;
+ clickable = true;
+ // get the url
+ i += Q_strlen( "<a href=" );
+ const char *pchURLEnd = Q_strstr( text + i, ">" );
+ Q_strncpy( pchURL, text + i, min( pchURLEnd - text - i + 1, cchURL ) );
+ i += ( pchURLEnd - text - i + 1 );
+
+ // get the url text
+ pchURLEnd = Q_strstr( text, "</a>" );
+ Q_strncpy( pchURLText, text + i, min( pchURLEnd - text - i + 1, cchURLText ) );
+ i += ( pchURLEnd - text - i );
+ i += Q_strlen( "</a>" );
+
+ // we're done
+ return i;
+ }
+ else if (!Q_strnicmp(text + i, "www.", 4))
+ {
+ // scan ahead for another '.'
+ bool bPeriodFound = false;
+ for (const char *ch = text + i + 5; ch != 0; ch++)
+ {
+ if (*ch == '.')
+ {
+ bPeriodFound = true;
+ break;
+ }
+ }
+
+ // URL found
+ if (bPeriodFound)
+ {
+ bURLFound = true;
+ }
+ }
+ else if (!Q_strnicmp(text + i, "http://", 7))
+ {
+ bURLFound = true;
+ }
+ else if (!Q_strnicmp(text + i, "ftp://", 6))
+ {
+ bURLFound = true;
+ }
+ else if (!Q_strnicmp(text + i, "steam://", 8))
+ {
+ bURLFound = true;
+ }
+ else if (!Q_strnicmp(text + i, "steambeta://", 12))
+ {
+ bURLFound = true;
+ }
+ else if (!Q_strnicmp(text + i, "mailto:", 7))
+ {
+ bURLFound = true;
+ }
+ else if (!Q_strnicmp(text + i, "\\\\", 2))
+ {
+ bURLFound = true;
+ }
+
+ if (bURLFound)
+ {
+ if (i == startPos)
+ {
+ // we're at the Start of a URL, so parse that out
+ clickable = true;
+ int outIndex = 0;
+ while (text[i] != 0 && !iswspace(text[i]))
+ {
+ pchURLText[outIndex++] = text[i++];
+ }
+ pchURLText[outIndex] = 0;
+ Q_strncpy( pchURL, pchURLText, cchURL );
+ return i;
+ }
+ else
+ {
+ // no url
+ break;
+ }
+ }
+
+ // increment and loop
+ i++;
+ }
+
+ // nothing found;
+ // parse out the text before the end
+ clickable = false;
+ int outIndex = 0;
+ int fromIndex = startPos;
+ while ( fromIndex < i && outIndex < cchURLText )
+ {
+ pchURLText[outIndex++] = text[fromIndex++];
+ }
+ pchURLText[outIndex] = 0;
+ Q_strncpy( pchURL, pchURLText, cchURL );
+
+ return i;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Executes the text-clicked command, which opens a web browser by
+// default.
+//-----------------------------------------------------------------------------
+void RichText::OnTextClicked(const wchar_t *wszText)
+{
+ // Strip leading/trailing quotes, which may be present on href tags or may not.
+ const wchar_t *pwchURL = wszText;
+ if ( pwchURL[0] == L'"' || pwchURL[0] == L'\'' )
+ pwchURL = wszText + 1;
+
+ char ansi[2048];
+ Q_UnicodeToUTF8( pwchURL, ansi, sizeof(ansi) );
+
+ size_t strLen = Q_strlen(ansi);
+ if ( strLen && ( ansi[strLen-1] == '"' || ansi[strLen] == '\'' ) )
+ {
+ ansi[strLen-1] = 0;
+ }
+
+ if ( m_hPanelToHandleClickingURLs.Get() )
+ {
+ PostMessage( m_hPanelToHandleClickingURLs.Get(), new KeyValues( "URLClicked", "url", ansi ) );
+ }
+ else
+ {
+ system()->ShellExecute( "open", ansi );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RichText::SetURLClickedHandler( Panel *pPanelToHandleClickMsg )
+{
+ m_hPanelToHandleClickingURLs = pPanelToHandleClickMsg;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+bool RichText::IsScrollbarVisible()
+{
+ return _vertScrollBar->IsVisible();
+}
+
+void RichText::SetUnderlineFont( HFont font )
+{
+ m_hFontUnderline = font;
+}
+
+bool RichText::IsAllTextAlphaZero() const
+{
+ return m_bAllTextAlphaIsZero;
+}
+
+bool RichText::HasText() const
+{
+ int c = m_TextStream.Count();
+ if ( c == 0 )
+ {
+ return false;
+ }
+ return true;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the height of the base font
+//-----------------------------------------------------------------------------
+int RichText::GetLineHeight()
+{
+ return surface()->GetFontTall( _font );
+}
+
+
+#ifdef DBGFLAG_VALIDATE
+//-----------------------------------------------------------------------------
+// Purpose: Run a global validation pass on all of our data structures and memory
+// allocations.
+// Input: validator - Our global validator object
+// pchName - Our name (typically a member var in our container)
+//-----------------------------------------------------------------------------
+void RichText::Validate( CValidator &validator, char *pchName )
+{
+ validator.Push( "vgui::RichText", this, pchName );
+
+ ValidateObj( m_TextStream );
+ ValidateObj( m_FormatStream );
+ ValidateObj( m_LineBreaks );
+ ValidateObj( _clickableTextPanels );
+ validator.ClaimMemory( m_pszInitialText );
+
+ BaseClass::Validate( validator, "vgui::RichText" );
+
+ validator.Pop();
+}
+#endif // DBGFLAG_VALIDATE
+
diff --git a/mp/src/vgui2/vgui_controls/RotatingProgressBar.cpp b/mp/src/vgui2/vgui_controls/RotatingProgressBar.cpp
new file mode 100644
index 00000000..b3356934
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/RotatingProgressBar.cpp
@@ -0,0 +1,200 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+
+#include <vgui_controls/RotatingProgressBar.h>
+
+#include <vgui/IVGui.h>
+#include <vgui/ILocalize.h>
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <KeyValues.h>
+
+#include "mathlib/mathlib.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( RotatingProgressBar );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+RotatingProgressBar::RotatingProgressBar(Panel *parent, const char *panelName) : ProgressBar(parent, panelName)
+{
+ m_flStartRadians = 0;
+ m_flEndRadians = 0;
+ m_flLastAngle = 0;
+
+ m_nTextureId = -1;
+ m_pszImageName = NULL;
+
+ m_flTickDelay = 30;
+
+ ivgui()->AddTickSignal(GetVPanel(), m_flTickDelay );
+
+ SetPaintBorderEnabled(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+RotatingProgressBar::~RotatingProgressBar()
+{
+ if ( vgui::surface() && m_nTextureId != -1 )
+ {
+ vgui::surface()->DestroyTextureID( m_nTextureId );
+ m_nTextureId = -1;
+ }
+
+ delete [] m_pszImageName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RotatingProgressBar::ApplySettings(KeyValues *inResourceData)
+{
+ const char *imageName = inResourceData->GetString("image", "");
+ if (*imageName)
+ {
+ SetImage( imageName );
+ }
+
+ // Find min and max rotations in radians
+ m_flStartRadians = DEG2RAD(inResourceData->GetFloat( "start_degrees", 0 ) );
+ m_flEndRadians = DEG2RAD( inResourceData->GetFloat( "end_degrees", 0 ) );
+
+ // Start at 0 progress
+ m_flLastAngle = m_flStartRadians;
+
+ // approach speed is specified in degrees per second.
+ // convert to radians per 1/30th of a second
+ float flDegressPerSecond = DEG2RAD( inResourceData->GetFloat( "approach_speed", 360.0 ) ); // default is super fast
+ m_flApproachSpeed = flDegressPerSecond * ( m_flTickDelay / 1000.0f ); // divide by number of frames in a second
+
+ m_flRotOriginX = inResourceData->GetFloat( "rot_origin_x_percent", 0.5f );
+ m_flRotOriginY = inResourceData->GetFloat( "rot_origin_y_percent", 0.5f );
+
+ m_flRotatingX = inResourceData->GetFloat( "rotating_x", 0 );
+ m_flRotatingY = inResourceData->GetFloat( "rotating_y", 0 );
+ m_flRotatingWide = inResourceData->GetFloat( "rotating_wide", 0 );
+ m_flRotatingTall = inResourceData->GetFloat( "rotating_tall", 0 );
+
+ BaseClass::ApplySettings( inResourceData );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RotatingProgressBar::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ if ( m_pszImageName && strlen( m_pszImageName ) > 0 )
+ {
+ if ( m_nTextureId == -1 )
+ {
+ m_nTextureId = surface()->CreateNewTextureID();
+ }
+
+ surface()->DrawSetTextureFile( m_nTextureId, m_pszImageName, true, false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets an image by file name
+//-----------------------------------------------------------------------------
+void RotatingProgressBar::SetImage(const char *imageName)
+{
+ if ( m_pszImageName )
+ {
+ delete [] m_pszImageName;
+ m_pszImageName = NULL;
+ }
+
+ const char *pszDir = "vgui/";
+ int len = Q_strlen(imageName) + 1;
+ len += strlen(pszDir);
+ m_pszImageName = new char[ len ];
+ Q_snprintf( m_pszImageName, len, "%s%s", pszDir, imageName );
+ InvalidateLayout(false, true); // force applyschemesettings to run
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RotatingProgressBar::PaintBackground()
+{
+ // No background
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update event when we aren't drawing so we don't get huge sweeps
+// when we start drawing it
+//-----------------------------------------------------------------------------
+void RotatingProgressBar::OnTick( void )
+{
+ float flDesiredAngle = RemapVal( GetProgress(), 0.0, 1.0, m_flStartRadians, m_flEndRadians );
+ m_flLastAngle = Approach( flDesiredAngle, m_flLastAngle, m_flApproachSpeed );
+
+ BaseClass::OnTick();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RotatingProgressBar::Paint()
+{
+ // we have an image that we rotate based on the progress,
+ // where '0' is not rotated,'90' is rotated 90 degrees to the right.
+ // Image is rotated around its center.
+
+ // desired rotation is GetProgress() ( 0.0 -> 1.0 ) mapped into
+ // ( m_flStartDegrees -> m_flEndDegrees )
+
+ vgui::surface()->DrawSetTexture( m_nTextureId );
+ vgui::surface()->DrawSetColor( Color(255,255,255,255) );
+
+ int wide, tall;
+ GetSize( wide, tall );
+
+ float mid_x = m_flRotatingX + m_flRotOriginX * m_flRotatingWide;
+ float mid_y = m_flRotatingY + m_flRotOriginY * m_flRotatingTall;
+
+ Vertex_t vert[4];
+
+ vert[0].Init( Vector2D( m_flRotatingX, m_flRotatingY ), Vector2D(0,0) );
+ vert[1].Init( Vector2D( m_flRotatingX+m_flRotatingWide, m_flRotatingY ), Vector2D(1,0) );
+ vert[2].Init( Vector2D( m_flRotatingX+m_flRotatingWide, m_flRotatingY+m_flRotatingTall ), Vector2D(1,1) );
+ vert[3].Init( Vector2D( m_flRotatingX, m_flRotatingY+m_flRotatingTall ), Vector2D(0,1) );
+
+ float flCosA = cos(m_flLastAngle);
+ float flSinA = sin(m_flLastAngle);
+
+ // rotate each point around (mid_x, mid_y) by flAngle radians
+ for ( int i=0;i<4;i++ )
+ {
+ Vector2D result;
+
+ // subtract the (x,y) we're rotating around, we'll add it on at the end.
+ vert[i].m_Position.x -= mid_x;
+ vert[i].m_Position.y -= mid_y;
+
+ result.x = ( vert[i].m_Position.x * flCosA - vert[i].m_Position.y * flSinA ) + mid_x;
+ result.y = ( vert[i].m_Position.x * flSinA + vert[i].m_Position.y * flCosA ) + mid_y;
+
+ vert[i].m_Position = result;
+ }
+
+ vgui::surface()->DrawTexturedPolygon( 4, vert );
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/ScalableImagePanel.cpp b/mp/src/vgui2/vgui_controls/ScalableImagePanel.cpp
new file mode 100644
index 00000000..8a2ad17d
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ScalableImagePanel.cpp
@@ -0,0 +1,266 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdio.h>
+
+#include <vgui/IBorder.h>
+#include <vgui/ISurface.h>
+#include <vgui/IScheme.h>
+#include <vgui/IBorder.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/ScalableImagePanel.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( ScalableImagePanel );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ScalableImagePanel::ScalableImagePanel(Panel *parent, const char *name) : Panel(parent, name)
+{
+ m_iSrcCornerHeight = 0;
+ m_iSrcCornerWidth = 0;
+
+ m_iCornerHeight = 0;
+ m_iCornerWidth = 0;
+
+ m_pszImageName = NULL;
+ m_pszDrawColorName = NULL;
+
+ m_DrawColor = Color(255,255,255,255);
+
+ m_flCornerWidthPercent = 0;
+ m_flCornerHeightPercent = 0;
+
+ m_iTextureID = surface()->CreateNewTextureID();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ScalableImagePanel::~ScalableImagePanel()
+{
+ delete [] m_pszImageName;
+ delete [] m_pszDrawColorName;
+
+ if ( vgui::surface() && m_iTextureID != -1 )
+ {
+ vgui::surface()->DestroyTextureID( m_iTextureID );
+ m_iTextureID = -1;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScalableImagePanel::SetImage(const char *imageName)
+{
+ if ( *imageName )
+ {
+ char szImage[MAX_PATH];
+
+ const char *pszDir = "vgui/";
+ int len = Q_strlen(imageName) + 1;
+ len += strlen(pszDir);
+ Q_snprintf( szImage, len, "%s%s", pszDir, imageName );
+
+ if ( m_pszImageName && V_stricmp( szImage, m_pszImageName ) == 0 )
+ return;
+
+ delete [] m_pszImageName;
+ m_pszImageName = new char[ len ];
+ Q_strncpy(m_pszImageName, szImage, len );
+ }
+ else
+ {
+ delete [] m_pszImageName;
+ m_pszImageName = NULL;
+ }
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScalableImagePanel::PaintBackground()
+{
+ int wide, tall;
+ GetSize(wide, tall);
+
+ surface()->DrawSetColor( m_DrawColor.r(), m_DrawColor.g(), m_DrawColor.b(), GetAlpha() );
+ surface()->DrawSetTexture( m_iTextureID );
+
+ int x = 0;
+ int y = 0;
+
+ float uvx = 0;
+ float uvy = 0;
+ float uvw = 0, uvh = 0;
+
+ float drawW, drawH;
+
+ int row, col;
+ for ( row=0;row<3;row++ )
+ {
+ x = 0;
+ uvx = 0;
+
+ if ( row == 0 || row == 2 )
+ {
+ //uvh - row 0 or 2, is src_corner_height
+ uvh = m_flCornerHeightPercent;
+ drawH = m_iCornerHeight;
+ }
+ else
+ {
+ //uvh - row 1, is tall - ( 2 * src_corner_height ) ( min 0 )
+ uvh = max( 1.0 - 2 * m_flCornerHeightPercent, 0.0f );
+ drawH = max( 0, ( tall - 2 * m_iCornerHeight ) );
+ }
+
+ for ( col=0;col<3;col++ )
+ {
+ if ( col == 0 || col == 2 )
+ {
+ //uvw - col 0 or 2, is src_corner_width
+ uvw = m_flCornerWidthPercent;
+ drawW = m_iCornerWidth;
+ }
+ else
+ {
+ //uvw - col 1, is wide - ( 2 * src_corner_width ) ( min 0 )
+ uvw = max( 1.0 - 2 * m_flCornerWidthPercent, 0.0f );
+ drawW = max( 0, ( wide - 2 * m_iCornerWidth ) );
+ }
+
+ Vector2D uv11( uvx, uvy );
+ Vector2D uv21( uvx+uvw, uvy );
+ Vector2D uv22( uvx+uvw, uvy+uvh );
+ Vector2D uv12( uvx, uvy+uvh );
+
+ vgui::Vertex_t verts[4];
+ verts[0].Init( Vector2D( x, y ), uv11 );
+ verts[1].Init( Vector2D( x+drawW, y ), uv21 );
+ verts[2].Init( Vector2D( x+drawW, y+drawH ), uv22 );
+ verts[3].Init( Vector2D( x, y+drawH ), uv12 );
+
+ vgui::surface()->DrawTexturedPolygon( 4, verts );
+
+ x += drawW;
+ uvx += uvw;
+ }
+
+ y += drawH;
+ uvy += uvh;
+ }
+
+ vgui::surface()->DrawSetTexture(0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets control settings for editing
+//-----------------------------------------------------------------------------
+void ScalableImagePanel::GetSettings(KeyValues *outResourceData)
+{
+ BaseClass::GetSettings(outResourceData);
+
+ if (m_pszDrawColorName)
+ {
+ outResourceData->SetString("drawcolor", m_pszDrawColorName);
+ }
+
+ outResourceData->SetInt("src_corner_height", m_iSrcCornerHeight);
+ outResourceData->SetInt("src_corner_width", m_iSrcCornerWidth);
+
+ outResourceData->SetInt("draw_corner_height", m_iCornerHeight);
+ outResourceData->SetInt("draw_corner_width", m_iCornerWidth);
+
+ if (m_pszImageName)
+ {
+ outResourceData->SetString("image", m_pszImageName);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies designer settings from res file
+//-----------------------------------------------------------------------------
+void ScalableImagePanel::ApplySettings(KeyValues *inResourceData)
+{
+ BaseClass::ApplySettings(inResourceData);
+
+ delete [] m_pszDrawColorName;
+ m_pszDrawColorName = NULL;
+
+ const char *pszDrawColor = inResourceData->GetString("drawcolor", "");
+ if (*pszDrawColor)
+ {
+ int r = 0, g = 0, b = 0, a = 255;
+ int len = Q_strlen(pszDrawColor) + 1;
+ m_pszDrawColorName = new char[ len ];
+ Q_strncpy( m_pszDrawColorName, pszDrawColor, len );
+
+ if (sscanf(pszDrawColor, "%d %d %d %d", &r, &g, &b, &a) >= 3)
+ {
+ // it's a direct color
+ m_DrawColor = Color(r, g, b, a);
+ }
+ else
+ {
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+ m_DrawColor = pScheme->GetColor(pszDrawColor, Color(0, 0, 0, 0));
+ }
+ }
+
+ m_iSrcCornerHeight = inResourceData->GetInt( "src_corner_height" );
+ m_iSrcCornerWidth = inResourceData->GetInt( "src_corner_width" );
+
+ m_iCornerHeight = inResourceData->GetInt( "draw_corner_height" );
+ m_iCornerWidth = inResourceData->GetInt( "draw_corner_width" );
+
+ if ( IsProportional() )
+ {
+ // scale the x and y up to our screen co-ords
+ m_iCornerHeight = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iCornerHeight);
+ m_iCornerWidth = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iCornerWidth);
+ }
+
+ const char *imageName = inResourceData->GetString("image", "");
+ SetImage( imageName );
+
+ InvalidateLayout();
+}
+
+void ScalableImagePanel::PerformLayout( void )
+{
+ if ( m_pszImageName )
+ {
+ surface()->DrawSetTextureFile( m_iTextureID, m_pszImageName, true, false);
+ }
+
+ // get image dimensions, compare to m_iSrcCornerHeight, m_iSrcCornerWidth
+ int wide,tall;
+ surface()->DrawGetTextureSize( m_iTextureID, wide, tall );
+
+ m_flCornerWidthPercent = ( wide > 0 ) ? ( (float)m_iSrcCornerWidth / (float)wide ) : 0;
+ m_flCornerHeightPercent = ( tall > 0 ) ? ( (float)m_iSrcCornerHeight / (float)tall ) : 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Describes editing details
+//-----------------------------------------------------------------------------
+const char *ScalableImagePanel::GetDescription()
+{
+ static char buf[1024];
+ _snprintf(buf, sizeof(buf), "%s string image, int src_corner_height, int src_corner_width, int draw_corner_height, int draw_corner_width", BaseClass::GetDescription());
+ return buf;
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/ScrollBar.cpp b/mp/src/vgui2/vgui_controls/ScrollBar.cpp
new file mode 100644
index 00000000..5330b41a
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ScrollBar.cpp
@@ -0,0 +1,802 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <assert.h>
+
+#include <vgui/IScheme.h>
+#include <vgui/ISystem.h>
+#include <vgui/IInput.h>
+#include <vgui/IImage.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/ScrollBar.h>
+#include <vgui_controls/ScrollBarSlider.h>
+#include <vgui_controls/Button.h>
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/ImagePanel.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+namespace
+{
+
+enum
+{ // scroll bar will scroll a little, then continuous scroll like in windows
+ SCROLL_BAR_DELAY = 400, // default delay for all scroll bars
+ SCROLL_BAR_SPEED = 50, // this is how fast the bar scrolls when you hold down the arrow button
+ SCROLLBAR_DEFAULT_WIDTH = 17,
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Scroll bar button-the arrow button that moves the slider up and down.
+//-----------------------------------------------------------------------------
+class ScrollBarButton : public Button
+{
+public:
+ ScrollBarButton(Panel *parent, const char *panelName, const char *text) : Button(parent, panelName, text)
+ {
+ SetButtonActivationType(ACTIVATE_ONPRESSED);
+
+ SetContentAlignment(Label::a_center);
+ }
+
+ void OnMouseFocusTicked()
+ {
+ // pass straight up to parent
+ CallParentFunction(new KeyValues("MouseFocusTicked"));
+ }
+
+ virtual void ApplySchemeSettings(IScheme *pScheme)
+ {
+ Button::ApplySchemeSettings(pScheme);
+
+ SetFont(pScheme->GetFont("Marlett", IsProportional() ));
+ SetDefaultBorder(pScheme->GetBorder("ScrollBarButtonBorder"));
+ SetDepressedBorder(pScheme->GetBorder("ScrollBarButtonDepressedBorder"));
+
+ SetDefaultColor(GetSchemeColor("ScrollBarButton.FgColor", pScheme), GetSchemeColor("ScrollBarButton.BgColor", pScheme));
+ SetArmedColor(GetSchemeColor("ScrollBarButton.ArmedFgColor", pScheme), GetSchemeColor("ScrollBarButton.ArmedBgColor", pScheme));
+ SetDepressedColor(GetSchemeColor("ScrollBarButton.DepressedFgColor", pScheme), GetSchemeColor("ScrollBarButton.DepressedBgColor", pScheme));
+ }
+
+ // Don't request focus.
+ // This will keep cursor focus in main window in text entry windows.
+ virtual void OnMousePressed(MouseCode code)
+ {
+ if (!IsEnabled())
+ return;
+
+ if (!IsMouseClickEnabled(code))
+ return;
+
+ if (IsUseCaptureMouseEnabled())
+ {
+ {
+ SetSelected(true);
+ Repaint();
+ }
+
+ // lock mouse input to going to this button
+ input()->SetMouseCapture(GetVPanel());
+ }
+ }
+ virtual void OnMouseReleased(MouseCode code)
+ {
+ if (!IsEnabled())
+ return;
+
+ if (!IsMouseClickEnabled(code))
+ return;
+
+ if (IsUseCaptureMouseEnabled())
+ {
+ {
+ SetSelected(false);
+ Repaint();
+ }
+
+ // lock mouse input to going to this button
+ input()->SetMouseCapture(NULL);
+ }
+
+ if( input()->GetMouseOver() == GetVPanel() )
+ {
+ SetArmed( true );
+ }
+ }
+
+};
+
+}
+
+vgui::Panel *ScrollBar_Vertical_Factory()
+{
+ return new ScrollBar(NULL, NULL, true );
+}
+
+vgui::Panel *ScrollBar_Horizontal_Factory()
+{
+ return new ScrollBar(NULL, NULL, false );
+}
+
+DECLARE_BUILD_FACTORY_CUSTOM_ALIAS( ScrollBar, ScrollBar_Vertical, ScrollBar_Vertical_Factory );
+DECLARE_BUILD_FACTORY_CUSTOM_ALIAS( ScrollBar, ScrollBar_Horizontal, ScrollBar_Horizontal_Factory );
+// Default is a horizontal one
+DECLARE_BUILD_FACTORY_CUSTOM( ScrollBar, ScrollBar_Horizontal_Factory );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+ScrollBar::ScrollBar(Panel *parent, const char *panelName, bool vertical) : Panel(parent, panelName)
+{
+ _slider=null;
+ _button[0]=null;
+ _button[1]=null;
+ _scrollDelay = SCROLL_BAR_DELAY;
+ _respond = true;
+ m_pUpArrow = NULL;
+ m_pLine = NULL;
+ m_pDownArrow = NULL;
+ m_pBox = NULL;
+ m_bNoButtons = false;
+ m_pOverriddenButtons[0] = NULL;
+ m_pOverriddenButtons[1] = NULL;
+
+ if (vertical)
+ {
+ // FIXME: proportional changes needed???
+ SetSlider(new ScrollBarSlider(NULL, "Slider", true));
+ SetButton(new ScrollBarButton(NULL, "UpButton", "t"), 0);
+ SetButton(new ScrollBarButton(NULL, "DownButton", "u"), 1);
+ _button[0]->SetTextInset(0, 1);
+ _button[1]->SetTextInset(0, -1);
+
+ SetSize(SCROLLBAR_DEFAULT_WIDTH, 64);
+ }
+ else
+ {
+ SetSlider(new ScrollBarSlider(NULL, NULL, false));
+ SetButton(new ScrollBarButton(NULL, NULL, "w"), 0);
+ SetButton(new ScrollBarButton(NULL, NULL, "4"), 1);
+ _button[0]->SetTextInset(0, 0);
+ _button[1]->SetTextInset(0, 0);
+
+ SetSize(64, SCROLLBAR_DEFAULT_WIDTH);
+ }
+
+ Panel::SetPaintBorderEnabled(true);
+ Panel::SetPaintBackgroundEnabled(false);
+ Panel::SetPaintEnabled(true);
+ SetButtonPressedScrollValue(20);
+ SetBlockDragChaining( true );
+
+ Validate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets up the width of the scrollbar according to the scheme
+//-----------------------------------------------------------------------------
+void ScrollBar::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ const char *resourceString = pScheme->GetResourceString("ScrollBar.Wide");
+
+ if (resourceString)
+ {
+ int value = atoi(resourceString);
+ if (IsProportional())
+ {
+ value = scheme()->GetProportionalScaledValueEx(GetScheme(), value);
+ }
+
+ if (_slider && _slider->IsVertical())
+ {
+ // we're vertical, so reset the width
+ SetSize( value, GetTall() );
+ }
+ else
+ {
+ // we're horizontal, so the width means the height
+ SetSize( GetWide(), value );
+ }
+ }
+
+ UpdateButtonsForImages();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the slider's Paint border enabled.
+//-----------------------------------------------------------------------------
+void ScrollBar::SetPaintBorderEnabled(bool state)
+{
+ if ( _slider )
+ {
+ _slider->SetPaintBorderEnabled( state );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBar::SetPaintBackgroundEnabled(bool state)
+{
+ if ( _slider )
+ {
+ _slider->SetPaintBackgroundEnabled( state );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBar::SetPaintEnabled(bool state)
+{
+ if ( _slider )
+ {
+ _slider->SetPaintEnabled( state );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Layout the scroll bar and buttons on screen
+//-----------------------------------------------------------------------------
+void ScrollBar::PerformLayout()
+{
+ if (_slider)
+ {
+ int wide, tall;
+ GetPaintSize(wide,tall);
+ if(_slider->IsVertical())
+ {
+ if ( m_bNoButtons )
+ {
+ _slider->SetBounds(0, 0, wide, tall + 1);
+ }
+ else
+ {
+ _slider->SetBounds(0, wide, wide, tall-(wide*2)+1);
+ _button[0]->SetBounds(0,0, wide, wide );
+ _button[1]->SetBounds(0,tall-wide ,wide, wide );
+ }
+ }
+ else
+ {
+ if ( m_bNoButtons )
+ {
+ _slider->SetBounds(tall, 0, wide, tall + 1);
+ }
+ else
+ {
+ _slider->SetBounds(tall, -1, wide-(tall*2)+1, tall + 1 );
+ _button[0]->SetBounds(0, 0, tall, tall);
+ _button[1]->SetBounds(wide-tall, 0, tall, tall);
+ }
+ }
+
+ // Place the images over the appropriate controls
+ int x,y;
+ if ( m_pUpArrow )
+ {
+ _button[0]->GetBounds( x,y,wide,tall );
+ m_pUpArrow->SetBounds( x,y,wide,tall );
+ }
+ if ( m_pDownArrow )
+ {
+ _button[1]->GetBounds( x,y,wide,tall );
+ m_pDownArrow->SetBounds( x,y,wide,tall );
+ }
+ if ( m_pLine )
+ {
+ _slider->GetBounds( x,y,wide,tall );
+ m_pLine->SetBounds( x,y,wide,tall );
+ }
+ if ( m_pBox )
+ {
+ m_pBox->SetBounds( 0, wide, wide, wide );
+ }
+
+ _slider->MoveToFront();
+ // after resizing our child, we should remind it to perform a layout
+ _slider->InvalidateLayout();
+
+ UpdateSliderImages();
+ }
+
+ if ( m_bAutoHideButtons )
+ {
+ SetScrollbarButtonsVisible( _slider->IsSliderVisible() );
+ }
+
+ // get tooltips to draw
+ Panel::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the value of the scroll bar slider.
+//-----------------------------------------------------------------------------
+void ScrollBar::SetValue(int value)
+{
+ _slider->SetValue(value);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the value of the scroll bar slider.
+//-----------------------------------------------------------------------------
+int ScrollBar::GetValue()
+{
+ return _slider->GetValue();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the range of the scroll bar slider.
+// This the range of numbers the slider can scroll through.
+//-----------------------------------------------------------------------------
+void ScrollBar::SetRange(int min,int max)
+{
+ _slider->SetRange(min,max);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the range of the scroll bar slider.
+// This the range of numbers the slider can scroll through.
+//-----------------------------------------------------------------------------
+void ScrollBar::GetRange(int &min, int &max)
+{
+ _slider->GetRange(min, max);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Send a message when the slider is moved.
+// Input : value -
+//-----------------------------------------------------------------------------
+void ScrollBar::SendSliderMoveMessage(int value)
+{
+ PostActionSignal(new KeyValues("ScrollBarSliderMoved", "position", value));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the Slider is dragged by the user
+// Input : value -
+//-----------------------------------------------------------------------------
+void ScrollBar::OnSliderMoved(int value)
+{
+ SendSliderMoveMessage(value);
+ UpdateSliderImages();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if the scrollbar is vertical (true) or horizontal (false)
+//-----------------------------------------------------------------------------
+bool ScrollBar::IsVertical()
+{
+ return _slider->IsVertical();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if the the scrollbar slider has full range.
+// Normally if you have a scroll bar and the range goes from a to b and
+// the slider is sized to c, the range will go from a to b-c.
+// This makes it so the slider goes from a to b fully.
+//-----------------------------------------------------------------------------
+bool ScrollBar::HasFullRange()
+{
+ return _slider->HasFullRange();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Setup the indexed scroll bar button with the input params.
+//-----------------------------------------------------------------------------
+//LEAK: new and old slider will leak
+void ScrollBar::SetButton(Button *button, int index)
+{
+ if(_button[index]!=null)
+ {
+ _button[index]->SetParent((Panel *)NULL);
+ }
+ _button[index]=button;
+ _button[index]->SetParent(this);
+ _button[index]->AddActionSignalTarget(this);
+ _button[index]->SetCommand(new KeyValues("ScrollButtonPressed", "index", index));
+
+ Validate();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the indexed scroll bar button
+//-----------------------------------------------------------------------------
+Button* ScrollBar::GetButton(int index)
+{
+ return _button[index];
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Set up the slider.
+//-----------------------------------------------------------------------------
+//LEAK: new and old slider will leak
+void ScrollBar::SetSlider(ScrollBarSlider *slider)
+{
+ if(_slider!=null)
+ {
+ _slider->SetParent((Panel *)NULL);
+ }
+ _slider=slider;
+ _slider->AddActionSignalTarget(this);
+ _slider->SetParent(this);
+
+ Validate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return a pointer to the slider.
+//-----------------------------------------------------------------------------
+ScrollBarSlider *ScrollBar::GetSlider()
+{
+ return _slider;
+}
+
+Button *ScrollBar::GetDepressedButton( int iIndex )
+{
+ if ( iIndex == 0 )
+ return ( m_pOverriddenButtons[0] ? m_pOverriddenButtons[0] : _button[0] );
+ return ( m_pOverriddenButtons[1] ? m_pOverriddenButtons[1] : _button[1] );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Scrolls in response to clicking and holding on up or down arrow
+// The idea is to have the slider move one step then delay a bit and then
+// the bar starts moving at normal speed. This gives a stepping feeling
+// to just clicking an arrow once.
+//-----------------------------------------------------------------------------
+void ScrollBar::OnMouseFocusTicked()
+{
+ int direction = 0;
+
+ // top button is down
+ if ( GetDepressedButton(0)->IsDepressed() )
+ {
+ direction = -1;
+ }
+ // bottom top button is down
+ else if (GetDepressedButton(1)->IsDepressed())
+ {
+ direction = 1;
+ }
+
+ // a button is down
+ if ( direction != 0 )
+ {
+ RespondToScrollArrow(direction);
+ if (_scrollDelay < system()->GetTimeMillis())
+ {
+ _scrollDelay = system()->GetTimeMillis() + SCROLL_BAR_SPEED;
+ _respond = true;
+ }
+ else
+ {
+ _respond = false;
+ }
+ }
+ // a button is not down.
+ else
+ {
+ // if neither button is down keep delay at max
+ _scrollDelay = system()->GetTimeMillis() + SCROLL_BAR_DELAY;
+ _respond = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: move scroll bar in response to the first button
+// Input: button and direction to move scroll bar when that button is pressed
+// direction can only by +/- 1
+// Output: whether button is down or not
+//-----------------------------------------------------------------------------
+void ScrollBar::RespondToScrollArrow(int const direction)
+{
+ if (_respond)
+ {
+ int newValue = _slider->GetValue() + (direction * _buttonPressedScrollValue);
+ _slider->SetValue(newValue);
+ SendSliderMoveMessage(newValue);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Trigger layout changes when the window size is changed.
+//-----------------------------------------------------------------------------
+void ScrollBar::OnSizeChanged(int wide, int tall)
+{
+ InvalidateLayout();
+ _slider->InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set how far the scroll bar slider moves when a scroll bar button is
+// pressed.
+//-----------------------------------------------------------------------------
+void ScrollBar::SetButtonPressedScrollValue(int value)
+{
+ _buttonPressedScrollValue=value;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the range of the rangewindow. This is how many
+// lines are displayed at one time
+// in the window the scroll bar is attached to.
+// This also controls the size of the slider, its size is proportional
+// to the number of lines displayed / total number of lines.
+//-----------------------------------------------------------------------------
+void ScrollBar::SetRangeWindow(int rangeWindow)
+{
+ _slider->SetRangeWindow(rangeWindow);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the range of the rangewindow. This is how many
+// lines are displayed at one time
+// in the window the scroll bar is attached to.
+// This also controls the size of the slider, its size is proportional
+// to the number of lines displayed / total number of lines.
+//-----------------------------------------------------------------------------
+int ScrollBar::GetRangeWindow()
+{
+ return _slider->GetRangeWindow();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBar::Validate()
+{
+ if ( _slider != null )
+ {
+ int buttonOffset = 0;
+
+ for( int i=0; i<2; i++ )
+ {
+ if( _button[i] != null )
+ {
+ if( _button[i]->IsVisible() )
+ {
+ if( _slider->IsVertical() )
+ {
+ buttonOffset += _button[i]->GetTall();
+ }
+ else
+ {
+ buttonOffset += _button[i]->GetWide();
+ }
+ }
+ }
+ }
+
+ _slider->SetButtonOffset(buttonOffset);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBar::SetScrollbarButtonsVisible(bool visible)
+{
+ for( int i=0; i<2; i++ )
+ {
+ if( _button[i] != null )
+ {
+ _button[i]->SetShouldPaint( visible );
+ _button[i]->SetEnabled( visible );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBar::UseImages( const char *pszUpArrow, const char *pszDownArrow, const char *pszLine, const char *pszBox )
+{
+ if ( pszUpArrow )
+ {
+ if ( !m_pUpArrow )
+ {
+ m_pUpArrow = new vgui::ImagePanel( this, "UpArrow" );
+ if ( m_pUpArrow )
+ {
+ m_pUpArrow->SetImage( pszUpArrow );
+ m_pUpArrow->SetShouldScaleImage( true );
+ m_pUpArrow->SetFgColor( Color( 255, 255, 255, 255 ) );
+ m_pUpArrow->SetAlpha( 255 );
+ m_pUpArrow->SetZPos( -1 );
+ }
+ }
+
+ m_pUpArrow->SetImage( pszUpArrow );
+ m_pUpArrow->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 );
+ }
+ else if ( m_pUpArrow )
+ {
+ m_pUpArrow->DeletePanel();
+ m_pUpArrow = NULL;
+ }
+
+ if ( pszDownArrow )
+ {
+ if ( !m_pDownArrow )
+ {
+ m_pDownArrow = new vgui::ImagePanel( this, "DownArrow" );
+ if ( m_pDownArrow )
+ {
+ m_pDownArrow->SetShouldScaleImage( true );
+ m_pDownArrow->SetFgColor( Color( 255, 255, 255, 255 ) );
+ m_pDownArrow->SetAlpha( 255 );
+ m_pDownArrow->SetZPos( -1 );
+ }
+ }
+
+ m_pDownArrow->SetImage( pszDownArrow );
+ m_pDownArrow->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 );
+ }
+ else if ( m_pDownArrow )
+ {
+ m_pDownArrow->DeletePanel();
+ m_pDownArrow = NULL;
+ }
+
+ if ( pszLine )
+ {
+ if ( !m_pLine )
+ {
+ m_pLine = new ImagePanel( this, "Line" );
+ if ( m_pLine )
+ {
+ m_pLine->SetShouldScaleImage( true );
+ m_pLine->SetZPos( -1 );
+ }
+ }
+
+ m_pLine->SetImage( pszLine );
+ m_pLine->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 );
+ }
+ else if ( m_pLine )
+ {
+ m_pLine->DeletePanel();
+ m_pLine = NULL;
+ }
+
+ if ( pszBox )
+ {
+ if ( !m_pBox )
+ {
+ m_pBox = new ImagePanel( this, "Box" );
+ if ( m_pBox )
+ {
+ m_pBox->SetShouldScaleImage( true );
+ m_pBox->SetZPos( -1 );
+ }
+ }
+
+ m_pBox->SetImage( pszBox );
+ m_pBox->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 );
+ }
+ else if ( m_pBox )
+ {
+ m_pBox->DeletePanel();
+ m_pBox = NULL;
+ }
+
+ UpdateButtonsForImages();
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBar::UpdateButtonsForImages( void )
+{
+ // Turn off parts of our drawing based on which images we're replacing it with
+ if ( m_pUpArrow || m_pDownArrow )
+ {
+ SetScrollbarButtonsVisible( false );
+ _button[0]->SetPaintBorderEnabled( false );
+ _button[1]->SetPaintBorderEnabled( false );
+ m_bAutoHideButtons = false;
+ }
+ if ( m_pLine || m_pBox )
+ {
+ SetPaintBackgroundEnabled( false );
+ SetPaintBorderEnabled( false );
+
+ if ( _slider )
+ {
+ _slider->SetPaintEnabled( false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBar::UpdateSliderImages( void )
+{
+ if ( m_pUpArrow && m_pDownArrow )
+ {
+ // set the alpha on the up arrow
+ int nMin, nMax;
+ GetRange( nMin, nMax );
+ int nScrollPos = GetValue();
+ int nRangeWindow = GetRangeWindow();
+ int nBottom = nMax - nRangeWindow;
+ if ( nBottom < 0 )
+ {
+ nBottom = 0;
+ }
+
+ // set the alpha on the up arrow
+ int nAlpha = ( nScrollPos - nMin <= 0 ) ? 90 : 255;
+ m_pUpArrow->SetAlpha( nAlpha );
+
+ // set the alpha on the down arrow
+ nAlpha = ( nScrollPos >= nBottom ) ? 90 : 255;
+ m_pDownArrow->SetAlpha( nAlpha );
+ }
+
+ if ( m_pLine && m_pBox )
+ {
+ ScrollBarSlider *pSlider = GetSlider();
+ if ( pSlider && pSlider->GetRangeWindow() > 0 )
+ {
+ int x, y, w, t, min, max;
+ m_pLine->GetBounds( x, y, w, t );
+
+ // If our slider needs layout, force it to do it now
+ if ( pSlider->IsLayoutInvalid() )
+ {
+ pSlider->InvalidateLayout( true );
+ }
+ pSlider->GetNobPos( min, max );
+
+ if ( IsVertical() )
+ {
+ m_pBox->SetBounds( x, y + min, w, ( max - min ) );
+ }
+ else
+ {
+ m_pBox->SetBounds( x + min, 0, (max-min), t );
+ }
+ }
+ }
+}
+void ScrollBar::ApplySettings( KeyValues *pInResourceData )
+{
+ BaseClass::ApplySettings( pInResourceData );
+
+ m_bNoButtons = pInResourceData->GetBool( "nobuttons", false );
+
+ KeyValues *pSliderKV = pInResourceData->FindKey( "Slider" );
+ if ( pSliderKV && _slider )
+ {
+ _slider->ApplySettings( pSliderKV );
+ }
+
+ KeyValues *pDownButtonKV = pInResourceData->FindKey( "DownButton" );
+ if ( pDownButtonKV && _button[0] )
+ {
+ _button[0]->ApplySettings( pDownButtonKV );
+ }
+
+ KeyValues *pUpButtonKV = pInResourceData->FindKey( "UpButton" );
+ if ( pUpButtonKV && _button[0] )
+ {
+ _button[1]->ApplySettings( pUpButtonKV );
+ }
+}
diff --git a/mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp b/mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp
new file mode 100644
index 00000000..da54f3f0
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ScrollBarSlider.cpp
@@ -0,0 +1,606 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#define PROTECTED_THINGS_DISABLE
+
+#include <vgui/IBorder.h>
+#include <vgui/IInput.h>
+#include <vgui/ISystem.h>
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <vgui/MouseCode.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/ScrollBarSlider.h>
+#include <vgui_controls/Controls.h>
+
+#include <math.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// The ScrollBarSlider is the scroll bar nob that moves up and down in through a range.
+//-----------------------------------------------------------------------------
+ScrollBarSlider::ScrollBarSlider(Panel *parent, const char *panelName, bool vertical) : Panel(parent, panelName)
+{
+ _vertical=vertical;
+ _dragging=false;
+ _value=0;
+ _range[0]=0;
+ _range[1]=0;
+ _rangeWindow=0;
+ _buttonOffset=0;
+ _ScrollBarSliderBorder=NULL;
+ RecomputeNobPosFromValue();
+ SetBlockDragChaining( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the size of the ScrollBarSlider nob
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::SetSize(int wide,int tall)
+{
+ BaseClass::SetSize(wide,tall);
+ RecomputeNobPosFromValue();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Whether the scroll bar is vertical (true) or not (false)
+//-----------------------------------------------------------------------------
+bool ScrollBarSlider::IsVertical()
+{
+ return _vertical;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the ScrollBarSlider value of the nob.
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::SetValue(int value)
+{
+ int oldValue = _value;
+
+ if (value > _range[1] - _rangeWindow)
+ {
+ // note our scrolling range must take into acount _rangeWindow
+ value = _range[1] - _rangeWindow;
+ }
+
+ if (value < _range[0])
+ {
+ value = _range[0];
+ }
+
+ _value = value;
+ RecomputeNobPosFromValue();
+
+ if (_value != oldValue)
+ {
+ SendScrollBarSliderMovedMessage();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the ScrollBarSlider value of the nob.
+//-----------------------------------------------------------------------------
+int ScrollBarSlider::GetValue()
+{
+ return _value;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::PerformLayout()
+{
+ RecomputeNobPosFromValue();
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Given the value of the ScrollBarSlider, adjust the ends of the nob.
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::RecomputeNobPosFromValue()
+{
+ int wide, tall;
+ GetPaintSize(wide, tall);
+
+ float fwide = (float)( wide - 1 );
+ float ftall = (float)( tall - 1 );
+ float frange = (float)(_range[1] -_range[0]);
+ float fvalue = (float)(_value - _range[0]);
+ float frangewindow = (float)(_rangeWindow);
+ float fper = ( frange != frangewindow ) ? fvalue / ( frange-frangewindow ) : 0;
+
+// Msg( "fwide: %f ftall: %f frange: %f fvalue: %f frangewindow: %f fper: %f\n",
+// fwide, ftall, frange, fvalue, frangewindow, fper );
+
+ if ( frangewindow > 0 )
+ {
+ if ( frange <= 0.0 )
+ {
+ frange = 1.0;
+ }
+
+ float width, length;
+ if (_vertical)
+ {
+ width = fwide;
+ length = ftall;
+ }
+ else
+ {
+ width = ftall;
+ length = fwide;
+ }
+
+ // our size is proportional to frangewindow/frange
+ // the scroll bar nob's length reflects the amount of stuff on the screen
+ // vs the total amount of stuff we could scroll through in window
+ // so if a window showed half its contents and the other half is hidden the
+ // scroll bar's length is half the window.
+ // if everything is on the screen no nob is displayed
+ // frange is how many 'lines' of stuff we can display
+ // frangewindow is how many 'lines' are in the display window
+
+ // proportion of whole window that is on screen
+ float proportion = frangewindow / frange;
+ float fnobsize = length * proportion;
+ if ( fnobsize < width ) fnobsize = (float)width;
+
+ float freepixels = length - fnobsize;
+
+ float firstpixel = freepixels * fper;
+
+ _nobPos[0] = (int)( firstpixel );
+ _nobPos[1] = (int)( firstpixel + fnobsize );
+
+ if ( _nobPos[1] > length )
+ {
+ _nobPos[0] = (int)( length - fnobsize );
+ _nobPos[1] = (int)length;
+ }
+
+ }
+
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the ScrollBarSlider value using the location of the nob ends.
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::RecomputeValueFromNobPos()
+{
+ int wide, tall;
+ GetPaintSize(wide, tall);
+
+ float fwide = (float)( wide - 1 );
+ float ftall = (float)( tall - 1 );
+ float frange = (float)( _range[1] - _range[0] );
+ float fvalue = (float)( _value - _range[0] );
+ float fnob = (float)_nobPos[0];
+ float frangewindow = (float)(_rangeWindow);
+
+ if ( frangewindow > 0 )
+ {
+ if ( frange <= 0.0 )
+ {
+ frange = 1.0;
+ }
+
+ // set local width and length
+ float width, length;
+ if ( _vertical )
+ {
+ width = fwide;
+ length = ftall;
+ }
+ else
+ {
+ width = ftall;
+ length = fwide;
+ }
+
+ // calculate the size of the nob
+ float proportion = frangewindow / frange;
+ float fnobsize = length * proportion;
+
+ if ( fnobsize < width )
+ {
+ fnobsize = width;
+ }
+
+ // Our scroll bar actually doesnt scroll through all frange lines in the truerange, we
+ // actually only scroll through frange-frangewindow number of lines so we must take that
+ // into account when we calculate the value
+ // convert to our local size system
+
+ // Make sure we don't divide by zero
+ if ( length - fnobsize == 0 )
+ {
+ fvalue = 0.0f;
+ }
+ else
+ {
+ fvalue = (frange - frangewindow) * ( fnob / ( length - fnobsize ) );
+ }
+ }
+
+ // check to see if we should just snap to the bottom
+ if (fabs(fvalue + _rangeWindow - _range[1]) < (0.01f * frange))
+ {
+ // snap to the end
+ _value = _range[1] - _rangeWindow;
+ }
+ else
+ {
+ // Take care of rounding issues.
+ _value = (int)( fvalue + _range[0] + 0.5);
+ }
+
+ // Clamp final result
+ _value = ( _value < (_range[1] - _rangeWindow) ) ? _value : (_range[1] - _rangeWindow);
+
+ if (_value < _range[0])
+ {
+ _value = _range[0];
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if the ScrollBarSlider can move through one or more pixels per
+// unit of its range.
+//-----------------------------------------------------------------------------
+bool ScrollBarSlider::HasFullRange()
+{
+ int wide, tall;
+ GetPaintSize(wide, tall);
+
+ float frangewindow = (float)(_rangeWindow);
+
+ float checkAgainst = 0;
+ if(_vertical)
+ {
+ checkAgainst = (float)tall;
+ }
+ else
+ {
+ checkAgainst = (float)wide;
+ }
+
+ if ( frangewindow > 0 )
+ {
+ if( frangewindow <= ( checkAgainst + _buttonOffset ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Inform other watchers that the ScrollBarSlider was moved
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::SendScrollBarSliderMovedMessage()
+{
+ // send a changed message
+ PostActionSignal(new KeyValues("ScrollBarSliderMoved", "position", _value));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if this slider is actually drawing itself
+//-----------------------------------------------------------------------------
+bool ScrollBarSlider::IsSliderVisible( void )
+{
+ int itemRange = _range[1] - _range[0];
+
+ // Don't draw nob, no items in list
+ if ( itemRange <= 0 )
+ return false ;
+
+ // Not enough range
+ if ( itemRange <= _rangeWindow )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetFgColor(GetSchemeColor("ScrollBarSlider.FgColor", pScheme));
+ SetBgColor(GetSchemeColor("ScrollBarSlider.BgColor", pScheme));
+
+ IBorder *newBorder = pScheme->GetBorder("ScrollBarSliderBorder");
+
+ if ( newBorder )
+ {
+ _ScrollBarSliderBorder = newBorder;
+ }
+ else
+ {
+ _ScrollBarSliderBorder = pScheme->GetBorder("ButtonBorder");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::ApplySettings( KeyValues *pInResourceData )
+{
+ BaseClass::ApplySettings( pInResourceData );
+
+ const char *pButtonBorderName = pInResourceData->GetString( "ButtonBorder", NULL );
+ if ( pButtonBorderName )
+ {
+ _ScrollBarSliderBorder = vgui::scheme()->GetIScheme( GetScheme() )->GetBorder( pButtonBorderName );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::Paint()
+{
+ int wide,tall;
+ GetPaintSize(wide,tall);
+
+ if ( !IsSliderVisible() )
+ return;
+
+ Color col = GetFgColor();
+ surface()->DrawSetColor(col);
+
+ if (_vertical)
+ {
+ if ( GetPaintBackgroundType() == 2 )
+ {
+ DrawBox( 1, _nobPos[0], wide - 2, _nobPos[1] - _nobPos[0], col, 1.0f );
+ }
+ else
+ {
+ // Nob
+ surface()->DrawFilledRect(1, _nobPos[0], wide - 2, _nobPos[1]);
+ }
+
+ // border
+ if (_ScrollBarSliderBorder)
+ {
+ _ScrollBarSliderBorder->Paint(0, _nobPos[0], wide, _nobPos[1]);
+ }
+ }
+ else
+ {
+ // horizontal nob
+ surface()->DrawFilledRect(_nobPos[0], 1, _nobPos[1], tall - 2 );
+
+ // border
+ if (_ScrollBarSliderBorder)
+ {
+ _ScrollBarSliderBorder->Paint(_nobPos[0] - 1, 1, _nobPos[1], tall );
+ }
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::PaintBackground()
+{
+// BaseClass::PaintBackground();
+
+ int wide,tall;
+ GetPaintSize(wide,tall);
+ surface()->DrawSetColor(GetBgColor());
+ surface()->DrawFilledRect(0, 0, wide-1, tall-1);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the range of the ScrollBarSlider
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::SetRange(int min,int max)
+{
+ if(max<min)
+ {
+ max=min;
+ }
+
+ if(min>max)
+ {
+ min=max;
+ }
+
+ _range[0]=min;
+ _range[1]=max;
+
+ // update the value (forces it within the range)
+ SetValue( _value );
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the range values of the ScrollBarSlider
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::GetRange(int& min,int& max)
+{
+ min=_range[0];
+ max=_range[1];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Respond to cursor movements, we only care about clicking and dragging
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::OnCursorMoved(int x,int y)
+{
+ if (!_dragging)
+ {
+ return;
+ }
+
+// input()->GetCursorPos(x, y);
+// ScreenToLocal(x, y);
+
+ int wide, tall;
+ GetPaintSize(wide, tall);
+
+ if (_vertical)
+ {
+ _nobPos[0] = _nobDragStartPos[0] + (y - _dragStartPos[1]);
+ _nobPos[1] = _nobDragStartPos[1] + (y - _dragStartPos[1]);
+
+ if (_nobPos[1] > tall)
+ {
+ _nobPos[0] = tall - (_nobPos[1] - _nobPos[0]);
+ _nobPos[1] = tall;
+ SetValue( _range[1] - _rangeWindow );
+ }
+ }
+ else
+ {
+ _nobPos[0] = _nobDragStartPos[0] + (x - _dragStartPos[0]);
+ _nobPos[1] = _nobDragStartPos[1] + (x - _dragStartPos[0]);
+
+ if (_nobPos[1] > wide)
+ {
+ _nobPos[0] = wide - (_nobPos[1] - _nobPos[0]);
+ _nobPos[1] = wide;
+ }
+
+ }
+ if (_nobPos[0] < 0)
+ {
+ _nobPos[1] = _nobPos[1] - _nobPos[0];
+ _nobPos[0] = 0;
+ SetValue(0);
+ }
+
+ InvalidateLayout(); // not invalidatelayout - because it won't draw while we're scrolling the slider
+ RecomputeValueFromNobPos();
+// Repaint();
+ SendScrollBarSliderMovedMessage();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Respond to mouse clicks on the ScrollBarSlider
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::OnMousePressed(MouseCode code)
+{
+ int x,y;
+ input()->GetCursorPos(x,y);
+ ScreenToLocal(x,y);
+
+ if (_vertical)
+ {
+ if ((y >= _nobPos[0]) && (y < _nobPos[1]))
+ {
+ _dragging = true;
+ input()->SetMouseCapture(GetVPanel());
+ _nobDragStartPos[0] = _nobPos[0];
+ _nobDragStartPos[1] = _nobPos[1];
+ _dragStartPos[0] = x;
+ _dragStartPos[1] = y;
+ }
+ else if (y < _nobPos[0])
+ {
+ // jump the bar up by the range window
+ int val = GetValue();
+ val -= _rangeWindow;
+ SetValue(val);
+ }
+ else if (y >= _nobPos[1])
+ {
+ // jump the bar down by the range window
+ int val = GetValue();
+ val += _rangeWindow;
+ SetValue(val);
+ }
+ }
+ else
+ {
+ if((x >= _nobPos[0]) && (x < _nobPos[1]))
+ {
+ _dragging = true;
+ input()->SetMouseCapture(GetVPanel());
+ _nobDragStartPos[0] = _nobPos[0];
+ _nobDragStartPos[1] = _nobPos[1];
+ _dragStartPos[0] = x;
+ _dragStartPos[1] = y;
+ }
+ else if (x < _nobPos[0])
+ {
+ // jump the bar up by the range window
+ int val = GetValue();
+ val -= _rangeWindow;
+ SetValue(val);
+ }
+ else if (x >= _nobPos[1])
+ {
+ // jump the bar down by the range window
+ int val = GetValue();
+ val += _rangeWindow;
+ SetValue(val);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Treat double clicks as single clicks
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::OnMouseDoublePressed(MouseCode code)
+{
+ OnMousePressed(code);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop looking for mouse events when mouse is up.
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::OnMouseReleased(MouseCode code)
+{
+ _dragging = false;
+ input()->SetMouseCapture(null);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the position of the ends of the ScrollBarSlider.
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::GetNobPos(int& min, int& max)
+{
+ min=_nobPos[0];
+ max=_nobPos[1];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the number of lines visible in the window the ScrollBarSlider is attached to
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::SetRangeWindow(int rangeWindow)
+{
+ _rangeWindow = rangeWindow;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the number of lines visible in the window the ScrollBarSlider is attached to
+//-----------------------------------------------------------------------------
+int ScrollBarSlider::GetRangeWindow()
+{
+ return _rangeWindow;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ScrollBarSlider::SetButtonOffset(int buttonOffset)
+{
+ _buttonOffset = buttonOffset;
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/ScrollableEditablePanel.cpp b/mp/src/vgui2/vgui_controls/ScrollableEditablePanel.cpp
new file mode 100644
index 00000000..5d753b04
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ScrollableEditablePanel.cpp
@@ -0,0 +1,86 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "vgui_controls/ScrollableEditablePanel.h"
+#include "vgui_controls/ScrollBar.h"
+#include "vgui_controls/ScrollBarSlider.h"
+#include "vgui_controls/Button.h"
+#include "KeyValues.h"
+
+// NOTE: This has to be the last file included!
+#include "tier0/memdbgon.h"
+
+
+using namespace vgui;
+
+ScrollableEditablePanel::ScrollableEditablePanel( vgui::Panel *pParent, vgui::EditablePanel *pChild, const char *pName ) :
+ BaseClass( pParent, pName )
+{
+ m_pChild = pChild;
+ m_pChild->SetParent( this );
+
+ m_pScrollBar = new vgui::ScrollBar( this, "VerticalScrollBar", true );
+ m_pScrollBar->SetWide( 16 );
+ m_pScrollBar->SetAutoResize( PIN_TOPRIGHT, AUTORESIZE_DOWN, 0, 0, -16, 0 );
+ m_pScrollBar->AddActionSignalTarget( this );
+}
+
+void ScrollableEditablePanel::ApplySettings( KeyValues *pInResourceData )
+{
+ BaseClass::ApplySettings( pInResourceData );
+
+ KeyValues *pScrollbarKV = pInResourceData->FindKey( "Scrollbar" );
+ if ( pScrollbarKV )
+ {
+ m_pScrollBar->ApplySettings( pScrollbarKV );
+ }
+}
+
+void ScrollableEditablePanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ m_pChild->SetWide( GetWide() - m_pScrollBar->GetWide() );
+ m_pScrollBar->SetRange( 0, m_pChild->GetTall() );
+ m_pScrollBar->SetRangeWindow( GetTall() );
+
+ if ( m_pScrollBar->GetSlider() )
+ {
+ m_pScrollBar->GetSlider()->SetFgColor( GetFgColor() );
+ }
+ if ( m_pScrollBar->GetButton(0) )
+ {
+ m_pScrollBar->GetButton(0)->SetFgColor( GetFgColor() );
+ }
+ if ( m_pScrollBar->GetButton(1) )
+ {
+ m_pScrollBar->GetButton(1)->SetFgColor( GetFgColor() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Called when the scroll bar moves
+//-----------------------------------------------------------------------------
+void ScrollableEditablePanel::OnScrollBarSliderMoved()
+{
+ InvalidateLayout();
+
+ int nScrollAmount = m_pScrollBar->GetValue();
+ m_pChild->SetPos( 0, -nScrollAmount );
+}
+
+//-----------------------------------------------------------------------------
+// respond to mouse wheel events
+//-----------------------------------------------------------------------------
+void ScrollableEditablePanel::OnMouseWheeled(int delta)
+{
+ int val = m_pScrollBar->GetValue();
+ val -= (delta * 50);
+ m_pScrollBar->SetValue( val );
+}
+
diff --git a/mp/src/vgui2/vgui_controls/SectionedListPanel.cpp b/mp/src/vgui2/vgui_controls/SectionedListPanel.cpp
new file mode 100644
index 00000000..120de368
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/SectionedListPanel.cpp
@@ -0,0 +1,2169 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdio.h>
+
+#include <vgui/IInput.h>
+#include <vgui/IPanel.h>
+#include <vgui/ILocalize.h>
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <KeyValues.h>
+#include <vgui/MouseCode.h>
+
+#include <vgui_controls/SectionedListPanel.h>
+#include <vgui_controls/Button.h>
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/ScrollBar.h>
+#include <vgui_controls/TextImage.h>
+#include <vgui_controls/ImageList.h>
+
+#include "utlvector.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+enum
+{
+ BUTTON_HEIGHT_DEFAULT = 20,
+ BUTTON_HEIGHT_SPACER = 7,
+ DEFAULT_LINE_SPACING = 20,
+ DEFAULT_SECTION_GAP = 8,
+ COLUMN_DATA_INDENT = 6,
+ COLUMN_DATA_GAP = 2,
+};
+
+namespace vgui
+{
+
+//-----------------------------------------------------------------------------
+// Purpose: header label that separates and names each section
+//-----------------------------------------------------------------------------
+SectionedListPanelHeader::SectionedListPanelHeader(SectionedListPanel *parent, const char *name, int sectionID) : Label(parent, name, "")
+{
+ m_pListPanel = parent;
+ m_iSectionID = sectionID;
+ SetTextImageIndex(-1);
+ ClearImages();
+ SetPaintBackgroundEnabled( false );
+}
+
+SectionedListPanelHeader::SectionedListPanelHeader(SectionedListPanel *parent, const wchar_t *name, int sectionID) : Label(parent, "SectionHeader", "")
+{
+ SetText(name);
+ SetVisible(false);
+ m_pListPanel = parent;
+ m_iSectionID = sectionID;
+ SetTextImageIndex(-1);
+ ClearImages();
+}
+
+void SectionedListPanelHeader::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetFgColor(GetSchemeColor("SectionedListPanel.HeaderTextColor", pScheme));
+ m_SectionDividerColor = GetSchemeColor("SectionedListPanel.DividerColor", pScheme);
+ SetBgColor(GetSchemeColor("SectionedListPanelHeader.BgColor", GetBgColor(), pScheme));
+ SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional()));
+ ClearImages();
+
+ HFont hFont = m_pListPanel->GetHeaderFont();
+ if ( hFont != INVALID_FONT )
+ {
+ SetFont( hFont );
+ }
+ else
+ {
+ SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional()));
+ }
+}
+
+void SectionedListPanelHeader::Paint()
+{
+ BaseClass::Paint();
+
+ int x, y, wide, tall;
+ GetBounds(x, y, wide, tall);
+
+ y = (tall - 2); // draw the line under the panel
+
+ surface()->DrawSetColor(m_SectionDividerColor);
+ surface()->DrawFilledRect(1, y, GetWide() - 2, y + 1);
+}
+
+void SectionedListPanelHeader::SetColor(Color col)
+{
+ m_SectionDividerColor = col;
+ SetFgColor(col);
+}
+void SectionedListPanelHeader::SetDividerColor(Color col )
+{
+ m_SectionDividerColor = col;
+}
+
+void SectionedListPanelHeader::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ // set up the text in the header
+ int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
+ if (colCount != GetImageCount())
+ {
+ // rebuild the image list
+ for (int i = 0; i < colCount; i++)
+ {
+ int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
+ IImage *image = NULL;
+ if (columnFlags & SectionedListPanel::HEADER_IMAGE)
+ {
+ //!! need some kind of image reference
+ image = NULL;
+ }
+ else
+ {
+ TextImage *textImage = new TextImage("");
+ textImage->SetFont(GetFont());
+ HFont fallback = m_pListPanel->GetColumnFallbackFontBySection( m_iSectionID, i );
+ if ( INVALID_FONT != fallback )
+ {
+ textImage->SetUseFallbackFont( true, fallback );
+ }
+ textImage->SetColor(GetFgColor());
+ image = textImage;
+ }
+
+ SetImageAtIndex(i, image, 0);
+ }
+ }
+
+ for (int repeat = 0; repeat <= 1; repeat++)
+ {
+ int xpos = 0;
+ for (int i = 0; i < colCount; i++)
+ {
+ int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
+ int columnWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
+ int maxWidth = columnWidth;
+
+ IImage *image = GetImageAtIndex(i);
+ if (!image)
+ {
+ xpos += columnWidth;
+ continue;
+ }
+
+ // set the image position within the label
+ int contentWide, wide, tall;
+ image->GetContentSize(wide, tall);
+ contentWide = wide;
+
+ // see if we can draw over the next few column headers (if we're left-aligned)
+ if (!(columnFlags & SectionedListPanel::COLUMN_RIGHT))
+ {
+ for (int j = i + 1; j < colCount; j++)
+ {
+ // see if this column header has anything for a header
+ int iwide = 0, itall = 0;
+ if (GetImageAtIndex(j))
+ {
+ GetImageAtIndex(j)->GetContentSize(iwide, itall);
+ }
+
+ if (iwide == 0)
+ {
+ // it's a blank header, ok to draw over it
+ maxWidth += m_pListPanel->GetColumnWidthBySection(m_iSectionID, j);
+ }
+ }
+ }
+ if (maxWidth >= 0)
+ {
+ wide = maxWidth;
+ }
+
+ if (columnFlags & SectionedListPanel::COLUMN_RIGHT)
+ {
+ SetImageBounds(i, xpos + wide - contentWide, wide - COLUMN_DATA_GAP);
+ }
+ else
+ {
+ SetImageBounds(i, xpos, wide - COLUMN_DATA_GAP);
+ }
+ xpos += columnWidth;
+
+ if (!(columnFlags & SectionedListPanel::HEADER_IMAGE))
+ {
+ Assert(dynamic_cast<TextImage *>(image) != NULL);
+ TextImage *textImage = (TextImage *)image;
+ textImage->SetFont(GetFont());
+ textImage->SetText(m_pListPanel->GetColumnTextBySection(m_iSectionID, i));
+ textImage->ResizeImageToContentMaxWidth( maxWidth );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Individual items in the list
+//-----------------------------------------------------------------------------
+class CItemButton : public Label
+{
+ DECLARE_CLASS_SIMPLE( CItemButton, Label );
+
+public:
+ CItemButton(SectionedListPanel *parent, int itemID) : Label(parent, NULL, "< item >")
+ {
+ m_pListPanel = parent;
+ m_iID = itemID;
+ m_pData = NULL;
+ Clear();
+ }
+
+ ~CItemButton()
+ {
+ // free all the keyvalues
+ if (m_pData)
+ {
+ m_pData->deleteThis();
+ }
+
+ // clear any section data
+ SetSectionID(-1);
+ }
+
+ void Clear()
+ {
+ m_bSelected = false;
+ m_bOverrideColors = false;
+ m_iSectionID = -1;
+ SetPaintBackgroundEnabled( false );
+ SetTextImageIndex(-1);
+ ClearImages();
+ }
+
+ int GetID()
+ {
+ return m_iID;
+ }
+
+ void SetID(int itemID)
+ {
+ m_iID = itemID;
+ }
+
+ int GetSectionID()
+ {
+ return m_iSectionID;
+ }
+
+ void SetSectionID(int sectionID)
+ {
+ if (sectionID != m_iSectionID)
+ {
+ // free any existing textimage list
+ ClearImages();
+ // delete any images we've created
+ for (int i = 0; i < m_TextImages.Count(); i++)
+ {
+ delete m_TextImages[i];
+ }
+ m_TextImages.RemoveAll();
+ // mark the list as needing rebuilding
+ InvalidateLayout();
+ }
+ m_iSectionID = sectionID;
+ }
+
+ void SetData(const KeyValues *data)
+ {
+ if (m_pData)
+ {
+ m_pData->deleteThis();
+ }
+
+ m_pData = data->MakeCopy();
+ InvalidateLayout();
+ }
+
+ KeyValues *GetData()
+ {
+ return m_pData;
+ }
+
+ virtual void PerformLayout()
+ {
+ // get our button text
+ int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
+ if (!m_pData || colCount < 1)
+ {
+ SetText("< unset >");
+ }
+ else
+ {
+ if (colCount != GetImageCount())
+ {
+ // rebuild the image list
+ for (int i = 0; i < colCount; i++)
+ {
+ int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
+ if (!(columnFlags & SectionedListPanel::COLUMN_IMAGE))
+ {
+ TextImage *image = new TextImage("");
+ m_TextImages.AddToTail(image);
+ image->SetFont( GetFont() );
+ HFont fallback = m_pListPanel->GetColumnFallbackFontBySection( m_iSectionID, i );
+ if ( INVALID_FONT != fallback )
+ {
+ image->SetUseFallbackFont( true, fallback );
+ }
+ SetImageAtIndex(i, image, 0);
+ }
+ }
+
+ {for ( int i = GetImageCount(); i < colCount; i++ ) // make sure we have enough image slots
+ {
+ AddImage( NULL, 0 );
+ }}
+ }
+
+ // set the text for each column
+ int xpos = 0;
+ for (int i = 0; i < colCount; i++)
+ {
+ const char *keyname = m_pListPanel->GetColumnNameBySection(m_iSectionID, i);
+
+ int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
+ int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
+
+ IImage *image = NULL;
+ if (columnFlags & SectionedListPanel::COLUMN_IMAGE)
+ {
+ // lookup which image is being referred to
+ if (m_pListPanel->m_pImageList)
+ {
+ int imageIndex = m_pData->GetInt(keyname, 0);
+ if (m_pListPanel->m_pImageList->IsValidIndex(imageIndex))
+ {
+ // 0 is always the blank image
+ if (imageIndex > 0)
+ {
+ image = m_pListPanel->m_pImageList->GetImage(imageIndex);
+ SetImageAtIndex(i, image, 0);
+ }
+ }
+ else
+ {
+ // this is mildly valid (CGamesList hits it because of the way it uses the image indices)
+ // Assert(!("Image index out of range for ImageList in SectionedListPanel"));
+ }
+ }
+ else
+ {
+ Assert(!("Images columns used in SectionedListPanel with no ImageList set"));
+ }
+ }
+ else
+ {
+ TextImage *textImage = dynamic_cast<TextImage *>(GetImageAtIndex(i));
+ if (textImage)
+ {
+ textImage->SetText(m_pData->GetString(keyname, ""));
+ textImage->ResizeImageToContentMaxWidth( maxWidth );
+
+ // set the text color based on the selection state - if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
+ VPANEL focus = input()->GetFocus();
+ if ( !m_bOverrideColors )
+ {
+ if (IsSelected() && !m_pListPanel->IsInEditMode())
+ {
+ if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
+ {
+ textImage->SetColor(m_ArmedFgColor2);
+ }
+ else
+ {
+ textImage->SetColor(m_OutOfFocusSelectedTextColor);
+ }
+ }
+ else if (columnFlags & SectionedListPanel::COLUMN_BRIGHT)
+ {
+ textImage->SetColor(m_ArmedFgColor1);
+ }
+ else
+ {
+ textImage->SetColor(m_FgColor2);
+ }
+ }
+ else
+ {
+ // custom colors
+ if (IsSelected() && (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))))
+ {
+ textImage->SetColor(m_ArmedFgColor2);
+ }
+ else
+ {
+ textImage->SetColor(GetFgColor());
+ }
+ }
+ }
+ image = textImage;
+ }
+
+ // set the image position within the label
+ int imageWide = 0, tall = 0;
+ int wide;
+ if (image)
+ {
+ image->GetContentSize(imageWide, tall);
+ }
+ if (maxWidth >= 0)
+ {
+ wide = maxWidth;
+ }
+ else
+ {
+ wide = imageWide;
+ }
+
+ if (i == 0 && !(columnFlags & SectionedListPanel::COLUMN_IMAGE))
+ {
+ // first column has an extra indent
+ SetImageBounds(i, xpos + COLUMN_DATA_INDENT, wide - (COLUMN_DATA_INDENT + COLUMN_DATA_GAP));
+ }
+ else
+ {
+ if (columnFlags & SectionedListPanel::COLUMN_CENTER)
+ {
+ int offSet = (wide / 2) - (imageWide / 2);
+ SetImageBounds(i, xpos + offSet, wide - offSet - COLUMN_DATA_GAP);
+ }
+ else if (columnFlags & SectionedListPanel::COLUMN_RIGHT)
+ {
+ SetImageBounds(i, xpos + wide - imageWide, wide - COLUMN_DATA_GAP);
+ }
+ else
+ {
+ SetImageBounds(i, xpos, wide - COLUMN_DATA_GAP);
+ }
+ }
+ xpos += wide;
+ }
+ }
+
+ BaseClass::PerformLayout();
+ }
+
+ virtual void ApplySchemeSettings(IScheme *pScheme)
+ {
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ m_ArmedFgColor1 = GetSchemeColor("SectionedListPanel.BrightTextColor", pScheme);
+ m_ArmedFgColor2 = GetSchemeColor("SectionedListPanel.SelectedTextColor", pScheme);
+ m_OutOfFocusSelectedTextColor = GetSchemeColor("SectionedListPanel.OutOfFocusSelectedTextColor", pScheme);
+ m_ArmedBgColor = GetSchemeColor("SectionedListPanel.SelectedBgColor", pScheme);
+
+ m_FgColor2 = GetSchemeColor("SectionedListPanel.TextColor", pScheme);
+
+ m_BgColor = GetSchemeColor("SectionedListPanel.BgColor", GetBgColor(), pScheme);
+ m_SelectionBG2Color = GetSchemeColor("SectionedListPanel.OutOfFocusSelectedBgColor", pScheme);
+
+
+ HFont hFont = m_pListPanel->GetRowFont();
+ if ( hFont != INVALID_FONT )
+ {
+ SetFont( hFont );
+ }
+ else
+ {
+ const char *fontName = pScheme->GetResourceString( "SectionedListPanel.Font" );
+ HFont font = pScheme->GetFont(fontName, IsProportional());
+ if ( font != INVALID_FONT )
+ {
+ SetFont( font );
+ }
+ }
+
+ ClearImages();
+ }
+
+ virtual void PaintBackground()
+ {
+ int wide, tall;
+ GetSize(wide, tall);
+
+ if (IsSelected() && !m_pListPanel->IsInEditMode())
+ {
+ VPANEL focus = input()->GetFocus();
+ // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
+ if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
+ {
+ surface()->DrawSetColor(m_ArmedBgColor);
+ }
+ else
+ {
+ surface()->DrawSetColor(m_SelectionBG2Color);
+ }
+ }
+ else
+ {
+ surface()->DrawSetColor(GetBgColor());
+ }
+ surface()->DrawFilledRect(0, 0, wide, tall);
+ }
+
+ virtual void Paint()
+ {
+ BaseClass::Paint();
+
+ if ( !m_bShowColumns )
+ return;
+
+ // Debugging code to show column widths
+ int wide, tall;
+ GetSize(wide, tall);
+ surface()->DrawSetColor( 255,255,255,255 );
+ surface()->DrawOutlinedRect(0, 0, wide, tall);
+
+ int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
+ if (m_pData && colCount >= 0)
+ {
+ int xpos = 0;
+ for (int i = 0; i < colCount; i++)
+ {
+ const char *keyname = m_pListPanel->GetColumnNameBySection(m_iSectionID, i);
+ int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
+ int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
+
+ IImage *image = NULL;
+ if (columnFlags & SectionedListPanel::COLUMN_IMAGE)
+ {
+ // lookup which image is being referred to
+ if (m_pListPanel->m_pImageList)
+ {
+ int imageIndex = m_pData->GetInt(keyname, 0);
+ if (m_pListPanel->m_pImageList->IsValidIndex(imageIndex))
+ {
+ if (imageIndex > 0)
+ {
+ image = m_pListPanel->m_pImageList->GetImage(imageIndex);
+ }
+ }
+ }
+ }
+ else
+ {
+ image = GetImageAtIndex(i);
+ }
+
+ int imageWide = 0, tall = 0;
+ int wide;
+ if (image)
+ {
+ image->GetContentSize(imageWide, tall);
+ }
+ if (maxWidth >= 0)
+ {
+ wide = maxWidth;
+ }
+ else
+ {
+ wide = imageWide;
+ }
+
+ xpos += wide;//max(maxWidth,wide);
+ surface()->DrawOutlinedRect( xpos, 0, xpos, GetTall() );
+ }
+ }
+ }
+
+ virtual void OnMousePressed(MouseCode code)
+ {
+ if ( m_pListPanel && m_pListPanel->IsClickable() && IsEnabled() )
+ {
+ if (code == MOUSE_LEFT)
+ {
+ m_pListPanel->PostActionSignal(new KeyValues("ItemLeftClick", "itemID", m_iID));
+ }
+ if (code == MOUSE_RIGHT)
+ {
+ KeyValues *msg = new KeyValues("ItemContextMenu", "itemID", m_iID);
+ msg->SetPtr("SubPanel", this);
+ m_pListPanel->PostActionSignal(msg);
+ }
+
+ m_pListPanel->SetSelectedItem(this);
+ }
+ }
+
+ void SetSelected(bool state)
+ {
+ if (m_bSelected != state)
+ {
+ if (state)
+ {
+ RequestFocus();
+ }
+ m_bSelected = state;
+ SetPaintBackgroundEnabled( state );
+ InvalidateLayout();
+ Repaint();
+ }
+ }
+
+ bool IsSelected()
+ {
+ return m_bSelected;
+ }
+
+ virtual void OnSetFocus()
+ {
+ InvalidateLayout(); // force the layout to be redone so we can change text color according to focus
+ BaseClass::OnSetFocus();
+ }
+
+ virtual void OnKillFocus()
+ {
+ InvalidateLayout(); // force the layout to be redone so we can change text color according to focus
+ BaseClass::OnSetFocus();
+ }
+
+ virtual void OnMouseDoublePressed(MouseCode code)
+ {
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Only do this if clicking is enabled.
+ //=============================================================================
+ if (m_pListPanel && m_pListPanel->IsClickable())
+ {
+ if (code == MOUSE_LEFT)
+ {
+ m_pListPanel->PostActionSignal(new KeyValues("ItemDoubleLeftClick", "itemID", m_iID));
+
+ // post up an enter key being hit
+ m_pListPanel->OnKeyCodeTyped(KEY_ENTER);
+ }
+ else
+ {
+ OnMousePressed(code);
+ }
+
+ m_pListPanel->SetSelectedItem(this);
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ }
+
+ void GetCellBounds(int column, int &xpos, int &columnWide)
+ {
+ xpos = 0, columnWide = 0;
+ int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
+ for (int i = 0; i < colCount; i++)
+ {
+ int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
+
+ IImage *image = GetImageAtIndex(i);
+ if (!image)
+ continue;
+
+ // set the image position within the label
+ int wide, tall;
+ image->GetContentSize(wide, tall);
+ if (maxWidth >= 0)
+ {
+ wide = maxWidth;
+ }
+
+ if (i == column)
+ {
+ // found the cell size, bail
+ columnWide = wide;
+ return;
+ }
+
+ xpos += wide;
+ }
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [menglish] gets the local coordinates of a cell using the max width for every column
+ //=============================================================================
+
+ void GetMaxCellBounds(int column, int &xpos, int &columnWide)
+ {
+ xpos = 0, columnWide = 0;
+ int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
+ for (int i = 0; i < colCount; i++)
+ {
+ int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
+
+ if (i == column)
+ {
+ // found the cell size, bail
+ columnWide = maxWidth;
+ return;
+ }
+
+ xpos += maxWidth;
+ }
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ virtual void SetOverrideColors( bool state )
+ {
+ m_bOverrideColors = state;
+ }
+
+ void SetShowColumns( bool bShow )
+ {
+ m_bShowColumns = bShow;
+ }
+
+private:
+ SectionedListPanel *m_pListPanel;
+ int m_iID;
+ int m_iSectionID;
+ KeyValues *m_pData;
+ Color m_FgColor2;
+ Color m_BgColor;
+ Color m_ArmedFgColor1;
+ Color m_ArmedFgColor2;
+ Color m_OutOfFocusSelectedTextColor;
+ Color m_ArmedBgColor;
+ Color m_SelectionBG2Color;
+ CUtlVector<vgui::TextImage *> m_TextImages;
+
+ bool m_bSelected;
+ bool m_bOverrideColors;
+ bool m_bShowColumns;
+};
+
+}; // namespace vgui
+
+DECLARE_BUILD_FACTORY( SectionedListPanel );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+SectionedListPanel::SectionedListPanel(vgui::Panel *parent, const char *name) : BaseClass(parent, name)
+{
+ m_pScrollBar = new ScrollBar(this, "SectionedScrollBar", true);
+ m_pScrollBar->SetVisible(false);
+ m_pScrollBar->AddActionSignalTarget(this);
+
+ m_iEditModeItemID = 0;
+ m_iEditModeColumn = 0;
+ m_bSortNeeded = false;
+ m_bVerticalScrollbarEnabled = true;
+ m_iLineSpacing = DEFAULT_LINE_SPACING;
+ m_iSectionGap = DEFAULT_SECTION_GAP;
+
+ m_pImageList = NULL;
+ m_bDeleteImageListWhenDone = false;
+
+ m_hHeaderFont = INVALID_FONT;
+ m_hRowFont = INVALID_FONT;
+
+ //=============================================================================
+ // HPE_BEGIN:
+ //=============================================================================
+ // [tj] Default clickability to true so existing controls aren't affected.
+ m_clickable = true;
+ // [tj] draw section headers by default so existing controls aren't affected.
+ m_bDrawSectionHeaders = true;
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+SectionedListPanel::~SectionedListPanel()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sorts the list
+//-----------------------------------------------------------------------------
+void SectionedListPanel::ReSortList()
+{
+ m_SortedItems.RemoveAll();
+
+ int sectionStart = 0;
+ // layout the buttons
+ for (int sectionIndex = 0; sectionIndex < m_Sections.Size(); sectionIndex++)
+ {
+ section_t &section = m_Sections[sectionIndex];
+ sectionStart = m_SortedItems.Count();
+
+ // find all the items in this section
+ for( int i = m_Items.Head(); i != m_Items.InvalidIndex(); i = m_Items.Next( i ) )
+ {
+ if (m_Items[i]->GetSectionID() == m_Sections[sectionIndex].m_iID)
+ {
+ // insert the items sorted
+ if (section.m_pSortFunc)
+ {
+ int insertionPoint = sectionStart;
+ for (;insertionPoint < m_SortedItems.Count(); insertionPoint++)
+ {
+ if (section.m_pSortFunc(this, i, m_SortedItems[insertionPoint]->GetID()))
+ break;
+ }
+
+ if (insertionPoint == m_SortedItems.Count())
+ {
+ m_SortedItems.AddToTail(m_Items[i]);
+ }
+ else
+ {
+ m_SortedItems.InsertBefore(insertionPoint, m_Items[i]);
+ }
+ }
+ else
+ {
+ // just add to the end
+ m_SortedItems.AddToTail(m_Items[i]);
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: iterates through and sets up the position of all the sections and items
+//-----------------------------------------------------------------------------
+void SectionedListPanel::PerformLayout()
+{
+ // lazy resort the list
+ if (m_bSortNeeded)
+ {
+ ReSortList();
+ m_bSortNeeded = false;
+ }
+
+ BaseClass::PerformLayout();
+
+ LayoutPanels(m_iContentHeight);
+
+ int cx, cy, cwide, ctall;
+ GetBounds(cx, cy, cwide, ctall);
+ if (m_iContentHeight > ctall && m_bVerticalScrollbarEnabled)
+ {
+ m_pScrollBar->SetVisible(true);
+ m_pScrollBar->MoveToFront();
+
+ m_pScrollBar->SetPos(cwide - m_pScrollBar->GetWide() - 2, 0);
+ m_pScrollBar->SetSize(m_pScrollBar->GetWide(), ctall - 2);
+
+ m_pScrollBar->SetRangeWindow(ctall);
+
+ m_pScrollBar->SetRange(0, m_iContentHeight);
+ m_pScrollBar->InvalidateLayout();
+ m_pScrollBar->Repaint();
+
+ // since we're just about to make the scrollbar visible, we need to re-layout
+ // the buttons since they depend on the scrollbar size
+ LayoutPanels(m_iContentHeight);
+ }
+ else
+ {
+ m_pScrollBar->SetValue(0);
+
+ bool bWasVisible = m_pScrollBar->IsVisible();
+ m_pScrollBar->SetVisible(false);
+
+ // When we hide the scrollbar, we need to layout the buttons because they'll have more width to work with
+ if ( bWasVisible )
+ {
+ LayoutPanels(m_iContentHeight);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: lays out the sections and rows in the panel
+//-----------------------------------------------------------------------------
+void SectionedListPanel::LayoutPanels(int &contentTall)
+{
+ int tall = GetSectionTall();
+ int x = 5, wide = GetWide() - 10;
+ int y = 5;
+
+ if (m_pScrollBar->IsVisible())
+ {
+ y -= m_pScrollBar->GetValue();
+ wide -= m_pScrollBar->GetWide();
+ }
+
+ int iStart = -1;
+ int iEnd = -1;
+
+ // layout the buttons
+ bool bFirstVisibleSection = true;
+ for (int sectionIndex = 0; sectionIndex < m_Sections.Size(); sectionIndex++)
+ {
+ section_t &section = m_Sections[sectionIndex];
+
+ iStart = -1;
+ iEnd = -1;
+ for (int i = 0; i < m_SortedItems.Count(); i++)
+ {
+ if (m_SortedItems[i]->GetSectionID() == m_Sections[sectionIndex].m_iID)
+ {
+ if (iStart == -1)
+ iStart = i;
+ iEnd = i;
+ }
+ }
+
+ // don't draw this section at all if their are no item in it
+ if (iStart == -1 && !section.m_bAlwaysVisible)
+ {
+ section.m_pHeader->SetVisible(false);
+ continue;
+ }
+
+ // Skip down a bit if this is not the first section to be drawn
+ if ( bFirstVisibleSection )
+ bFirstVisibleSection = false;
+ else
+ y += m_iSectionGap;
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Only draw the header if it is enabled
+ //=============================================================================
+ int nMinNextSectionY = y + section.m_iMinimumHeight;
+ if (m_bDrawSectionHeaders)
+ {
+ // draw the header
+ section.m_pHeader->SetBounds(x, y, wide, tall);
+ section.m_pHeader->SetVisible(true);
+ y += tall;
+ }
+ else
+ {
+ section.m_pHeader->SetVisible(false);
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ if (iStart == -1 && section.m_bAlwaysVisible)
+ {
+ }
+ else
+ {
+ // arrange all the items in this section underneath
+ for (int i = iStart; i <= iEnd; i++)
+ {
+ CItemButton *item = m_SortedItems[i]; //items[i];
+ item->SetBounds(x, y, wide, m_iLineSpacing);
+
+ // setup edit mode
+ if (m_hEditModePanel.Get() && m_iEditModeItemID == item->GetID())
+ {
+ int cx, cwide;
+ item->GetCellBounds(1, cx, cwide);
+ m_hEditModePanel->SetBounds(cx, y, cwide, tall);
+ }
+
+ y += m_iLineSpacing;
+ }
+ }
+
+ // Add space, if needed to fulfill minimum requested content height
+ if ( y < nMinNextSectionY )
+ y = nMinNextSectionY;
+ }
+
+ // calculate height
+ contentTall = y;
+ if (m_pScrollBar->IsVisible())
+ {
+ contentTall += m_pScrollBar->GetValue();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ensures that the specified item is visible in the display
+//-----------------------------------------------------------------------------
+void SectionedListPanel::ScrollToItem(int iItem)
+{
+ int tall = GetSectionTall();
+ int itemX, itemY ;
+ int nCurrentValue = m_pScrollBar->GetValue();
+
+ // find out where the item is
+ m_Items[iItem]->GetPos(itemX, itemY);
+ // add in the current scrollbar position
+ itemY += nCurrentValue;
+
+ // compare that in the list
+ int cx, cy, cwide, ctall;
+ GetBounds(cx, cy, cwide, ctall);
+ if (m_iContentHeight > ctall)
+ {
+ if (itemY < nCurrentValue)
+ {
+ // scroll up
+ m_pScrollBar->SetValue(itemY);
+ }
+ else if (itemY > nCurrentValue + ctall - tall)
+ {
+ // scroll down
+ m_pScrollBar->SetValue(itemY - ctall + tall);
+ }
+ else
+ {
+ // keep the current value
+ }
+ }
+ else
+ {
+ // area isn't big enough, just remove the scrollbar
+ m_pScrollBar->SetValue(0);
+ }
+
+ // reset scrollbar
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets background color & border
+//-----------------------------------------------------------------------------
+void SectionedListPanel::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetBgColor(GetSchemeColor("SectionedListPanel.BgColor", GetBgColor(), pScheme));
+ SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
+
+ FOR_EACH_LL( m_Items, j )
+ {
+ m_Items[j]->SetShowColumns( m_bShowColumns );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void SectionedListPanel::SetHeaderFont( HFont hFont )
+{
+ m_hHeaderFont = hFont;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+HFont SectionedListPanel::GetHeaderFont( void ) const
+{
+ return m_hHeaderFont;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void SectionedListPanel::SetRowFont( HFont hFont )
+{
+ m_hRowFont = hFont;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+HFont SectionedListPanel::GetRowFont( void ) const
+{
+ return m_hRowFont;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void SectionedListPanel::ApplySettings(KeyValues *inResourceData)
+{
+ BaseClass::ApplySettings(inResourceData);
+ m_iLineSpacing = inResourceData->GetInt("linespacing", 0);
+ if (!m_iLineSpacing)
+ {
+ m_iLineSpacing = DEFAULT_LINE_SPACING;
+ }
+ if (IsProportional())
+ {
+ m_iLineSpacing = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iLineSpacing);
+ }
+
+ m_iSectionGap = inResourceData->GetInt("sectiongap", 0);
+ if (!m_iSectionGap)
+ {
+ m_iSectionGap = DEFAULT_SECTION_GAP;
+ }
+ if (IsProportional())
+ {
+ m_iSectionGap = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iSectionGap);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: passes on proportional state to children
+//-----------------------------------------------------------------------------
+void SectionedListPanel::SetProportional(bool state)
+{
+ BaseClass::SetProportional(state);
+
+ // now setup the section headers and items
+ int i;
+ for (i = 0; i < m_Sections.Count(); i++)
+ {
+ m_Sections[i].m_pHeader->SetProportional(state);
+ }
+ FOR_EACH_LL( m_Items, j )
+ {
+ m_Items[j]->SetProportional(state);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets whether or not the vertical scrollbar should ever be displayed
+//-----------------------------------------------------------------------------
+void SectionedListPanel::SetVerticalScrollbar(bool state)
+{
+ m_bVerticalScrollbarEnabled = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds a new section
+//-----------------------------------------------------------------------------
+void SectionedListPanel::AddSection(int sectionID, const char *name, SectionSortFunc_t sortFunc)
+{
+ SectionedListPanelHeader *header = new SectionedListPanelHeader(this, name, sectionID);
+ AddSection(sectionID, header, sortFunc);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds a new section
+//-----------------------------------------------------------------------------
+void SectionedListPanel::AddSection(int sectionID, const wchar_t *name, SectionSortFunc_t sortFunc)
+{
+ SectionedListPanelHeader *header = new SectionedListPanelHeader(this, name, sectionID);
+ AddSection(sectionID, header, sortFunc);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a new section, given object
+//-----------------------------------------------------------------------------
+void SectionedListPanel::AddSection(int sectionID, SectionedListPanelHeader *header, SectionSortFunc_t sortFunc)
+{
+ header = SETUP_PANEL( header );
+ int index = m_Sections.AddToTail();
+ m_Sections[index].m_iID = sectionID;
+ m_Sections[index].m_pHeader = header;
+ m_Sections[index].m_pSortFunc = sortFunc;
+ m_Sections[index].m_bAlwaysVisible = false;
+ m_Sections[index].m_iMinimumHeight = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: removes all the sections from the current panel
+//-----------------------------------------------------------------------------
+void SectionedListPanel::RemoveAllSections()
+{
+ for (int i = 0; i < m_Sections.Count(); i++)
+ {
+ if (!m_Sections.IsValidIndex(i))
+ continue;
+
+ m_Sections[i].m_pHeader->SetVisible(false);
+ m_Sections[i].m_pHeader->MarkForDeletion();
+ }
+
+ m_Sections.RemoveAll();
+ m_Sections.Purge();
+ m_SortedItems.RemoveAll();
+
+ InvalidateLayout();
+ ReSortList();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds a new column to a section
+//-----------------------------------------------------------------------------
+bool SectionedListPanel::AddColumnToSection(int sectionID, const char *columnName, const char *columnText, int columnFlags, int width, HFont fallbackFont /*= INVALID_FONT*/ )
+{
+ wchar_t wtext[64];
+ wchar_t *pwtext = g_pVGuiLocalize->Find(columnText);
+ if (!pwtext)
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode(columnText, wtext, sizeof(wtext));
+ pwtext = wtext;
+ }
+ return AddColumnToSection(sectionID, columnName, pwtext, columnFlags, width, fallbackFont );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: as above but with wchar_t's
+//-----------------------------------------------------------------------------
+bool SectionedListPanel::AddColumnToSection(int sectionID, const char *columnName, const wchar_t *columnText, int columnFlags, int width, HFont fallbackFont /*= INVALID_FONT*/ )
+{
+ int index = FindSectionIndexByID(sectionID);
+ if (index < 0)
+ return false;
+ section_t &section = m_Sections[index];
+
+ // add the new column to the sections' list
+ index = section.m_Columns.AddToTail();
+ column_t &column = section.m_Columns[index];
+
+ Q_strncpy(column.m_szColumnName, columnName, sizeof(column.m_szColumnName));
+ wcsncpy(column.m_szColumnText, columnText, sizeof(column.m_szColumnText) / sizeof(wchar_t));
+ column.m_szColumnText[sizeof(column.m_szColumnText) / sizeof(wchar_t) - 1] = 0;
+ column.m_iColumnFlags = columnFlags;
+ column.m_iWidth = width;
+ column.m_hFallbackFont = fallbackFont;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: modifies the text in an existing column
+//-----------------------------------------------------------------------------
+bool SectionedListPanel::ModifyColumn(int sectionID, const char *columnName, const wchar_t *columnText)
+{
+ int index = FindSectionIndexByID(sectionID);
+ if (index < 0)
+ return false;
+ section_t &section = m_Sections[index];
+
+ // find the specified column
+ int columnIndex;
+ for (columnIndex = 0; columnIndex < section.m_Columns.Count(); columnIndex++)
+ {
+ if (!stricmp(section.m_Columns[columnIndex].m_szColumnName, columnName))
+ break;
+ }
+ if (!section.m_Columns.IsValidIndex(columnIndex))
+ return false;
+ column_t &column = section.m_Columns[columnIndex];
+
+ // modify the text
+ wcsncpy(column.m_szColumnText, columnText, sizeof(column.m_szColumnText) / sizeof(wchar_t));
+ column.m_szColumnText[sizeof(column.m_szColumnText) / sizeof(wchar_t) - 1] = 0;
+ section.m_pHeader->InvalidateLayout();
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds an item to the list; returns itemID
+//-----------------------------------------------------------------------------
+int SectionedListPanel::AddItem(int sectionID, const KeyValues *data)
+{
+ int itemID = GetNewItemButton();
+ ModifyItem(itemID, sectionID, data);
+
+ // not sorted but in list
+ m_SortedItems.AddToTail(m_Items[itemID]);
+ m_bSortNeeded = true;
+
+ return itemID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: modifies an existing item; returns false if the item does not exist
+//-----------------------------------------------------------------------------
+bool SectionedListPanel::ModifyItem(int itemID, int sectionID, const KeyValues *data)
+{
+ if ( !m_Items.IsValidIndex(itemID) )
+ return false;
+
+ InvalidateLayout();
+ m_Items[itemID]->SetSectionID(sectionID);
+ m_Items[itemID]->SetData(data);
+ m_Items[itemID]->InvalidateLayout();
+ m_bSortNeeded = true;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void SectionedListPanel::SetItemFgColor( int itemID, Color color )
+{
+ Assert( m_Items.IsValidIndex(itemID) );
+ if ( !m_Items.IsValidIndex(itemID) )
+ return;
+
+ m_Items[itemID]->SetFgColor( color );
+ m_Items[itemID]->SetOverrideColors( true );
+ m_Items[itemID]->InvalidateLayout();
+}
+
+//=============================================================================
+// HPE_BEGIN:
+// [menglish] Setter for the background color similar to the foreground color
+//=============================================================================
+
+void SectionedListPanel::SetItemBgColor( int itemID, Color color )
+{
+ Assert( m_Items.IsValidIndex(itemID) );
+ if ( !m_Items.IsValidIndex(itemID) )
+ return;
+
+ m_Items[itemID]->SetBgColor( color );
+ m_Items[itemID]->SetPaintBackgroundEnabled( true );
+ m_Items[itemID]->SetOverrideColors( true );
+ m_Items[itemID]->InvalidateLayout();
+}
+
+void SectionedListPanel::SetItemFont( int itemID, HFont font )
+{
+ Assert( m_Items.IsValidIndex(itemID) );
+ if ( !m_Items.IsValidIndex(itemID) )
+ return;
+
+ m_Items[itemID]->SetFont( font );
+}
+
+void SectionedListPanel::SetItemEnabled( int itemID, bool bEnabled )
+{
+ Assert( m_Items.IsValidIndex(itemID) );
+ if ( !m_Items.IsValidIndex(itemID) )
+ return;
+
+ m_Items[itemID]->SetEnabled( bEnabled );
+}
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+//-----------------------------------------------------------------------------
+// Purpose: sets the color of a section text & underline
+//-----------------------------------------------------------------------------
+void SectionedListPanel::SetSectionFgColor(int sectionID, Color color)
+{
+ if (!m_Sections.IsValidIndex(sectionID))
+ return;
+
+ m_Sections[sectionID].m_pHeader->SetColor(color);
+}
+//-----------------------------------------------------------------------------
+// Purpose: added so you can change the divider color AFTER the main color.
+//-----------------------------------------------------------------------------
+void SectionedListPanel::SetSectionDividerColor( int sectionID, Color color)
+{
+ if (!m_Sections.IsValidIndex(sectionID))
+ return;
+
+ m_Sections[sectionID].m_pHeader->SetDividerColor(color);
+}
+//-----------------------------------------------------------------------------
+// Purpose: forces a section to always be visible
+//-----------------------------------------------------------------------------
+void SectionedListPanel::SetSectionAlwaysVisible(int sectionID, bool visible)
+{
+ if (!m_Sections.IsValidIndex(sectionID))
+ return;
+
+ m_Sections[sectionID].m_bAlwaysVisible = visible;
+}
+void SectionedListPanel::SetFontSection(int sectionID, HFont font)
+{
+ if (!m_Sections.IsValidIndex(sectionID))
+ return;
+
+ m_Sections[sectionID].m_pHeader->SetFont(font);
+}
+void SectionedListPanel::SetSectionMinimumHeight(int sectionID, int iMinimumHeight)
+{
+ if (!m_Sections.IsValidIndex(sectionID))
+ return;
+
+ m_Sections[sectionID].m_iMinimumHeight = iMinimumHeight;
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: removes an item from the list; returns false if the item does not exist or is already removed
+//-----------------------------------------------------------------------------
+bool SectionedListPanel::RemoveItem(int itemID)
+{
+ if ( !m_Items.IsValidIndex(itemID) )
+ return false;
+
+ m_SortedItems.FindAndRemove(m_Items[itemID]);
+ m_bSortNeeded = true;
+
+ m_Items[itemID]->MarkForDeletion();
+ m_Items.Remove(itemID);
+
+ InvalidateLayout();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the number of columns in a section
+//-----------------------------------------------------------------------------
+int SectionedListPanel::GetColumnCountBySection(int sectionID)
+{
+ int index = FindSectionIndexByID(sectionID);
+ if (index < 0)
+ return NULL;
+
+ return m_Sections[index].m_Columns.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the name of a column by section and column index; returns NULL if there are no more columns
+// valid range of columnIndex is [0, GetColumnCountBySection)
+//-----------------------------------------------------------------------------
+const char *SectionedListPanel::GetColumnNameBySection(int sectionID, int columnIndex)
+{
+ int index = FindSectionIndexByID(sectionID);
+ if (index < 0 || columnIndex >= m_Sections[index].m_Columns.Size())
+ return NULL;
+
+ return m_Sections[index].m_Columns[columnIndex].m_szColumnName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the text for a column by section and column index
+//-----------------------------------------------------------------------------
+const wchar_t *SectionedListPanel::GetColumnTextBySection(int sectionID, int columnIndex)
+{
+ int index = FindSectionIndexByID(sectionID);
+ if (index < 0 || columnIndex >= m_Sections[index].m_Columns.Size())
+ return NULL;
+
+ return m_Sections[index].m_Columns[columnIndex].m_szColumnText;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the type of a column by section and column index
+//-----------------------------------------------------------------------------
+int SectionedListPanel::GetColumnFlagsBySection(int sectionID, int columnIndex)
+{
+ int index = FindSectionIndexByID(sectionID);
+ if (index < 0)
+ return 0;
+
+ if (columnIndex >= m_Sections[index].m_Columns.Size())
+ return 0;
+
+ return m_Sections[index].m_Columns[columnIndex].m_iColumnFlags;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int SectionedListPanel::GetColumnWidthBySection(int sectionID, int columnIndex)
+{
+ int index = FindSectionIndexByID(sectionID);
+ if (index < 0)
+ return 0;
+
+ if (columnIndex >= m_Sections[index].m_Columns.Size())
+ return 0;
+
+ return m_Sections[index].m_Columns[columnIndex].m_iWidth;
+}
+
+//=============================================================================
+// HPE_BEGIN:
+// [menglish] Gets the column index by the string identifier
+//=============================================================================
+
+int SectionedListPanel::GetColumnIndexByName(int sectionID, char* name)
+{
+ int index = FindSectionIndexByID(sectionID);
+ if (index < 0)
+ return 0;
+
+ for ( int columnIndex = 0; columnIndex < m_Sections[index].m_Columns.Count(); ++ columnIndex)
+ {
+ if( !V_strcmp( m_Sections[index].m_Columns[columnIndex].m_szColumnName, name ) )
+ return columnIndex;
+ }
+
+ return -1;
+}
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Purpose: returns -1 if section not found
+//-----------------------------------------------------------------------------
+int SectionedListPanel::FindSectionIndexByID(int sectionID)
+{
+ for (int i = 0; i < m_Sections.Size(); i++)
+ {
+ if (m_Sections[i].m_iID == sectionID)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the scrollbar is moved
+//-----------------------------------------------------------------------------
+void SectionedListPanel::OnSliderMoved()
+{
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Scrolls the list according to the mouse wheel movement
+//-----------------------------------------------------------------------------
+void SectionedListPanel::OnMouseWheeled(int delta)
+{
+ if (m_hEditModePanel.Get())
+ {
+ // ignore mouse wheel in edit mode, forward right up to parent
+ CallParentFunction(new KeyValues("MouseWheeled", "delta", delta));
+ return;
+ }
+
+ // scroll the window based on the delta
+ int val = m_pScrollBar->GetValue();
+ val -= (delta * BUTTON_HEIGHT_DEFAULT * 3);
+ m_pScrollBar->SetValue(val);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Resets the scrollbar position on size change
+//-----------------------------------------------------------------------------
+void SectionedListPanel::OnSizeChanged(int wide, int tall)
+{
+ BaseClass::OnSizeChanged(wide, tall);
+ m_pScrollBar->SetValue(0);
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: deselects any items
+//-----------------------------------------------------------------------------
+void SectionedListPanel::OnMousePressed(MouseCode code)
+{
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Only do this if clicking is enabled.
+ //=============================================================================
+ if (m_clickable){
+ ClearSelection();
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================}
+}
+//-----------------------------------------------------------------------------
+// Purpose: deselects any items
+//-----------------------------------------------------------------------------
+void SectionedListPanel::ClearSelection( void )
+{
+ SetSelectedItem((CItemButton *)NULL);
+}
+
+void SectionedListPanel::MoveSelectionDown( void )
+{
+ int itemID = GetSelectedItem();
+ if (itemID == -1)
+ return;
+
+ if (!m_SortedItems.Count()) // if the list has been emptied
+ return;
+
+ int i;
+ for (i = 0; i < m_SortedItems.Count(); i++)
+ {
+ if (m_SortedItems[i]->GetID() == itemID)
+ break;
+ }
+
+ Assert(i != m_SortedItems.Count());
+
+ // we're already on the end
+ if (i >= m_SortedItems.Count() - 1)
+ return;
+
+ int newItemID = m_SortedItems[i + 1]->GetID();
+ SetSelectedItem(m_Items[newItemID]);
+ ScrollToItem(newItemID);
+}
+
+void SectionedListPanel::MoveSelectionUp( void )
+{
+ int itemID = GetSelectedItem();
+ if (itemID == -1)
+ return;
+
+ if (!m_SortedItems.Count()) // if the list has been emptied
+ return;
+
+ int i;
+ for (i = 0; i < m_SortedItems.Count(); i++)
+ {
+ if (m_SortedItems[i]->GetID() == itemID)
+ break;
+ }
+
+ Assert(i != m_SortedItems.Count());
+
+ // we're already on the end
+ if (i == 0 || i >= m_SortedItems.Count() )
+ return;
+
+ int newItemID = m_SortedItems[i - 1]->GetID();
+ SetSelectedItem(m_Items[newItemID]);
+ ScrollToItem(newItemID);
+}
+
+void SectionedListPanel::NavigateTo( void )
+{
+ BaseClass::NavigateTo();
+
+ if ( m_SortedItems.Count() )
+ {
+ int nItemID = m_SortedItems[ 0 ]->GetID();
+ SetSelectedItem( m_Items[ nItemID ] );
+ ScrollToItem( nItemID );
+ }
+
+ RequestFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: arrow key movement handler
+//-----------------------------------------------------------------------------
+void SectionedListPanel::OnKeyCodePressed( KeyCode code )
+{
+ if (m_hEditModePanel.Get())
+ {
+ // ignore arrow keys in edit mode
+ // forward right up to parent so that tab focus change doesn't occur
+ CallParentFunction(new KeyValues("KeyCodePressed", "code", code));
+ return;
+ }
+
+ int buttonTall = GetSectionTall();
+
+ ButtonCode_t nButtonCode = GetBaseButtonCode( code );
+
+ if ( nButtonCode == KEY_XBUTTON_DOWN ||
+ nButtonCode == KEY_XSTICK1_DOWN ||
+ nButtonCode == KEY_XSTICK2_DOWN ||
+ code == KEY_DOWN )
+ {
+ int itemID = GetSelectedItem();
+ MoveSelectionDown();
+ if ( itemID != GetSelectedItem() )
+ {
+ // Only eat the input if it did something
+ return;
+ }
+ }
+ else if ( nButtonCode == KEY_XBUTTON_UP ||
+ nButtonCode == KEY_XSTICK1_UP ||
+ nButtonCode == KEY_XSTICK2_UP ||
+ code == KEY_UP)
+ {
+ int itemID = GetSelectedItem();
+ MoveSelectionUp();
+ if ( itemID != GetSelectedItem() )
+ {
+ // Only eat the input if it did something
+ return;
+ }
+ }
+ else if (code == KEY_PAGEDOWN)
+ {
+ // calculate info for # of rows
+ int cx, cy, cwide, ctall;
+ GetBounds(cx, cy, cwide, ctall);
+
+ int rowsperpage = ctall/buttonTall;
+
+ int itemID = GetSelectedItem();
+ int lastValidItem = itemID;
+ int secID = m_Items[itemID]->GetSectionID();
+ int i=0;
+ int row = m_SortedItems.Find(m_Items[itemID]);
+
+ while ( i < rowsperpage )
+ {
+ if ( m_SortedItems.IsValidIndex(++row) )
+ {
+ itemID = m_SortedItems[row]->GetID();
+ lastValidItem = itemID;
+ i++;
+
+ // if we switched sections, then count the section header as a row
+ if (m_Items[itemID]->GetSectionID() != secID)
+ {
+ secID = m_Items[itemID]->GetSectionID();
+ i++;
+ }
+ }
+ else
+ {
+ itemID = lastValidItem;
+ break;
+ }
+ }
+ SetSelectedItem(m_Items[itemID]);
+ ScrollToItem(itemID);
+ return;
+ }
+ else if (code == KEY_PAGEUP)
+ {
+ // calculate info for # of rows
+ int cx, cy, cwide, ctall;
+ GetBounds(cx, cy, cwide, ctall);
+ int rowsperpage = ctall/buttonTall;
+
+ int itemID = GetSelectedItem();
+ int lastValidItem = itemID;
+ int secID = m_Items[itemID]->GetSectionID();
+ int i=0;
+ int row = m_SortedItems.Find(m_Items[itemID]);
+ while ( i < rowsperpage )
+ {
+ if ( m_SortedItems.IsValidIndex(--row) )
+ {
+ itemID = m_SortedItems[row]->GetID();
+ lastValidItem = itemID;
+ i++;
+
+ // if we switched sections, then count the section header as a row
+ if (m_Items[itemID]->GetSectionID() != secID)
+ {
+ secID = m_Items[itemID]->GetSectionID();
+ i++;
+ }
+ }
+ else
+ {
+ SetSelectedItem(m_Items[lastValidItem]);
+ m_pScrollBar->SetValue(0);
+ return;
+ }
+ }
+ SetSelectedItem(m_Items[itemID]);
+ ScrollToItem(itemID);
+ return;
+ }
+ else if ( code == KEY_ENTER || nButtonCode == KEY_XBUTTON_A )
+ {
+ Panel *pSelectedItem = m_hSelectedItem;
+ if ( pSelectedItem )
+ {
+ pSelectedItem->OnMousePressed( MOUSE_LEFT );
+ }
+ return;
+ }
+
+ BaseClass::OnKeyCodePressed( code );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Clears the list
+//-----------------------------------------------------------------------------
+void SectionedListPanel::DeleteAllItems()
+{
+ FOR_EACH_LL( m_Items, i )
+ {
+ m_Items[i]->SetVisible(false);
+ m_Items[i]->Clear();
+
+ // don't delete, move to free list
+ int freeIndex = m_FreeItems.AddToTail();
+ m_FreeItems[freeIndex] = m_Items[i];
+ }
+
+ m_Items.RemoveAll();
+ m_SortedItems.RemoveAll();
+ m_hSelectedItem = NULL;
+ InvalidateLayout();
+ m_bSortNeeded = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Changes the current list selection
+//-----------------------------------------------------------------------------
+void SectionedListPanel::SetSelectedItem(CItemButton *item)
+{
+ if (m_hSelectedItem.Get() == item)
+ return;
+
+ // deselect the current item
+ if (m_hSelectedItem.Get())
+ {
+ m_hSelectedItem->SetSelected(false);
+ }
+
+ // set the new item
+ m_hSelectedItem = item;
+ if (m_hSelectedItem.Get())
+ {
+ m_hSelectedItem->SetSelected(true);
+ }
+
+ Repaint();
+ PostActionSignal(new KeyValues("ItemSelected", "itemID", m_hSelectedItem.Get() ? m_hSelectedItem->GetID() : -1));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int SectionedListPanel::GetSelectedItem()
+{
+ if (m_hSelectedItem.Get())
+ {
+ return m_hSelectedItem->GetID();
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets which item is currently selected
+//-----------------------------------------------------------------------------
+void SectionedListPanel::SetSelectedItem(int itemID)
+{
+ if ( m_Items.IsValidIndex(itemID) )
+ {
+ SetSelectedItem(m_Items[itemID]);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the data of a selected item
+//-----------------------------------------------------------------------------
+KeyValues *SectionedListPanel::GetItemData(int itemID)
+{
+ Assert(m_Items.IsValidIndex(itemID));
+ if ( !m_Items.IsValidIndex(itemID) )
+ return NULL;
+
+ return m_Items[itemID]->GetData();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns what section an item is in
+//-----------------------------------------------------------------------------
+int SectionedListPanel::GetItemSection(int itemID)
+{
+ if ( !m_Items.IsValidIndex(itemID) )
+ return -1;
+
+ return m_Items[itemID]->GetSectionID();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the itemID is valid for use
+//-----------------------------------------------------------------------------
+bool SectionedListPanel::IsItemIDValid(int itemID)
+{
+ return m_Items.IsValidIndex(itemID);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the itemID is valid for use
+//-----------------------------------------------------------------------------
+int SectionedListPanel::GetHighestItemID()
+{
+ return m_Items.MaxElementIndex();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: item iterators
+//-----------------------------------------------------------------------------
+int SectionedListPanel::GetItemCount()
+{
+ return m_SortedItems.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: item iterators
+//-----------------------------------------------------------------------------
+int SectionedListPanel::GetItemIDFromRow(int row)
+{
+ if ( !m_SortedItems.IsValidIndex(row) )
+ return -1;
+
+ return m_SortedItems[row]->GetID();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the row that this itemID occupies. -1 if the itemID is invalid
+//-----------------------------------------------------------------------------
+int SectionedListPanel::GetRowFromItemID(int itemID)
+{
+ for (int i = 0; i < m_SortedItems.Count(); i++)
+ {
+ if ( m_SortedItems[i]->GetID() == itemID )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the local coordinates of a cell
+//-----------------------------------------------------------------------------
+bool SectionedListPanel::GetCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall)
+{
+ x = y = wide = tall = 0;
+ if ( !IsItemIDValid(itemID) )
+ return false;
+
+ // get the item
+ CItemButton *item = m_Items[itemID];
+
+ if ( !item->IsVisible() )
+ return false;
+
+ //!! ignores column for now
+ item->GetBounds(x, y, wide, tall);
+ item->GetCellBounds(column, x, wide);
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the local coordinates of a section header
+//-----------------------------------------------------------------------------
+bool SectionedListPanel::GetSectionHeaderBounds(int sectionID, int &x, int &y, int &wide, int &tall)
+{
+ x = y = wide = tall = 0;
+ int index = FindSectionIndexByID(sectionID);
+ if (index < 0 || !m_Sections[index].m_pHeader )
+ return false;
+
+ m_Sections[index].m_pHeader->GetBounds( x, y, wide, tall );
+ return true;
+}
+
+//=============================================================================
+// HPE_BEGIN:
+// [menglish] Gets the local coordinates of a cell using the max width for every column
+// Gets the local coordinates of a cell
+//=============================================================================
+
+bool SectionedListPanel::GetMaxCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall)
+{
+ x = y = wide = tall = 0;
+ if ( !IsItemIDValid(itemID) )
+ return false;
+
+ // get the item
+ CItemButton *item = m_Items[itemID];
+
+ if ( !item->IsVisible() )
+ return false;
+
+ //!! ignores column for now
+ item->GetBounds(x, y, wide, tall);
+ item->GetMaxCellBounds(column, x, wide);
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the local coordinates of a cell
+//-----------------------------------------------------------------------------
+bool SectionedListPanel::GetItemBounds(int itemID, int &x, int &y, int &wide, int &tall)
+{
+ x = y = wide = tall = 0;
+ if ( !IsItemIDValid(itemID) )
+ return false;
+
+ // get the item
+ CItemButton *item = m_Items[itemID];
+
+ if ( !item->IsVisible() )
+ return false;
+
+ //!! ignores column for now
+ item->GetBounds(x, y, wide, tall);
+ return true;
+}
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Purpose: forces an item to redraw
+//-----------------------------------------------------------------------------
+void SectionedListPanel::InvalidateItem(int itemID)
+{
+ if ( !IsItemIDValid(itemID) )
+ return;
+
+ m_Items[itemID]->InvalidateLayout();
+ m_Items[itemID]->Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set up a field for editing
+//-----------------------------------------------------------------------------
+void SectionedListPanel::EnterEditMode(int itemID, int column, vgui::Panel *editPanel)
+{
+ m_hEditModePanel = editPanel;
+ m_iEditModeItemID = itemID;
+ m_iEditModeColumn = column;
+ editPanel->SetParent(this);
+ editPanel->SetVisible(true);
+ editPanel->RequestFocus();
+ editPanel->MoveToFront();
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: leaves editing mode
+//-----------------------------------------------------------------------------
+void SectionedListPanel::LeaveEditMode()
+{
+ if (m_hEditModePanel.Get())
+ {
+ InvalidateItem(m_iEditModeItemID);
+ m_hEditModePanel->SetVisible(false);
+ m_hEditModePanel->SetParent((Panel *)NULL);
+ m_hEditModePanel = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if we are currently in inline editing mode
+//-----------------------------------------------------------------------------
+bool SectionedListPanel::IsInEditMode()
+{
+ return (m_hEditModePanel.Get() != NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: list used to match indexes in image columns to image pointers
+//-----------------------------------------------------------------------------
+void SectionedListPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone)
+{
+ m_bDeleteImageListWhenDone = deleteImageListWhenDone;
+ m_pImageList = imageList;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void SectionedListPanel::OnSetFocus()
+{
+ if (m_hSelectedItem.Get())
+ {
+ m_hSelectedItem->RequestFocus();
+ }
+ else
+ {
+ BaseClass::OnSetFocus();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int SectionedListPanel::GetSectionTall()
+{
+ if (m_Sections.Count())
+ {
+ HFont font = m_Sections[0].m_pHeader->GetFont();
+ if (font != INVALID_FONT)
+ {
+ return surface()->GetFontTall(font) + BUTTON_HEIGHT_SPACER;
+ }
+ }
+
+ return BUTTON_HEIGHT_DEFAULT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the size required to fully draw the contents of the panel
+//-----------------------------------------------------------------------------
+void SectionedListPanel::GetContentSize(int &wide, int &tall)
+{
+ // make sure our layout is done
+ if (IsLayoutInvalid())
+ {
+ if (m_bSortNeeded)
+ {
+ ReSortList();
+ m_bSortNeeded = false;
+ }
+ LayoutPanels(m_iContentHeight);
+ }
+
+ wide = GetWide();
+ tall = m_iContentHeight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the index of a new item button
+//-----------------------------------------------------------------------------
+int SectionedListPanel::GetNewItemButton()
+{
+ int itemID = m_Items.AddToTail();
+ if (m_FreeItems.Count())
+ {
+ // reusing an existing CItemButton
+ m_Items[itemID] = m_FreeItems[m_FreeItems.Head()];
+ m_Items[itemID]->SetID(itemID);
+ m_Items[itemID]->SetVisible(true);
+ m_FreeItems.Remove(m_FreeItems.Head());
+ }
+ else
+ {
+ // create a new CItemButton
+ m_Items[itemID] = SETUP_PANEL(new CItemButton(this, itemID));
+ m_Items[itemID]->SetShowColumns( m_bShowColumns );
+ }
+
+ // Gross. Le's hope this isn't the only property that doesn't get defaulted
+ // properly when an item is recycled.....
+ m_Items[itemID]->SetEnabled( true );
+
+ return itemID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns fallback font to use for text image for this column
+// Input : sectionID -
+// columnIndex -
+// Output : virtual HFont
+//-----------------------------------------------------------------------------
+HFont SectionedListPanel::GetColumnFallbackFontBySection( int sectionID, int columnIndex )
+{
+ int index = FindSectionIndexByID(sectionID);
+ if (index < 0)
+ return INVALID_FONT;
+
+ if (columnIndex >= m_Sections[index].m_Columns.Size())
+ return INVALID_FONT;
+
+ return m_Sections[index].m_Columns[columnIndex].m_hFallbackFont;
+}
diff --git a/mp/src/vgui2/vgui_controls/Slider.cpp b/mp/src/vgui2/vgui_controls/Slider.cpp
new file mode 100644
index 00000000..9a620448
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/Slider.cpp
@@ -0,0 +1,954 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdio.h>
+#define PROTECTED_THINGS_DISABLE
+
+#include <vgui/MouseCode.h>
+#include <KeyValues.h>
+#include <vgui/IBorder.h>
+#include <vgui/IInput.h>
+#include <vgui/ISystem.h>
+#include <vgui/IScheme.h>
+#include <vgui/ISurface.h>
+#include <vgui/ILocalize.h>
+
+#include <vgui_controls/Slider.h>
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/TextImage.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( Slider );
+
+static const float NOB_SIZE = 8.0f;
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a slider bar with ticks underneath it
+//-----------------------------------------------------------------------------
+Slider::Slider(Panel *parent, const char *panelName ) : BaseClass(parent, panelName)
+{
+ m_bIsDragOnRepositionNob = false;
+ _dragging = false;
+ _value = 0;
+ _range[0] = 0;
+ _range[1] = 0;
+ _buttonOffset = 0;
+ _sliderBorder = NULL;
+ _insetBorder = NULL;
+ m_nNumTicks = 10;
+ _leftCaption = NULL;
+ _rightCaption = NULL;
+
+ _subrange[ 0 ] = 0;
+ _subrange[ 1 ] = 0;
+ m_bUseSubRange = false;
+ m_bInverted = false;
+
+ SetThumbWidth( 8 );
+ RecomputeNobPosFromValue();
+ AddActionSignalTarget(this);
+ SetBlockDragChaining( true );
+}
+
+// This allows the slider to behave like it's larger than what's actually being drawn
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : bEnable -
+// 0 -
+// 100 -
+//-----------------------------------------------------------------------------
+void Slider::SetSliderThumbSubRange( bool bEnable, int nMin /*= 0*/, int nMax /*= 100*/ )
+{
+ m_bUseSubRange = bEnable;
+ _subrange[ 0 ] = nMin;
+ _subrange[ 1 ] = nMax;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the size of the slider bar.
+// Warning less than 30 pixels tall and everything probably won't fit.
+//-----------------------------------------------------------------------------
+void Slider::OnSizeChanged(int wide,int tall)
+{
+ BaseClass::OnSizeChanged(wide,tall);
+
+ RecomputeNobPosFromValue();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the value of the slider to one of the ticks.
+//-----------------------------------------------------------------------------
+void Slider::SetValue(int value, bool bTriggerChangeMessage)
+{
+ int oldValue=_value;
+
+ if ( _range[0] < _range[1] )
+ {
+ if(value<_range[0])
+ {
+ value=_range[0];
+ }
+ if(value>_range[1])
+ {
+ value=_range[1];
+ }
+ }
+ else
+ {
+ if(value<_range[1])
+ {
+ value=_range[1];
+ }
+ if(value>_range[0])
+ {
+ value=_range[0];
+ }
+ }
+
+ _value = value;
+
+ RecomputeNobPosFromValue();
+
+ if (_value != oldValue && bTriggerChangeMessage)
+ {
+ SendSliderMovedMessage();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the value of the slider
+//-----------------------------------------------------------------------------
+int Slider::GetValue()
+{
+ return _value;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Layout the slider before drawing it on screen.
+//-----------------------------------------------------------------------------
+void Slider::PerformLayout()
+{
+ BaseClass::PerformLayout();
+ RecomputeNobPosFromValue();
+
+ if (_leftCaption)
+ {
+ _leftCaption->ResizeImageToContent();
+ }
+ if (_rightCaption)
+ {
+ _rightCaption->ResizeImageToContent();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the nob on the slider in response to changing its value.
+//-----------------------------------------------------------------------------
+void Slider::RecomputeNobPosFromValue()
+{
+ //int wide,tall;
+ //GetPaintSize(wide,tall);
+ int x, y, wide, tall;
+ GetTrackRect( x, y, wide, tall );
+
+ float usevalue = _value;
+ int *userange = &_range[ 0 ];
+ if ( m_bUseSubRange )
+ {
+ userange = &_subrange[ 0 ];
+ usevalue = clamp( _value, _subrange[ 0 ], _subrange[ 1 ] );
+ }
+
+ float fwide=(float)wide;
+ float frange=(float)(userange[1] -userange[0]);
+ float fvalue=(float)(usevalue -userange[0]);
+ float fper = (frange != 0.0f) ? fvalue / frange : 0.0f;
+
+ if ( m_bInverted )
+ fper = 1.0f - fper;
+
+ float freepixels = fwide - _nobSize;
+ float leftpixel = (float)x;
+ float firstpixel = leftpixel + freepixels * fper + 0.5f;
+
+ _nobPos[0]=(int)( firstpixel );
+ _nobPos[1]=(int)( firstpixel + _nobSize );
+
+
+ int rightEdge = x + wide;
+
+ if(_nobPos[1]> rightEdge )
+ {
+ _nobPos[0]=rightEdge-((int)_nobSize);
+ _nobPos[1]=rightEdge;
+ }
+
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sync the slider's value up with the nob's position.
+//-----------------------------------------------------------------------------
+void Slider::RecomputeValueFromNobPos()
+{
+ int value = EstimateValueAtPos( _nobPos[ 0 ], 0 );
+ SetValue( value );
+}
+
+int Slider::EstimateValueAtPos( int localMouseX, int /*localMouseY*/ )
+{
+ int x, y, wide, tall;
+ GetTrackRect( x, y, wide, tall );
+
+ int *userange = &_range[ 0 ];
+ if ( m_bUseSubRange )
+ {
+ userange = &_subrange[ 0 ];
+ }
+
+ float fwide = (float)wide;
+ float fvalue = (float)( _value - userange[0] );
+ float fnob = (float)( localMouseX - x );
+ float freepixels = fwide - _nobSize;
+
+ // Map into reduced range
+ fvalue = freepixels != 0.0f ? fnob / freepixels : 0.0f;
+
+ return (int) (RemapVal( fvalue, 0.0, 1.0, userange[0], userange[1] ));
+}
+
+void Slider::SetInverted( bool bInverted )
+{
+ m_bInverted = bInverted;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Send a message to interested parties when the slider moves
+//-----------------------------------------------------------------------------
+void Slider::SendSliderMovedMessage()
+{
+ // send a changed message
+ KeyValues *pParams = new KeyValues("SliderMoved", "position", _value);
+ pParams->SetPtr( "panel", this );
+ PostActionSignal( pParams );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Send a message to interested parties when the user begins dragging the slider
+//-----------------------------------------------------------------------------
+void Slider::SendSliderDragStartMessage()
+{
+ // send a message
+ KeyValues *pParams = new KeyValues("SliderDragStart", "position", _value);
+ pParams->SetPtr( "panel", this );
+ PostActionSignal( pParams );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Send a message to interested parties when the user ends dragging the slider
+//-----------------------------------------------------------------------------
+void Slider::SendSliderDragEndMessage()
+{
+ // send a message
+ KeyValues *pParams = new KeyValues("SliderDragEnd", "position", _value);
+ pParams->SetPtr( "panel", this );
+ PostActionSignal( pParams );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Slider::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetFgColor(GetSchemeColor("Slider.NobColor", pScheme));
+ // this line is useful for debugging
+ //SetBgColor(GetSchemeColor("0 0 0 255"));
+
+ m_TickColor = pScheme->GetColor( "Slider.TextColor", GetFgColor() );
+ m_TrackColor = pScheme->GetColor( "Slider.TrackColor", GetFgColor() );
+
+#ifdef _X360
+ m_DepressedBgColor = GetSchemeColor("Slider.NobFocusColor", pScheme);
+#endif
+
+ m_DisabledTextColor1 = pScheme->GetColor( "Slider.DisabledTextColor1", GetFgColor() );
+ m_DisabledTextColor2 = pScheme->GetColor( "Slider.DisabledTextColor2", GetFgColor() );
+
+ _sliderBorder = pScheme->GetBorder("ButtonBorder");
+ _insetBorder = pScheme->GetBorder("ButtonDepressedBorder");
+
+ if ( _leftCaption )
+ {
+ _leftCaption->SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional() ));
+ }
+
+ if ( _rightCaption )
+ {
+ _rightCaption->SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional() ));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Slider::GetSettings(KeyValues *outResourceData)
+{
+ BaseClass::GetSettings(outResourceData);
+
+ char buf[256];
+ if (_leftCaption)
+ {
+ _leftCaption->GetUnlocalizedText(buf, sizeof(buf));
+ outResourceData->SetString("leftText", buf);
+ }
+
+ if (_rightCaption)
+ {
+ _rightCaption->GetUnlocalizedText(buf, sizeof(buf));
+ outResourceData->SetString("rightText", buf);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Slider::ApplySettings(KeyValues *inResourceData)
+{
+ BaseClass::ApplySettings(inResourceData);
+
+ const char *left = inResourceData->GetString("leftText", NULL);
+ const char *right = inResourceData->GetString("rightText", NULL);
+
+ int thumbWidth = inResourceData->GetInt("thumbwidth", 0);
+ if (thumbWidth != 0)
+ {
+ SetThumbWidth(thumbWidth);
+ }
+
+ SetTickCaptions(left, right);
+
+ int nNumTicks = inResourceData->GetInt( "numTicks", -1 );
+ if ( nNumTicks >= 0 )
+ {
+ SetNumTicks( nNumTicks );
+ }
+
+ int nCurrentRange[2];
+ GetRange( nCurrentRange[0], nCurrentRange[1] );
+ KeyValues *pRangeMin = inResourceData->FindKey( "rangeMin", false );
+ KeyValues *pRangeMax = inResourceData->FindKey( "rangeMax", false );
+ bool bDoClamp = false;
+ if ( pRangeMin )
+ {
+ _range[0] = inResourceData->GetInt( "rangeMin" );
+ bDoClamp = true;
+ }
+ if ( pRangeMax )
+ {
+ _range[1] = inResourceData->GetInt( "rangeMax" );
+ bDoClamp = true;
+ }
+
+ if ( bDoClamp )
+ {
+ ClampRange();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *Slider::GetDescription()
+{
+ static char buf[1024];
+ Q_snprintf(buf, sizeof(buf), "%s, string leftText, string rightText", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the rectangle to draw the slider track in.
+//-----------------------------------------------------------------------------
+void Slider::GetTrackRect( int& x, int& y, int& w, int& h )
+{
+ int wide, tall;
+ GetPaintSize( wide, tall );
+
+ x = 0;
+ y = 8;
+ w = wide - (int)_nobSize;
+ h = 4;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw everything on screen
+//-----------------------------------------------------------------------------
+void Slider::Paint()
+{
+ DrawTicks();
+
+ DrawTickLabels();
+
+ // Draw nob last so it draws over ticks.
+ DrawNob();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw the ticks below the slider.
+//-----------------------------------------------------------------------------
+void Slider::DrawTicks()
+{
+ int x, y;
+ int wide,tall;
+ GetTrackRect( x, y, wide, tall );
+
+ // Figure out how to draw the ticks
+// GetPaintSize( wide, tall );
+
+ float fwide = (float)wide;
+ float freepixels = fwide - _nobSize;
+
+ float leftpixel = _nobSize / 2.0f;
+
+ float pixelspertick = freepixels / ( m_nNumTicks );
+
+ y += (int)_nobSize;
+ int tickHeight = 5;
+
+ if (IsEnabled())
+ {
+ surface()->DrawSetColor( m_TickColor ); //vgui::Color( 127, 140, 127, 255 ) );
+ for ( int i = 0; i <= m_nNumTicks; i++ )
+ {
+ int xpos = (int)( leftpixel + i * pixelspertick );
+
+ surface()->DrawFilledRect( xpos, y, xpos + 1, y + tickHeight );
+ }
+ }
+ else
+ {
+ surface()->DrawSetColor( m_DisabledTextColor1 ); //vgui::Color( 127, 140, 127, 255 ) );
+ for ( int i = 0; i <= m_nNumTicks; i++ )
+ {
+ int xpos = (int)( leftpixel + i * pixelspertick );
+ surface()->DrawFilledRect( xpos+1, y+1, xpos + 2, y + tickHeight + 1 );
+ }
+ surface()->DrawSetColor( m_DisabledTextColor2 ); //vgui::Color( 127, 140, 127, 255 ) );
+ for ( int i = 0; i <= m_nNumTicks; i++ )
+ {
+ int xpos = (int)( leftpixel + i * pixelspertick );
+ surface()->DrawFilledRect( xpos, y, xpos + 1, y + tickHeight );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw Tick labels under the ticks.
+//-----------------------------------------------------------------------------
+void Slider::DrawTickLabels()
+{
+ int x, y;
+ int wide,tall;
+ GetTrackRect( x, y, wide, tall );
+
+ // Figure out how to draw the ticks
+// GetPaintSize( wide, tall );
+ y += (int)NOB_SIZE + 4;
+
+ // Draw Start and end range values
+ if (IsEnabled())
+ surface()->DrawSetTextColor( m_TickColor ); //vgui::Color( 127, 140, 127, 255 ) );
+ else
+ surface()->DrawSetTextColor( m_DisabledTextColor1 ); //vgui::Color( 127, 140, 127, 255 ) );
+
+
+ if ( _leftCaption != NULL )
+ {
+ _leftCaption->SetPos(0, y);
+ if (IsEnabled())
+ {
+ _leftCaption->SetColor( m_TickColor );
+ }
+ else
+ {
+ _leftCaption->SetColor( m_DisabledTextColor1 );
+ }
+
+ _leftCaption->Paint();
+ }
+
+ if ( _rightCaption != NULL)
+ {
+ int rwide, rtall;
+ _rightCaption->GetSize(rwide, rtall);
+ _rightCaption->SetPos((int)(wide - rwide) , y);
+ if (IsEnabled())
+ {
+ _rightCaption->SetColor( m_TickColor );
+ }
+ else
+ {
+ _rightCaption->SetColor( m_DisabledTextColor1 );
+ }
+
+ _rightCaption->Paint();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw the nob part of the slider.
+//-----------------------------------------------------------------------------
+void Slider::DrawNob()
+{
+ // horizontal nob
+ int x, y;
+ int wide,tall;
+ GetTrackRect( x, y, wide, tall );
+ Color col = GetFgColor();
+#ifdef _X360
+ if(HasFocus())
+ {
+ col = m_DepressedBgColor;
+ }
+#endif
+ surface()->DrawSetColor(col);
+
+ int nobheight = 16;
+
+ surface()->DrawFilledRect(
+ _nobPos[0],
+ y + tall / 2 - nobheight / 2,
+ _nobPos[1],
+ y + tall / 2 + nobheight / 2);
+ // border
+ if (_sliderBorder)
+ {
+ _sliderBorder->Paint(
+ _nobPos[0],
+ y + tall / 2 - nobheight / 2,
+ _nobPos[1],
+ y + tall / 2 + nobheight / 2);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the text labels of the Start and end ticks.
+//-----------------------------------------------------------------------------
+void Slider::SetTickCaptions( const char *left, const char *right )
+{
+ if (left)
+ {
+ if (_leftCaption)
+ {
+ _leftCaption->SetText(left);
+ }
+ else
+ {
+ _leftCaption = new TextImage(left);
+ }
+ }
+ if (right)
+ {
+ if (_rightCaption)
+ {
+ _rightCaption->SetText(right);
+ }
+ else
+ {
+ _rightCaption = new TextImage(right);
+ }
+ }
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the text labels of the Start and end ticks.
+//-----------------------------------------------------------------------------
+void Slider::SetTickCaptions( const wchar_t *left, const wchar_t *right )
+{
+ if (left)
+ {
+ if (_leftCaption)
+ {
+ _leftCaption->SetText(left);
+ }
+ else
+ {
+ _leftCaption = new TextImage(left);
+ }
+ }
+ if (right)
+ {
+ if (_rightCaption)
+ {
+ _rightCaption->SetText(right);
+ }
+ else
+ {
+ _rightCaption = new TextImage(right);
+ }
+ }
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw the slider track
+//-----------------------------------------------------------------------------
+void Slider::PaintBackground()
+{
+ BaseClass::PaintBackground();
+
+ int x, y;
+ int wide,tall;
+
+ GetTrackRect( x, y, wide, tall );
+
+ surface()->DrawSetColor( m_TrackColor );
+ surface()->DrawFilledRect( x, y, x + wide, y + tall );
+ if (_insetBorder)
+ {
+ _insetBorder->Paint( x, y, x + wide, y + tall );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the range of the slider.
+//-----------------------------------------------------------------------------
+void Slider::SetRange(int min,int max)
+{
+ _range[0]=min;
+ _range[1]=max;
+
+ ClampRange();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sanity check and clamp the range if necessary.
+//-----------------------------------------------------------------------------
+void Slider::ClampRange()
+{
+ if ( _range[0] < _range[1] )
+ {
+ if(_value<_range[0])
+ {
+ SetValue( _range[0], false );
+ }
+ else if( _value>_range[1])
+ {
+ SetValue( _range[1], false );
+ }
+ }
+ else
+ {
+ if(_value<_range[1])
+ {
+ SetValue( _range[1], false );
+ }
+ else if( _value>_range[0])
+ {
+ SetValue( _range[0], false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the max and min values of the slider
+//-----------------------------------------------------------------------------
+void Slider::GetRange(int& min,int& max)
+{
+ min=_range[0];
+ max=_range[1];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Respond when the cursor is moved in our window if we are clicking
+// and dragging.
+//-----------------------------------------------------------------------------
+void Slider::OnCursorMoved(int x,int y)
+{
+ if(!_dragging)
+ {
+ return;
+ }
+
+// input()->GetCursorPos(x,y);
+ input()->GetCursorPosition( x, y );
+ ScreenToLocal(x,y);
+
+// int wide,tall;
+// GetPaintSize(wide,tall);
+ int _x, _y, wide, tall;
+ GetTrackRect( _x, _y, wide, tall );
+
+ _nobPos[0]=_nobDragStartPos[0]+(x-_dragStartPos[0]);
+ _nobPos[1]=_nobDragStartPos[1]+(x-_dragStartPos[0]);
+
+ int rightEdge = _x +wide;
+ int unclamped = _nobPos[ 0 ];
+
+ if(_nobPos[1]>rightEdge)
+ {
+ _nobPos[0]=rightEdge-(_nobPos[1]-_nobPos[0]);
+ _nobPos[1]=rightEdge;
+ }
+
+ if(_nobPos[0]<_x)
+ {
+ int offset = _x - _nobPos[0];
+ _nobPos[1]=_nobPos[1]-offset;
+ _nobPos[0]=0;
+ }
+
+ int value = EstimateValueAtPos( unclamped, 0 );
+ SetValue( value );
+
+ // RecomputeValueFromNobPos();
+ Repaint();
+ SendSliderMovedMessage();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If you click on the slider outside of the nob, the nob jumps
+// to the click position, and if this setting is enabled, the nob
+// is then draggable from the new position until the mouse is released
+// Input : state -
+//-----------------------------------------------------------------------------
+void Slider::SetDragOnRepositionNob( bool state )
+{
+ m_bIsDragOnRepositionNob = state;
+}
+
+bool Slider::IsDragOnRepositionNob() const
+{
+ return m_bIsDragOnRepositionNob;
+}
+
+bool Slider::IsDragged( void ) const
+{
+ return _dragging;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Respond to mouse presses. Trigger Record staring positon.
+//-----------------------------------------------------------------------------
+void Slider::OnMousePressed(MouseCode code)
+{
+ int x,y;
+
+ if (!IsEnabled())
+ return;
+
+// input()->GetCursorPos(x,y);
+ input()->GetCursorPosition( x, y );
+
+ ScreenToLocal(x,y);
+ RequestFocus();
+
+ bool startdragging = false, bPostDragStartSignal = false;
+
+ if ((x >= _nobPos[0]) && (x < _nobPos[1]))
+ {
+ startdragging = true;
+ bPostDragStartSignal = true;
+ }
+ else
+ {
+ // we clicked elsewhere on the slider; move the nob to that position
+ int min, max;
+ GetRange(min, max);
+ if ( m_bUseSubRange )
+ {
+ min = _subrange[ 0 ];
+ max = _subrange[ 1 ];
+ }
+
+// int wide = GetWide();
+ int _x, _y, wide, tall;
+ GetTrackRect( _x, _y, wide, tall );
+ if ( wide > 0 )
+ {
+ float frange = ( float )( max - min );
+ float clickFrac = clamp( ( float )( x - _x ) / (float)( wide - 1 ), 0.0f, 1.0f );
+
+ float value = (float)min + clickFrac * frange;
+
+ startdragging = IsDragOnRepositionNob();
+
+ if ( startdragging )
+ {
+ _dragging = true; // Required when as
+ SendSliderDragStartMessage();
+ }
+
+ SetValue( ( int )( value + 0.5f ) );
+ }
+ }
+
+ if ( startdragging )
+ {
+ // drag the nob
+ _dragging = true;
+ input()->SetMouseCapture(GetVPanel());
+ _nobDragStartPos[0] = _nobPos[0];
+ _nobDragStartPos[1] = _nobPos[1];
+ _dragStartPos[0] = x;
+ _dragStartPos[1] = y;
+ }
+
+ if ( bPostDragStartSignal )
+ SendSliderDragStartMessage();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Just handle double presses like mouse presses
+//-----------------------------------------------------------------------------
+void Slider::OnMouseDoublePressed(MouseCode code)
+{
+ OnMousePressed(code);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#ifdef _X360
+void Slider::OnKeyCodePressed(KeyCode code)
+{
+ switch ( GetBaseButtonCode( code ) )
+ {
+ case KEY_XBUTTON_LEFT:
+ case KEY_XSTICK1_LEFT:
+ case KEY_XSTICK2_LEFT:
+ SetValue(GetValue() - 1);
+ break;
+ case KEY_XBUTTON_RIGHT:
+ case KEY_XSTICK1_RIGHT:
+ case KEY_XSTICK2_RIGHT:
+ SetValue(GetValue() + 1);
+ break;
+ default:
+ BaseClass::OnKeyCodePressed(code);
+ break;
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle key presses
+//-----------------------------------------------------------------------------
+void Slider::OnKeyCodeTyped(KeyCode code)
+{
+ switch (code)
+ {
+ // for now left and right arrows just open or close submenus if they are there.
+ case KEY_LEFT:
+ case KEY_DOWN:
+ {
+ int val = GetValue();
+ SetValue(val-1);
+ break;
+ }
+ case KEY_RIGHT:
+ case KEY_UP:
+ {
+ int val = GetValue();
+ SetValue(val+1);
+ break;
+ }
+ case KEY_PAGEDOWN:
+ {
+ int min, max;
+ GetRange(min, max);
+ float range = (float) max-min;
+ float pertick = range/m_nNumTicks;
+ int val = GetValue();
+ SetValue(val - (int) pertick);
+ break;
+ }
+ case KEY_PAGEUP:
+ {
+ int min, max;
+ GetRange(min, max);
+ float range = (float) max-min;
+ float pertick = range/m_nNumTicks;
+ int val = GetValue();
+ SetValue(val + (int) pertick);
+ break;
+ }
+ case KEY_HOME:
+ {
+ int min, max;
+ GetRange(min, max);
+ SetValue(min);
+ break;
+ }
+ case KEY_END:
+ {
+ int min, max;
+ GetRange(min, max);
+ SetValue(max);
+ break;
+ }
+ default:
+ BaseClass::OnKeyCodeTyped(code);
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop dragging when the mouse is released.
+//-----------------------------------------------------------------------------
+void Slider::OnMouseReleased(MouseCode code)
+{
+ if ( _dragging )
+ {
+ _dragging=false;
+ input()->SetMouseCapture(null);
+ }
+
+ if ( IsEnabled() )
+ {
+ SendSliderDragEndMessage();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the nob's position (the ends of each side of the nob)
+//-----------------------------------------------------------------------------
+void Slider::GetNobPos(int& min, int& max)
+{
+ min=_nobPos[0];
+ max=_nobPos[1];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Slider::SetButtonOffset(int buttonOffset)
+{
+ _buttonOffset=buttonOffset;
+}
+
+void Slider::SetThumbWidth( int width )
+{
+ _nobSize = (float)width;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the number of ticks that appear under the slider.
+//-----------------------------------------------------------------------------
+void Slider::SetNumTicks( int ticks )
+{
+ m_nNumTicks = ticks;
+}
diff --git a/mp/src/vgui2/vgui_controls/Splitter.cpp b/mp/src/vgui2/vgui_controls/Splitter.cpp
new file mode 100644
index 00000000..fc127a38
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/Splitter.cpp
@@ -0,0 +1,765 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include <vgui/IScheme.h>
+#include <vgui/Cursor.h>
+#include <vgui/IInput.h>
+#include <vgui_controls/Splitter.h>
+#include "tier1/KeyValues.h"
+#include <limits.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+
+enum
+{
+ SPLITTER_HANDLE_WIDTH = 4
+};
+
+
+//-----------------------------------------------------------------------------
+// Splitter handle
+//-----------------------------------------------------------------------------
+namespace vgui
+{
+
+class SplitterHandle : public Panel
+{
+ DECLARE_CLASS_SIMPLE( SplitterHandle, Panel );
+
+public:
+ SplitterHandle( Splitter *parent, const char *name, SplitterMode_t mode, int nIndex );
+ ~SplitterHandle();
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+ virtual void OnMousePressed( MouseCode code );
+ virtual void OnMouseReleased( MouseCode code );
+ virtual void OnCursorMoved( int x, int y );
+ virtual void OnMouseDoublePressed( MouseCode code );
+
+private:
+ SplitterMode_t m_nMode;
+ int m_nIndex;
+ bool m_bDragging;
+};
+
+} // end namespace vgui
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+SplitterHandle::SplitterHandle( Splitter *parent, const char *name, SplitterMode_t mode, int nIndex ) : BaseClass( parent, name )
+{
+ int w, h;
+ parent->GetSize( w, h );
+
+ if ( mode == SPLITTER_MODE_HORIZONTAL )
+ {
+ SetSize( w, SPLITTER_HANDLE_WIDTH );
+ SetCursor( dc_sizens );
+ }
+ else
+ {
+ SetSize( SPLITTER_HANDLE_WIDTH, h );
+ SetCursor( dc_sizewe );
+ }
+
+ SetVisible( true );
+ SetPaintBackgroundEnabled( false );
+ SetPaintEnabled( false );
+ SetPaintBorderEnabled( true );
+ m_bDragging = false;
+ m_nIndex = nIndex;
+ m_nMode = mode;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+SplitterHandle::~SplitterHandle()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Scheme settings
+//-----------------------------------------------------------------------------
+void SplitterHandle::ApplySchemeSettings(IScheme *pScheme)
+{
+ // Cache off background color stored in SetSplitterColor
+ Color c = GetBgColor();
+ SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
+ BaseClass::ApplySchemeSettings(pScheme);
+ SetBgColor( c );
+}
+
+
+//-----------------------------------------------------------------------------
+// Capture mouse when dragging
+//-----------------------------------------------------------------------------
+void SplitterHandle::OnMousePressed(MouseCode code)
+{
+ if ( !m_bDragging )
+ {
+ input()->SetMouseCapture(GetVPanel());
+ m_bDragging = true;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Release mouse capture when finished dragging
+//-----------------------------------------------------------------------------
+void SplitterHandle::OnMouseReleased(MouseCode code)
+{
+ if ( m_bDragging )
+ {
+ input()->SetMouseCapture(NULL);
+ m_bDragging = false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// While dragging, update the splitter position
+//-----------------------------------------------------------------------------
+void SplitterHandle::OnCursorMoved(int x, int y)
+{
+ if (m_bDragging)
+ {
+ input()->GetCursorPos( x, y );
+ Splitter *pSplitter = assert_cast<Splitter*>( GetParent() );
+ pSplitter->ScreenToLocal( x,y );
+ pSplitter->SetSplitterPosition( m_nIndex, (m_nMode == SPLITTER_MODE_HORIZONTAL) ? y : x );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Double-click: make both panels on either side of the splitter equal size
+//-----------------------------------------------------------------------------
+void SplitterHandle::OnMouseDoublePressed( MouseCode code )
+{
+ Splitter *pSplitter = assert_cast<Splitter*>( GetParent() );
+ pSplitter->EvenlyRespaceSplitters();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Returns a panel that chains user configs
+//-----------------------------------------------------------------------------
+namespace vgui
+{
+
+class SplitterChildPanel : public EditablePanel
+{
+ DECLARE_CLASS_SIMPLE( SplitterChildPanel, EditablePanel );
+
+public:
+ SplitterChildPanel( Panel *parent, const char *panelName ) : BaseClass( parent, panelName )
+ {
+ SetPaintBackgroundEnabled( false );
+ SetPaintEnabled( false );
+ SetPaintBorderEnabled( false );
+ }
+
+ virtual ~SplitterChildPanel() {}
+
+ // Children may have user config settings
+ bool HasUserConfigSettings()
+ {
+ return true;
+ }
+};
+
+} // end namespace vgui
+
+//-----------------------------------------------------------------------------
+//
+// Splitter panel
+//
+//-----------------------------------------------------------------------------
+vgui::Panel *Splitter_V_Factory()
+{
+ return new Splitter( NULL, NULL, SPLITTER_MODE_VERTICAL, 1 );
+}
+
+vgui::Panel *Splitter_H_Factory()
+{
+ return new Splitter( NULL, NULL, SPLITTER_MODE_HORIZONTAL, 1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+Splitter::Splitter( Panel *parent, const char *name, SplitterMode_t mode, int nCount ) : BaseClass( parent, name )
+{
+ Assert( nCount >= 1 );
+ m_Mode = mode;
+
+ SetPaintBackgroundEnabled( false );
+ SetPaintEnabled( false );
+ SetPaintBorderEnabled( false );
+
+ RecreateSplitters( nCount );
+
+ EvenlyRespaceSplitters();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+Splitter::~Splitter()
+{
+ m_Splitters.RemoveAll();
+}
+
+void Splitter::RecreateSplitters( int nCount )
+{
+ int i;
+ int c = m_Splitters.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ delete m_Splitters[ i ].m_pPanel;
+ delete m_Splitters[ i ].m_pHandle;
+ }
+ m_Splitters.RemoveAll();
+
+ for ( i = 0; i < (nCount + 1); ++i )
+ {
+ char pBuffer[512];
+ Q_snprintf( pBuffer, sizeof(pBuffer), "child%d", i );
+
+ int nIndex = m_Splitters.AddToTail( );
+ SplitterChildPanel *pEditablePanel = new SplitterChildPanel( this, pBuffer );
+ m_Splitters[nIndex].m_pPanel = pEditablePanel;
+ m_Splitters[nIndex].m_bLocked = false;
+ m_Splitters[nIndex].m_nLockedSize = 0;
+ }
+
+ // We do this in 2 loops so that the first N children are actual child panels
+ for ( i = 0; i < nCount; ++i )
+ {
+ SplitterHandle *pHandle = new SplitterHandle( this, "SplitterHandle", m_Mode, i );
+ m_Splitters[i].m_pHandle = pHandle;
+ pHandle->MoveToFront();
+ }
+ m_Splitters[nCount].m_pHandle = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the splitter color
+//-----------------------------------------------------------------------------
+void Splitter::SetSplitterColor( Color c )
+{
+ int nCount = m_Splitters.Count() - 1;
+ if ( c.a() != 0 )
+ {
+ for ( int i = 0; i < nCount; ++i )
+ {
+ m_Splitters[i].m_pHandle->SetBgColor( c );
+ m_Splitters[i].m_pHandle->SetPaintBackgroundEnabled( true );
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < nCount; ++i )
+ {
+ m_Splitters[i].m_pHandle->SetPaintBackgroundEnabled( false );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Enables borders on the splitters
+//-----------------------------------------------------------------------------
+void Splitter::EnableBorders( bool bEnable )
+{
+ int nCount = m_Splitters.Count() - 1;
+ for ( int i = 0; i < nCount; ++i )
+ {
+ m_Splitters[i].m_pHandle->SetPaintBorderEnabled( bEnable );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// controls splitters
+//-----------------------------------------------------------------------------
+int Splitter::GetSplitterCount() const
+{
+ return m_Splitters.Count() - 1;
+}
+
+
+//-----------------------------------------------------------------------------
+// controls splitters
+//-----------------------------------------------------------------------------
+int Splitter::GetSubPanelCount() const
+{
+ return m_Splitters.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies resouce settings
+//-----------------------------------------------------------------------------
+void Splitter::ApplySettings(KeyValues *inResourceData)
+{
+ BaseClass::ApplySettings(inResourceData);
+
+ // Look for splitter positions
+ int nSplitterCount = GetSplitterCount();
+ for ( int i = 0; i < nSplitterCount; ++i )
+ {
+ char pBuffer[512];
+ Q_snprintf( pBuffer, sizeof(pBuffer), "splitter%d", i );
+
+ int nSplitterPos = inResourceData->GetInt( pBuffer , -1 );
+ if ( nSplitterPos >= 0 )
+ {
+ SetSplitterPosition( i, nSplitterPos );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int Splitter::GetPosRange()
+{
+ int w, h;
+ GetSize( w, h );
+ int nPosRange = (m_Mode == SPLITTER_MODE_HORIZONTAL) ? h : w;
+ return nPosRange;
+}
+
+
+//-----------------------------------------------------------------------------
+// Locks the size of a particular child in pixels.
+//-----------------------------------------------------------------------------
+void Splitter::LockChildSize( int nChildIndex, int nSize )
+{
+ Assert( nChildIndex < m_Splitters.Count() );
+ SplitterInfo_t &info = m_Splitters[nChildIndex];
+ nSize += SPLITTER_HANDLE_WIDTH;
+ if ( !info.m_bLocked || (info.m_nLockedSize != nSize) )
+ {
+ float flPrevPos = (nChildIndex > 0) ? m_Splitters[nChildIndex-1].m_flPos : 0.0f;
+ float flOldSize = info.m_flPos - flPrevPos;
+ float flDelta = nSize - flOldSize;
+ int nCount = m_Splitters.Count();
+ for ( int i = nChildIndex; i < nCount-1; ++i )
+ {
+ m_Splitters[i].m_flPos += flDelta;
+ }
+ m_Splitters[nCount-1].m_flPos = GetPosRange();
+
+ info.m_bLocked = true;
+ info.m_nLockedSize = nSize;
+ InvalidateLayout();
+ }
+}
+
+void Splitter::UnlockChildSize( int nChildIndex )
+{
+ Assert( nChildIndex < m_Splitters.Count() );
+ SplitterInfo_t &info = m_Splitters[nChildIndex];
+ if ( info.m_bLocked )
+ {
+ info.m_bLocked = false;
+
+ float flPrevPos = (nChildIndex > 0) ? m_Splitters[nChildIndex-1].m_flPos : 0.0f;
+ float flBelowSize = GetPosRange() - flPrevPos;
+
+ int nLockedSize = ComputeLockedSize( nChildIndex + 1 );
+ int nUnlockedCount = 1;
+ int nCount = m_Splitters.Count();
+ for ( int i = nChildIndex + 1; i < nCount; ++i )
+ {
+ if ( !m_Splitters[i].m_bLocked )
+ {
+ ++nUnlockedCount;
+ }
+ }
+
+ float flUnlockedSize = ( flBelowSize - nLockedSize ) / nUnlockedCount;
+
+ for ( int i = nChildIndex; i < nCount; ++i )
+ {
+ if ( !m_Splitters[i].m_bLocked )
+ {
+ m_Splitters[i].m_flPos = flPrevPos + flUnlockedSize;
+ }
+ else
+ {
+ m_Splitters[i].m_flPos = flPrevPos + m_Splitters[i].m_nLockedSize;
+ }
+ flPrevPos = m_Splitters[i].m_flPos;
+ }
+ InvalidateLayout();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Called when size changes
+//-----------------------------------------------------------------------------
+void Splitter::OnSizeChanged( int newWide, int newTall )
+{
+ BaseClass::OnSizeChanged( newWide, newTall );
+
+ // Don't resize if it's degenerate and won't show up anyway...
+ if ( newTall <= 0 || newWide <= 0 )
+ return;
+
+ int nLockedSize = 0;
+ float flUnlockedSize = 0.0f;
+ int nCount = m_Splitters.Count();
+ float flLastPos = 0.0f;
+ int nUnlockedCount = 0;
+ for ( int i = 0; i < nCount; ++i )
+ {
+ SplitterInfo_t &info = m_Splitters[i];
+ if ( info.m_bLocked )
+ {
+ nLockedSize += info.m_nLockedSize;
+ }
+ else
+ {
+ ++nUnlockedCount;
+ flUnlockedSize += info.m_flPos - flLastPos;
+ }
+ flLastPos = info.m_flPos;
+ }
+
+ int nNewTotalSize = (m_Mode == SPLITTER_MODE_HORIZONTAL) ? newTall : newWide;
+ int nNewUnlockedSize = nNewTotalSize - nLockedSize;
+ if ( nNewUnlockedSize < nUnlockedCount * SPLITTER_HANDLE_WIDTH )
+ {
+ nNewUnlockedSize = nUnlockedCount * SPLITTER_HANDLE_WIDTH;
+ }
+
+ float flRatio = nNewUnlockedSize / flUnlockedSize;
+ float flLastPrevPos = 0.0f;
+ flLastPos = 0.0f;
+ for ( int i = 0; i < nCount - 1; ++i )
+ {
+ SplitterInfo_t &info = m_Splitters[i];
+ if ( info.m_bLocked )
+ {
+ flLastPrevPos = info.m_flPos;
+ info.m_flPos = flLastPos + info.m_nLockedSize;
+ }
+ else
+ {
+ float flNewSize = info.m_flPos - flLastPrevPos;
+ flNewSize *= flRatio;
+ flLastPrevPos = info.m_flPos;
+ info.m_flPos = flLastPos + flNewSize;
+ }
+ flLastPos = info.m_flPos;
+ }
+
+ // Clamp the bottom to 1.0
+ m_Splitters[nCount-1].m_flPos = nNewTotalSize;
+}
+
+
+//-----------------------------------------------------------------------------
+// Splitter position
+//-----------------------------------------------------------------------------
+int Splitter::GetSplitterPosition( int nIndex )
+{
+ return (int)( m_Splitters[nIndex].m_flPos + 0.5f );
+}
+
+void Splitter::SetSplitterPosition( int nIndex, int nPos )
+{
+ int nPosRange = GetPosRange();
+ if ( nPosRange == 0 )
+ return;
+
+ // If we're locked to a sibling, move the previous sibling first
+ while ( ( nIndex >= 0 ) && m_Splitters[nIndex].m_bLocked )
+ {
+ nPos -= m_Splitters[nIndex].m_nLockedSize;
+ --nIndex;
+ }
+ if ( nIndex < 0 )
+ return;
+
+ // Clamp to the valid positional range
+ int i;
+ int nMinPos = 0;
+ for ( i = 0; i < nIndex; ++i )
+ {
+ if ( !m_Splitters[i].m_bLocked )
+ {
+ nMinPos += SPLITTER_HANDLE_WIDTH;
+ }
+ else
+ {
+ nMinPos += m_Splitters[i].m_nLockedSize;
+ }
+ }
+
+ int nMaxPos = nPosRange - SPLITTER_HANDLE_WIDTH;
+ int c = GetSplitterCount();
+ for ( i = nIndex + 1; i < c; ++i )
+ {
+ if ( !m_Splitters[i].m_bLocked )
+ {
+ nMaxPos -= SPLITTER_HANDLE_WIDTH;
+ }
+ else
+ {
+ nMaxPos -= m_Splitters[i].m_nLockedSize;
+ }
+ }
+ nPos = clamp( nPos, nMinPos, nMaxPos );
+
+ m_Splitters[nIndex].m_flPos = nPos;
+ int p = nPos;
+ for ( i = nIndex - 1 ; i >= 0; --i )
+ {
+ int nMinPrevPos;
+ int nMaxPrevPos;
+ if ( !m_Splitters[i+1].m_bLocked )
+ {
+ nMinPrevPos = -INT_MAX;
+ nMaxPrevPos = nPos - SPLITTER_HANDLE_WIDTH;
+ }
+ else
+ {
+ nMinPrevPos = nMaxPrevPos = p - m_Splitters[i+1].m_nLockedSize;
+ }
+
+ int nCurPos = GetSplitterPosition( i );
+ if ( nMaxPrevPos < nCurPos || nMinPrevPos > nCurPos )
+ {
+ m_Splitters[ i ].m_flPos = nMaxPrevPos;
+ p = nMaxPrevPos;
+ }
+ else
+ {
+ p = m_Splitters[ i ].m_flPos;
+ }
+ }
+
+ for ( i = nIndex + 1 ; i < c; ++i )
+ {
+ int nMinNextPos;
+ int nMaxNextPos;
+ if ( !m_Splitters[i].m_bLocked )
+ {
+ nMinNextPos = nPos + SPLITTER_HANDLE_WIDTH;
+ nMaxNextPos = INT_MAX;
+ }
+ else
+ {
+ nMinNextPos = nMaxNextPos = nPos + m_Splitters[i].m_nLockedSize;
+ }
+
+ int nCurPos = GetSplitterPosition( i );
+ if ( nMinNextPos > nCurPos || nMaxNextPos < nCurPos )
+ {
+ m_Splitters[ i ].m_flPos = nMinNextPos;
+ nPos = nMinNextPos;
+ }
+ else
+ {
+ nPos = m_Splitters[ i ].m_flPos;
+ }
+ }
+
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the locked size
+//-----------------------------------------------------------------------------
+int Splitter::ComputeLockedSize( int nStartingIndex )
+{
+ int nLockedSize = 0;
+ int nCount = m_Splitters.Count();
+ for ( int i = nStartingIndex; i < nCount; ++i )
+ {
+ if ( m_Splitters[i].m_bLocked )
+ {
+ nLockedSize += m_Splitters[i].m_nLockedSize;
+ }
+ }
+ return nLockedSize;
+}
+
+
+//-----------------------------------------------------------------------------
+// Evenly respaces all the splitters
+//-----------------------------------------------------------------------------
+void Splitter::EvenlyRespaceSplitters( )
+{
+ int nSplitterCount = GetSubPanelCount();
+ if ( nSplitterCount == 0 )
+ return;
+
+ int nLockedSize = ComputeLockedSize( 0 );
+ float flUnlockedSize = (float)( GetPosRange() - nLockedSize );
+ float flDPos = flUnlockedSize / (float)nSplitterCount;
+ if ( flDPos < SPLITTER_HANDLE_WIDTH )
+ {
+ flDPos = SPLITTER_HANDLE_WIDTH;
+ }
+ float flPos = 0.0f;
+ for ( int i = 0; i < nSplitterCount; ++i )
+ {
+ if ( !m_Splitters[i].m_bLocked )
+ {
+ flPos += flDPos;
+ }
+ else
+ {
+ flPos += m_Splitters[i].m_nLockedSize;
+ }
+ m_Splitters[i].m_flPos = flPos;
+ }
+
+ InvalidateLayout();
+}
+
+void Splitter::RespaceSplitters( float *flFractions )
+{
+ int nSplitterCount = GetSubPanelCount();
+ if ( nSplitterCount == 0 )
+ return;
+
+ float flPos = 0.0f;
+ int nPosRange = GetPosRange();
+ for ( int i = 0; i < nSplitterCount; ++i )
+ {
+ flPos += flFractions[i];
+ m_Splitters[i].m_flPos = flPos * nPosRange;
+ }
+
+ Assert( flPos == 1.0f );
+
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: sets user settings
+//-----------------------------------------------------------------------------
+void Splitter::ApplyUserConfigSettings(KeyValues *userConfig)
+{
+ BaseClass::ApplyUserConfigSettings( userConfig );
+
+ // read the splitter sizes
+ int c = m_Splitters.Count();
+ float *pFractions = (float*)_alloca( c * sizeof(float) );
+ float flTotalSize = 0.0f;
+ for ( int i = 0; i < c; i++ )
+ {
+ char name[128];
+ _snprintf(name, sizeof(name), "%d_splitter_pos", i);
+ pFractions[i] = userConfig->GetFloat( name, flTotalSize + SPLITTER_HANDLE_WIDTH + 1 );
+ flTotalSize = pFractions[i];
+ }
+
+ if ( flTotalSize != 0.0f )
+ {
+ int nPosRange = GetPosRange();
+ for ( int i = 0; i < c; ++i )
+ {
+ pFractions[i] /= flTotalSize;
+ m_Splitters[i].m_flPos = pFractions[i] * nPosRange;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns user config settings for this control
+//-----------------------------------------------------------------------------
+void Splitter::GetUserConfigSettings(KeyValues *userConfig)
+{
+ BaseClass::GetUserConfigSettings( userConfig );
+
+ // save which columns are hidden
+ int c = m_Splitters.Count();
+ for ( int i = 0 ; i < c; i++ )
+ {
+ char name[128];
+ _snprintf(name, sizeof(name), "%d_splitter_pos", i);
+ userConfig->SetFloat( name, m_Splitters[i].m_flPos );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Called to perform layout
+//-----------------------------------------------------------------------------
+void Splitter::PerformLayout( )
+{
+ BaseClass::PerformLayout();
+
+ int nSplitterCount = GetSubPanelCount();
+ if ( nSplitterCount == 0 )
+ return;
+
+ int w, h;
+ GetSize( w, h );
+
+ int nLastPos = 0;
+ for ( int i = 0; i < nSplitterCount; ++i )
+ {
+ Panel *pChild = m_Splitters[i].m_pPanel;
+ SplitterHandle *pHandle = m_Splitters[i].m_pHandle;
+ int nSplitterPos = (int)( m_Splitters[i].m_flPos + 0.5f );
+
+ if ( m_Mode == SPLITTER_MODE_HORIZONTAL )
+ {
+ pChild->SetPos( 0, nLastPos );
+ pChild->SetSize( w, nSplitterPos - nLastPos );
+ if ( pHandle )
+ {
+ pHandle->SetPos( 0, nSplitterPos );
+ pHandle->SetSize( w, SPLITTER_HANDLE_WIDTH );
+ }
+ }
+ else
+ {
+ pChild->SetPos( nLastPos, 0 );
+ pChild->SetSize( nSplitterPos - nLastPos, h );
+ if ( pHandle )
+ {
+ pHandle->SetPos( nSplitterPos, 0 );
+ pHandle->SetSize( SPLITTER_HANDLE_WIDTH, h );
+ }
+ }
+
+ nLastPos = nSplitterPos + SPLITTER_HANDLE_WIDTH;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Splitter::GetSettings( KeyValues *outResourceData )
+{
+ BaseClass::GetSettings( outResourceData );
+}
diff --git a/mp/src/vgui2/vgui_controls/TextEntry.cpp b/mp/src/vgui2/vgui_controls/TextEntry.cpp
new file mode 100644
index 00000000..3b0c189f
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/TextEntry.cpp
@@ -0,0 +1,4279 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <utlvector.h>
+
+#include <vgui/Cursor.h>
+#include <vgui/IInput.h>
+#include <vgui/IScheme.h>
+#include <vgui/ISystem.h>
+#include <vgui/ISurface.h>
+#include <vgui/ILocalize.h>
+#include <vgui/IPanel.h>
+#include <KeyValues.h>
+#include <vgui/MouseCode.h>
+
+#include <vgui_controls/Menu.h>
+#include <vgui_controls/ScrollBar.h>
+#include <vgui_controls/TextEntry.h>
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/MenuItem.h>
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+enum
+{
+ // maximum size of text buffer
+ BUFFER_SIZE=999999,
+};
+
+using namespace vgui;
+
+static const int DRAW_OFFSET_X = 3,DRAW_OFFSET_Y = 1;
+
+DECLARE_BUILD_FACTORY( TextEntry );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+TextEntry::TextEntry(Panel *parent, const char *panelName) : BaseClass(parent, panelName)
+{
+ SetTriplePressAllowed( true );
+
+ _font = INVALID_FONT;
+ _smallfont = INVALID_FONT;
+
+ m_szComposition[ 0 ] = L'\0';
+
+ m_bAllowNumericInputOnly = false;
+ m_bAllowNonAsciiCharacters = false;
+ _hideText = false;
+ _editable = false;
+ _verticalScrollbar = false;
+ _cursorPos = 0;
+ _currentStartIndex = 0;
+ _horizScrollingAllowed = true;
+ _cursorIsAtEnd = false;
+ _putCursorAtEnd = false;
+ _multiline = false;
+ _cursorBlinkRate = 400;
+ _mouseSelection = false;
+ _mouseDragSelection = false;
+ _vertScrollBar=NULL;
+ _catchEnterKey = false;
+ _maxCharCount = -1;
+ _charCount = 0;
+ _wrap = false; // don't wrap by default
+ _sendNewLines = false; // don't pass on a newline msg by default
+ _drawWidth = 0;
+ m_bAutoProgressOnHittingCharLimit = false;
+ m_pIMECandidates = NULL;
+ m_hPreviousIME = input()->GetEnglishIMEHandle();
+ m_bDrawLanguageIDAtLeft = false;
+ m_nLangInset = 0;
+ m_bUseFallbackFont = false;
+ m_hFallbackFont = INVALID_FONT;
+
+ //a -1 for _select[0] means that the selection is empty
+ _select[0] = -1;
+ _select[1] = -1;
+ m_pEditMenu = NULL;
+
+ //this really just inits it when in here
+ ResetCursorBlink();
+
+ SetCursor(dc_ibeam);
+
+ SetEditable(true);
+
+ // initialize the line break array
+ m_LineBreaks.AddToTail(BUFFER_SIZE);
+
+ _recalculateBreaksIndex = 0;
+
+ _selectAllOnFirstFocus = false;
+ _selectAllOnFocusAlways = false;
+
+ //position the cursor so it is at the end of the text
+ GotoTextEnd();
+
+ // If keyboard focus is in an edit control, don't chain keyboard mappings up to parents since it could mess with typing in text.
+ SetAllowKeyBindingChainToParent( false );
+
+ REGISTER_COLOR_AS_OVERRIDABLE( _disabledFgColor, "disabledFgColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _disabledBgColor, "disabledBgColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _selectionColor, "selectionColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _selectionTextColor, "selectionTextColor_override" );
+ REGISTER_COLOR_AS_OVERRIDABLE( _defaultSelectionBG2Color, "defaultSelectionBG2Color_override" );
+}
+
+
+TextEntry::~TextEntry()
+{
+ delete m_pEditMenu;
+ delete m_pIMECandidates;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TextEntry::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetFgColor(GetSchemeColor("TextEntry.TextColor", pScheme));
+ SetBgColor(GetSchemeColor("TextEntry.BgColor", pScheme));
+
+ _cursorColor = GetSchemeColor("TextEntry.CursorColor", pScheme);
+ _disabledFgColor = GetSchemeColor("TextEntry.DisabledTextColor", pScheme);
+ _disabledBgColor = GetSchemeColor("TextEntry.DisabledBgColor", pScheme);
+
+ _selectionTextColor = GetSchemeColor("TextEntry.SelectedTextColor", GetFgColor(), pScheme);
+ _selectionColor = GetSchemeColor("TextEntry.SelectedBgColor", pScheme);
+ _defaultSelectionBG2Color = GetSchemeColor("TextEntry.OutOfFocusSelectedBgColor", pScheme);
+ _focusEdgeColor = GetSchemeColor("TextEntry.FocusEdgeColor", Color(0, 0, 0, 0), pScheme);
+
+ SetBorder( pScheme->GetBorder("ButtonDepressedBorder"));
+
+ if ( _font == INVALID_FONT ) _font = pScheme->GetFont("Default", IsProportional() );
+ if ( _smallfont == INVALID_FONT ) _smallfont = pScheme->GetFont( "DefaultVerySmall", IsProportional() );
+
+ SetFont( _font );
+}
+
+void TextEntry::SetSelectionTextColor( const Color& clr )
+{
+ _selectionTextColor = clr;
+}
+
+void TextEntry::SetSelectionBgColor( const Color& clr )
+{
+ _selectionColor = clr;
+}
+
+void TextEntry::SetSelectionUnfocusedBgColor( const Color& clr )
+{
+ _defaultSelectionBG2Color = clr;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the color of the background when the control is disabled
+//-----------------------------------------------------------------------------
+void TextEntry::SetDisabledBgColor(Color col)
+{
+ _disabledBgColor = col;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sends a message if the data has changed
+// Turns off any selected text in the window if we are not using the edit menu
+//-----------------------------------------------------------------------------
+void TextEntry::OnKillFocus()
+{
+ m_szComposition[ 0 ] = L'\0';
+ HideIMECandidates();
+
+ if (_dataChanged)
+ {
+ FireActionSignal();
+ _dataChanged = false;
+ }
+
+ // check if we clicked the right mouse button or if it is down
+ bool mouseRightClicked = input()->WasMousePressed(MOUSE_RIGHT);
+ bool mouseRightUp = input()->WasMouseReleased(MOUSE_RIGHT);
+ bool mouseRightDown = input()->IsMouseDown(MOUSE_RIGHT);
+
+ if (mouseRightClicked || mouseRightDown || mouseRightUp )
+ {
+ int cursorX, cursorY;
+ input()->GetCursorPos(cursorX, cursorY);
+
+ // if we're right clicking within our window, we don't actually kill focus
+ if (IsWithin(cursorX, cursorY))
+ return;
+ }
+
+ // clear any selection
+ SelectNone();
+
+ // move the cursor to the start
+// GotoTextStart();
+
+ PostActionSignal( new KeyValues( "TextKillFocus" ) );
+
+ // chain
+ BaseClass::OnKillFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Wipe line breaks after the size of a panel has been changed
+//-----------------------------------------------------------------------------
+void TextEntry::OnSizeChanged(int newWide, int newTall)
+{
+ BaseClass::OnSizeChanged(newWide, newTall);
+
+ // blow away the line breaks list
+ _recalculateBreaksIndex = 0;
+ m_LineBreaks.RemoveAll();
+ m_LineBreaks.AddToTail(BUFFER_SIZE);
+
+ // if we're bigger, see if we can scroll left to put more text in the window
+ if (newWide > _drawWidth)
+ {
+ ScrollLeftForResize();
+ }
+
+ _drawWidth = newWide;
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the text array - convert ANSI text to unicode and pass to unicode function
+//-----------------------------------------------------------------------------
+void TextEntry::SetText(const char *text)
+{
+ if (!text)
+ {
+ text = "";
+ }
+
+ if (text[0] == '#')
+ {
+ // check for localization
+ wchar_t *wsz = g_pVGuiLocalize->Find(text);
+ if (wsz)
+ {
+ SetText(wsz);
+ return;
+ }
+ }
+
+ size_t len = strlen( text );
+ if ( len < 1023 )
+ {
+ wchar_t unicode[ 1024 ];
+ g_pVGuiLocalize->ConvertANSIToUnicode( text, unicode, sizeof( unicode ) );
+ SetText( unicode );
+ }
+ else
+ {
+ size_t lenUnicode = ( len * sizeof( wchar_t ) + 4 );
+ wchar_t *unicode = ( wchar_t * ) malloc( lenUnicode );
+ g_pVGuiLocalize->ConvertANSIToUnicode( text, unicode, lenUnicode );
+ SetText( unicode );
+ free( unicode );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the text array
+// Using this function will cause all lineBreaks to be discarded.
+// This is because this fxn replaces the contents of the text buffer.
+// For modifying large buffers use insert functions.
+//-----------------------------------------------------------------------------
+void TextEntry::SetText(const wchar_t *wszText)
+{
+ if (!wszText)
+ {
+ wszText = L"";
+ }
+ int textLen = wcslen(wszText);
+ m_TextStream.RemoveAll();
+ m_TextStream.EnsureCapacity(textLen);
+
+ int missed_count = 0;
+ for (int i = 0; i < textLen; i++)
+ {
+ if(wszText[i]=='\r') // don't insert \r characters
+ {
+ missed_count++;
+ continue;
+ }
+ m_TextStream.AddToTail(wszText[i]);
+ SetCharAt(wszText[i], i-missed_count);
+ }
+
+ GotoTextStart();
+ SelectNone();
+
+ // reset the data changed flag
+ _dataChanged = false;
+
+ // blow away the line breaks list
+ _recalculateBreaksIndex = 0;
+ m_LineBreaks.RemoveAll();
+ m_LineBreaks.AddToTail(BUFFER_SIZE);
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the value of char at index position.
+//-----------------------------------------------------------------------------
+void TextEntry::SetCharAt(wchar_t ch, int index)
+{
+ if ((ch == '\n') || (ch == '\0'))
+ {
+ // if its not at the end of the buffer it matters.
+ // redo the linebreaks
+ //if (index != m_TextStream.Count())
+ {
+ _recalculateBreaksIndex = 0;
+ m_LineBreaks.RemoveAll();
+ m_LineBreaks.AddToTail(BUFFER_SIZE);
+ }
+ }
+
+ if (index < 0)
+ return;
+
+ if (index >= m_TextStream.Count())
+ {
+ m_TextStream.AddMultipleToTail(index - m_TextStream.Count() + 1);
+ }
+ m_TextStream[index] = ch;
+ _dataChanged = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Restarts the time of the next cursor blink
+//-----------------------------------------------------------------------------
+void TextEntry::ResetCursorBlink()
+{
+ _cursorBlink=false;
+ _cursorNextBlinkTime=system()->GetTimeMillis()+_cursorBlinkRate;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Hides the text buffer so it will not be drawn
+//-----------------------------------------------------------------------------
+void TextEntry::SetTextHidden(bool bHideText)
+{
+ _hideText = bHideText;
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: return character width
+//-----------------------------------------------------------------------------
+int getCharWidth(HFont font, wchar_t ch)
+{
+ if (!iswcntrl(ch))
+ {
+ int a, b, c;
+ surface()->GetCharABCwide(font, ch, a, b, c);
+ return (a + b + c);
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Given cursor's position in the text buffer, convert it to
+// the local window's x and y pixel coordinates
+// Input: cursorPos: cursor index
+// Output: cx, cy, the corresponding coords in the local window
+//-----------------------------------------------------------------------------
+void TextEntry::CursorToPixelSpace(int cursorPos, int &cx, int &cy)
+{
+ int yStart = GetYStart();
+
+ int x = DRAW_OFFSET_X, y = yStart;
+ _pixelsIndent = 0;
+ int lineBreakIndexIndex = 0;
+
+ for (int i = GetStartDrawIndex(lineBreakIndexIndex); i < m_TextStream.Count(); i++)
+ {
+ wchar_t ch = m_TextStream[i];
+ if (_hideText)
+ {
+ ch = '*';
+ }
+
+ // if we've found the position, break
+ if (cursorPos == i)
+ {
+ // even if this is a line break entry for the cursor, the next insert
+ // will be at this position, which will push the line break forward one
+ // so don't push the cursor down a line here...
+ /*if (!_putCursorAtEnd)
+ {
+ // if we've passed a line break go to that
+ if (m_LineBreaks[lineBreakIndexIndex] == i)
+ {
+ // add another line
+ AddAnotherLine(x,y);
+ lineBreakIndexIndex++;
+ }
+ }*/
+ break;
+ }
+
+ // if we've passed a line break go to that
+ if (m_LineBreaks.Count() &&
+ lineBreakIndexIndex < m_LineBreaks.Count() &&
+ m_LineBreaks[lineBreakIndexIndex] == i)
+ {
+ // add another line
+ AddAnotherLine(x,y);
+ lineBreakIndexIndex++;
+ }
+
+ // add to the current position
+ x += getCharWidth(_font, ch);
+ }
+
+ if ( m_bDrawLanguageIDAtLeft )
+ {
+ x += m_nLangInset;
+ }
+
+ cx = x;
+ cy = y;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Converts local pixel coordinates to an index in the text buffer
+// This function appears to be used only in response to mouse clicking
+// Input : cx -
+// cy - pixel location
+//-----------------------------------------------------------------------------
+int TextEntry::PixelToCursorSpace(int cx, int cy)
+{
+
+ int w, h;
+ GetSize(w, h);
+ cx = clamp(cx, 0, w+100);
+ cy = clamp(cy, 0, h);
+
+ _putCursorAtEnd = false; // Start off assuming we clicked somewhere in the text
+
+ int fontTall = surface()->GetFontTall(_font);
+
+ // where to Start reading
+ int yStart = GetYStart();
+ int x = DRAW_OFFSET_X, y = yStart;
+ _pixelsIndent = 0;
+ int lineBreakIndexIndex = 0;
+
+ int startIndex = GetStartDrawIndex(lineBreakIndexIndex);
+ bool onRightLine = false;
+ int i;
+ for (i = startIndex; i < m_TextStream.Count(); i++)
+ {
+ wchar_t ch = m_TextStream[i];
+ if (_hideText)
+ {
+ ch = '*';
+ }
+
+ // if we are on the right line but off the end of if put the cursor at the end of the line
+ if (m_LineBreaks[lineBreakIndexIndex] == i )
+ {
+ // add another line
+ AddAnotherLine(x,y);
+ lineBreakIndexIndex++;
+
+ if (onRightLine)
+ {
+ _putCursorAtEnd = true;
+ return i;
+ }
+ }
+
+ // check to see if we're on the right line
+ if (cy < yStart)
+ {
+ // cursor is above panel
+ onRightLine = true;
+ _putCursorAtEnd = true; // this will make the text scroll up if needed
+ }
+ else if (cy >= y && (cy < (y + fontTall + DRAW_OFFSET_Y)))
+ {
+ onRightLine = true;
+ }
+
+ int wide = getCharWidth(_font, ch);
+
+ // if we've found the position, break
+ if (onRightLine)
+ {
+ if (cx > GetWide()) // off right side of window
+ {
+ }
+ else if (cx < (DRAW_OFFSET_X + _pixelsIndent) || cy < yStart) // off left side of window
+ {
+ return i; // move cursor one to left
+ }
+
+ if (cx >= x && cx < (x + wide))
+ {
+ // check which side of the letter they're on
+ if (cx < (x + (wide * 0.5))) // left side
+ {
+ return i;
+ }
+ else // right side
+ {
+ return i + 1;
+ }
+ }
+ }
+ x += wide;
+ }
+
+ return i;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws a character in the panel
+// Input: ch - character to draw
+// font - font to use
+// x, y - pixel location to draw char at
+// Output: returns the width of the character drawn
+//-----------------------------------------------------------------------------
+int TextEntry::DrawChar(wchar_t ch, HFont font, int index, int x, int y)
+{
+ // add to the current position
+ int charWide = getCharWidth(font, ch);
+ int fontTall=surface()->GetFontTall(font);
+ if (!iswcntrl(ch))
+ {
+ // draw selection, if any
+ int selection0 = -1, selection1 = -1;
+ GetSelectedRange(selection0, selection1);
+
+ if (index >= selection0 && index < selection1)
+ {
+ // draw background selection color
+ VPANEL focus = input()->GetFocus();
+ Color bgColor;
+ bool hasFocus = HasFocus();
+ bool childOfFocus = focus && ipanel()->HasParent(focus, GetVPanel());
+
+ // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
+ if ( hasFocus || childOfFocus )
+ {
+ bgColor = _selectionColor;
+ }
+ else
+ {
+ bgColor =_defaultSelectionBG2Color;
+ }
+
+ surface()->DrawSetColor(bgColor);
+
+ surface()->DrawFilledRect(x, y, x + charWide, y + 1 + fontTall);
+
+ // reset text color
+ surface()->DrawSetTextColor(_selectionTextColor);
+ }
+ if (index == selection1)
+ {
+ // we've come out of selection, reset the color
+ surface()->DrawSetTextColor(GetFgColor());
+ }
+
+ surface()->DrawSetTextPos(x, y);
+ surface()->DrawUnicodeChar(ch);
+
+ return charWide;
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw the cursor, cursor is not drawn when it is blinked gone
+// Input: x,y where to draw cursor
+// Output: returns true if cursor was drawn.
+//-----------------------------------------------------------------------------
+bool TextEntry::DrawCursor(int x, int y)
+{
+ if (!_cursorBlink)
+ {
+ int cx, cy;
+ CursorToPixelSpace(_cursorPos, cx, cy);
+ surface()->DrawSetColor(_cursorColor);
+ int fontTall=surface()->GetFontTall(_font);
+ surface()->DrawFilledRect(cx, cy, cx + 1, cy + fontTall);
+ return true;
+ }
+ return false;
+}
+
+bool TextEntry::NeedsEllipses( HFont font, int *pIndex )
+{
+ Assert( pIndex );
+ *pIndex = -1;
+ int wide = DRAW_OFFSET_X; // buffer on left and right end of text.
+ for ( int i = 0; i < m_TextStream.Count(); ++i )
+ {
+ wide += getCharWidth( font , m_TextStream[i] );
+ if (wide > _drawWidth)
+ {
+ *pIndex = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws the text in the panel
+//-----------------------------------------------------------------------------
+void TextEntry::PaintBackground()
+{
+ BaseClass::PaintBackground();
+
+ // draw background
+ Color col;
+ if (IsEnabled())
+ {
+ col = GetBgColor();
+ }
+ else
+ {
+ col = _disabledBgColor;
+ }
+ Color saveBgColor = col;
+
+ int wide, tall;
+ GetSize( wide, tall );
+
+// surface()->DrawSetColor(col);
+// surface()->DrawFilledRect(0, 0, wide, tall);
+
+ // where to Start drawing
+ int x = DRAW_OFFSET_X + _pixelsIndent, y = GetYStart();
+
+ m_nLangInset = 0;
+
+ int langlen = 0;
+ wchar_t shortcode[ 5 ];
+ shortcode[ 0 ] = L'\0';
+
+ if ( m_bAllowNonAsciiCharacters )
+ {
+ input()->GetIMELanguageShortCode( shortcode, sizeof( shortcode ) );
+
+ if ( shortcode[ 0 ] != L'\0' &&
+ wcsicmp( shortcode, L"EN" ) )
+ {
+ m_nLangInset = 0;
+ langlen = wcslen( shortcode );
+ for ( int i = 0; i < langlen; ++i )
+ {
+ m_nLangInset += getCharWidth( _smallfont, shortcode[ i ] );
+ }
+
+ m_nLangInset += 4;
+
+ if ( m_bDrawLanguageIDAtLeft )
+ {
+ x += m_nLangInset;
+ }
+
+ wide -= m_nLangInset;
+ }
+ }
+
+ HFont useFont = _font;
+
+ surface()->DrawSetTextFont(useFont);
+ if (IsEnabled())
+ {
+ col = GetFgColor();
+ }
+ else
+ {
+ col = _disabledFgColor;
+ }
+ surface()->DrawSetTextColor(col);
+ _pixelsIndent = 0;
+
+ int lineBreakIndexIndex = 0;
+ int startIndex = GetStartDrawIndex(lineBreakIndexIndex);
+ int remembery = y;
+
+ int oldEnd = m_TextStream.Count();
+ int oldCursorPos = _cursorPos;
+ int nCompStart = -1;
+ int nCompEnd = -1;
+
+ // FIXME: Should insert at cursor pos instead
+ bool composing = m_bAllowNonAsciiCharacters && wcslen( m_szComposition ) > 0;
+ bool invertcomposition = input()->GetShouldInvertCompositionString();
+
+ if ( composing )
+ {
+ nCompStart = _cursorPos;
+
+ wchar_t *s = m_szComposition;
+ while ( *s != L'\0' )
+ {
+ m_TextStream.InsertBefore( _cursorPos, *s );
+ ++s;
+ ++_cursorPos;
+ }
+
+ nCompEnd = _cursorPos;
+ }
+
+ bool highlight_composition = ( nCompStart != -1 && nCompEnd != -1 ) ? true : false;
+
+ // draw text with an elipsis
+ if ( (!_multiline) && (!_horizScrollingAllowed) )
+ {
+ int endIndex = m_TextStream.Count();
+ // In editable windows only do the ellipsis if we don't have focus.
+ // In non editable windows do it all the time.
+ if ( (!HasFocus() && (IsEditable())) || (!IsEditable()) )
+ {
+ int i = -1;
+
+ // loop through all the characters and sum their widths
+ bool addEllipses = NeedsEllipses( useFont, &i );
+ if ( addEllipses &&
+ !IsEditable() &&
+ m_bUseFallbackFont &&
+ INVALID_FONT != m_hFallbackFont )
+ {
+ // Switch to small font!!!
+ useFont = m_hFallbackFont;
+ surface()->DrawSetTextFont(useFont);
+ addEllipses = NeedsEllipses( useFont, &i );
+ }
+ if (addEllipses)
+ {
+ int elipsisWidth = 3 * getCharWidth(useFont, '.');
+ while (elipsisWidth > 0 && i >= 0)
+ {
+ elipsisWidth -= getCharWidth(useFont, m_TextStream[i]);
+ i--;
+ }
+ endIndex = i + 1;
+ }
+
+ // if we take off less than the last 3 chars we have to make sure
+ // we take off the last 3 chars so selected text will look right.
+ if (m_TextStream.Count() - endIndex < 3 && m_TextStream.Count() - endIndex > 0 )
+ {
+ endIndex = m_TextStream.Count() - 3;
+ }
+ }
+ // draw the text
+ int i;
+ for (i = startIndex; i < endIndex; i++)
+ {
+ wchar_t ch = m_TextStream[i];
+ if (_hideText)
+ {
+ ch = '*';
+ }
+
+ bool iscompositionchar = false;
+
+ if ( highlight_composition )
+ {
+ iscompositionchar = ( i >= nCompStart && i < nCompEnd ) ? true : false;
+ if ( iscompositionchar )
+ {
+ // Set the underline color to the text color
+ surface()->DrawSetColor( col );
+
+ int w = getCharWidth( useFont, ch );
+
+ if ( invertcomposition )
+ {
+ // Invert color
+ surface()->DrawSetTextColor( saveBgColor );
+ surface()->DrawSetColor( col );
+
+ surface()->DrawFilledRect(x, 0, x+w, tall);
+ // Set the underline color to the text color
+ surface()->DrawSetColor( saveBgColor );
+ }
+
+ surface()->DrawFilledRect( x, tall - 2, x + w, tall - 1 );
+ }
+ }
+
+
+ // draw the character and update xposition
+ x += DrawChar(ch, useFont, i, x, y);
+
+ // Restore color
+ surface()->DrawSetTextColor(col);
+
+ }
+ if (endIndex < m_TextStream.Count()) // add an elipsis
+ {
+ x += DrawChar('.', useFont, i, x, y);
+ i++;
+ x += DrawChar('.', useFont, i, x, y);
+ i++;
+ x += DrawChar('.', useFont, i, x, y);
+ i++;
+ }
+ }
+ else
+ {
+ // draw the text
+ for ( int i = startIndex; i < m_TextStream.Count(); i++)
+ {
+ wchar_t ch = m_TextStream[i];
+ if (_hideText)
+ {
+ ch = '*';
+ }
+
+ // if we've passed a line break go to that
+ if ( _multiline && m_LineBreaks[lineBreakIndexIndex] == i)
+ {
+ // add another line
+ AddAnotherLine(x, y);
+ lineBreakIndexIndex++;
+ }
+
+ bool iscompositionchar = false;
+
+ if ( highlight_composition )
+ {
+ iscompositionchar = ( i >= nCompStart && i < nCompEnd ) ? true : false;
+ if ( iscompositionchar )
+ {
+ // Set the underline color to the text color
+ surface()->DrawSetColor( col );
+
+ int w = getCharWidth( useFont, ch );
+
+ if ( invertcomposition )
+ {
+ // Invert color
+ surface()->DrawSetTextColor( saveBgColor );
+ surface()->DrawFilledRect(x, 0, x+w, tall);
+ // Set the underline color to the text color
+ surface()->DrawSetColor( saveBgColor );
+ }
+
+ surface()->DrawFilledRect( x, tall - 2, x + w, tall - 1 );
+ }
+ }
+
+ // draw the character and update xposition
+ x += DrawChar(ch, useFont, i, x, y);
+
+ // Restore color
+ surface()->DrawSetTextColor(col);
+ }
+ }
+
+ // custom border
+ //!! need to replace this with scheme stuff (TextEntryBorder/TextEntrySelectedBorder)
+ surface()->DrawSetColor(50, 50, 50, 255);
+
+ if (IsEnabled() && IsEditable() && HasFocus())
+ {
+ // set a more distinct border color
+ surface()->DrawSetColor(0, 0, 0, 255);
+
+ DrawCursor (x, y);
+
+ if ( composing )
+ {
+ LocalToScreen( x, y );
+ input()->SetCandidateWindowPos( x, y );
+ }
+ }
+
+ int newEnd = m_TextStream.Count();
+ int remove = newEnd - oldEnd;
+ if ( remove > 0 )
+ {
+ m_TextStream.RemoveMultiple( oldCursorPos, remove );
+ }
+ _cursorPos = oldCursorPos;
+
+ if ( HasFocus() && m_bAllowNonAsciiCharacters && langlen > 0 )
+ {
+ wide += m_nLangInset;
+
+ if ( m_bDrawLanguageIDAtLeft )
+ {
+ x = 0;
+ }
+ else
+ {
+ // Draw language identififer
+ x = wide - m_nLangInset;
+ }
+
+ surface()->DrawSetColor( col );
+
+ surface()->DrawFilledRect( x, 2, x + m_nLangInset-2, tall - 2 );
+
+ saveBgColor[ 3 ] = 255;
+ surface()->DrawSetTextColor( saveBgColor );
+
+ x += 1;
+
+ surface()->DrawSetTextFont(_smallfont);
+ for ( int i = 0; i < langlen; ++i )
+ {
+ x += DrawChar( shortcode[ i ], _smallfont, i, x, remembery );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when data changes or panel size changes
+//-----------------------------------------------------------------------------
+void TextEntry::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ RecalculateLineBreaks();
+
+ // recalculate scrollbar position
+ if (_verticalScrollbar)
+ {
+ LayoutVerticalScrollBarSlider();
+ }
+
+ // force a Repaint
+ Repaint();
+}
+
+// moves x,y to the Start of the next line of text
+void TextEntry::AddAnotherLine(int &cx, int &cy)
+{
+ cx = DRAW_OFFSET_X + _pixelsIndent;
+ cy += (surface()->GetFontTall(_font) + DRAW_OFFSET_Y);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Recalculates line breaks
+//-----------------------------------------------------------------------------
+void TextEntry::RecalculateLineBreaks()
+{
+ if (!_multiline || _hideText)
+ return;
+
+ if (m_TextStream.Count() < 1)
+ return;
+
+ HFont font = _font;
+
+ // line break to our width -2 pixel to keep cursor blinking in window
+ // (assumes borders are 1 pixel)
+ int wide = GetWide()-2;
+
+ // subtract the scrollbar width
+ if (_vertScrollBar)
+ {
+ wide -= _vertScrollBar->GetWide();
+ }
+
+ int charWidth;
+ int x = DRAW_OFFSET_X, y = DRAW_OFFSET_Y;
+
+ int wordStartIndex = 0;
+ int wordLength = 0;
+ bool hasWord = false;
+ bool justStartedNewLine = true;
+ bool wordStartedOnNewLine = true;
+
+ int startChar;
+ if (_recalculateBreaksIndex <= 0)
+ {
+ m_LineBreaks.RemoveAll();
+ startChar=0;
+ }
+ else
+ {
+ // remove the rest of the linebreaks list since its out of date.
+ for (int i=_recalculateBreaksIndex+1; i < m_LineBreaks.Count(); ++i)
+ {
+ m_LineBreaks.Remove((int)i);
+ --i; // removing shrinks the list!
+ }
+ startChar = m_LineBreaks[_recalculateBreaksIndex];
+ }
+
+ // handle the case where this char is a new line, in that case
+ // we have already taken its break index into account above so skip it.
+ if (m_TextStream[startChar] == '\r' || m_TextStream[startChar] == '\n')
+ {
+ startChar++;
+ }
+
+ // loop through all the characters
+ int i;
+ for (i = startChar; i < m_TextStream.Count(); ++i)
+ {
+ wchar_t ch = m_TextStream[i];
+
+ // line break only on whitespace characters
+ if (!iswspace(ch))
+ {
+ if (hasWord)
+ {
+ // append to the current word
+ }
+ else
+ {
+ // Start a new word
+ wordStartIndex = i;
+ hasWord = true;
+ wordStartedOnNewLine = justStartedNewLine;
+ wordLength = 0;
+ }
+ }
+ else
+ {
+ // whitespace/punctuation character
+ // end the word
+ hasWord = false;
+ }
+
+ // get the width
+ charWidth = getCharWidth(font, ch);
+ if (!iswcntrl(ch))
+ {
+ justStartedNewLine = false;
+ }
+
+ // check to see if the word is past the end of the line [wordStartIndex, i)
+ if ((x + charWidth) >= wide || ch == '\r' || ch == '\n')
+ {
+ // add another line
+ AddAnotherLine(x,y);
+
+ justStartedNewLine = true;
+ hasWord = false;
+
+ if (ch == '\r' || ch == '\n')
+ {
+ // set the break at the current character
+ m_LineBreaks.AddToTail(i);
+ }
+ else if (wordStartedOnNewLine)
+ {
+ // word is longer than a line, so set the break at the current cursor
+ m_LineBreaks.AddToTail(i);
+ }
+ else
+ {
+ // set it at the last word Start
+ m_LineBreaks.AddToTail(wordStartIndex);
+
+ // just back to reparse the next line of text
+ i = wordStartIndex;
+ }
+
+ // reset word length
+ wordLength = 0;
+ }
+
+ // add to the size
+ x += charWidth;
+ wordLength += charWidth;
+ }
+
+ _charCount = i-1;
+
+ // end the list
+ m_LineBreaks.AddToTail(BUFFER_SIZE);
+
+ // set up the scrollbar
+ LayoutVerticalScrollBarSlider();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recalculate where the vertical scroll bar slider should be
+// based on the current cursor line we are on.
+//-----------------------------------------------------------------------------
+void TextEntry::LayoutVerticalScrollBarSlider()
+{
+ // set up the scrollbar
+ if (_vertScrollBar)
+ {
+ int wide, tall;
+ GetSize (wide, tall);
+
+ // make sure we factor in insets
+ int ileft, iright, itop, ibottom;
+ GetInset(ileft, iright, itop, ibottom);
+
+ // with a scroll bar we take off the inset
+ wide -= iright;
+
+ _vertScrollBar->SetPos(wide - _vertScrollBar->GetWide(), 0);
+ // scrollbar is inside the borders.
+ _vertScrollBar->SetSize(_vertScrollBar->GetWide(), tall - ibottom - itop);
+
+ // calculate how many lines we can fully display
+ int displayLines = tall / (surface()->GetFontTall(_font) + DRAW_OFFSET_Y);
+ int numLines = m_LineBreaks.Count();
+
+ if (numLines <= displayLines)
+ {
+ // disable the scrollbar
+ _vertScrollBar->SetEnabled(false);
+ _vertScrollBar->SetRange(0, numLines);
+ _vertScrollBar->SetRangeWindow(numLines);
+ _vertScrollBar->SetValue(0);
+ }
+ else
+ {
+ // set the scrollbars range
+ _vertScrollBar->SetRange(0, numLines);
+ _vertScrollBar->SetRangeWindow(displayLines);
+
+ _vertScrollBar->SetEnabled(true);
+
+ // this should make it scroll one line at a time
+ _vertScrollBar->SetButtonPressedScrollValue(1);
+
+ // set the value to view the last entries
+ int val = _vertScrollBar->GetValue();
+ int maxval = _vertScrollBar->GetValue() + displayLines;
+ if (GetCursorLine() < val )
+ {
+ while (GetCursorLine() < val)
+ {
+ val--;
+ }
+ }
+ else if (GetCursorLine() >= maxval)
+ {
+ while (GetCursorLine() >= maxval)
+ {
+ maxval++;
+ }
+ maxval -= displayLines;
+ val = maxval;
+ }
+ else
+ {
+ //val = GetCursorLine();
+ }
+
+ _vertScrollBar->SetValue(val);
+ _vertScrollBar->InvalidateLayout();
+ _vertScrollBar->Repaint();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Set boolean value of baseclass variables.
+//-----------------------------------------------------------------------------
+void TextEntry::SetEnabled(bool state)
+{
+ BaseClass::SetEnabled(state);
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets whether text wraps around multiple lines or not
+// Input : state - true or false
+//-----------------------------------------------------------------------------
+void TextEntry::SetMultiline(bool state)
+{
+ _multiline = state;
+}
+
+bool TextEntry::IsMultiline()
+{
+ return _multiline;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets whether or not the edit catches and stores ENTER key presses
+//-----------------------------------------------------------------------------
+void TextEntry::SetCatchEnterKey(bool state)
+{
+ _catchEnterKey = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets whether a vertical scrollbar is visible
+// Input : state - true or false
+//-----------------------------------------------------------------------------
+void TextEntry::SetVerticalScrollbar(bool state)
+{
+ _verticalScrollbar = state;
+
+ if (_verticalScrollbar)
+ {
+ if (!_vertScrollBar)
+ {
+ _vertScrollBar = new ScrollBar(this, "ScrollBar", true);
+ _vertScrollBar->AddActionSignalTarget(this);
+ }
+
+ _vertScrollBar->SetVisible(true);
+ }
+ else if (_vertScrollBar)
+ {
+ _vertScrollBar->SetVisible(false);
+ }
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets _editable flag
+// Input : state - true or false
+//-----------------------------------------------------------------------------
+void TextEntry::SetEditable(bool state)
+{
+ if ( state )
+ {
+ SetDropEnabled( true, 1.0f );
+ }
+ else
+ {
+ SetDropEnabled( false );
+ }
+ _editable = state;
+}
+
+const wchar_t *UnlocalizeUnicode( wchar_t *unicode )
+{
+ if ( !unicode )
+ return L"";
+
+ if ( *unicode == L'#' )
+ {
+ char lookup[ 512 ];
+ g_pVGuiLocalize->ConvertUnicodeToANSI( unicode + 1, lookup, sizeof( lookup ) );
+ return g_pVGuiLocalize->Find( lookup );
+ }
+ return unicode;
+}
+
+Menu * TextEntry::GetEditMenu()
+{
+ return m_pEditMenu;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create cut/copy/paste dropdown menu
+//-----------------------------------------------------------------------------
+void TextEntry::CreateEditMenu()
+{
+ // create a drop down cut/copy/paste menu appropriate for this object's states
+ if (m_pEditMenu)
+ delete m_pEditMenu;
+ m_pEditMenu = new Menu(this, "EditMenu");
+
+ m_pEditMenu->SetFont( _font );
+
+ // add cut/copy/paste drop down options if its editable, just copy if it is not
+ if (_editable && !_hideText)
+ {
+ m_pEditMenu->AddMenuItem("#TextEntry_Cut", new KeyValues("DoCutSelected"), this);
+ }
+
+ if ( !_hideText )
+ {
+ m_pEditMenu->AddMenuItem("#TextEntry_Copy", new KeyValues("DoCopySelected"), this);
+ }
+
+ if (_editable)
+ {
+ m_pEditMenu->AddMenuItem("#TextEntry_Paste", new KeyValues("DoPaste"), this);
+ }
+
+
+ if ( m_bAllowNonAsciiCharacters )
+ {
+ IInput::LanguageItem *langs = NULL;
+
+ int count = input()->GetIMELanguageList( NULL, 0 );
+ if ( count > 0 )
+ {
+ langs = new IInput::LanguageItem[ count ];
+ input()->GetIMELanguageList( langs, count );
+
+ // Create a submenu
+ Menu *subMenu = new Menu( this, "LanguageMenu" );
+
+ subMenu->SetFont( _font );
+
+ for ( int i = 0; i < count; ++i )
+ {
+ int id = subMenu->AddCheckableMenuItem( "Language", UnlocalizeUnicode( langs[ i ].menuname ), new KeyValues( "DoLanguageChanged", "handle", langs[ i ].handleValue ), this );
+ if ( langs[ i ].active )
+ {
+ subMenu->SetMenuItemChecked( id, true );
+ }
+ }
+
+ m_pEditMenu->AddCascadingMenuItem( "Language", "#TextEntry_Language", "", this, subMenu );
+
+ delete[] langs;
+ }
+
+ IInput::ConversionModeItem *modes = NULL;
+
+ count = input()->GetIMEConversionModes( NULL, 0 );
+ // if count == 0 then native mode is the only mode...
+ if ( count > 0 )
+ {
+ modes = new IInput::ConversionModeItem[ count ];
+ input()->GetIMEConversionModes( modes, count );
+
+ // Create a submenu
+ Menu *subMenu = new Menu( this, "ConversionModeMenu" );
+
+ subMenu->SetFont( _font );
+
+ for ( int i = 0; i < count; ++i )
+ {
+ int id = subMenu->AddCheckableMenuItem( "ConversionMode", UnlocalizeUnicode( modes[ i ].menuname ), new KeyValues( "DoConversionModeChanged", "handle", modes[ i ].handleValue ), this );
+ if ( modes[ i ].active )
+ {
+ subMenu->SetMenuItemChecked( id, true );
+ }
+ }
+
+ m_pEditMenu->AddCascadingMenuItem( "ConversionMode", "#TextEntry_ConversionMode", "", this, subMenu );
+
+ delete[] modes;
+ }
+
+ IInput::SentenceModeItem *sentencemodes = NULL;
+
+ count = input()->GetIMESentenceModes( NULL, 0 );
+ // if count == 0 then native mode is the only mode...
+ if ( count > 0 )
+ {
+ sentencemodes = new IInput::SentenceModeItem[ count ];
+ input()->GetIMESentenceModes( sentencemodes, count );
+
+ // Create a submenu
+ Menu *subMenu = new Menu( this, "SentenceModeMenu" );
+
+ subMenu->SetFont( _font );
+
+ for ( int i = 0; i < count; ++i )
+ {
+ int id = subMenu->AddCheckableMenuItem( "SentenceMode", UnlocalizeUnicode( sentencemodes[ i ].menuname ), new KeyValues( "DoConversionModeChanged", "handle", modes[ i ].handleValue ), this );
+ if ( modes[ i ].active )
+ {
+ subMenu->SetMenuItemChecked( id, true );
+ }
+ }
+
+ m_pEditMenu->AddCascadingMenuItem( "SentenceMode", "#TextEntry_SentenceMode", "", this, subMenu );
+
+ delete[] sentencemodes;
+ }
+ }
+
+
+ m_pEditMenu->SetVisible(false);
+ m_pEditMenu->SetParent(this);
+ m_pEditMenu->AddActionSignalTarget(this);
+}
+
+//-----------------------------------------------------------------------------
+// Purpsoe: Returns state of _editable flag
+//-----------------------------------------------------------------------------
+bool TextEntry::IsEditable()
+{
+ return _editable && IsEnabled();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: We want single line windows to scroll horizontally and select text
+// in response to clicking and holding outside window
+//-----------------------------------------------------------------------------
+void TextEntry::OnMouseFocusTicked()
+{
+ // if a button is down move the scrollbar slider the appropriate direction
+ if (_mouseDragSelection) // text is being selected via mouse clicking and dragging
+ {
+ OnCursorMoved(0,0); // we want the text to scroll as if we were dragging
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If a cursor enters the window, we are not elegible for
+// MouseFocusTicked events
+//-----------------------------------------------------------------------------
+void TextEntry::OnCursorEntered()
+{
+ _mouseDragSelection = false; // outside of window dont recieve drag scrolling ticks
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: When the cursor is outside the window, if we are holding the mouse
+// button down, then we want the window to scroll the text one char at a time
+// using Ticks
+//-----------------------------------------------------------------------------
+void TextEntry::OnCursorExited() // outside of window recieve drag scrolling ticks
+{
+ if (_mouseSelection)
+ _mouseDragSelection = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle selection of text by mouse
+//-----------------------------------------------------------------------------
+void TextEntry::OnCursorMoved(int x, int y)
+{
+ if (_mouseSelection)
+ {
+ // update the cursor position
+ int x, y;
+ input()->GetCursorPos(x, y);
+ ScreenToLocal(x, y);
+ _cursorPos = PixelToCursorSpace(x, y);
+
+ // if we are at Start of buffer don't put cursor at end, this will keep
+ // window from scrolling up to a blank line
+ if (_cursorPos == 0)
+ _putCursorAtEnd = false;
+
+ // scroll if we went off left side
+ if (_cursorPos == _currentStartIndex)
+ {
+ if (_cursorPos > 0)
+ _cursorPos--;
+
+ ScrollLeft();
+ _cursorPos = _currentStartIndex;
+ }
+ if ( _cursorPos != _select[1])
+ {
+ _select[1] = _cursorPos;
+ Repaint();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle Mouse button down events.
+//-----------------------------------------------------------------------------
+void TextEntry::OnMousePressed(MouseCode code)
+{
+ if (code == MOUSE_LEFT)
+ {
+ bool keepChecking = SelectCheck( true );
+ if ( !keepChecking )
+ {
+ BaseClass::OnMousePressed( code );
+ return;
+ }
+
+ // move the cursor to where the mouse was pressed
+ int x, y;
+ input()->GetCursorPos(x, y);
+ ScreenToLocal(x, y);
+
+ _cursorIsAtEnd = _putCursorAtEnd; // save this off before calling PixelToCursorSpace()
+ _cursorPos = PixelToCursorSpace(x, y);
+ // if we are at Start of buffer don't put cursor at end, this will keep
+ // window from scrolling up to a blank line
+ if (_cursorPos == 0)
+ _putCursorAtEnd = false;
+
+ // enter selection mode
+ input()->SetMouseCapture(GetVPanel());
+ _mouseSelection = true;
+
+ if (_select[0] < 0)
+ {
+ // if no initial selection position, Start selection position at cursor
+ _select[0] = _cursorPos;
+ }
+ _select[1] = _cursorPos;
+
+ ResetCursorBlink();
+ RequestFocus();
+ Repaint();
+ }
+ else if (code == MOUSE_RIGHT) // check for context menu open
+ {
+ CreateEditMenu();
+ Assert(m_pEditMenu);
+
+ OpenEditMenu();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle mouse button up events
+//-----------------------------------------------------------------------------
+void TextEntry::OnMouseReleased(MouseCode code)
+{
+ _mouseSelection = false;
+
+ input()->SetMouseCapture(NULL);
+
+ // make sure something has been selected
+ int cx0, cx1;
+ if (GetSelectedRange(cx0, cx1))
+ {
+ if (cx1 - cx0 == 0)
+ {
+ // nullify selection
+ _select[0] = -1;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : code -
+//-----------------------------------------------------------------------------
+void TextEntry::OnMouseTriplePressed( MouseCode code )
+{
+ BaseClass::OnMouseTriplePressed( code );
+
+ // left triple clicking on a word selects all
+ if (code == MOUSE_LEFT)
+ {
+ GotoTextEnd();
+
+ SelectAllText( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle mouse double clicks
+//-----------------------------------------------------------------------------
+void TextEntry::OnMouseDoublePressed(MouseCode code)
+{
+ // left double clicking on a word selects the word
+ if (code == MOUSE_LEFT)
+ {
+ // move the cursor just as if you single clicked.
+ OnMousePressed(code);
+ // then find the start and end of the word we are in to highlight it.
+ int selectSpot[2];
+ GotoWordLeft();
+ selectSpot[0] = _cursorPos;
+ GotoWordRight();
+ selectSpot[1] = _cursorPos;
+
+ if (_cursorPos > 0)
+ {
+ if (iswspace(m_TextStream[_cursorPos - 1]))
+ {
+ selectSpot[1]--;
+ _cursorPos--;
+ }
+
+ _select[0] = selectSpot[0];
+ _select[1] = selectSpot[1];
+ _mouseSelection = true;
+ }
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turn off text selection code when mouse button is not down
+//-----------------------------------------------------------------------------
+void TextEntry::OnMouseCaptureLost()
+{
+ _mouseSelection = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Only pass some keys upwards
+// everything else we don't relay to the parent
+//-----------------------------------------------------------------------------
+void TextEntry::OnKeyCodePressed(KeyCode code)
+{
+ // Pass enter on only if _catchEnterKey isn't set
+ if ( code == KEY_ENTER )
+ {
+ if ( !_catchEnterKey )
+ {
+ Panel::OnKeyCodePressed( code );
+ return;
+ }
+ }
+
+ // Forward on just a few key codes, everything else can be handled by TextEntry itself
+ switch ( code )
+ {
+ case KEY_F1:
+ case KEY_F2:
+ case KEY_F3:
+ case KEY_F4:
+ case KEY_F5:
+ case KEY_F6:
+ case KEY_F7:
+ case KEY_F8:
+ case KEY_F9:
+ case KEY_F10:
+ case KEY_F11:
+ case KEY_F12:
+ case KEY_ESCAPE:
+ case KEY_APP:
+ Panel::OnKeyCodePressed( code );
+ return;
+ }
+
+ // Pass on the joystick and mouse codes
+ if ( IsMouseCode(code) || IsNovintButtonCode(code) || IsJoystickCode(code) || IsJoystickButtonCode(code) ||
+ IsJoystickPOVCode(code) || IsJoystickPOVCode(code) || IsJoystickAxisCode(code) )
+ {
+ Panel::OnKeyCodePressed( code );
+ return;
+ }
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Masks which keys get chained up
+// Maps keyboard input to text window functions.
+//-----------------------------------------------------------------------------
+void TextEntry::OnKeyCodeTyped(KeyCode code)
+{
+ _cursorIsAtEnd = _putCursorAtEnd;
+ _putCursorAtEnd = false;
+
+ bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+ bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+ bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
+ bool winkey = (input()->IsKeyDown(KEY_LWIN) || input()->IsKeyDown(KEY_RWIN));
+ bool fallThrough = false;
+
+ if ( ( ctrl || ( winkey && IsOSX() ) ) && !alt)
+ {
+ switch(code)
+ {
+ case KEY_A:
+ SelectAllText(false);
+ // move the cursor to the end
+ _cursorPos = _select[1];
+ break;
+
+ case KEY_INSERT:
+ case KEY_C:
+ {
+ CopySelected();
+ break;
+ }
+ case KEY_V:
+ {
+ DeleteSelected();
+ Paste();
+ break;
+ }
+ case KEY_X:
+ {
+ CopySelected();
+ DeleteSelected();
+ break;
+ }
+ case KEY_Z:
+ {
+ Undo();
+ break;
+ }
+ case KEY_RIGHT:
+ {
+ GotoWordRight();
+ break;
+ }
+ case KEY_LEFT:
+ {
+ GotoWordLeft();
+ break;
+ }
+ case KEY_ENTER:
+ {
+ // insert a newline
+ if (_multiline)
+ {
+ DeleteSelected();
+ SaveUndoState();
+ InsertChar('\n');
+ }
+ // fire newlines back to the main target if asked to
+ if(_sendNewLines)
+ {
+ PostActionSignal(new KeyValues("TextNewLine"));
+ }
+ break;
+ }
+ case KEY_HOME:
+ {
+ GotoTextStart();
+ break;
+ }
+ case KEY_END:
+ {
+ GotoTextEnd();
+ break;
+ }
+ case KEY_PAGEUP:
+ {
+ OnChangeIME( true );
+ }
+ break;
+ case KEY_PAGEDOWN:
+ {
+ OnChangeIME( false );
+ }
+ break;
+ case KEY_UP:
+ case KEY_DOWN:
+ if ( m_bAllowNonAsciiCharacters )
+ {
+ FlipToLastIME();
+ }
+ else
+ {
+ fallThrough = true;
+ }
+ break;
+ default:
+ {
+ fallThrough = true;
+ break;
+ }
+ }
+ }
+ else if (alt)
+ {
+ // do nothing with ALT-x keys
+ if ( !m_bAllowNonAsciiCharacters || ( code != KEY_BACKQUOTE ) )
+ {
+ fallThrough = true;
+ }
+ }
+ else
+ {
+ switch(code)
+ {
+ case KEY_TAB:
+ case KEY_LSHIFT:
+ case KEY_RSHIFT:
+ case KEY_ESCAPE:
+ {
+ fallThrough = true;
+ break;
+ }
+ case KEY_INSERT:
+ {
+ if (shift)
+ {
+ DeleteSelected();
+ Paste();
+ }
+ else
+ {
+ fallThrough = true;
+ }
+
+ break;
+ }
+ case KEY_DELETE:
+ {
+ if (shift)
+ {
+ // shift-delete is cut
+ CopySelected();
+ DeleteSelected();
+ }
+ else
+ {
+ Delete();
+ }
+ break;
+ }
+ case KEY_LEFT:
+ {
+ GotoLeft();
+ break;
+ }
+ case KEY_RIGHT:
+ {
+ GotoRight();
+ break;
+ }
+ case KEY_UP:
+ {
+ if (_multiline)
+ {
+ GotoUp();
+ }
+ else
+ {
+ fallThrough = true;
+ }
+ break;
+ }
+ case KEY_DOWN:
+ {
+ if (_multiline)
+ {
+ GotoDown();
+ }
+ else
+ {
+ fallThrough = true;
+ }
+ break;
+ }
+ case KEY_HOME:
+ {
+ if (_multiline)
+ {
+ GotoFirstOfLine();
+ }
+ else
+ {
+ GotoTextStart();
+ }
+ break;
+ }
+ case KEY_END:
+ {
+ GotoEndOfLine();
+ break;
+ }
+ case KEY_BACKSPACE:
+ {
+ int x0, x1;
+ if (GetSelectedRange(x0, x1))
+ {
+ // act just like delete if there is a selection
+ DeleteSelected();
+ }
+ else
+ {
+ Backspace();
+ }
+ break;
+ }
+ case KEY_ENTER:
+ {
+ // insert a newline
+ if (_multiline && _catchEnterKey)
+ {
+ DeleteSelected();
+ SaveUndoState();
+ InsertChar('\n');
+ }
+ else
+ {
+ fallThrough = true;
+ }
+ // fire newlines back to the main target if asked to
+ if(_sendNewLines)
+ {
+ PostActionSignal(new KeyValues("TextNewLine"));
+ }
+ break;
+ }
+ case KEY_PAGEUP:
+ {
+ int val = 0;
+ fallThrough = (!_multiline) && (!_vertScrollBar);
+ if (_vertScrollBar)
+ {
+ val = _vertScrollBar->GetValue();
+ }
+
+ // if there is a scroll bar scroll down one rangewindow
+ if (_multiline)
+ {
+ int displayLines = GetTall() / (surface()->GetFontTall(_font) + DRAW_OFFSET_Y);
+ // move the cursor down
+ for (int i=0; i < displayLines; i++)
+ {
+ GotoUp();
+ }
+ }
+
+ // if there is a scroll bar scroll down one rangewindow
+ if (_vertScrollBar)
+ {
+ int window = _vertScrollBar->GetRangeWindow();
+ int newval = _vertScrollBar->GetValue();
+ int linesToMove = window - (val - newval);
+ _vertScrollBar->SetValue(val - linesToMove - 1);
+ }
+ break;
+
+ }
+ case KEY_PAGEDOWN:
+ {
+ int val = 0;
+ fallThrough = (!_multiline) && (!_vertScrollBar);
+ if (_vertScrollBar)
+ {
+ val = _vertScrollBar->GetValue();
+ }
+
+ if (_multiline)
+ {
+ int displayLines = GetTall() / (surface()->GetFontTall(_font) + DRAW_OFFSET_Y);
+ // move the cursor down
+ for (int i=0; i < displayLines; i++)
+ {
+ GotoDown();
+ }
+ }
+
+ // if there is a scroll bar scroll down one rangewindow
+ if (_vertScrollBar)
+ {
+ int window = _vertScrollBar->GetRangeWindow();
+ int newval = _vertScrollBar->GetValue();
+ int linesToMove = window - (newval - val);
+ _vertScrollBar->SetValue(val + linesToMove + 1);
+ }
+ break;
+ }
+
+ case KEY_F1:
+ case KEY_F2:
+ case KEY_F3:
+ case KEY_F4:
+ case KEY_F5:
+ case KEY_F6:
+ case KEY_F7:
+ case KEY_F8:
+ case KEY_F9:
+ case KEY_F10:
+ case KEY_F11:
+ case KEY_F12:
+ {
+ fallThrough = true;
+ break;
+ }
+
+ default:
+ {
+ // return if any other char is pressed.
+ // as it will be a unicode char.
+ // and we don't want select[1] changed unless a char was pressed that this fxn handles
+ return;
+ }
+ }
+ }
+
+ // select[1] is the location in the line where the blinking cursor started
+ _select[1] = _cursorPos;
+
+ if (_dataChanged)
+ {
+ FireActionSignal();
+ }
+
+ // chain back on some keys
+ if (fallThrough)
+ {
+ _putCursorAtEnd=_cursorIsAtEnd; // keep state of cursor on fallthroughs
+ BaseClass::OnKeyCodeTyped(code);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Masks which keys get chained up
+// Maps keyboard input to text window functions.
+//-----------------------------------------------------------------------------
+void TextEntry::OnKeyTyped(wchar_t unichar)
+{
+ _cursorIsAtEnd = _putCursorAtEnd;
+ _putCursorAtEnd=false;
+
+ bool fallThrough = false;
+
+ // KeyCodes handle all non printable chars
+ if (iswcntrl(unichar) || unichar == 9 ) // tab key (code 9) is printable but handled elsewhere
+ return;
+
+ // do readonly keys
+ if (!IsEditable())
+ {
+ BaseClass::OnKeyTyped(unichar);
+ return;
+ }
+
+ if (unichar != 0)
+ {
+ DeleteSelected();
+ SaveUndoState();
+ InsertChar(unichar);
+ }
+
+ // select[1] is the location in the line where the blinking cursor started
+ _select[1] = _cursorPos;
+
+ if (_dataChanged)
+ {
+ FireActionSignal();
+ }
+
+ // chain back on some keys
+ if (fallThrough)
+ {
+ _putCursorAtEnd=_cursorIsAtEnd; // keep state of cursor on fallthroughs
+ BaseClass::OnKeyTyped(unichar);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Scrolls the list according to the mouse wheel movement
+//-----------------------------------------------------------------------------
+void TextEntry::OnMouseWheeled(int delta)
+{
+ if (_vertScrollBar)
+ {
+ MoveScrollBar(delta);
+ }
+ else
+ {
+ // if we don't use the input, chain back
+ BaseClass::OnMouseWheeled(delta);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Scrolls the list
+// Input : delta - amount to move scrollbar up
+//-----------------------------------------------------------------------------
+void TextEntry::MoveScrollBar(int delta)
+{
+ if (_vertScrollBar)
+ {
+ int val = _vertScrollBar->GetValue();
+ val -= (delta * 3);
+ _vertScrollBar->SetValue(val);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called every frame the entry has keyboard focus;
+// blinks the text cursor
+//-----------------------------------------------------------------------------
+void TextEntry::OnKeyFocusTicked()
+{
+ int time=system()->GetTimeMillis();
+ if(time>_cursorNextBlinkTime)
+ {
+ _cursorBlink=!_cursorBlink;
+ _cursorNextBlinkTime=time+_cursorBlinkRate;
+ Repaint();
+ }
+}
+
+Panel *TextEntry::GetDragPanel()
+{
+ if ( input()->IsMouseDown( MOUSE_LEFT ) )
+ {
+ int x, y;
+ input()->GetCursorPos(x, y);
+ ScreenToLocal(x, y);
+ int cursor = PixelToCursorSpace(x, y);
+
+ int cx0, cx1;
+ bool check = GetSelectedRange( cx0, cx1 );
+
+ if ( check && cursor >= cx0 && cursor < cx1 )
+ {
+ // Don't deselect in this case!!!
+ return BaseClass::GetDragPanel();
+ }
+ return NULL;
+ }
+
+ return BaseClass::GetDragPanel();
+}
+
+void TextEntry::OnCreateDragData( KeyValues *msg )
+{
+ BaseClass::OnCreateDragData( msg );
+
+ char txt[ 256 ];
+ GetText( txt, sizeof( txt ) );
+
+ int r0, r1;
+ if ( GetSelectedRange( r0, r1 ) && r0 != r1 )
+ {
+ int len = r1 - r0;
+ if ( len > 0 && r0 < 1024 )
+ {
+ char selection[ 512 ];
+ Q_strncpy( selection, &txt[ r0 ], len + 1 );
+ selection[ len ] = 0;
+ msg->SetString( "text", selection );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if we are selecting text (so we can highlight it)
+//-----------------------------------------------------------------------------
+bool TextEntry::SelectCheck( bool fromMouse /*=false*/ )
+{
+ bool bret = true;
+ if (!HasFocus() || !(input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)))
+ {
+ bool deselect = true;
+ int cx0, cx1;
+ if ( fromMouse &&
+ GetDragPanel() != NULL )
+ {
+ // move the cursor to where the mouse was pressed
+ int x, y;
+ input()->GetCursorPos(x, y);
+ ScreenToLocal(x, y);
+ int cursor = PixelToCursorSpace(x, y);
+
+ bool check = GetSelectedRange( cx0, cx1 );
+
+ if ( check && cursor >= cx0 && cursor < cx1 )
+ {
+ // Don't deselect in this case!!!
+ deselect = false;
+ bret = false;
+ }
+ }
+
+ if ( deselect )
+ {
+ _select[0] = -1;
+ }
+ }
+ else if (_select[0] == -1)
+ {
+ _select[0] = _cursorPos;
+ }
+ return bret;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set the maximum number of chars in the text buffer
+//-----------------------------------------------------------------------------
+void TextEntry::SetMaximumCharCount(int maxChars)
+{
+ _maxCharCount = maxChars;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+int TextEntry::GetMaximumCharCount()
+{
+ return _maxCharCount;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void TextEntry::SetAutoProgressOnHittingCharLimit(bool state)
+{
+ m_bAutoProgressOnHittingCharLimit = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set whether to wrap the text buffer
+//-----------------------------------------------------------------------------
+void TextEntry::SetWrap(bool wrap)
+{
+ _wrap = wrap;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set whether to pass newline msgs to parent
+//-----------------------------------------------------------------------------
+void TextEntry::SendNewLine(bool send)
+{
+ _sendNewLines = send;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Tell if an index is a linebreakindex
+//-----------------------------------------------------------------------------
+bool TextEntry::IsLineBreak(int index)
+{
+ for (int i=0; i<m_LineBreaks.Count(); ++i)
+ {
+ if (index == m_LineBreaks[i])
+ return true;
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the cursor one character to the left, scroll the text
+// horizontally if needed
+//-----------------------------------------------------------------------------
+void TextEntry::GotoLeft()
+{
+ SelectCheck();
+
+ // if we are on a line break just move the cursor to the prev line
+ if (IsLineBreak(_cursorPos))
+ {
+ // if we're already on the prev line at the end dont put it on the end
+ if (!_cursorIsAtEnd)
+ _putCursorAtEnd = true;
+ }
+ // if we are not at Start decrement cursor
+ if (!_putCursorAtEnd && _cursorPos > 0)
+ {
+ _cursorPos--;
+ }
+
+ ScrollLeft();
+
+ ResetCursorBlink();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the cursor one character to the right, scroll the text
+// horizontally if needed
+//-----------------------------------------------------------------------------
+void TextEntry::GotoRight()
+{
+ SelectCheck();
+
+ // if we are on a line break just move the cursor to the next line
+ if (IsLineBreak(_cursorPos))
+ {
+ if (_cursorIsAtEnd)
+ {
+ _putCursorAtEnd = false;
+ }
+ else
+ {
+ // if we are not at end increment cursor
+ if (_cursorPos < m_TextStream.Count())
+ {
+ _cursorPos++;
+ }
+ }
+ }
+ else
+ {
+ // if we are not at end increment cursor
+ if (_cursorPos < m_TextStream.Count())
+ {
+ _cursorPos++;
+ }
+
+ // if we are on a line break move the cursor to end of line
+ if (IsLineBreak(_cursorPos))
+ {
+ if (!_cursorIsAtEnd)
+ _putCursorAtEnd = true;
+ }
+ }
+ // scroll right if we need to
+ ScrollRight();
+
+ ResetCursorBlink();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find out what line the cursor is on
+//-----------------------------------------------------------------------------
+int TextEntry::GetCursorLine()
+{
+ // find which line the cursor is on
+ int cursorLine;
+ for (cursorLine = 0; cursorLine < m_LineBreaks.Count(); cursorLine++)
+ {
+ if (_cursorPos < m_LineBreaks[cursorLine])
+ break;
+ }
+
+ if (_putCursorAtEnd) // correct for when cursor is at end of line rather than Start of next
+ {
+ // we are not at end of buffer, in which case there is no next line to be at the Start of
+ if (_cursorPos != m_TextStream.Count() )
+ cursorLine--;
+ }
+
+ return cursorLine;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the cursor one line up
+//-----------------------------------------------------------------------------
+void TextEntry::GotoUp()
+{
+ SelectCheck();
+
+ if (_cursorIsAtEnd)
+ {
+ if ( (GetCursorLine() - 1 ) == 0) // we are on first line
+ {
+ // stay at end of line
+ _putCursorAtEnd = true;
+ return; // dont move the cursor
+ }
+ else
+ _cursorPos--;
+ }
+
+ int cx, cy;
+ CursorToPixelSpace(_cursorPos, cx, cy);
+
+ // move the cursor to the previous line
+ MoveCursor(GetCursorLine() - 1, cx);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the cursor one line down
+//-----------------------------------------------------------------------------
+void TextEntry::GotoDown()
+{
+ SelectCheck();
+
+ if (_cursorIsAtEnd)
+ {
+ _cursorPos--;
+ if (_cursorPos < 0)
+ _cursorPos = 0;
+ }
+
+ int cx, cy;
+ CursorToPixelSpace(_cursorPos, cx, cy);
+
+ // move the cursor to the next line
+ MoveCursor(GetCursorLine() + 1, cx);
+ if (!_putCursorAtEnd && _cursorIsAtEnd )
+ {
+ _cursorPos++;
+ if (_cursorPos > m_TextStream.Count())
+ {
+ _cursorPos = m_TextStream.Count();
+ }
+ }
+ LayoutVerticalScrollBarSlider();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the starting ypixel positon for a walk through the window
+//-----------------------------------------------------------------------------
+int TextEntry::GetYStart()
+{
+ if (_multiline)
+ {
+ // just Start from the top
+ return DRAW_OFFSET_Y;
+ }
+
+ int fontTall = surface()->GetFontTall(_font);
+ return (GetTall() / 2) - (fontTall / 2);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the cursor to a line, need to know how many pixels are in a line
+//-----------------------------------------------------------------------------
+void TextEntry::MoveCursor(int line, int pixelsAcross)
+{
+ // clamp to a valid line
+ if (line < 0)
+ line = 0;
+ if (line >= m_LineBreaks.Count())
+ line = m_LineBreaks.Count() -1;
+
+ // walk the whole text set looking for our place
+ // work out where to Start checking
+
+ int yStart = GetYStart();
+
+ int x = DRAW_OFFSET_X, y = yStart;
+ int lineBreakIndexIndex = 0;
+ _pixelsIndent = 0;
+ int i;
+ for ( i = 0; i < m_TextStream.Count(); i++)
+ {
+ wchar_t ch = m_TextStream[i];
+
+ if (_hideText)
+ {
+ ch = '*';
+ }
+
+ // if we've passed a line break go to that
+ if (m_LineBreaks[lineBreakIndexIndex] == i)
+ {
+ if (lineBreakIndexIndex == line)
+ {
+ _putCursorAtEnd = true;
+ _cursorPos = i;
+ break;
+ }
+
+ // add another line
+ AddAnotherLine(x,y);
+ lineBreakIndexIndex++;
+
+ }
+
+ // add to the current position
+ int charWidth = getCharWidth(_font, ch);
+
+ if (line == lineBreakIndexIndex)
+ {
+ // check to see if we're in range
+ if ((x + (charWidth / 2)) > pixelsAcross)
+ {
+ // found position
+ _cursorPos = i;
+ break;
+ }
+ }
+
+ x += charWidth;
+ }
+
+ // if we never find the cursor it must be past the end
+ // of the text buffer, to let's just slap it on the end of the text buffer then.
+ if (i == m_TextStream.Count())
+ {
+ GotoTextEnd();
+ }
+
+ LayoutVerticalScrollBarSlider();
+ ResetCursorBlink();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turn horizontal scrolling on or off.
+// Horizontal scrolling is disabled in multline windows.
+// Toggling this will disable it in single line windows as well.
+//-----------------------------------------------------------------------------
+void TextEntry::SetHorizontalScrolling(bool status)
+{
+ _horizScrollingAllowed = status;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Horizontal scrolling function, not used in multiline windows
+// Function will scroll the buffer to the left if the cursor is not in the window
+// scroll left if we need to
+//-----------------------------------------------------------------------------
+void TextEntry::ScrollLeft()
+{
+ if (_multiline) // early out
+ {
+ return;
+ }
+
+ if (!_horizScrollingAllowed) //early out
+ {
+ return;
+ }
+
+ if(_cursorPos < _currentStartIndex) // scroll left if we need to
+ {
+ if (_cursorPos < 0)// dont scroll past the Start of buffer
+ {
+ _cursorPos=0;
+ }
+ _currentStartIndex = _cursorPos;
+ }
+
+ LayoutVerticalScrollBarSlider();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TextEntry::ScrollLeftForResize()
+{
+ if (_multiline) // early out
+ {
+ return;
+ }
+
+ if (!_horizScrollingAllowed) //early out
+ {
+ return;
+ }
+
+ while (_currentStartIndex > 0) // go until we hit leftmost
+ {
+ _currentStartIndex--;
+ int nVal = _currentStartIndex;
+
+ // check if the cursor is now off the screen
+ if (IsCursorOffRightSideOfWindow(_cursorPos))
+ {
+ _currentStartIndex++; // we've gone too far, return it
+ break;
+ }
+
+ // IsCursorOffRightSideOfWindow actually fixes the _currentStartIndex,
+ // so if our value changed that menas we really are off the screen
+ if (nVal != _currentStartIndex)
+ break;
+ }
+ LayoutVerticalScrollBarSlider();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Horizontal scrolling function, not used in multiline windows
+// Scroll one char right until the cursor is visible in the window.
+// We do this one char at a time because char width isn't a constant.
+//-----------------------------------------------------------------------------
+void TextEntry::ScrollRight()
+{
+ if (!_horizScrollingAllowed)
+ return;
+
+ if (_multiline)
+ {
+ }
+ // check if cursor is off the right side of window
+ else if (IsCursorOffRightSideOfWindow(_cursorPos))
+ {
+ _currentStartIndex++; //scroll over
+ ScrollRight(); // scroll again, check if cursor is in window yet
+ }
+
+ LayoutVerticalScrollBarSlider();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check and see if cursor position is off the right side of the window
+// just compare cursor's pixel coords with the window size coords.
+// Input: an integer cursor Position, if you pass _cursorPos fxn will tell you
+// if current cursor is outside window.
+// Output: true: cursor is outside right edge or window
+// false: cursor is inside right edge
+//-----------------------------------------------------------------------------
+bool TextEntry::IsCursorOffRightSideOfWindow(int cursorPos)
+{
+ int cx, cy;
+ CursorToPixelSpace(cursorPos, cx, cy);
+ int wx=GetWide()-1; //width of inside of window is GetWide()-1
+ if ( wx <= 0 )
+ return false;
+
+ return (cx >= wx);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check and see if cursor position is off the left side of the window
+// just compare cursor's pixel coords with the window size coords.
+// Input: an integer cursor Position, if you pass _cursorPos fxn will tell you
+// if current cursor is outside window.
+// Output: true - cursor is outside left edge or window
+// false - cursor is inside left edge
+//-----------------------------------------------------------------------------
+bool TextEntry::IsCursorOffLeftSideOfWindow(int cursorPos)
+{
+ int cx, cy;
+ CursorToPixelSpace(cursorPos, cx, cy);
+ return (cx <= 0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the cursor over to the Start of the next word to the right
+//-----------------------------------------------------------------------------
+void TextEntry::GotoWordRight()
+{
+ SelectCheck();
+
+ // search right until we hit a whitespace character or a newline
+ while (++_cursorPos < m_TextStream.Count())
+ {
+ if (iswspace(m_TextStream[_cursorPos]))
+ break;
+ }
+
+ // search right until we hit an nonspace character
+ while (++_cursorPos < m_TextStream.Count())
+ {
+ if (!iswspace(m_TextStream[_cursorPos]))
+ break;
+ }
+
+ if (_cursorPos > m_TextStream.Count())
+ _cursorPos = m_TextStream.Count();
+
+ // now we are at the start of the next word
+
+ // scroll right if we need to
+ ScrollRight();
+
+ LayoutVerticalScrollBarSlider();
+ ResetCursorBlink();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the cursor over to the Start of the next word to the left
+//-----------------------------------------------------------------------------
+void TextEntry::GotoWordLeft()
+{
+ SelectCheck();
+
+ if (_cursorPos < 1)
+ return;
+
+ // search left until we hit an nonspace character
+ while (--_cursorPos >= 0)
+ {
+ if (!iswspace(m_TextStream[_cursorPos]))
+ break;
+ }
+
+ // search left until we hit a whitespace character
+ while (--_cursorPos >= 0)
+ {
+ if (iswspace(m_TextStream[_cursorPos]))
+ {
+ break;
+ }
+ }
+
+ // we end one character off
+ _cursorPos++;
+ // now we are at the Start of the previous word
+
+
+ // scroll left if we need to
+ ScrollLeft();
+
+ LayoutVerticalScrollBarSlider();
+ ResetCursorBlink();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move cursor to the Start of the text buffer
+//-----------------------------------------------------------------------------
+void TextEntry::GotoTextStart()
+{
+ SelectCheck();
+ _cursorPos = 0; // set cursor to Start
+ _putCursorAtEnd = false;
+ _currentStartIndex=0; // scroll over to Start
+
+ LayoutVerticalScrollBarSlider();
+ ResetCursorBlink();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move cursor to the end of the text buffer
+//-----------------------------------------------------------------------------
+void TextEntry::GotoTextEnd()
+{
+ SelectCheck();
+ _cursorPos=m_TextStream.Count(); // set cursor to end of buffer
+ _putCursorAtEnd = true; // move cursor Start of next line
+ ScrollRight(); // scroll over until cursor is on screen
+
+ LayoutVerticalScrollBarSlider();
+ ResetCursorBlink();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move cursor to the Start of the current line
+//-----------------------------------------------------------------------------
+void TextEntry::GotoFirstOfLine()
+{
+ SelectCheck();
+ // to get to the Start of the line you have to take into account line wrap
+ // we have to figure out at which point the line wraps
+ // given the current cursor position, select[1], find the index that is the
+ // line Start to the left of the cursor
+ //_cursorPos = 0; //TODO: this is wrong, should go to first non-whitespace first, then to zero
+ _cursorPos = GetCurrentLineStart();
+ _putCursorAtEnd = false;
+
+ _currentStartIndex=_cursorPos;
+
+ LayoutVerticalScrollBarSlider();
+ ResetCursorBlink();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the index of the first char on the current line
+//-----------------------------------------------------------------------------
+int TextEntry::GetCurrentLineStart()
+{
+ if (!_multiline) // quick out for non multline buffers
+ return _currentStartIndex;
+
+ int i;
+ if (IsLineBreak(_cursorPos))
+ {
+ for (i = 0; i < m_LineBreaks.Count(); ++i )
+ {
+ if (_cursorPos == m_LineBreaks[i])
+ break;
+ }
+ if (_cursorIsAtEnd)
+ {
+ if (i > 0)
+ {
+ return m_LineBreaks[i-1];
+ }
+ return m_LineBreaks[0];
+ }
+ else
+ return _cursorPos; // we are already at Start
+ }
+
+ for ( i = 0; i < m_LineBreaks.Count(); ++i )
+ {
+ if (_cursorPos < m_LineBreaks[i])
+ {
+ if (i == 0)
+ return 0;
+ else
+ return m_LineBreaks[i-1];
+ }
+ }
+ // if there were no line breaks, the first char in the line is the Start of the buffer
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move cursor to the end of the current line
+//-----------------------------------------------------------------------------
+void TextEntry::GotoEndOfLine()
+{
+ SelectCheck();
+ // to get to the end of the line you have to take into account line wrap in the buffer
+ // we have to figure out at which point the line wraps
+ // given the current cursor position, select[1], find the index that is the
+ // line end to the right of the cursor
+ //_cursorPos=m_TextStream.Count(); //TODO: this is wrong, should go to last non-whitespace, then to true EOL
+ _cursorPos = GetCurrentLineEnd();
+ _putCursorAtEnd = true;
+
+ ScrollRight();
+
+ LayoutVerticalScrollBarSlider();
+ ResetCursorBlink();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the index of the last char on the current line
+//-----------------------------------------------------------------------------
+int TextEntry::GetCurrentLineEnd()
+{
+ int i;
+ if (IsLineBreak(_cursorPos) )
+ {
+ for ( i = 0; i < m_LineBreaks.Count()-1; ++i )
+ {
+ if (_cursorPos == m_LineBreaks[i])
+ break;
+ }
+ if (!_cursorIsAtEnd)
+ {
+ if (i == m_LineBreaks.Count()-2 )
+ m_TextStream.Count();
+ else
+ return m_LineBreaks[i+1];
+ }
+ else
+ return _cursorPos; // we are already at end
+ }
+
+ for ( i = 0; i < m_LineBreaks.Count()-1; i++ )
+ {
+ if ( _cursorPos < m_LineBreaks[i])
+ {
+ return m_LineBreaks[i];
+ }
+ }
+ return m_TextStream.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Insert a character into the text buffer
+//-----------------------------------------------------------------------------
+void TextEntry::InsertChar(wchar_t ch)
+{
+ // throw away redundant linefeed characters
+ if (ch == '\r')
+ return;
+
+ // no newline characters in single-line dialogs
+ if (!_multiline && ch == '\n')
+ return;
+
+ // no tab characters
+ if (ch == '\t')
+ return;
+
+ if (m_bAllowNumericInputOnly)
+ {
+ if (!iswdigit(ch) && ((char)ch != '.'))
+ {
+ surface()->PlaySound("Resource\\warning.wav");
+ return;
+ }
+ }
+
+ // check against unicode characters
+ if (!m_bAllowNonAsciiCharacters)
+ {
+ if (ch > 127)
+ return;
+ }
+
+ // don't add characters if the max char count has been reached
+ // ding at the user
+ if (_maxCharCount > -1 && m_TextStream.Count() >= _maxCharCount)
+ {
+ if (_maxCharCount>0 && _multiline && _wrap)
+ {
+ // if we wrap lines rather than stopping
+ while (m_TextStream.Count() > _maxCharCount)
+ {
+ if (_recalculateBreaksIndex==0)
+ {
+ // we can get called before this has been run for the first time :)
+ RecalculateLineBreaks();
+ }
+ if (m_LineBreaks[0]> m_TextStream.Count())
+ {
+ // if the line break is the past the end of the buffer recalc
+ _recalculateBreaksIndex=-1;
+ RecalculateLineBreaks();
+ }
+
+ if (m_LineBreaks[0]+1 < m_TextStream.Count())
+ {
+ // delete the line
+ m_TextStream.RemoveMultiple(0, m_LineBreaks[0]);
+
+ // in case we just deleted text from where the cursor is
+ if (_cursorPos> m_TextStream.Count())
+ {
+ _cursorPos = m_TextStream.Count();
+ }
+ else
+ { // shift the cursor up. don't let it wander past zero
+ _cursorPos-=m_LineBreaks[0]+1;
+ if (_cursorPos<0)
+ {
+ _cursorPos=0;
+ }
+ }
+
+ // move any selection area up
+ if(_select[0]>-1)
+ {
+ _select[0] -=m_LineBreaks[0]+1;
+
+ if(_select[0] <=0)
+ {
+ _select[0] =-1;
+ }
+
+ _select[1] -=m_LineBreaks[0]+1;
+ if(_select[1] <=0)
+ {
+ _select[1] =-1;
+ }
+
+ }
+
+ // now redraw the buffer
+ for (int i = m_TextStream.Count() - 1; i >= 0; i--)
+ {
+ SetCharAt(m_TextStream[i], i+1);
+ }
+
+ // redo all the line breaks
+ _recalculateBreaksIndex=-1;
+ RecalculateLineBreaks();
+
+ }
+ }
+
+ }
+ else
+ {
+ // make a sound
+ // we've hit the max character limit
+ surface()->PlaySound("Resource\\warning.wav");
+ return;
+ }
+ }
+
+
+ if (_wrap)
+ {
+ // when wrapping you always insert the new char at the end of the buffer
+ SetCharAt(ch, m_TextStream.Count());
+ _cursorPos=m_TextStream.Count();
+ }
+ else
+ {
+ // move chars right 1 starting from cursor, then replace cursorPos with char and increment cursor
+ for (int i = m_TextStream.Count()- 1; i >= _cursorPos; i--)
+ {
+ SetCharAt(m_TextStream[i], i+1);
+ }
+
+ SetCharAt(ch, _cursorPos);
+ _cursorPos++;
+ }
+
+ // if its a newline char we can't do the slider until we recalc the line breaks
+ if (ch == '\n')
+ {
+ RecalculateLineBreaks();
+ }
+
+ // see if we've hit the char limit
+ if (m_bAutoProgressOnHittingCharLimit && m_TextStream.Count() == _maxCharCount)
+ {
+ // move the next panel (most likely another TextEntry)
+ RequestFocusNext();
+ }
+
+ // scroll right if this pushed the cursor off screen
+ ScrollRight();
+
+ _dataChanged = true;
+
+ CalcBreakIndex();
+ LayoutVerticalScrollBarSlider();
+ ResetCursorBlink();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the lineBreakIndex index of the line before the cursor
+// note _recalculateBreaksIndex < 0 flags RecalculateLineBreaks
+// to figure it all out from scratch
+//-----------------------------------------------------------------------------
+void TextEntry::CalcBreakIndex()
+{
+ // an optimization to handle when the cursor is at the end of the buffer.
+ // pays off if the buffer is large, and the search loop would be long.
+ if (_cursorPos == m_TextStream.Count())
+ {
+ // we know m_LineBreaks array always has at least one element in it (99999 sentinel)
+ // when there is just one line this will make recalc = -1 which is ok.
+ _recalculateBreaksIndex = m_LineBreaks.Count()-2;
+ return;
+ }
+
+ _recalculateBreaksIndex=0;
+ // find the line break just before the cursor position
+ while (_cursorPos > m_LineBreaks[_recalculateBreaksIndex])
+ ++_recalculateBreaksIndex;
+
+ // -1 is ok.
+ --_recalculateBreaksIndex;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Insert a string into the text buffer, this is just a series
+// of char inserts because we have to check each char is ok to insert
+//-----------------------------------------------------------------------------
+void TextEntry::InsertString(const wchar_t *wszText)
+{
+ SaveUndoState();
+
+ for (const wchar_t *ch = wszText; *ch != 0; ++ch)
+ {
+ InsertChar(*ch);
+ }
+
+ if (_dataChanged)
+ {
+ FireActionSignal();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Converts an ansi string to unicode and inserts it into the text stream
+//-----------------------------------------------------------------------------
+void TextEntry::InsertString(const char *text)
+{
+ // check for to see if the string is in the localization tables
+ if (text[0] == '#')
+ {
+ wchar_t *wsz = g_pVGuiLocalize->Find(text);
+ if (wsz)
+ {
+ InsertString(wsz);
+ return;
+ }
+ }
+
+ // straight convert the ansi to unicode and insert
+ wchar_t unicode[1024];
+ g_pVGuiLocalize->ConvertANSIToUnicode(text, unicode, sizeof(unicode));
+ InsertString(unicode);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle the effect of user hitting backspace key
+// we delete the char before the cursor and reformat the text so it
+// behaves like in windows.
+//-----------------------------------------------------------------------------
+void TextEntry::Backspace()
+{
+ if (!IsEditable())
+ return;
+
+ //if you are at the first position don't do anything
+ if(_cursorPos==0)
+ {
+ return;
+ }
+
+ //if the line is empty, don't do anything
+ if(m_TextStream.Count()==0)
+ {
+ return;
+ }
+
+ SaveUndoState();
+
+ //shift chars left one, starting at the cursor position, then make the line one smaller
+ for(int i=_cursorPos;i<m_TextStream.Count(); ++i)
+ {
+ SetCharAt(m_TextStream[i],i-1);
+ }
+ m_TextStream.Remove(m_TextStream.Count() - 1);
+
+ // As we hit the Start of the window, expose more chars so we can see what we are deleting
+ if (_cursorPos==_currentStartIndex)
+ {
+ // windows tabs over 6 chars
+ if (_currentStartIndex-6 >= 0) // dont scroll if there are not enough chars to scroll
+ {
+ _currentStartIndex-=6;
+ }
+ else
+ _currentStartIndex=0;
+ }
+
+ //move the cursor left one
+ _cursorPos--;
+
+ _dataChanged = true;
+
+ // recalculate linebreaks (the fast incremental linebreak function doesn't work in this case)
+ _recalculateBreaksIndex = 0;
+ m_LineBreaks.RemoveAll();
+ m_LineBreaks.AddToTail(BUFFER_SIZE);
+
+ LayoutVerticalScrollBarSlider();
+ ResetCursorBlink();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deletes the current selection, if any, moving the cursor to the Start
+// of the selection
+//-----------------------------------------------------------------------------
+void TextEntry::DeleteSelected()
+{
+ if (!IsEditable())
+ return;
+
+ // if the line is empty, don't do anything
+ if (m_TextStream.Count() == 0)
+ return;
+
+ // get the range to delete
+ int x0, x1;
+ if (!GetSelectedRange(x0, x1))
+ {
+ // no selection, don't touch anything
+ return;
+ }
+
+ SaveUndoState();
+
+ // shift chars left one starting after cursor position, then make the line one smaller
+ int dif = x1 - x0;
+ for (int i = 0; i < dif; ++i)
+ {
+ m_TextStream.Remove(x0);
+ }
+
+ // clear any selection
+ SelectNone();
+ ResetCursorBlink();
+
+ // move the cursor to just after the deleted section
+ _cursorPos = x0;
+
+ _dataChanged = true;
+
+ _recalculateBreaksIndex = 0;
+ m_LineBreaks.RemoveAll();
+ m_LineBreaks.AddToTail(BUFFER_SIZE);
+
+ CalcBreakIndex();
+
+ LayoutVerticalScrollBarSlider();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle the effect of the user hitting the delete key
+// removes the char in front of the cursor
+//-----------------------------------------------------------------------------
+void TextEntry::Delete()
+{
+ if (!IsEditable())
+ return;
+
+ // if the line is empty, don't do anything
+ if (m_TextStream.Count() == 0)
+ return;
+
+ // get the range to delete
+ int x0, x1;
+ if (!GetSelectedRange(x0, x1))
+ {
+ // no selection, so just delete the one character
+ x0 = _cursorPos;
+ x1 = x0 + 1;
+
+ // if we're at the end of the line don't do anything
+ if (_cursorPos >= m_TextStream.Count())
+ return;
+ }
+
+ SaveUndoState();
+
+ // shift chars left one starting after cursor position, then make the line one smaller
+ int dif = x1 - x0;
+ for (int i = 0; i < dif; i++)
+ {
+ m_TextStream.Remove((int)x0);
+ }
+
+ ResetCursorBlink();
+
+ // clear any selection
+ SelectNone();
+
+ // move the cursor to just after the deleted section
+ _cursorPos = x0;
+
+ _dataChanged = true;
+
+ _recalculateBreaksIndex = 0;
+ m_LineBreaks.RemoveAll();
+ m_LineBreaks.AddToTail(BUFFER_SIZE);
+
+ CalcBreakIndex();
+
+ LayoutVerticalScrollBarSlider();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Declare a selection empty
+//-----------------------------------------------------------------------------
+void TextEntry::SelectNone()
+{
+ // tag the selection as empty
+ _select[0] = -1;
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Load in the selection range so cx0 is the Start and cx1 is the end
+// from smallest to highest (right to left)
+//-----------------------------------------------------------------------------
+bool TextEntry::GetSelectedRange(int& cx0,int& cx1)
+{
+ // if there is nothing selected return false
+ if (_select[0] == -1)
+ {
+ return false;
+ }
+
+ // sort the two position so cx0 is the smallest
+ cx0=_select[0];
+ cx1=_select[1];
+ int temp;
+ if(cx1<cx0){temp=cx0;cx0=cx1;cx1=temp;}
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Opens the cut/copy/paste dropdown menu
+//-----------------------------------------------------------------------------
+void TextEntry::OpenEditMenu()
+{
+ // get cursor position, this is local to this text edit window
+ int cursorX, cursorY;
+ input()->GetCursorPos(cursorX, cursorY);
+
+ /* !! disabled since it recursively gets panel pointers, potentially across dll boundaries,
+ and doesn't need to be necessary (it's just for handling windowed mode)
+
+ // find the frame that has no parent (the one on the desktop)
+ Panel *panel = this;
+ while ( panel->GetParent() != NULL)
+ {
+ panel = panel->GetParent();
+ }
+ panel->ScreenToLocal(cursorX, cursorY);
+ int x, y;
+ // get base panel's postition
+ panel->GetPos(x, y);
+
+ // adjust our cursor position accordingly
+ cursorX += x;
+ cursorY += y;
+ */
+
+ int x0, x1;
+ if (GetSelectedRange(x0, x1)) // there is something selected
+ {
+ m_pEditMenu->SetItemEnabled("&Cut", true);
+ m_pEditMenu->SetItemEnabled("C&opy", true);
+ }
+ else // there is nothing selected, disable cut/copy options
+ {
+ m_pEditMenu->SetItemEnabled("&Cut", false);
+ m_pEditMenu->SetItemEnabled("C&opy", false);
+ }
+ m_pEditMenu->SetVisible(true);
+ m_pEditMenu->RequestFocus();
+
+ // relayout the menu immediately so that we know it's size
+ m_pEditMenu->InvalidateLayout(true);
+ int menuWide, menuTall;
+ m_pEditMenu->GetSize(menuWide, menuTall);
+
+ // work out where the cursor is and therefore the best place to put the menu
+ int wide, tall;
+ surface()->GetScreenSize(wide, tall);
+
+ if (wide - menuWide > cursorX)
+ {
+ // menu hanging right
+ if (tall - menuTall > cursorY)
+ {
+ // menu hanging down
+ m_pEditMenu->SetPos(cursorX, cursorY);
+ }
+ else
+ {
+ // menu hanging up
+ m_pEditMenu->SetPos(cursorX, cursorY - menuTall);
+ }
+ }
+ else
+ {
+ // menu hanging left
+ if (tall - menuTall > cursorY)
+ {
+ // menu hanging down
+ m_pEditMenu->SetPos(cursorX - menuWide, cursorY);
+ }
+ else
+ {
+ // menu hanging up
+ m_pEditMenu->SetPos(cursorX - menuWide, cursorY - menuTall);
+ }
+ }
+
+ m_pEditMenu->RequestFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Cuts the selected chars from the buffer and
+// copies them into the clipboard
+//-----------------------------------------------------------------------------
+void TextEntry::CutSelected()
+{
+ CopySelected();
+ DeleteSelected();
+ // have to request focus if we used the menu
+ RequestFocus();
+
+ if ( _dataChanged )
+ {
+ FireActionSignal();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Copies the selected chars into the clipboard
+//-----------------------------------------------------------------------------
+void TextEntry::CopySelected()
+{
+ if (_hideText)
+ return;
+
+ int x0, x1;
+ if (GetSelectedRange(x0, x1))
+ {
+ CUtlVector<wchar_t> buf;
+ for (int i = x0; i < x1; i++)
+ {
+ if ( m_TextStream[i]=='\n')
+ {
+ buf.AddToTail( '\r' );
+ }
+ buf.AddToTail(m_TextStream[i]);
+ }
+ buf.AddToTail('\0');
+ system()->SetClipboardText(buf.Base(), x1 - x0);
+ }
+
+ // have to request focus if we used the menu
+ RequestFocus();
+
+ if ( _dataChanged )
+ {
+ FireActionSignal();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Pastes the selected chars from the clipboard into the text buffer
+// truncates if text is longer than our _maxCharCount
+//-----------------------------------------------------------------------------
+void TextEntry::Paste()
+{
+ if (!IsEditable())
+ return;
+
+ CUtlVector<wchar_t> buf;
+ int bufferSize = system()->GetClipboardTextCount();
+ if (!m_bAutoProgressOnHittingCharLimit)
+ {
+ bufferSize = _maxCharCount > 0 ? _maxCharCount + 1 : system()->GetClipboardTextCount(); // +1 for terminator
+ }
+
+ buf.AddMultipleToTail(bufferSize);
+ int len = system()->GetClipboardText(0, buf.Base(), bufferSize * sizeof(wchar_t));
+ if (len < 1)
+ return;
+
+ SaveUndoState();
+ bool bHaveMovedFocusAwayFromCurrentEntry = false;
+
+ // insert all the characters
+ for (int i = 0; i < len && buf[i] != 0; i++)
+ {
+ if (m_bAutoProgressOnHittingCharLimit)
+ {
+ // see if we're about to hit the char limit
+ if (m_TextStream.Count() == _maxCharCount)
+ {
+ // move the next panel (most likely another TextEntry)
+ RequestFocusNext();
+ // copy the remainder into the clipboard
+ wchar_t *remainingText = &buf[i];
+ system()->SetClipboardText(remainingText, len - i - 1);
+ // set the next entry to paste
+ if (GetVParent() && ipanel()->GetCurrentKeyFocus(GetVParent()) != GetVPanel())
+ {
+ bHaveMovedFocusAwayFromCurrentEntry = true;
+ ipanel()->SendMessage(ipanel()->GetCurrentKeyFocus(GetVParent()), new KeyValues("DoPaste"), GetVPanel());
+ }
+ break;
+ }
+ }
+
+ // insert the character
+ InsertChar(buf[i]);
+ }
+
+ // restore the original clipboard text if neccessary
+ if (m_bAutoProgressOnHittingCharLimit)
+ {
+ system()->SetClipboardText(buf.Base(), bufferSize);
+ }
+
+ _dataChanged = true;
+ FireActionSignal();
+
+ if (!bHaveMovedFocusAwayFromCurrentEntry)
+ {
+ // have to request focus if we used the menu
+ RequestFocus();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Reverts back to last saved changes
+//-----------------------------------------------------------------------------
+void TextEntry::Undo()
+{
+ _cursorPos = _undoCursorPos;
+ m_TextStream.CopyArray(m_UndoTextStream.Base(), m_UndoTextStream.Count());
+
+ InvalidateLayout();
+ Repaint();
+ SelectNone();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Saves the current state to the undo stack
+//-----------------------------------------------------------------------------
+void TextEntry::SaveUndoState()
+{
+ _undoCursorPos = _cursorPos;
+ m_UndoTextStream.CopyArray(m_TextStream.Base(), m_TextStream.Count());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the index in the text buffer of the
+// character the drawing should Start at
+//-----------------------------------------------------------------------------
+int TextEntry::GetStartDrawIndex(int &lineBreakIndexIndex)
+{
+ int startIndex = 0;
+
+ int numLines = m_LineBreaks.Count();
+ int startLine = 0;
+
+ // determine the Start point from the scroll bar
+ // do this only if we are not selecting text in the window with the mouse
+ if (_vertScrollBar && !_mouseDragSelection)
+ {
+ // skip to line indicated by scrollbar
+ startLine = _vertScrollBar->GetValue();
+ }
+ else
+ {
+ // check to see if the cursor is off the screen-multiline case
+ HFont font = _font;
+ int displayLines = GetTall() / (surface()->GetFontTall(font) + DRAW_OFFSET_Y);
+ if (displayLines < 1)
+ {
+ displayLines = 1;
+ }
+ if (numLines > displayLines)
+ {
+ int cursorLine = GetCursorLine();
+
+ startLine = _currentStartLine;
+
+ // see if that is visible
+ if (cursorLine < _currentStartLine)
+ {
+ // cursor is above visible area; scroll back
+ startLine = cursorLine;
+ if (_vertScrollBar)
+ {
+ MoveScrollBar( 1 ); // should be calibrated for speed
+ // adjust startline incase we hit a limit
+ startLine = _vertScrollBar->GetValue();
+ }
+ }
+ else if (cursorLine > (_currentStartLine + displayLines - 1))
+ {
+ // cursor is down below visible area; scroll forward
+ startLine = cursorLine - displayLines + 1;
+ if (_vertScrollBar)
+ {
+ MoveScrollBar( -1 );
+ startLine = _vertScrollBar->GetValue();
+ }
+ }
+ }
+ else if (!_multiline)
+ {
+ // check to see if cursor is off the right side of screen-single line case
+ // get cursor's x coordinate in pixel space
+ bool done = false;
+ while ( !done )
+ {
+ done = true;
+ int x = DRAW_OFFSET_X;
+ for (int i = _currentStartIndex; i < m_TextStream.Count(); i++)
+ {
+ done = false;
+ wchar_t ch = m_TextStream[i];
+ if (_hideText)
+ {
+ ch = '*';
+ }
+
+ // if we've found the position, break
+ if (_cursorPos == i)
+ {
+ break;
+ }
+
+ // add to the current position
+ x += getCharWidth(font, ch);
+ }
+
+ if ( x >= GetWide() )
+ {
+ _currentStartIndex++;
+ // Keep searching...
+ continue;
+ }
+
+ if ( x <= 0 )
+ {
+ // dont go past the Start of buffer
+ if (_currentStartIndex > 0)
+ _currentStartIndex--;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (startLine > 0)
+ {
+ lineBreakIndexIndex = startLine;
+ if (startLine && startLine < m_LineBreaks.Count())
+ {
+ startIndex = m_LineBreaks[startLine - 1];
+ }
+ }
+
+ if (!_horizScrollingAllowed)
+ return 0;
+
+ _currentStartLine = startLine;
+ if (_multiline)
+ return startIndex;
+ else
+ return _currentStartIndex;
+
+
+}
+
+// helper accessors for common gets
+float TextEntry::GetValueAsFloat()
+{
+ int nTextLength = GetTextLength() + 1;
+ char* txt = ( char* )_alloca( nTextLength * sizeof( char ) );
+ GetText( txt, nTextLength );
+
+ return V_atof( txt );
+}
+
+int TextEntry::GetValueAsInt()
+{
+ int nTextLength = GetTextLength() + 1;
+ char* txt = ( char* )_alloca( nTextLength * sizeof( char ) );
+ GetText( txt, nTextLength );
+
+ return V_atoi( txt );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a string from text buffer
+// Input: offset - index to Start reading from
+// bufLenInBytes - length of string
+//-----------------------------------------------------------------------------
+void TextEntry::GetText(OUT_Z_BYTECAP(bufLenInBytes) char *buf, int bufLenInBytes)
+{
+ Assert(bufLenInBytes >= sizeof(buf[0]));
+ if (m_TextStream.Count())
+ {
+ // temporarily null terminate the text stream so we can use the conversion function
+ int nullTerminatorIndex = m_TextStream.AddToTail((wchar_t)0);
+ g_pVGuiLocalize->ConvertUnicodeToANSI(m_TextStream.Base(), buf, bufLenInBytes);
+ m_TextStream.FastRemove(nullTerminatorIndex);
+ }
+ else
+ {
+ // no characters in the stream
+ buf[0] = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a string from text buffer
+// Input: offset - index to Start reading from
+// bufLen - length of string
+//-----------------------------------------------------------------------------
+void TextEntry::GetText(OUT_Z_BYTECAP(bufLenInBytes) wchar_t *wbuf, int bufLenInBytes)
+{
+ Assert(bufLenInBytes >= sizeof(wbuf[0]));
+ int len = m_TextStream.Count();
+ if (m_TextStream.Count())
+ {
+ int terminator = min(len, (bufLenInBytes / (int)sizeof(wchar_t)) - 1);
+ wcsncpy(wbuf, m_TextStream.Base(), terminator);
+ wbuf[terminator] = 0;
+ }
+ else
+ {
+ wbuf[0] = 0;
+ }
+}
+
+void TextEntry::GetTextRange( wchar_t *buf, int from, int numchars )
+{
+ int len = m_TextStream.Count();
+ int cpChars = max( 0, min( numchars, len - from ) );
+
+ wcsncpy( buf, m_TextStream.Base() + max( 0, min( len, from ) ), cpChars );
+ buf[ cpChars ] = 0;
+}
+
+void TextEntry::GetTextRange( char *buf, int from, int numchars )
+{
+ int len = m_TextStream.Count();
+ int cpChars = max( 0, min( numchars, len - from ) );
+
+ g_pVGuiLocalize->ConvertUnicodeToANSI( m_TextStream.Base() + max( 0, min( len, from ) ), buf, cpChars + 1 );
+ buf[ cpChars ] = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sends a message that the text has changed
+//-----------------------------------------------------------------------------
+void TextEntry::FireActionSignal()
+{
+ PostActionSignal(new KeyValues("TextChanged"));
+ _dataChanged = false; // reset the data changed flag
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the font of the buffer text
+// Input: font to change to
+//-----------------------------------------------------------------------------
+void TextEntry::SetFont(HFont font)
+{
+ _font = font;
+ InvalidateLayout();
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the scrollbar slider is moved
+//-----------------------------------------------------------------------------
+void TextEntry::OnSliderMoved()
+{
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool TextEntry::RequestInfo(KeyValues *outputData)
+{
+ if (!stricmp(outputData->GetName(), "GetText"))
+ {
+ wchar_t wbuf[256];
+ GetText(wbuf, 255);
+ outputData->SetWString("text", wbuf);
+ return true;
+ }
+ else if (!stricmp(outputData->GetName(), "GetState"))
+ {
+ char buf[64];
+ GetText(buf, sizeof(buf));
+ outputData->SetInt("state", atoi(buf));
+ return true;
+ }
+ return BaseClass::RequestInfo(outputData);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TextEntry::OnSetText(const wchar_t *text)
+{
+ SetText(text);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: as above, but sets an integer
+//-----------------------------------------------------------------------------
+void TextEntry::OnSetState(int state)
+{
+ char buf[64];
+ Q_snprintf(buf, sizeof(buf), "%d", state);
+ SetText(buf);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TextEntry::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings( inResourceData );
+
+ _font = scheme()->GetIScheme( GetScheme() )->GetFont( inResourceData->GetString( "font", "Default" ), IsProportional() );
+ SetFont( _font );
+
+ SetTextHidden((bool)inResourceData->GetInt("textHidden", 0));
+ SetEditable((bool)inResourceData->GetInt("editable", 1));
+ SetMaximumCharCount(inResourceData->GetInt("maxchars", -1));
+ SetAllowNumericInputOnly(inResourceData->GetInt("NumericInputOnly", 0));
+ SetAllowNonAsciiCharacters(inResourceData->GetInt("unicode", 0));
+ SelectAllOnFirstFocus(inResourceData->GetInt("selectallonfirstfocus", 0));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TextEntry::GetSettings( KeyValues *outResourceData )
+{
+ BaseClass::GetSettings( outResourceData );
+ outResourceData->SetInt("textHidden", _hideText);
+ outResourceData->SetInt("editable", IsEditable());
+ outResourceData->SetInt("maxchars", GetMaximumCharCount());
+ outResourceData->SetInt("NumericInputOnly", m_bAllowNumericInputOnly);
+ outResourceData->SetInt("unicode", m_bAllowNonAsciiCharacters);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *TextEntry::GetDescription()
+{
+ static char buf[1024];
+ Q_snprintf(buf, sizeof(buf), "%s, bool textHidden, bool editable, bool unicode, bool NumericInputOnly, int maxchars", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the number of lines in the window
+//-----------------------------------------------------------------------------
+int TextEntry::GetNumLines()
+{
+ return m_LineBreaks.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the height of the text entry window so all text will fit inside
+//-----------------------------------------------------------------------------
+void TextEntry::SetToFullHeight()
+{
+ PerformLayout();
+ int wide, tall;
+ GetSize(wide, tall);
+
+ tall = GetNumLines() * (surface()->GetFontTall(_font) + DRAW_OFFSET_Y) + DRAW_OFFSET_Y + 2;
+ SetSize (wide, tall);
+ PerformLayout();
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Select all the text.
+//-----------------------------------------------------------------------------
+void TextEntry::SelectAllText( bool bResetCursorPos )
+{
+ // if there's no text at all, select none
+ if ( m_TextStream.Count() == 0 )
+ {
+ _select[0] = -1;
+ }
+ else
+ {
+ _select[0] = 0;
+ }
+
+ _select[1] = m_TextStream.Count();
+
+ if ( bResetCursorPos )
+ {
+ _cursorPos = _select[1];
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Select no text.
+//-----------------------------------------------------------------------------
+void TextEntry::SelectNoText()
+{
+ _select[0] = -1;
+ _select[1] = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the width of the text entry window so all text will fit inside
+//-----------------------------------------------------------------------------
+void TextEntry::SetToFullWidth()
+{
+ // probably be problems if you try using this on multi line buffers
+ // or buffers with clickable text in them.
+ if (_multiline)
+ return;
+
+ PerformLayout();
+ int wide = 2*DRAW_OFFSET_X; // buffer on left and right end of text.
+
+ // loop through all the characters and sum their widths
+ for (int i = 0; i < m_TextStream.Count(); ++i)
+ {
+ wide += getCharWidth(_font, m_TextStream[i]);
+ }
+
+ // height of one line of text
+ int tall = (surface()->GetFontTall(_font) + DRAW_OFFSET_Y) + DRAW_OFFSET_Y + 2;
+
+ SetSize (wide, tall);
+ PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TextEntry::SelectAllOnFirstFocus( bool status )
+{
+ _selectAllOnFirstFocus = status;
+}
+
+void TextEntry::SelectAllOnFocusAlways( bool status )
+{
+ _selectAllOnFirstFocus = status;
+ _selectAllOnFocusAlways = status;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when the text entry receives focus
+//-----------------------------------------------------------------------------
+void TextEntry::OnSetFocus()
+{
+ // see if we should highlight all on selection
+ if (_selectAllOnFirstFocus)
+ {
+ _select[1] = m_TextStream.Count();
+ _select[0] = _select[1] > 0 ? 0 : -1;
+ _cursorPos = _select[1]; // cursor at end of line
+ if ( !_selectAllOnFocusAlways )
+ {
+ _selectAllOnFirstFocus = false;
+ }
+ }
+ else if (input()->IsKeyDown(KEY_TAB) || input()->WasKeyReleased(KEY_TAB))
+ {
+ // if we've tabbed to this field then move to the end of the text
+ GotoTextEnd();
+ // clear any selection
+ SelectNone();
+ }
+
+ BaseClass::OnSetFocus();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the width we have to draw text in.
+// Do not use in multiline windows.
+//-----------------------------------------------------------------------------
+void TextEntry::SetDrawWidth(int width)
+{
+ _drawWidth = width;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the width we have to draw text in.
+//-----------------------------------------------------------------------------
+int TextEntry::GetDrawWidth()
+{
+ return _drawWidth;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void TextEntry::SetAllowNonAsciiCharacters(bool state)
+{
+ m_bAllowNonAsciiCharacters = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void TextEntry::SetAllowNumericInputOnly(bool state)
+{
+ m_bAllowNumericInputOnly = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : forward -
+//-----------------------------------------------------------------------------
+void TextEntry::OnChangeIME( bool forward )
+{
+ // Only change ime if Unicode aware
+ if ( m_bAllowNonAsciiCharacters )
+ {
+ input()->OnChangeIME( forward );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : handleValue -
+//-----------------------------------------------------------------------------
+void TextEntry::LanguageChanged( int handleValue )
+{
+ input()->OnChangeIMEByHandle( handleValue );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : handleValue -
+//-----------------------------------------------------------------------------
+void TextEntry::ConversionModeChanged( int handleValue )
+{
+ input()->OnChangeIMEConversionModeByHandle( handleValue );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : handleValue -
+//-----------------------------------------------------------------------------
+void TextEntry::SentenceModeChanged( int handleValue )
+{
+ input()->OnChangeIMESentenceModeByHandle( handleValue );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *compstr -
+//-----------------------------------------------------------------------------
+void TextEntry::CompositionString( const wchar_t *compstr )
+{
+ wcsncpy( m_szComposition, compstr, sizeof( m_szComposition ) / sizeof( wchar_t ) - 1 );
+ m_szComposition[ sizeof( m_szComposition ) / sizeof( wchar_t ) - 1 ] = L'\0';
+}
+
+void TextEntry::ShowIMECandidates()
+{
+ HideIMECandidates();
+
+ int c = input()->GetCandidateListCount();
+ if ( c == 0 )
+ {
+ return;
+ }
+
+ m_pIMECandidates = new Menu( this, "IMECandidatesMenu" );
+
+ int pageStart = input()->GetCandidateListPageStart();
+ int pageSize = input()->GetCandidateListPageSize();
+ int selected = input()->GetCandidateListSelectedItem();
+
+ int startAtOne = input()->CandidateListStartsAtOne() ? 1 : 0;
+
+ if ( ( selected < pageStart ) || ( selected >= pageStart + pageSize ) )
+ {
+ pageStart = ( selected / pageSize ) * pageSize;
+ input()->SetCandidateListPageStart( pageStart );
+ }
+
+ for ( int i = pageStart; i < pageStart + pageSize; ++i )
+ {
+ if ( i >= c )
+ continue;
+
+ bool isSelected = ( i == selected ) ? true : false;
+
+ wchar_t unicode[ 32 ];
+ input()->GetCandidate( i, unicode, sizeof( unicode ) );
+
+ wchar_t label[ 64 ];
+ _snwprintf( label, sizeof( label ) / sizeof( wchar_t ) - 1, L"%i %s", i - pageStart + startAtOne, unicode );
+ label[ sizeof( label ) / sizeof( wchar_t ) - 1 ] = L'\0';
+
+ int id = m_pIMECandidates->AddMenuItem( "Candidate", label, (KeyValues *)NULL, this );
+ if ( isSelected )
+ {
+ m_pIMECandidates->SetCurrentlyHighlightedItem( id );
+ }
+ }
+
+ m_pIMECandidates->SetVisible(true);
+ m_pIMECandidates->SetParent(this);
+ m_pIMECandidates->AddActionSignalTarget(this);
+ m_pIMECandidates->SetKeyBoardInputEnabled( false );
+
+ int cx, cy;
+ CursorToPixelSpace(_cursorPos, cx, cy);
+ cy = GetTall();
+
+ LocalToScreen( cx, cy );
+
+ //m_pIMECandidates->SetPos( cx, cy );
+
+ // relayout the menu immediately so that we know it's size
+ m_pIMECandidates->InvalidateLayout(true);
+ int menuWide, menuTall;
+ m_pIMECandidates->GetSize(menuWide, menuTall);
+
+ // work out where the cursor is and therefore the best place to put the menu
+ int wide, tall;
+ surface()->GetScreenSize(wide, tall);
+
+ if (wide - menuWide > cx)
+ {
+ // menu hanging right
+ if (tall - menuTall > cy)
+ {
+ // menu hanging down
+ m_pIMECandidates->SetPos(cx, cy);
+ }
+ else
+ {
+ // menu hanging up
+ m_pIMECandidates->SetPos(cx, cy - menuTall - GetTall());
+ }
+ }
+ else
+ {
+ // menu hanging left
+ if (tall - menuTall > cy)
+ {
+ // menu hanging down
+ m_pIMECandidates->SetPos(cx - menuWide, cy);
+ }
+ else
+ {
+ // menu hanging up
+ m_pIMECandidates->SetPos(cx - menuWide, cy - menuTall-GetTall());
+ }
+ }
+}
+
+void TextEntry::HideIMECandidates()
+{
+ if ( m_pIMECandidates )
+ {
+ m_pIMECandidates->SetVisible( false );
+ }
+ delete m_pIMECandidates;
+ m_pIMECandidates = NULL;
+}
+
+void TextEntry::UpdateIMECandidates()
+{
+ if ( !m_pIMECandidates )
+ return;
+
+ int c = input()->GetCandidateListCount();
+ if ( c == 0 )
+ {
+ HideIMECandidates();
+ return;
+ }
+
+ int oldCount = m_pIMECandidates->GetItemCount();
+ int newCount = input()->GetCandidateListPageSize();
+
+ if ( oldCount != newCount )
+ {
+ // Recreate the entire menu
+ ShowIMECandidates();
+ return;
+ }
+
+ int pageSize = input()->GetCandidateListPageSize();
+ int selected = input()->GetCandidateListSelectedItem();
+ int pageStart = input()->GetCandidateListPageStart();
+
+ if ( ( selected < pageStart ) || selected >= pageStart + pageSize )
+ {
+ pageStart = ( selected / pageSize ) * pageSize;
+ input()->SetCandidateListPageStart( pageStart );
+ }
+
+ int startAtOne = input()->CandidateListStartsAtOne() ? 1 : 0;
+
+ for ( int i = pageStart; i < pageStart + pageSize; ++i )
+ {
+ int id = m_pIMECandidates->GetMenuID( i - pageStart );
+
+ MenuItem *item = m_pIMECandidates->GetMenuItem( id );
+ if ( !item )
+ continue;
+
+ if ( i >= c )
+ {
+ item->SetVisible( false );
+ continue;
+ }
+ else
+ {
+ item->SetVisible( true );
+ }
+
+ bool isSelected = ( i == selected ) ? true : false;
+
+ wchar_t unicode[ 32 ];
+ input()->GetCandidate( i, unicode, sizeof( unicode ) );
+
+ wchar_t label[ 64 ];
+ _snwprintf( label, sizeof( label ) / sizeof( wchar_t ) - 1, L"%i %s", i - pageStart + startAtOne, unicode );
+ label[ sizeof( label ) / sizeof( wchar_t ) - 1 ] = L'\0';
+ item->SetText( label );
+ if ( isSelected )
+ {
+ m_pIMECandidates->SetCurrentlyHighlightedItem( id );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TextEntry::FlipToLastIME()
+{
+ int hCurrentIME = input()->GetCurrentIMEHandle();
+ int hEnglishIME = input()->GetEnglishIMEHandle();
+
+ bool isEnglish = ( hCurrentIME == hEnglishIME ) ? true : false;
+
+ // If in english, flip back to previous
+ if ( isEnglish )
+ {
+ input()->OnChangeIMEByHandle( m_hPreviousIME );
+ }
+ else
+ {
+ // If not, remember language and flip to english...
+ m_hPreviousIME = hCurrentIME;
+ input()->OnChangeIMEByHandle( hEnglishIME );
+ }
+}
+
+void TextEntry::SetDrawLanguageIDAtLeft( bool state )
+{
+ m_bDrawLanguageIDAtLeft = state;
+}
+
+bool TextEntry::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist )
+{
+ menu->AddMenuItem( "replace", "#TextEntry_ReplaceText", "replace", this );
+ menu->AddMenuItem( "append", "#TextEntry_AppendText", "append", this );
+ menu->AddMenuItem( "prepend", "#TextEntry_PrependText", "prepend", this );
+ return true;
+}
+
+bool TextEntry::IsDroppable( CUtlVector< KeyValues * >& msglist )
+{
+ if ( msglist.Count() != 1 )
+ return false;
+
+ if ( !IsEnabled() )
+ return false;
+
+ KeyValues *msg = msglist[ 0 ];
+
+ const wchar_t *txt = msg->GetWString( "text", L"" );
+ if ( !txt || txt[ 0 ] == L'\0' )
+ return false;
+
+ return true;
+}
+
+void TextEntry::OnPanelDropped( CUtlVector< KeyValues * >& msglist )
+{
+ if ( msglist.Count() != 1 )
+ return;
+
+ KeyValues *data = msglist[ 0 ];
+
+ const wchar_t *newText = data->GetWString( "text" );
+ if ( !newText || newText[ 0 ] == L'\0' )
+ return;
+
+ char const *cmd = data->GetString( "command" );
+ if ( !Q_stricmp( cmd, "replace" ) ||
+ !Q_stricmp( cmd, "default" ) )
+ {
+ SetText( newText );
+ _dataChanged = true;
+ FireActionSignal();
+ }
+ else if ( !Q_stricmp( cmd, "append" ) )
+ {
+ int newLen = wcslen( newText );
+ int curLen = m_TextStream.Count();
+
+ size_t outsize = sizeof( wchar_t ) * ( newLen + curLen + 1 );
+ wchar_t *out = (wchar_t *)_alloca( outsize );
+ Q_memset( out, 0, outsize );
+ wcsncpy( out, m_TextStream.Base(), curLen );
+ wcsncat( out, newText, wcslen( newText ) );
+ out[ newLen + curLen ] = L'\0';
+ SetText( out );
+ _dataChanged = true;
+ FireActionSignal();
+ }
+ else if ( !Q_stricmp( cmd, "prepend" ) )
+ {
+ int newLen = wcslen( newText );
+ int curLen = m_TextStream.Count();
+
+ size_t outsize = sizeof( wchar_t ) * ( newLen + curLen + 1 );
+ wchar_t *out = (wchar_t *)_alloca( outsize );
+ Q_memset( out, 0, outsize );
+ wcsncpy( out, newText, wcslen( newText ) );
+ wcsncat( out, m_TextStream.Base(), curLen );
+ out[ newLen + curLen ] = L'\0';
+ SetText( out );
+ _dataChanged = true;
+ FireActionSignal();
+ }
+}
+
+int TextEntry::GetTextLength() const
+{
+ return m_TextStream.Count();
+}
+
+bool TextEntry::IsTextFullySelected() const
+{
+ if ( _select[ 0 ] != 0 )
+ return false;
+
+ if ( _select[ 1 ] != GetTextLength() )
+ return false;
+
+ return true;
+}
+
+void TextEntry::SetUseFallbackFont( bool bState, HFont hFallback )
+{
+ m_bUseFallbackFont = bState;
+ m_hFallbackFont = hFallback;
+}
diff --git a/mp/src/vgui2/vgui_controls/TextImage.cpp b/mp/src/vgui2/vgui_controls/TextImage.cpp
new file mode 100644
index 00000000..d4825ad7
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/TextImage.cpp
@@ -0,0 +1,985 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Implementation of vgui::TextImage control
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <assert.h>
+#include <malloc.h>
+
+#include <vgui/IPanel.h>
+#include <vgui/ISurface.h>
+#include <vgui/IScheme.h>
+#include <vgui/IInput.h>
+#include <vgui/ILocalize.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/TextImage.h>
+#include <vgui_controls/Controls.h>
+
+#include "tier0/dbg.h"
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+// enable this define if you want unlocalized strings logged to files unfound.txt and unlocalized.txt
+// #define LOG_UNLOCALIZED_STRINGS
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+TextImage::TextImage(const char *text) : Image()
+{
+ _utext = NULL;
+ _textBufferLen = 0;
+ _font = INVALID_FONT;
+ _fallbackFont = INVALID_FONT;
+ _unlocalizedTextSymbol = INVALID_LOCALIZE_STRING_INDEX;
+ _drawWidth = 0;
+ _textBufferLen = 0;
+ _textLen = 0;
+ m_bWrap = false;
+ m_bWrapCenter = false;
+ m_LineBreaks.RemoveAll();
+ m_LineXIndent.RemoveAll();
+ m_pwszEllipsesPosition = NULL;
+ m_bUseFallbackFont = false;
+ m_bRenderUsingFallbackFont = false;
+ m_bAllCaps = false;
+
+ SetText(text); // set the text.
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+TextImage::TextImage(const wchar_t *wszText) : Image()
+{
+ _utext = NULL;
+ _textBufferLen = 0;
+ _font = INVALID_FONT;
+ _fallbackFont = INVALID_FONT;
+ _unlocalizedTextSymbol = INVALID_LOCALIZE_STRING_INDEX;
+ _drawWidth = 0;
+ _textBufferLen = 0;
+ _textLen = 0;
+ m_bWrap = false;
+ m_bWrapCenter = false;
+ m_LineBreaks.RemoveAll();
+ m_LineXIndent.RemoveAll();
+ m_bUseFallbackFont = false;
+ m_bRenderUsingFallbackFont = false;
+ m_bAllCaps = false;
+
+ SetText(wszText); // set the text.
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+TextImage::~TextImage()
+{
+ delete [] _utext;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: takes the string and looks it up in the localization file to convert it to unicode
+//-----------------------------------------------------------------------------
+void TextImage::SetText(const char *text)
+{
+ if (!text)
+ {
+ text = "";
+ }
+
+ // check for localization
+ if (*text == '#')
+ {
+ // try lookup in localization tables
+ _unlocalizedTextSymbol = g_pVGuiLocalize->FindIndex(text + 1);
+
+ if (_unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX)
+ {
+ wchar_t *unicode = g_pVGuiLocalize->GetValueByIndex(_unlocalizedTextSymbol);
+ SetText(unicode);
+ return;
+ }
+ else
+ {
+ // could not find string
+ // debug code for logging unlocalized strings
+#if defined(LOG_UNLOCALIZED_STRINGS)
+ if (*text)
+ {
+ // write out error to unfound.txt log file
+ static bool first = true;
+ FILE *f;
+ if (first)
+ {
+ first = false;
+ f = fopen("unfound.txt", "wt");
+ }
+ else
+ {
+ f = fopen("unfound.txt", "at");
+ }
+
+ if (f)
+ {
+ fprintf(f, "\"%s\"\n", text);
+ fclose(f);
+ }
+ }
+#endif // LOG_UNLOCALIZED_STRINGS
+ }
+ }
+ else
+ {
+ // debug code for logging unlocalized strings
+#if defined(LOG_UNLOCALIZED_STRINGS)
+ if (text[0])
+ {
+ // setting a label to be ANSI text, write out error to unlocalized.txt log file
+ static bool first = true;
+ FILE *f;
+ if (first)
+ {
+ first = false;
+ f = fopen("unlocalized.txt", "wt");
+ }
+ else
+ {
+ f = fopen("unlocalized.txt", "at");
+ }
+ if (f)
+ {
+ fprintf(f, "\"%s\"\n", text);
+ fclose(f);
+ }
+ }
+#endif // LOG_UNLOCALIZED_STRINGS
+ }
+
+ // convert the ansi string to unicode and use that
+ wchar_t unicode[1024];
+ g_pVGuiLocalize->ConvertANSIToUnicode(text, unicode, sizeof(unicode));
+ SetText(unicode);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the width that the text can be.
+//-----------------------------------------------------------------------------
+void TextImage::SetDrawWidth(int width)
+{
+ _drawWidth = width;
+ m_bRecalculateTruncation = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the width that the text can be.
+//-----------------------------------------------------------------------------
+void TextImage::GetDrawWidth(int &width)
+{
+ width = _drawWidth;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets unicode text directly
+//-----------------------------------------------------------------------------
+void TextImage::SetText(const wchar_t *unicode, bool bClearUnlocalizedSymbol)
+{
+ if ( bClearUnlocalizedSymbol )
+ {
+ // Clear out unlocalized text symbol so that changing dialog variables
+ // doesn't stomp over the custom unicode string we're being set to.
+ _unlocalizedTextSymbol = INVALID_LOCALIZE_STRING_INDEX;
+ }
+
+ if (!unicode)
+ {
+ unicode = L"";
+ }
+
+ // reallocate the buffer if necessary
+ _textLen = (short)wcslen(unicode);
+ if (_textLen >= _textBufferLen)
+ {
+ delete [] _utext;
+ _textBufferLen = (short)(_textLen + 1);
+ _utext = new wchar_t[_textBufferLen];
+ }
+
+ m_LineBreaks.RemoveAll();
+ m_LineXIndent.RemoveAll();
+
+ // store the text as unicode
+ wcscpy(_utext, unicode);
+
+ m_bRecalculateTruncation = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the text in the textImage
+//-----------------------------------------------------------------------------
+void TextImage::GetText(char *buffer, int bufferSize)
+{
+ g_pVGuiLocalize->ConvertUnicodeToANSI(_utext, buffer, bufferSize);
+
+ if ( m_bAllCaps )
+ {
+ // Uppercase all the letters
+ for ( int i = Q_strlen( buffer ); i >= 0; --i )
+ {
+ buffer[ i ] = toupper( buffer[ i ] );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the text in the textImage
+//-----------------------------------------------------------------------------
+void TextImage::GetText(wchar_t *buffer, int bufLenInBytes)
+{
+ wcsncpy(buffer, _utext, bufLenInBytes / sizeof(wchar_t));
+
+ if ( m_bAllCaps )
+ {
+ // Uppercase all the letters
+ for ( int i = Q_wcslen( buffer ) - 1; i >= 0; --i )
+ {
+ buffer[ i ] = towupper( buffer[ i ] );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void TextImage::GetUnlocalizedText(char *buffer, int bufferSize)
+{
+ if (_unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX)
+ {
+ const char *text = g_pVGuiLocalize->GetNameByIndex(_unlocalizedTextSymbol);
+ buffer[0] = '#';
+ Q_strncpy(buffer + 1, text, bufferSize - 1);
+ buffer[bufferSize-1] = 0;
+ }
+ else
+ {
+ GetText(buffer, bufferSize);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: unlocalized text symbol
+//-----------------------------------------------------------------------------
+StringIndex_t TextImage::GetUnlocalizedTextSymbol()
+{
+ return _unlocalizedTextSymbol;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Changes the current font
+//-----------------------------------------------------------------------------
+void TextImage::SetFont(HFont font)
+{
+ _font = font;
+ m_bRecalculateTruncation = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the font of the text.
+//-----------------------------------------------------------------------------
+HFont TextImage::GetFont()
+{
+ if ( m_bRenderUsingFallbackFont )
+ {
+ return _fallbackFont;
+ }
+
+ return _font;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the size of the TextImage. This is directly tied to drawWidth
+//-----------------------------------------------------------------------------
+void TextImage::SetSize(int wide, int tall)
+{
+ Image::SetSize(wide, tall);
+ _drawWidth = wide;
+ m_bRecalculateTruncation = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw the Image on screen.
+//-----------------------------------------------------------------------------
+void TextImage::Paint()
+{
+ int wide, tall;
+ GetSize(wide, tall);
+
+ if (!_utext || GetFont() == INVALID_FONT )
+ return;
+
+ if (m_bRecalculateTruncation)
+ {
+ if ( m_bWrap || m_bWrapCenter )
+ {
+ RecalculateNewLinePositions();
+ }
+
+ RecalculateEllipsesPosition();
+ }
+
+ DrawSetTextColor(GetColor());
+ HFont font = GetFont();
+ DrawSetTextFont(font);
+
+ int lineHeight = surface()->GetFontTall(font);
+ float x = 0.0f;
+ int y = 0;
+ int iIndent = 0;
+ int iNextColorChange = 0;
+
+ int px, py;
+ GetPos(px, py);
+
+ int currentLineBreak = 0;
+
+ if ( m_bWrapCenter && m_LineXIndent.Count() )
+ {
+ x = m_LineXIndent[0];
+ }
+
+ for (wchar_t *wsz = _utext; *wsz != 0; wsz++)
+ {
+ wchar_t ch = wsz[0];
+
+ if ( m_bAllCaps )
+ {
+ ch = towupper( ch );
+ }
+
+ if ( m_ColorChangeStream.Count() > iNextColorChange )
+ {
+ if ( m_ColorChangeStream[iNextColorChange].textStreamIndex == (wsz - _utext) )
+ {
+ DrawSetTextColor( m_ColorChangeStream[iNextColorChange].color );
+ iNextColorChange++;
+ }
+ }
+
+ // check for special characters
+ if ( ch == '\r' || ch <= 8 )
+ {
+ // ignore, just use \n for newlines
+ continue;
+ }
+ else if (ch == '\n')
+ {
+ // newline
+ iIndent++;
+ if ( m_bWrapCenter && iIndent < m_LineXIndent.Count() )
+ {
+ x = m_LineXIndent[iIndent];
+ }
+ else
+ {
+ x = 0;
+ }
+ y += lineHeight;
+ continue;
+ }
+ else if (ch == '&')
+ {
+ // "&&" means draw a single ampersand, single one is a shortcut character
+ if (wsz[1] == '&')
+ {
+ // just move on and draw the second ampersand
+ wsz++;
+ }
+ else
+ {
+ // draw the underline, then continue to the next character without moving forward
+#ifdef VGUI_DRAW_HOTKEYS_ENABLED
+ surface()->DrawSetTextPos(x + px, y + py);
+ surface()->DrawUnicodeChar('_');
+#endif
+ continue;
+ }
+ }
+
+ // see if we've hit the truncated portion of the string
+ if (wsz == m_pwszEllipsesPosition)
+ {
+ // string is truncated, draw ellipses
+ for (int i = 0; i < 3; i++)
+ {
+ surface()->DrawSetTextPos(x + px, y + py);
+ surface()->DrawUnicodeChar('.');
+ x += surface()->GetCharacterWidth(font, '.');
+ }
+ break;
+ }
+
+ if (currentLineBreak != m_LineBreaks.Count())
+ {
+ if (wsz == m_LineBreaks[currentLineBreak])
+ {
+ // newline
+ iIndent++;
+ if ( m_bWrapCenter && iIndent < m_LineXIndent.Count() )
+ {
+ x = m_LineXIndent[iIndent];
+ }
+ else
+ {
+ x = 0;
+ }
+
+ y += lineHeight;
+ currentLineBreak++;
+ }
+ }
+
+ // Underlined text wants to draw the spaces anyway
+#if USE_GETKERNEDCHARWIDTH
+ wchar_t chBefore = 0;
+ wchar_t chAfter = 0;
+ if ( wsz > _utext )
+ chBefore = wsz[-1];
+ chAfter = wsz[1];
+ float flWide = 0.0f, flabcA = 0.0f;
+ surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA );
+ if ( ch == L' ' )
+ x = ceil( x );
+
+ surface()->DrawSetTextPos( x + px, y + py);
+ surface()->DrawUnicodeChar(ch);
+ x += floor(flWide + 0.6);
+#else
+ surface()->DrawSetTextPos( x + px, y + py);
+ surface()->DrawUnicodeChar(ch);
+ x += surface()->GetCharacterWidth(font, ch);
+#endif
+ }
+
+ // Useful debugging
+ /*
+ DrawSetColor(GetColor());
+ DrawOutlinedRect( 0,0, _drawWidth, tall );
+ */
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the size of a text string in pixels
+//-----------------------------------------------------------------------------
+void TextImage::GetTextSize(int &wide, int &tall)
+{
+ wide = 0;
+ tall = 0;
+ int maxWide = 0;
+ const wchar_t *text = _utext;
+
+ HFont font = _font;
+ if ( font == INVALID_FONT )
+ return;
+
+ if ( m_bWrap || m_bWrapCenter )
+ {
+ RecalculateNewLinePositions();
+ }
+
+ // For height, use the remapped font
+ int fontHeight = surface()->GetFontTall(GetFont());
+ tall = fontHeight;
+
+ int textLen = wcslen(text);
+ for (int i = 0; i < textLen; i++)
+ {
+ wchar_t ch = text[i];
+
+ // handle stupid special characters, these should be removed
+ if ( ch == '&' )
+ {
+ continue;
+ }
+
+ if ( m_bAllCaps )
+ {
+ ch = towupper( ch );
+ }
+
+#if USE_GETKERNEDCHARWIDTH
+ wchar_t chBefore = 0;
+ wchar_t chAfter = 0;
+ if ( i > 0 )
+ chBefore = text[i-1];
+ chAfter = text[i+1];
+ float flWide = 0.0f, flabcA;
+ surface()->GetKernedCharWidth( font, text[i], chBefore, chAfter, flWide, flabcA );
+ if ( text[i] == L' ' )
+ flWide = ceil( flWide );
+ wide += floor( flWide + 0.6);
+#else
+ int a, b, c;
+ surface()->GetCharABCwide(font, ch, a, b, c);
+ wide += (a + b + c);
+#endif
+
+
+ if (ch == '\n')
+ {
+ tall += fontHeight;
+ if(wide>maxWide)
+ {
+ maxWide=wide;
+ }
+ wide=0; // new line, wide is reset...
+ }
+
+ if ( m_bWrap || m_bWrapCenter )
+ {
+ for(int j=0; j<m_LineBreaks.Count(); j++)
+ {
+ if ( &text[i] == m_LineBreaks[j] )
+ {
+ tall += fontHeight;
+ if(wide>maxWide)
+ {
+ maxWide=wide;
+ }
+ wide=0; // new line, wide is reset...
+ }
+ }
+ }
+
+ }
+#ifdef OSX
+ wide += 2;
+ if ( textLen < 3 )
+ wide += 3;
+#endif
+ if (wide < maxWide)
+ {
+ // maxWide only gets set if a newline is in the label
+ wide = maxWide;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the size of the text in the image
+//-----------------------------------------------------------------------------
+void TextImage::GetContentSize(int &wide, int &tall)
+{
+ GetTextSize(wide, tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Resize the text image to the content size
+//-----------------------------------------------------------------------------
+void TextImage::ResizeImageToContent()
+{
+ int wide, tall;
+ GetContentSize(wide, tall);
+ SetSize(wide, tall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recalculates line breaks
+//-----------------------------------------------------------------------------
+void TextImage::RecalculateNewLinePositions()
+{
+ HFont font = GetFont();
+
+ int charWidth;
+ int x = 0;
+
+ //int wordStartIndex = 0;
+ wchar_t *wordStartIndex = _utext;
+ int wordLength = 0;
+ bool hasWord = false;
+ bool justStartedNewLine = true;
+ bool wordStartedOnNewLine = true;
+
+ int startChar = 0;
+
+ // clear the line breaks list
+ m_LineBreaks.RemoveAll();
+ m_LineXIndent.RemoveAll();
+
+ // handle the case where this char is a new line, in that case
+ // we have already taken its break index into account above so skip it.
+ if (_utext[startChar] == '\r' || _utext[startChar] == '\n')
+ {
+ startChar++;
+ }
+
+ // loop through all the characters
+ for (wchar_t *wsz = &_utext[startChar]; *wsz != 0; wsz++)
+ {
+ // handle stupid special characters, these should be removed
+ // 0x01, 0x02 and 0x03 are color escape characters and should be ignored
+ if ( ( wsz[0] == '&' || wsz[0] == 0x01 || wsz[0] == 0x02 || wsz[0] == 0x03 ) && wsz[1] != 0 )
+ {
+ wsz++;
+ }
+
+ wchar_t ch = wsz[0];
+
+ if ( m_bAllCaps )
+ {
+ ch = towupper( ch );
+ }
+
+ // line break only on whitespace characters
+ if (!iswspace(ch))
+ {
+ if ( !hasWord )
+ {
+ // Start a new word
+ wordStartIndex = wsz;
+ hasWord = true;
+ wordStartedOnNewLine = justStartedNewLine;
+ wordLength = 0;
+ }
+ //else append to the current word
+ }
+ else
+ {
+ // whitespace/punctuation character
+ // end the word
+ hasWord = false;
+ }
+
+ // get the width
+#if USE_GETKERNEDCHARWIDTH
+ wchar_t chBefore = 0;
+ wchar_t chAfter = 0;
+ if ( wsz > _utext )
+ chBefore = wsz[-1];
+ chAfter = wsz[1];
+ float flWide = 0.0f, flabcA = 0.0f;
+ surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA );
+ charWidth = floor( flWide + 0.6 );
+#else
+ charWidth = surface()->GetCharacterWidth(font, ch);
+#endif
+ if (!iswcntrl(ch))
+ {
+ justStartedNewLine = false;
+ }
+
+ // check to see if the word is past the end of the line [wordStartIndex, i)
+ if ((x + charWidth) > _drawWidth || ch == '\r' || ch == '\n')
+ {
+ justStartedNewLine = true;
+ hasWord = false;
+
+ if (ch == '\r' || ch == '\n')
+ {
+ // set the break at the current character
+ //don't do this, paint will manually wrap on newline chars
+ // m_LineBreaks.AddToTail(i);
+ }
+ else if (wordStartedOnNewLine)
+ {
+ // word is longer than a line, so set the break at the current cursor
+ m_LineBreaks.AddToTail(wsz);
+ }
+ else
+ {
+ // set it at the last word Start
+ m_LineBreaks.AddToTail(wordStartIndex);
+
+ // just back to reparse the next line of text
+ // ywb 8/1/07: Back off one extra char since the 'continue' will increment wsz for us by one in the for loop
+ wsz = wordStartIndex - 1;
+ }
+
+ // reset word length
+ wordLength = 0;
+ x = 0;
+ continue;
+ }
+
+ // add to the size
+ x += charWidth;
+ wordLength += charWidth;
+ }
+
+ RecalculateCenterWrapIndents();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculates where the text should be truncated
+//-----------------------------------------------------------------------------
+void TextImage::RecalculateEllipsesPosition()
+{
+ m_bRecalculateTruncation = false;
+ m_pwszEllipsesPosition = NULL;
+
+ //don't insert ellipses on wrapped strings
+ if ( m_bWrap || m_bWrapCenter )
+ return;
+
+ // don't truncate strings with newlines
+ if (wcschr(_utext, '\n') != NULL)
+ return;
+
+ if ( _drawWidth == 0 )
+ {
+ int h;
+ GetSize( _drawWidth, h );
+ }
+
+ for ( int check = 0; check < (m_bUseFallbackFont ? 2 : 1); ++check )
+ {
+ HFont font = GetFont();
+ if ( check == 1 && _fallbackFont != INVALID_FONT )
+ {
+ m_pwszEllipsesPosition = NULL;
+ font = _fallbackFont;
+ m_bRenderUsingFallbackFont = true;
+ }
+
+ int ellipsesWidth = 3 * surface()->GetCharacterWidth(font, '.');
+ int x = 0;
+
+ for (wchar_t *wsz = _utext; *wsz != 0; wsz++)
+ {
+ wchar_t ch = wsz[0];
+
+ if ( m_bAllCaps )
+ {
+ ch = towupper( ch );
+ }
+
+ // check for special characters
+ if (ch == '\r')
+ {
+ // ignore, just use \n for newlines
+ continue;
+ }
+ else if (ch == '&')
+ {
+ // "&&" means draw a single ampersand, single one is a shortcut character
+ if (wsz[1] == '&')
+ {
+ // just move on and draw the second ampersand
+ wsz++;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+#if USE_GETKERNEDCHARWIDTH
+ wchar_t chBefore = 0;
+ wchar_t chAfter = 0;
+ if ( wsz > _utext )
+ chBefore = wsz[-1];
+ chAfter = wsz[1];
+ float flWide = 0.0f, flabcA = 0.0f;
+ surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA );
+ int len = floor( flWide + 0.6 );
+#else
+ int len = surface()->GetCharacterWidth(font, ch);
+#endif
+
+ // don't truncate the first character
+ if (wsz == _utext)
+ {
+ x += len;
+ continue;
+ }
+
+ if (x + len + ellipsesWidth > _drawWidth)
+ {
+ // potential have an ellipses, see if the remaining characters will fit
+ int remainingLength = len;
+ for (const wchar_t *rwsz = wsz + 1; *rwsz != 0; rwsz++)
+ {
+#if USE_GETKERNEDCHARWIDTH
+ wchar_t chBefore = 0;
+ wchar_t chAfter = 0;
+ if ( rwsz > _utext )
+ chBefore = rwsz[-1];
+ chAfter = rwsz[1];
+ float flWide = 0.0f, flabcA = 0.0f;
+ surface()->GetKernedCharWidth( font, *rwsz, chBefore, chAfter, flWide, flabcA );
+ int len = floor( flWide + 0.6 );
+ remainingLength += floor( flWide + 0.6 );
+#else
+ remainingLength += surface()->GetCharacterWidth(font, *rwsz);
+#endif
+ }
+
+ if (x + remainingLength > _drawWidth)
+ {
+ // looks like we've got an ellipses situation
+ m_pwszEllipsesPosition = wsz;
+ break;
+ }
+ }
+
+ x += len;
+ }
+
+ // Didn't need ellipses...
+ if ( !m_pwszEllipsesPosition )
+ break;
+ }
+}
+
+void TextImage::SetWrap( bool bWrap )
+{
+ m_bWrap = bWrap;
+}
+
+void TextImage::SetCenterWrap( bool bWrap )
+{
+ m_bWrapCenter = bWrap;
+}
+
+
+void TextImage::SetUseFallbackFont( bool bState, HFont hFallback )
+{
+ m_bUseFallbackFont = bState;
+ _fallbackFont = hFallback;
+}
+
+void TextImage::SetAllCaps( bool bAllCaps )
+{
+ m_bAllCaps = bAllCaps;
+}
+
+void TextImage::ResizeImageToContentMaxWidth( int nMaxWidth )
+{
+ _drawWidth = nMaxWidth;
+ // Since we might have to use the "fallback" font, go ahead and recalc the ellipses state first to see if that's the case
+ // NOTE: I think there may be a race condition lurking here, but this seems to work.
+ if ( m_bRecalculateTruncation )
+ {
+ if ( m_bWrap || m_bWrapCenter )
+ {
+ RecalculateNewLinePositions();
+ }
+
+ RecalculateEllipsesPosition();
+ }
+
+ ResizeImageToContent();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: For center wrapping of multi-line text images, determines the indent each line needs to be centered
+//-----------------------------------------------------------------------------
+void TextImage::RecalculateCenterWrapIndents()
+{
+ m_LineXIndent.RemoveAll();
+
+ if ( !m_bWrapCenter )
+ return;
+
+ if (!_utext || GetFont() == INVALID_FONT )
+ return;
+
+ HFont font = GetFont();
+ int px, py;
+ GetPos(px, py);
+
+ int currentLineBreak = 0;
+ int iCurLineW = 0;
+
+ for (wchar_t *wsz = _utext; *wsz != 0; wsz++)
+ {
+ wchar_t ch = wsz[0];
+
+ if ( m_bAllCaps )
+ {
+ ch = towupper( ch );
+ }
+
+ // check for special characters
+ if (ch == '\r')
+ {
+ // ignore, just use \n for newlines
+ continue;
+ }
+ else if (ch == '\n')
+ {
+ int iIdx = m_LineXIndent.AddToTail();
+ m_LineXIndent[iIdx] = (_drawWidth - iCurLineW) * 0.5;
+
+ iCurLineW = 0;
+ continue;
+ }
+ else if (ch == '&')
+ {
+ // "&&" means draw a single ampersand, single one is a shortcut character
+ if (wsz[1] == '&')
+ {
+ // just move on and draw the second ampersand
+ wsz++;
+ }
+ else
+ {
+ // draw the underline, then continue to the next character without moving forward
+ continue;
+ }
+ }
+
+ // Don't need to check ellipses, they're not used when wrapping
+
+ if (currentLineBreak != m_LineBreaks.Count())
+ {
+ if (wsz == m_LineBreaks[currentLineBreak])
+ {
+ int iIdx = m_LineXIndent.AddToTail();
+ m_LineXIndent[iIdx] = (_drawWidth - iCurLineW) * 0.5;
+
+ iCurLineW = 0;
+ currentLineBreak++;
+ }
+ }
+
+#if USE_GETKERNEDCHARWIDTH
+ wchar_t chBefore = 0;
+ wchar_t chAfter = 0;
+ if ( wsz > _utext )
+ chBefore = wsz[-1];
+ chAfter = wsz[1];
+ float flWide = 0.0f, flabcA = 0.0f;
+ surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA );
+ iCurLineW += floor( flWide + 0.6 );
+#else
+ iCurLineW += surface()->GetCharacterWidth(font, ch);
+#endif
+ }
+
+ // Add the final line
+ int iIdx = m_LineXIndent.AddToTail();
+ m_LineXIndent[iIdx] = (_drawWidth - iCurLineW) * 0.5;
+}
+
+void TextImage::AddColorChange( Color col, int iTextStreamIndex )
+{
+ label_colorchange_t tmpChange;
+ tmpChange.color = col;
+ tmpChange.textStreamIndex = iTextStreamIndex;
+ m_ColorChangeStream.Insert( tmpChange );
+}
+
+void TextImage::SetColorChangeStream( CUtlSortVector<label_colorchange_t,CColorChangeListLess> *pUtlVecStream )
+{
+ ClearColorChangeStream();
+
+ m_ColorChangeStream = *pUtlVecStream;
+}
diff --git a/mp/src/vgui2/vgui_controls/ToggleButton.cpp b/mp/src/vgui2/vgui_controls/ToggleButton.cpp
new file mode 100644
index 00000000..292faa38
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ToggleButton.cpp
@@ -0,0 +1,102 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <vgui/KeyCode.h>
+
+#include <vgui_controls/ToggleButton.h>
+
+#include <KeyValues.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY_DEFAULT_TEXT( ToggleButton, ToggleButton );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+ToggleButton::ToggleButton(Panel *parent, const char *panelName, const char* text) : Button(parent, panelName, text)
+{
+ SetButtonActivationType(ACTIVATE_ONPRESSED);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turns double-click into normal click
+//-----------------------------------------------------------------------------
+void ToggleButton::OnMouseDoublePressed(MouseCode code)
+{
+ OnMousePressed(code);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Color ToggleButton::GetButtonFgColor()
+{
+ if (IsSelected())
+ {
+ // highlight the text when depressed
+ return _selectedColor;
+ }
+ else
+ {
+ return BaseClass::GetButtonFgColor();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool ToggleButton::CanBeDefaultButton(void)
+{
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggles the state of the button
+//-----------------------------------------------------------------------------
+void ToggleButton::DoClick()
+{
+ if (IsSelected())
+ {
+ ForceDepressed(false);
+ }
+ else if (!IsSelected())
+ {
+ ForceDepressed(true);
+ }
+
+ SetSelected(!IsSelected());
+ FireActionSignal();
+
+ // post a button toggled message
+ KeyValues *msg = new KeyValues("ButtonToggled");
+ msg->SetInt("state", (int)IsSelected());
+ PostActionSignal(msg);
+
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ToggleButton::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+ _selectedColor = GetSchemeColor("ToggleButton.SelectedTextColor", pScheme);
+}
+
+void ToggleButton::OnKeyCodePressed(KeyCode code)
+{
+ if (code != KEY_ENTER)
+ {
+ BaseClass::OnKeyCodePressed(code);
+ }
+}
+
diff --git a/mp/src/vgui2/vgui_controls/ToolWindow.cpp b/mp/src/vgui2/vgui_controls/ToolWindow.cpp
new file mode 100644
index 00000000..3208f128
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/ToolWindow.cpp
@@ -0,0 +1,478 @@
+//========= Copyright 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>
+
+// 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_pFactory( factory )
+{
+ if ( inGlobalList )
+ {
+ s_ToolWindows.AddToTail( this );
+ }
+
+ // create the property sheet
+ m_pPropertySheet = new PropertySheet(this, "ToolWindowSheet", true );
+ m_pPropertySheet->ShowContextButtons( contextlabel );
+ m_pPropertySheet->AddPage( page, title, 0, contextMenu );
+ m_pPropertySheet->AddActionSignalTarget(this);
+ m_pPropertySheet->SetSmallTabs( true );
+ m_pPropertySheet->SetKBNavigationEnabled( false );
+
+ SetSmallCaption( true );
+
+ SetMenuButtonResponsive(false);
+ SetMinimizeButtonVisible(false);
+ SetCloseButtonVisible(true);
+ SetMoveable( true );
+ SetSizeable(true);
+
+ SetClipToParent( false );
+ SetVisible( true );
+
+ SetDeleteSelfOnClose( true );
+
+ SetTitle( "", false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+ToolWindow::~ToolWindow()
+{
+ // These don't actually kill the children of the property sheet
+ m_pPropertySheet->RemoveAllPages();
+
+ s_ToolWindows.FindAndRemove( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Pass through to sheet
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool ToolWindow::IsDraggableTabContainer() const
+{
+ return m_pPropertySheet->IsDraggableTab();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a pointer to the PropertySheet this dialog encapsulates
+// Output : PropertySheet *
+//-----------------------------------------------------------------------------
+PropertySheet *ToolWindow::GetPropertySheet()
+{
+ return m_pPropertySheet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets a pointer to the currently active page.
+// Output : Panel
+//-----------------------------------------------------------------------------
+Panel *ToolWindow::GetActivePage()
+{
+ return m_pPropertySheet->GetActivePage();
+}
+
+void ToolWindow::SetActivePage( Panel *page )
+{
+ m_pPropertySheet->SetActivePage( page );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Wrapped function
+//-----------------------------------------------------------------------------
+void ToolWindow::AddPage(Panel *page, const char *title, bool contextMenu)
+{
+ m_pPropertySheet->AddPage(page, title, 0, contextMenu );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *page -
+//-----------------------------------------------------------------------------
+void ToolWindow::RemovePage( Panel *page )
+{
+ m_pPropertySheet->RemovePage( page );
+ if ( m_pPropertySheet->GetNumPages() == 0 )
+ {
+ MarkForDeletion();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets up the sheet
+//-----------------------------------------------------------------------------
+void ToolWindow::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int x, y, wide, tall;
+ GetClientArea(x, y, wide, tall);
+ m_pPropertySheet->SetBounds(x, y, wide, tall);
+ m_pPropertySheet->InvalidateLayout(); // tell the propertysheet to redraw!
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Overrides build mode so it edits the sub panel
+//-----------------------------------------------------------------------------
+void ToolWindow::ActivateBuildMode()
+{
+ // no subpanel, no build mode
+ EditablePanel *panel = dynamic_cast<EditablePanel *>(GetActivePage());
+ if (!panel)
+ return;
+
+ panel->ActivateBuildMode();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ToolWindow::RequestFocus(int direction)
+{
+ m_pPropertySheet->RequestFocus(direction);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *factory -
+//-----------------------------------------------------------------------------
+void ToolWindow::SetToolWindowFactory( IToolWindowFactory *factory )
+{
+ m_pFactory = factory;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : IToolWindowFactory
+//-----------------------------------------------------------------------------
+IToolWindowFactory *ToolWindow::GetToolWindowFactory()
+{
+ return m_pFactory;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: To fill the space left by other tool windows
+// Input : edge: 0=all, 1=top, 2=right, 3=bottom, 4=left
+// Output :
+//-----------------------------------------------------------------------------
+
+void ToolWindow::Grow( int edge, int from_x, int from_y )
+{
+ int status_h = 24;
+ int menubar_h = 27;
+
+ int sw, sh;
+ surface()->GetScreenSize( sw, sh );
+
+ int old_x, old_y, old_w, old_h;
+ GetBounds( old_x, old_y, old_w, old_h );
+
+ int new_x, new_y, new_w, new_h;
+ new_x = old_x;
+ new_y = old_y;
+ new_w = old_w;
+ new_h = old_h;
+
+ int c = GetToolWindowCount();
+
+ // grow up
+ if ( ( edge == 0 ) || ( edge == 1 ) )
+ {
+ // first shrink the edge back to the grow point
+ if ( from_y >= 0 )
+ {
+ old_h = old_h - ( from_y - old_y );
+ old_y = from_y;
+ }
+
+ // now grow the edge as far as it can go
+ new_h = old_h + ( old_y - menubar_h );
+ new_y = menubar_h;
+
+ for ( int i = 0 ; i < c; ++i )
+ {
+ ToolWindow *tw = GetToolWindow( i );
+ Assert( tw );
+ if ( ( !tw ) || ( tw == this ) )
+ continue;
+
+ // Get panel bounds
+ int x, y, w, h;
+ tw->GetBounds( x, y, w, h );
+
+ // grow it
+ if ( ( ( ( old_x > x ) && ( old_x < x + w ) )
+ || ( ( old_x + old_w > x ) && ( old_x + old_w < x + w ) )
+ || ( ( old_x <= x ) && old_x + old_w >= x + w ))
+ && ( ( old_y >= y + h ) && ( new_y < y + h ) ) )
+ {
+ new_h = old_h + ( old_y - ( y + h ) );
+ new_y = y + h;
+ }
+ }
+ old_h = new_h;
+ old_y = new_y;
+ }
+
+ // grow right
+ if ( ( edge == 0 ) || ( edge == 2 ) )
+ {
+ // first shrink the edge back to the grow point
+ if ( from_x >= 0 )
+ {
+ old_w = from_x - old_x;
+ }
+
+ // now grow the edge as far as it can go
+ new_w = sw - old_x;
+
+ for ( int i = 0 ; i < c; ++i )
+ {
+ ToolWindow *tw = GetToolWindow( i );
+ Assert( tw );
+ if ( ( !tw ) || ( tw == this ) )
+ continue;
+
+ // Get panel bounds
+ int x, y, w, h;
+ tw->GetBounds( x, y, w, h );
+
+ // grow it
+ if ( ( ( ( old_y > y ) && ( old_y < y + h ) )
+ || ( ( old_y + old_h > y ) && ( old_y + old_h < y + h ) )
+ || ( ( old_y <= y ) && old_y + old_h >= y + h ))
+ && ( ( old_x + old_w <= x ) && ( new_w > x - old_x ) ) )
+ {
+ new_w = x - old_x;
+ }
+ }
+ old_w = new_w;
+ }
+
+ // grow down
+ if ( ( edge == 0 ) || ( edge == 3 ) )
+ {
+ // first shrink the edge back to the grow point
+ if ( from_y >= 0 )
+ {
+ old_h = from_y - old_y;
+ }
+
+ // now grow the edge as far as it can go
+ new_h = sh - old_y - status_h;
+
+ for ( int i = 0 ; i < c; ++i )
+ {
+ ToolWindow *tw = GetToolWindow( i );
+ Assert( tw );
+ if ( ( !tw ) || ( tw == this ) )
+ continue;
+
+ // Get panel bounds
+ int x, y, w, h;
+ tw->GetBounds( x, y, w, h );
+
+ // grow it
+ if ( ( ( ( old_x > x ) && ( old_x < x + w ) )
+ || ( ( old_x + old_w > x ) && ( old_x + old_w < x + w ) )
+ || ( ( old_x <= x ) && old_x + old_w >= x + w ))
+ && ( ( old_y + old_h <= y ) && ( new_h > y - old_y ) ) )
+ {
+ new_h = y - old_y;
+ }
+ }
+ old_h = new_h;
+ }
+
+ // grow left
+ if ( ( edge == 0 ) || ( edge == 4 ) )
+ {
+ // first shrink the edge back to the grow point
+ if ( from_x >= 0 )
+ {
+ old_w = old_w - ( from_x - old_x );
+ old_x = from_x;
+ }
+
+ // now grow the edge as far as it can go
+ new_w = old_w + old_x;
+ new_x = 0;
+
+ for ( int i = 0 ; i < c; ++i )
+ {
+ ToolWindow *tw = GetToolWindow( i );
+ Assert( tw );
+ if ( ( !tw ) || ( tw == this ) )
+ continue;
+
+ // Get panel bounds
+ int x, y, w, h;
+ tw->GetBounds( x, y, w, h );
+
+ // grow it
+ if ( ( ( ( old_y > y ) && ( old_y < y + h ) )
+ || ( ( old_y + old_h > y ) && ( old_y + old_h < y + h ) )
+ || ( ( old_y <= y ) && old_y + old_h >= y + h ))
+ && ( ( old_x >= x + w ) && ( new_x < x + w ) ) )
+ {
+ new_w = old_w + ( old_x - ( x + w ) );
+ new_x = x + w;
+ }
+ }
+ old_w = new_w;
+ old_x = new_x;
+ }
+
+ // Set panel bounds
+ SetBounds( new_x, new_y, new_w, new_h );
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calls Grow based on where the mouse is.
+// over titlebar: grows all edges ( from mouse pos )
+// over edge grab area: grows just that edge
+// over corner grab area: grows the two adjacent edges
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+
+void ToolWindow::GrowFromClick()
+{
+ int mx, my;
+ input()->GetCursorPos( mx, my );
+
+ int esz, csz, brsz, ch;
+ esz = GetDraggerSize();
+ csz = GetCornerSize();
+ brsz = GetBottomRightSize();
+ ch = GetCaptionHeight();
+
+ int x, y, w, h;
+ GetBounds( x, y, w, h );
+
+ // upper right
+ if ( ( mx > x+w-csz-1 ) && ( my < y+csz ) )
+ {
+ Grow(1);
+ Grow(2);
+ }
+ // lower right (the big one)
+ else if ( ( mx > x+w-brsz-1 ) && ( my > y+h-brsz-1 ) )
+ {
+ Grow(2);
+ Grow(3);
+ }
+ // lower left
+ else if ( ( mx < x+csz ) && ( my > y+h-csz-1 ) )
+ {
+ Grow(3);
+ Grow(4);
+ }
+ // upper left
+ else if ( ( mx < x+csz ) && ( my < y+csz ) )
+ {
+ Grow(4);
+ Grow(1);
+ }
+ // top edge
+ else if ( my < y+esz )
+ {
+ Grow(1);
+ }
+ // right edge
+ else if ( mx > x+w-esz-1 )
+ {
+ Grow(2);
+ }
+ // bottom edge
+ else if ( my > y+h-esz-1 )
+ {
+ Grow(3);
+ }
+ // left edge
+ else if ( mx < x+esz )
+ {
+ Grow(4);
+ }
+ // otherwise (if over the grab bar), grow all edges (from the clicked point)
+ else if ( my < y + ch )
+ {
+ Grow(0, mx, my);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output :
+//-----------------------------------------------------------------------------
+
+void ToolWindow::OnMouseDoublePressed( MouseCode code )
+{
+ GrowFromClick();
+}
+
+void ToolWindow::OnMousePressed( MouseCode code )
+{
+ switch ( code )
+ {
+ case MOUSE_MIDDLE:
+ GrowFromClick();
+ break;
+ default:
+ BaseClass::OnMousePressed( code );
+ }
+}
diff --git a/mp/src/vgui2/vgui_controls/Tooltip.cpp b/mp/src/vgui2/vgui_controls/Tooltip.cpp
new file mode 100644
index 00000000..4debfbda
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/Tooltip.cpp
@@ -0,0 +1,406 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+// This class is a message box that has two buttons, ok and cancel instead of
+// just the ok button of a message box. We use a message box class for the ok button
+// and implement another button here.
+//=============================================================================//
+
+#include <math.h>
+#define PROTECTED_THINGS_DISABLE
+
+#include <vgui/IInput.h>
+#include <vgui/ISystem.h>
+#include <vgui/ISurface.h>
+#include <vgui/IScheme.h>
+#include <vgui/IVGui.h>
+#include <vgui/IPanel.h>
+
+#include <vgui_controls/Tooltip.h>
+#include <vgui_controls/TextEntry.h>
+#include <vgui_controls/Controls.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+
+//------------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------------
+static vgui::DHANDLE< TextEntry > s_TooltipWindow;
+static int s_iTooltipWindowCount = 0;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+BaseTooltip::BaseTooltip(Panel *parent, const char *text)
+{
+ SetText(text);
+
+ _displayOnOneLine = false;
+ _makeVisible = false;
+ _isDirty = false;
+ _enabled = true;
+
+ _tooltipDelay = 500; // default delay for opening tooltips
+ _delay = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Reset the tooltip delay timer
+//-----------------------------------------------------------------------------
+void BaseTooltip::ResetDelay()
+{
+ _isDirty = true;
+ _delay = system()->GetTimeMillis() + _tooltipDelay;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the tooltip delay before a tooltip comes up.
+//-----------------------------------------------------------------------------
+void BaseTooltip::SetTooltipDelay( int tooltipDelay )
+{
+ _tooltipDelay = tooltipDelay;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the tooltip delay before a tooltip comes up.
+//-----------------------------------------------------------------------------
+int BaseTooltip::GetTooltipDelay()
+{
+ return _tooltipDelay;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the tool tip to display on one line only
+// Default is multiple lines.
+//-----------------------------------------------------------------------------
+void BaseTooltip::SetTooltipFormatToSingleLine()
+{
+ _displayOnOneLine = true;
+ _isDirty = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the tool tip to display on multiple lines.
+//-----------------------------------------------------------------------------
+void BaseTooltip::SetTooltipFormatToMultiLine()
+{
+ _displayOnOneLine = false;
+ _isDirty = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Display the tooltip
+//-----------------------------------------------------------------------------
+void BaseTooltip::ShowTooltip(Panel *currentPanel)
+{
+ _makeVisible = true;
+
+ PerformLayout();
+}
+
+void BaseTooltip::SetEnabled( bool bState )
+{
+ _enabled = bState;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool BaseTooltip::ShouldLayout( void )
+{
+ if (!_makeVisible)
+ return false;
+
+ if (_delay > system()->GetTimeMillis())
+ return false;
+
+ // We only need to layout when we first become visible
+ if ( !_isDirty )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Display the tooltip
+//-----------------------------------------------------------------------------
+void BaseTooltip::HideTooltip()
+{
+ _makeVisible = false;
+ _isDirty = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the tooltip text
+//-----------------------------------------------------------------------------
+void BaseTooltip::SetText(const char *text)
+{
+ _isDirty = true;
+
+ if (!text)
+ {
+ text = "";
+ }
+
+ if (m_Text.Size() > 0)
+ {
+ m_Text.RemoveAll();
+ }
+
+ for (unsigned int i = 0; i < strlen(text); i++)
+ {
+ m_Text.AddToTail(text[i]);
+ }
+ m_Text.AddToTail('\0');
+
+ if (s_TooltipWindow.Get() && m_pParent == s_TooltipWindow.Get()->GetParent())
+ {
+ s_TooltipWindow->SetText(m_Text.Base());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the tooltip text
+//-----------------------------------------------------------------------------
+const char *BaseTooltip::GetText()
+{
+ return m_Text.Base();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Position the tool tip
+//-----------------------------------------------------------------------------
+void BaseTooltip::PositionWindow( Panel *pTipPanel )
+{
+ int iTipW, iTipH;
+ pTipPanel->GetSize( iTipW, iTipH );
+
+ int cursorX, cursorY;
+ input()->GetCursorPos(cursorX, cursorY);
+
+ int wide, tall;
+ surface()->GetScreenSize(wide, tall);
+
+ if (wide - iTipW > cursorX)
+ {
+ cursorY += 20;
+ // menu hanging right
+ if (tall - iTipH > cursorY)
+ {
+ // menu hanging down
+ pTipPanel->SetPos(cursorX, cursorY);
+ }
+ else
+ {
+ // menu hanging up
+ pTipPanel->SetPos(cursorX, cursorY - iTipH - 20);
+ }
+ }
+ else
+ {
+ // menu hanging left
+ if (tall - iTipH > cursorY)
+ {
+ // menu hanging down
+ pTipPanel->SetPos(cursorX - iTipW, cursorY);
+ }
+ else
+ {
+ // menu hanging up
+ pTipPanel->SetPos(cursorX - iTipW, cursorY - iTipH - 20 );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+TextTooltip::TextTooltip(Panel *parent, const char *text) : BaseTooltip( parent, text )
+{
+ if (!s_TooltipWindow.Get())
+ {
+ s_TooltipWindow = new TextEntry(NULL, "tooltip");
+
+ s_TooltipWindow->InvalidateLayout(false, true);
+
+ // this bit of hackery is necessary because tooltips don't get ApplySchemeSettings called from their parents
+ IScheme *pScheme = scheme()->GetIScheme( s_TooltipWindow->GetScheme() );
+ s_TooltipWindow->SetBgColor(s_TooltipWindow->GetSchemeColor("Tooltip.BgColor", s_TooltipWindow->GetBgColor(), pScheme));
+ s_TooltipWindow->SetFgColor(s_TooltipWindow->GetSchemeColor("Tooltip.TextColor", s_TooltipWindow->GetFgColor(), pScheme));
+ s_TooltipWindow->SetBorder(pScheme->GetBorder("ToolTipBorder"));
+ s_TooltipWindow->SetFont( pScheme->GetFont("DefaultSmall", s_TooltipWindow->IsProportional()));
+ }
+ s_iTooltipWindowCount++;
+
+ // this line prevents the main window from losing focus
+ // when a tooltip pops up
+ s_TooltipWindow->MakePopup(false, true);
+ s_TooltipWindow->SetKeyBoardInputEnabled( false );
+ s_TooltipWindow->SetMouseInputEnabled( false );
+
+ SetText(text);
+ s_TooltipWindow->SetText(m_Text.Base());
+ s_TooltipWindow->SetEditable(false);
+ s_TooltipWindow->SetMultiline(true);
+ s_TooltipWindow->SetVisible(false);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+TextTooltip::~TextTooltip()
+{
+ if (--s_iTooltipWindowCount < 1)
+ {
+ if ( s_TooltipWindow.Get() )
+ {
+ s_TooltipWindow->MarkForDeletion();
+ }
+ s_TooltipWindow = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the tooltip text
+//-----------------------------------------------------------------------------
+void TextTooltip::SetText(const char *text)
+{
+ BaseTooltip::SetText( text );
+
+ if (s_TooltipWindow.Get())
+ {
+ s_TooltipWindow->SetText(m_Text.Base());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the font from the scheme
+//-----------------------------------------------------------------------------
+void TextTooltip::ApplySchemeSettings(IScheme *pScheme)
+{
+ if ( s_TooltipWindow )
+ {
+ s_TooltipWindow->SetFont(pScheme->GetFont("DefaultSmall", s_TooltipWindow->IsProportional()));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Display the tooltip
+//-----------------------------------------------------------------------------
+void TextTooltip::ShowTooltip(Panel *currentPanel)
+{
+ if ( s_TooltipWindow.Get() )
+ {
+ int nLen = s_TooltipWindow->GetTextLength();
+
+ if ( nLen <= 0 )
+ {
+ // Empty tool tip, no need to show it
+ _makeVisible = false;
+ return;
+ }
+
+ char *pBuf = (char*)_alloca( nLen+1 );
+ s_TooltipWindow->GetText( pBuf, nLen+1 );
+ Panel *pCurrentParent = s_TooltipWindow->GetParent();
+
+ _isDirty = _isDirty || ( pCurrentParent != currentPanel );
+ s_TooltipWindow->SetText( m_Text.Base() );
+ s_TooltipWindow->SetParent(currentPanel);
+ }
+ BaseTooltip::ShowTooltip( currentPanel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Display the tooltip
+//-----------------------------------------------------------------------------
+void TextTooltip::PerformLayout()
+{
+ if ( !ShouldLayout() )
+ return;
+ // we're ready, just make us visible
+ if ( !s_TooltipWindow.Get() )
+ return;
+
+ _isDirty = false;
+
+ s_TooltipWindow->SetVisible(true);
+ s_TooltipWindow->MakePopup( false, true );
+ s_TooltipWindow->SetKeyBoardInputEnabled( false );
+ s_TooltipWindow->SetMouseInputEnabled( false );
+
+ // relayout the textwindow immediately so that we know it's size
+ //m_pTextEntry->InvalidateLayout(true);
+
+ SizeTextWindow();
+ PositionWindow( s_TooltipWindow );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Size the text window so all the text fits inside it.
+//-----------------------------------------------------------------------------
+void TextTooltip::SizeTextWindow()
+{
+ if ( !s_TooltipWindow.Get() )
+ return;
+
+ if (_displayOnOneLine)
+ {
+ // We want the tool tip to be one line
+ s_TooltipWindow->SetMultiline(false);
+ s_TooltipWindow->SetToFullWidth();
+ }
+ else
+ {
+ // We want the tool tip to be one line
+ s_TooltipWindow->SetMultiline(false);
+ s_TooltipWindow->SetToFullWidth();
+ int wide, tall;
+ s_TooltipWindow->GetSize( wide, tall );
+ double newWide = sqrt( (2.0/1) * wide * tall );
+ double newTall = (1/2) * newWide;
+ s_TooltipWindow->SetMultiline(true);
+ s_TooltipWindow->SetSize((int)newWide, (int)newTall );
+ s_TooltipWindow->SetToFullHeight();
+
+ s_TooltipWindow->GetSize( wide, tall );
+
+ if (( wide < 100 ) && ( s_TooltipWindow->GetNumLines() == 2) )
+ {
+ s_TooltipWindow->SetMultiline(false);
+ s_TooltipWindow->SetToFullWidth();
+ }
+ else
+ {
+
+ while ( (float)wide/(float)tall < 2 )
+ {
+ s_TooltipWindow->SetSize( wide + 1, tall );
+ s_TooltipWindow->SetToFullHeight();
+ s_TooltipWindow->GetSize( wide, tall );
+ }
+ }
+ s_TooltipWindow->GetSize( wide, tall );
+ // ivgui()->DPrintf("End Ratio: %f\n", (float)wide/(float)tall);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Display the tooltip
+//-----------------------------------------------------------------------------
+void TextTooltip::HideTooltip()
+{
+ if ( s_TooltipWindow.Get() )
+ {
+ s_TooltipWindow->SetVisible(false);
+ }
+
+ BaseTooltip::HideTooltip();
+}
+
diff --git a/mp/src/vgui2/vgui_controls/TreeView.cpp b/mp/src/vgui2/vgui_controls/TreeView.cpp
new file mode 100644
index 00000000..066ebd50
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/TreeView.cpp
@@ -0,0 +1,2854 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <assert.h>
+
+#define PROTECTED_THINGS_DISABLE
+
+#include <vgui/Cursor.h>
+#include <vgui/IScheme.h>
+#include <vgui/IInput.h>
+#include <vgui/IPanel.h>
+#include <vgui/ISurface.h>
+#include <vgui/ISystem.h>
+#include <vgui/IVGui.h>
+#include <vgui/KeyCode.h>
+#include <KeyValues.h>
+#include <vgui/MouseCode.h>
+
+#include <vgui_controls/TreeView.h>
+#include <vgui_controls/ScrollBar.h>
+#include <vgui_controls/TextEntry.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/Button.h>
+#include <vgui_controls/TextImage.h>
+#include <vgui_controls/ImageList.h>
+#include <vgui_controls/ImagePanel.h>
+
+#include "tier1/utlstring.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+using namespace vgui;
+enum
+{
+ WINDOW_BORDER_WIDTH=2 // the width of the window's border
+};
+
+#define TREE_INDENT_AMOUNT 20
+
+namespace vgui
+{
+
+//-----------------------------------------------------------------------------
+// Purpose: Displays an editable text field for the text control
+//-----------------------------------------------------------------------------
+class TreeNodeText : public TextEntry
+{
+ DECLARE_CLASS_SIMPLE( TreeNodeText, TextEntry );
+
+public:
+ TreeNodeText(Panel *parent, const char *panelName, TreeView *tree) : BaseClass(parent, panelName), m_pTree( tree )
+ {
+ m_bEditingInPlace = false;
+ m_bLabelEditingAllowed = false;
+ SetDragEnabled( false );
+ SetDropEnabled( false );
+ AddActionSignalTarget( this );
+ m_bArmForEditing = false;
+ m_bWaitingForRelease = false;
+ m_lArmingTime = 0L;
+ SetAllowKeyBindingChainToParent( true );
+ }
+
+ MESSAGE_FUNC( OnTextChanged, "TextChanged" )
+ {
+ GetParent()->InvalidateLayout();
+ }
+
+ bool IsKeyRebound( KeyCode code, int modifiers )
+ {
+ // If in editing mode, don't try and chain keypresses
+ if ( m_bEditingInPlace )
+ {
+ return false;
+ }
+
+ return BaseClass::IsKeyRebound( code, modifiers );
+ }
+
+ virtual void PaintBackground()
+ {
+ BaseClass::PaintBackground();
+
+ if ( !m_bLabelEditingAllowed )
+ return;
+
+ if ( !m_bEditingInPlace )
+ return;
+
+ int w, h;
+ GetSize( w, h );
+ surface()->DrawSetColor( GetFgColor() );
+ surface()->DrawOutlinedRect( 0, 0, w, h );
+ }
+
+ virtual void ApplySchemeSettings(IScheme *pScheme)
+ {
+ TextEntry::ApplySchemeSettings(pScheme);
+ SetBorder(NULL);
+ SetCursor(dc_arrow);
+ }
+
+ virtual void OnKeyCodeTyped(KeyCode code)
+ {
+ if ( m_bEditingInPlace )
+ {
+ if ( code == KEY_ENTER )
+ {
+ FinishEditingInPlace();
+ }
+ else if ( code == KEY_ESCAPE )
+ {
+ FinishEditingInPlace( true );
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped( code );
+ }
+ return;
+ }
+ else if ( code == KEY_ENTER && IsLabelEditingAllowed() )
+ {
+ EnterEditingInPlace();
+ }
+ else
+ {
+ // let parent deal with it (don't chain back to TextEntry)
+ CallParentFunction(new KeyValues("KeyCodeTyped", "code", code));
+ }
+ }
+
+#define CLICK_TO_EDIT_DELAY_MSEC 500
+
+ virtual void OnTick()
+ {
+ BaseClass::OnTick();
+ if ( m_bArmForEditing )
+ {
+ long msecSinceArming = system()->GetTimeMillis() - m_lArmingTime;
+
+ if ( msecSinceArming > CLICK_TO_EDIT_DELAY_MSEC )
+ {
+ m_bArmForEditing = false;
+ m_bWaitingForRelease = false;
+ ivgui()->RemoveTickSignal( GetVPanel() );
+ EnterEditingInPlace();
+ }
+ }
+ }
+
+ virtual void OnMouseReleased( MouseCode code )
+ {
+ if ( m_bEditingInPlace )
+ {
+ BaseClass::OnMouseReleased( code );
+ return;
+ }
+ else
+ {
+ if ( m_bWaitingForRelease && !IsBeingDragged() )
+ {
+ m_bArmForEditing = true;
+ m_bWaitingForRelease = false;
+ m_lArmingTime = system()->GetTimeMillis();
+ ivgui()->AddTickSignal( GetVPanel() );
+ }
+ else
+ {
+ m_bWaitingForRelease = false;
+ }
+ }
+
+ // let parent deal with it
+ CallParentFunction(new KeyValues("MouseReleased", "code", code));
+ }
+
+ virtual void OnCursorMoved( int x, int y )
+ {
+ // let parent deal with it
+ CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y));
+ }
+
+ virtual void OnMousePressed(MouseCode code)
+ {
+ if ( m_bEditingInPlace )
+ {
+ BaseClass::OnMousePressed( code );
+ return;
+ }
+ else
+ {
+ bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+ bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+
+ // make sure there is only one item selected
+ // before "WaitingForRelease" which leads to label editing.
+ CUtlVector< int > list;
+ m_pTree->GetSelectedItems( list );
+ bool bIsOnlyOneItemSelected = ( list.Count() == 1 );
+
+ if ( !shift &&
+ !ctrl &&
+ !m_bArmForEditing &&
+ IsLabelEditingAllowed() &&
+ bIsOnlyOneItemSelected &&
+ IsTextFullySelected() &&
+ !IsBeingDragged() )
+ {
+ m_bWaitingForRelease = true;
+ }
+ }
+
+ // let parent deal with it
+ CallParentFunction(new KeyValues("MousePressed", "code", code));
+ }
+
+ void SetLabelEditingAllowed( bool state )
+ {
+ m_bLabelEditingAllowed = state;
+ }
+
+ bool IsLabelEditingAllowed()
+ {
+ return m_bLabelEditingAllowed;
+ }
+
+ virtual void OnMouseDoublePressed(MouseCode code)
+ {
+ // Once we are editing, double pressing shouldn't chain up
+ if ( m_bEditingInPlace )
+ {
+ BaseClass::OnMouseDoublePressed( code );
+ return;
+ }
+
+ if ( m_bArmForEditing )
+ {
+ m_bArmForEditing = false;
+ m_bWaitingForRelease = false;
+ ivgui()->RemoveTickSignal( GetVPanel() );
+ }
+
+ CallParentFunction(new KeyValues("MouseDoublePressed", "code", code));
+ }
+
+ void EnterEditingInPlace()
+ {
+ if ( m_bEditingInPlace )
+ return;
+
+ m_bEditingInPlace = true;
+ char buf[ 1024 ];
+ GetText( buf, sizeof( buf ) );
+ m_OriginalText = buf;
+ SetCursor(dc_ibeam);
+ SetEditable( true );
+ SelectNone();
+ GotoTextEnd();
+ RequestFocus();
+ SelectAllText(false);
+ m_pTree->SetLabelBeingEdited( true );
+ }
+
+ void FinishEditingInPlace( bool revert = false )
+ {
+ if ( !m_bEditingInPlace )
+ return;
+
+ m_pTree->SetLabelBeingEdited( false );
+ SetEditable( false );
+ SetCursor(dc_arrow);
+ m_bEditingInPlace = false;
+ char buf[ 1024 ];
+ GetText( buf, sizeof( buf ) );
+
+ // Not actually changed...
+ if ( !Q_strcmp( buf, m_OriginalText.Get() ) )
+ return;
+
+ if ( revert )
+ {
+ SetText( m_OriginalText.Get() );
+ GetParent()->InvalidateLayout();
+ }
+ else
+ {
+ KeyValues *kv = new KeyValues( "LabelChanged", "original", m_OriginalText.Get(), "changed", buf );
+ PostActionSignal( kv );
+ }
+ }
+
+ virtual void OnKillFocus()
+ {
+ BaseClass::OnKillFocus();
+
+ FinishEditingInPlace();
+ }
+
+ virtual void OnMouseWheeled(int delta)
+ {
+ if ( m_bEditingInPlace )
+ {
+ BaseClass::OnMouseWheeled( delta );
+ return;
+ }
+
+ CallParentFunction(new KeyValues("MouseWheeled", "delta", delta));
+ }
+ // editable - cursor normal, and ability to edit text
+
+ bool IsBeingEdited() const
+ {
+ return m_bEditingInPlace;
+ }
+
+private:
+
+ bool m_bEditingInPlace;
+ CUtlString m_OriginalText;
+ bool m_bLabelEditingAllowed;
+
+ bool m_bArmForEditing;
+ bool m_bWaitingForRelease;
+ long m_lArmingTime;
+ TreeView *m_pTree;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: icon for the tree node (folder icon, file icon, etc.)
+//-----------------------------------------------------------------------------
+class TreeNodeImage : public ImagePanel
+{
+public:
+ TreeNodeImage(Panel *parent, const char *name) : ImagePanel(parent, name)
+ {
+ SetBlockDragChaining( true );
+ }
+
+ //!! this could possibly be changed to just disallow mouse input on the image panel
+ virtual void OnMousePressed(MouseCode code)
+ {
+ // let parent deal with it
+ CallParentFunction(new KeyValues("MousePressed", "code", code));
+ }
+
+ virtual void OnMouseDoublePressed(MouseCode code)
+ {
+ // let parent deal with it
+ CallParentFunction(new KeyValues("MouseDoublePressed", "code", code));
+ }
+
+ virtual void OnMouseWheeled(int delta)
+ {
+ // let parent deal with it
+ CallParentFunction(new KeyValues("MouseWheeled", "delta", delta));
+ }
+
+ virtual void OnCursorMoved( int x, int y )
+ {
+ // let parent deal with it
+ CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y));
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Scrollable area of the tree control, holds the tree itself only
+//-----------------------------------------------------------------------------
+class TreeViewSubPanel : public Panel
+{
+public:
+ TreeViewSubPanel(Panel *parent) : Panel(parent) {}
+
+ virtual void ApplySchemeSettings(IScheme *pScheme)
+ {
+ Panel::ApplySchemeSettings(pScheme);
+
+ SetBorder(NULL);
+ }
+
+ virtual void OnMouseWheeled(int delta)
+ {
+ // let parent deal with it
+ CallParentFunction(new KeyValues("MouseWheeled", "delta", delta));
+ }
+ virtual void OnMousePressed(MouseCode code)
+ {
+ // let parent deal with it
+ CallParentFunction(new KeyValues("MousePressed", "code", code));
+ }
+ virtual void OnMouseDoublePressed(MouseCode code)
+ {
+ // let parent deal with it
+ CallParentFunction(new KeyValues("MouseDoublePressed", "code", code));
+ }
+
+ virtual void OnCursorMoved( int x, int y )
+ {
+ // let parent deal with it
+ CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y));
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: A single entry in the tree
+//-----------------------------------------------------------------------------
+class TreeNode : public Panel
+{
+ DECLARE_CLASS_SIMPLE( TreeNode, Panel );
+
+public:
+ TreeNode(Panel *parent, TreeView *pTreeView);
+ ~TreeNode();
+ void SetText(const char *pszText);
+ void SetFont(HFont font);
+ void SetKeyValues(KeyValues *data);
+ bool IsSelected();
+ // currently unused, could be re-used if necessary
+// bool IsInFocus();
+ virtual void PaintBackground();
+ virtual void PerformLayout();
+ TreeNode *GetParentNode();
+ int GetChildrenCount();
+ void ClearChildren();
+ int ComputeInsertionPosition( TreeNode *pChild );
+ int FindChild( TreeNode *pChild );
+ void AddChild(TreeNode *pChild);
+ void SetNodeExpanded(bool bExpanded);
+ bool IsExpanded();
+ int CountVisibleNodes();
+ void CalculateVisibleMaxWidth();
+ void OnChildWidthChange();
+ int GetMaxChildrenWidth();
+ int GetVisibleMaxWidth();
+ int GetDepth();
+ bool HasParent(TreeNode *pTreeNode);
+ bool IsBeingDisplayed();
+ virtual void SetVisible(bool state);
+ virtual void Paint();
+ virtual void ApplySchemeSettings(IScheme *pScheme);
+ virtual void SetBgColor( Color color );
+ virtual void SetFgColor( Color color );
+ virtual void OnSetFocus();
+ void SelectPrevChild(TreeNode *pCurrentChild);
+ void SelectNextChild(TreeNode *pCurrentChild);
+
+ int GetPrevChildItemIndex( TreeNode *pCurrentChild );
+ int GetNextChildItemIndex( TreeNode *pCurrentChild );
+
+ virtual void ClosePreviousParents( TreeNode *pPreviousParent );
+ virtual void StepInto( bool bClosePrevious=true );
+ virtual void StepOut( bool bClosePrevious=true );
+ virtual void StepOver( bool bClosePrevious=true );
+ virtual void OnKeyCodeTyped(KeyCode code);
+ virtual void OnMouseWheeled(int delta);
+ virtual void OnMousePressed( MouseCode code);
+ virtual void OnMouseReleased( MouseCode code);
+ virtual void OnCursorMoved( int x, int y );
+ virtual bool IsDragEnabled() const;
+ void PositionAndSetVisibleNodes(int &nStart, int &nCount, int x, int &y);
+
+ // counts items above this item including itself
+ int CountVisibleIndex();
+
+ virtual void OnCreateDragData( KeyValues *msg );
+ // For handling multiple selections...
+ virtual void OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles );
+ virtual void OnMouseDoublePressed( MouseCode code );
+ TreeNode *FindItemUnderMouse( int &nStart, int& nCount, int x, int &y, int mx, int my );
+ MESSAGE_FUNC_PARAMS( OnLabelChanged, "LabelChanged", data );
+ void EditLabel();
+ void SetLabelEditingAllowed( bool state );
+ bool IsLabelEditingAllowed() const;
+
+ virtual bool IsDroppable( CUtlVector< KeyValues * >& msglist );
+ virtual void OnPanelDropped( CUtlVector< KeyValues * >& msglist );
+ virtual HCursor GetDropCursor( CUtlVector< KeyValues * >& msglist );
+ virtual bool GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist );
+
+ void FindNodesInRange( CUtlVector< TreeNode * >& list, int startIndex, int endIndex );
+
+ void RemoveChildren();
+
+ void SetSelectionTextColor( const Color& clr );
+ void SetSelectionBgColor( const Color& clr );
+ void SetSelectionUnfocusedBgColor( const Color& clr );
+public:
+ int m_ItemIndex;
+ int m_ParentIndex;
+ KeyValues *m_pData;
+ CUtlVector<TreeNode *> m_Children;
+ bool m_bExpand;
+
+private:
+
+ void FindNodesInRange_R( CUtlVector< TreeNode * >& list, bool& finished, bool& foundStart, int startIndex, int endIndex );
+
+ int m_iNodeWidth;
+ int m_iMaxVisibleWidth;
+
+ TreeNodeText *m_pText;
+ TextImage *m_pExpandImage;
+ TreeNodeImage *m_pImagePanel;
+
+ bool m_bExpandableWithoutChildren;
+
+ TreeView *m_pTreeView;
+ int m_nClickedItem;
+ bool m_bClickedSelected;
+};
+
+
+TreeNode::TreeNode(Panel *parent, TreeView *pTreeView) :
+ BaseClass(parent, "TreeNode" ),
+ m_nClickedItem( 0 ),
+ m_bClickedSelected( false )
+{
+ m_pData = NULL;
+ m_pTreeView = pTreeView;
+ m_ItemIndex = -1;
+ m_iNodeWidth = 0;
+ m_iMaxVisibleWidth = 0;
+
+ m_pExpandImage = new TextImage("+");
+ m_pExpandImage->SetPos(3, 1);
+
+ m_pImagePanel = new TreeNodeImage(this, "TreeImage");
+ m_pImagePanel->SetPos(TREE_INDENT_AMOUNT, 3);
+
+ m_pText = new TreeNodeText(this, "TreeNodeText",pTreeView);
+ m_pText->SetMultiline(false);
+ m_pText->SetEditable(false);
+ m_pText->SetPos(TREE_INDENT_AMOUNT*2, 0);
+ m_pText->AddActionSignalTarget( this );
+
+ m_bExpand = false;
+ m_bExpandableWithoutChildren = false;
+}
+
+TreeNode::~TreeNode()
+{
+ delete m_pExpandImage;
+ if ( m_pData )
+ {
+ m_pData->deleteThis();
+ }
+}
+
+void TreeNode::SetText(const char *pszText)
+{
+ m_pText->SetText(pszText);
+ InvalidateLayout();
+}
+
+void TreeNode::SetLabelEditingAllowed( bool state )
+{
+ Assert( m_pTreeView->IsLabelEditingAllowed() );
+ m_pText->SetLabelEditingAllowed( state );
+}
+
+bool TreeNode::IsLabelEditingAllowed() const
+{
+ return m_pText->IsLabelEditingAllowed();
+}
+
+bool TreeNode::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist )
+{
+ return m_pTreeView->GetItemDropContextMenu( m_ItemIndex, menu, msglist );
+}
+
+bool TreeNode::IsDroppable( CUtlVector< KeyValues * >& msglist )
+{
+ return m_pTreeView->IsItemDroppable( m_ItemIndex, msglist );
+}
+
+void TreeNode::OnPanelDropped( CUtlVector< KeyValues * >& msglist )
+{
+ m_pTreeView->OnItemDropped( m_ItemIndex, msglist );
+}
+
+HCursor TreeNode::GetDropCursor( CUtlVector< KeyValues * >& msglist )
+{
+ return m_pTreeView->GetItemDropCursor( m_ItemIndex, msglist );
+}
+
+
+void TreeNode::OnCreateDragData( KeyValues *msg )
+{
+ // make sure the dragged item appears selected,
+ // on the off chance it appears deselected by a cntl mousedown
+ m_pTreeView->AddSelectedItem( m_ItemIndex, false );
+
+ m_pTreeView->GenerateDragDataForItem( m_ItemIndex, msg );
+}
+
+// For handling multiple selections...
+void TreeNode::OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles )
+{
+ CUtlVector< int > list;
+ m_pTreeView->GetSelectedItems( list );
+ int c = list.Count();
+ // walk this in reverse order so that panels are in order of selection
+ // even though GetSelectedItems returns items in reverse selection order
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ int itemIndex = list[ i ];
+ // Skip self
+ if ( itemIndex == m_ItemIndex )
+ continue;
+
+ dragabbles.AddToTail( ( Panel * )m_pTreeView->GetItem( itemIndex ) );
+ }
+}
+
+void TreeNode::OnLabelChanged( KeyValues *data )
+{
+ char const *oldString = data->GetString( "original" );
+ char const *newString = data->GetString( "changed" );
+ if ( m_pTreeView->IsLabelEditingAllowed() )
+ {
+ m_pTreeView->OnLabelChanged( m_ItemIndex, oldString, newString );
+ }
+}
+
+void TreeNode::EditLabel()
+{
+ if ( m_pText->IsLabelEditingAllowed() &&
+ !m_pText->IsBeingEdited() )
+ {
+ m_pText->EnterEditingInPlace();
+ }
+}
+
+void TreeNode::SetFont(HFont font)
+{
+ Assert( font );
+ if ( !font )
+ return;
+
+ m_pText->SetFont(font);
+ m_pExpandImage->SetFont(font);
+ InvalidateLayout();
+ int i;
+ for (i=0;i<GetChildrenCount();i++)
+ {
+ m_Children[i]->SetFont(font);
+ }
+}
+
+void TreeNode::SetKeyValues(KeyValues *data)
+{
+ if ( m_pData != data )
+ {
+ if (m_pData)
+ {
+ m_pData->deleteThis();
+ }
+
+ m_pData = data->MakeCopy();
+ }
+
+ // set text
+ m_pText->SetText(data->GetString("Text", ""));
+ m_bExpandableWithoutChildren = data->GetInt("Expand");
+ InvalidateLayout();
+}
+
+bool TreeNode::IsSelected()
+{
+ return m_pTreeView->IsItemSelected( m_ItemIndex );
+}
+
+void TreeNode::PaintBackground()
+{
+ if ( !m_pText->IsBeingEdited() )
+ {
+ // setup panel drawing
+ if ( IsSelected() )
+ {
+ m_pText->SelectAllText(false);
+ }
+ else
+ {
+ m_pText->SelectNoText();
+ }
+ }
+
+ BaseClass::PaintBackground();
+}
+
+
+// currently unused, could be re-used if necessary
+/*
+bool TreeNode::IsInFocus()
+{
+ // check if our parent or one of it's children has focus
+ VPANEL focus = input()->GetFocus();
+ return (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())));
+}
+*/
+
+void TreeNode::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int width = 0;
+ if (m_pData->GetInt("SelectedImage", 0) == 0 &&
+ m_pData->GetInt("Image", 0) == 0)
+ {
+ width = TREE_INDENT_AMOUNT;
+ }
+ else
+ {
+ width = TREE_INDENT_AMOUNT * 2;
+ }
+
+ m_pText->SetPos(width, 0);
+
+ int contentWide, contentTall;
+ m_pText->SetToFullWidth();
+ m_pText->GetSize(contentWide, contentTall);
+ contentWide += 10;
+ m_pText->SetSize( contentWide, m_pTreeView->GetRowHeight() );
+ width += contentWide;
+ SetSize(width, m_pTreeView->GetRowHeight());
+
+ m_iNodeWidth = width;
+ CalculateVisibleMaxWidth();
+}
+
+TreeNode *TreeNode::GetParentNode()
+{
+ if (m_pTreeView->m_NodeList.IsValidIndex(m_ParentIndex))
+ {
+ return m_pTreeView->m_NodeList[m_ParentIndex];
+ }
+ return NULL;
+}
+
+int TreeNode::GetChildrenCount()
+{
+ return m_Children.Count();
+}
+
+int TreeNode::ComputeInsertionPosition( TreeNode *pChild )
+{
+ if ( !m_pTreeView->m_pSortFunc )
+ {
+ return GetChildrenCount() - 1;
+ }
+
+ int start = 0, end = GetChildrenCount() - 1;
+ while (start <= end)
+ {
+ int mid = (start + end) >> 1;
+ if ( m_pTreeView->m_pSortFunc( m_Children[mid]->m_pData, pChild->m_pData ) )
+ {
+ start = mid + 1;
+ }
+ else if ( m_pTreeView->m_pSortFunc( pChild->m_pData, m_Children[mid]->m_pData ) )
+ {
+ end = mid - 1;
+ }
+ else
+ {
+ return mid;
+ }
+ }
+ return end;
+}
+
+int TreeNode::FindChild( TreeNode *pChild )
+{
+ if ( !m_pTreeView->m_pSortFunc )
+ {
+ AssertMsg( 0, "This code has never been tested. Is it correct?" );
+ for ( int i = 0; i < GetChildrenCount(); ++i )
+ {
+ if ( m_Children[i] == pChild )
+ return i;
+ }
+ return -1;
+ }
+
+ // Find the first entry <= to the child
+ int start = 0, end = GetChildrenCount() - 1;
+ while (start <= end)
+ {
+ int mid = (start + end) >> 1;
+
+ if ( m_Children[mid] == pChild )
+ return mid;
+
+ if ( m_pTreeView->m_pSortFunc( m_Children[mid]->m_pData, pChild->m_pData ) )
+ {
+ start = mid + 1;
+ }
+ else
+ {
+ end = mid - 1;
+ }
+ }
+
+ int nMax = GetChildrenCount();
+ while( end < nMax )
+ {
+ // Stop when we reach a child that has a different value
+ if ( m_pTreeView->m_pSortFunc( pChild->m_pData, m_Children[end]->m_pData ) )
+ return -1;
+
+ if ( m_Children[end] == pChild )
+ return end;
+
+ ++end;
+ }
+
+ return -1;
+}
+
+void TreeNode::AddChild(TreeNode *pChild)
+{
+ int i = ComputeInsertionPosition( pChild );
+ m_Children.InsertAfter( i, pChild );
+}
+
+void TreeNode::SetNodeExpanded(bool bExpanded)
+{
+ m_bExpand = bExpanded;
+
+ if (m_bExpand)
+ {
+ // see if we have any child nodes
+ if (GetChildrenCount() < 1)
+ {
+ // we need to get our children from the control
+ m_pTreeView->GenerateChildrenOfNode(m_ItemIndex);
+
+ // if we still don't have any children, then hide the expand button
+ if (GetChildrenCount() < 1)
+ {
+ m_bExpand = false;
+ m_bExpandableWithoutChildren = false;
+ m_pTreeView->InvalidateLayout();
+ return;
+ }
+ }
+
+ m_pExpandImage->SetText("-");
+ }
+ else
+ {
+ m_pExpandImage->SetText("+");
+
+ if ( m_bExpandableWithoutChildren && GetChildrenCount() > 0 )
+ {
+ m_pTreeView->RemoveChildrenOfNode( m_ItemIndex );
+ }
+
+ // check if we've closed down on one of our children, if so, we get the focus
+ int selectedItem = m_pTreeView->GetFirstSelectedItem();
+ if (selectedItem != -1 && m_pTreeView->m_NodeList[selectedItem]->HasParent(this))
+ {
+ m_pTreeView->AddSelectedItem( m_ItemIndex, true );
+ }
+ }
+ CalculateVisibleMaxWidth();
+ m_pTreeView->InvalidateLayout();
+}
+
+bool TreeNode::IsExpanded()
+{
+ return m_bExpand;
+}
+
+int TreeNode::CountVisibleNodes()
+{
+ int count = 1; // count myself
+ if (m_bExpand)
+ {
+ int i;
+ for (i=0;i<m_Children.Count();i++)
+ {
+ count += m_Children[i]->CountVisibleNodes();
+ }
+ }
+ return count;
+}
+
+void TreeNode::CalculateVisibleMaxWidth()
+{
+ int width;
+ if (m_bExpand)
+ {
+ int childMaxWidth = GetMaxChildrenWidth();
+ childMaxWidth += TREE_INDENT_AMOUNT;
+
+ width = max(childMaxWidth, m_iNodeWidth);
+ }
+ else
+ {
+ width = m_iNodeWidth;
+ }
+ if (width != m_iMaxVisibleWidth)
+ {
+ m_iMaxVisibleWidth = width;
+ if (GetParentNode())
+ {
+ GetParentNode()->OnChildWidthChange();
+ }
+ else
+ {
+ m_pTreeView->InvalidateLayout();
+ }
+ }
+}
+
+void TreeNode::OnChildWidthChange()
+{
+ CalculateVisibleMaxWidth();
+}
+
+int TreeNode::GetMaxChildrenWidth()
+{
+ int maxWidth = 0;
+ int i;
+ for (i=0;i<GetChildrenCount();i++)
+ {
+ int childWidth = m_Children[i]->GetVisibleMaxWidth();
+ if (childWidth > maxWidth)
+ {
+ maxWidth = childWidth;
+ }
+ }
+ return maxWidth;
+}
+
+int TreeNode::GetVisibleMaxWidth()
+{
+ return m_iMaxVisibleWidth;
+}
+
+int TreeNode::GetDepth()
+{
+ int depth = 0;
+ TreeNode *pParent = GetParentNode();
+ while (pParent)
+ {
+ depth++;
+ pParent = pParent->GetParentNode();
+ }
+ return depth;
+}
+
+bool TreeNode::HasParent(TreeNode *pTreeNode)
+{
+ TreeNode *pParent = GetParentNode();
+ while (pParent)
+ {
+ if (pParent == pTreeNode)
+ return true;
+ pParent = pParent->GetParentNode();
+ }
+ return false;
+}
+
+bool TreeNode::IsBeingDisplayed()
+{
+ TreeNode *pParent = GetParentNode();
+ while (pParent)
+ {
+ // our parents aren't showing us
+ if (!pParent->m_bExpand)
+ return false;
+
+ pParent = pParent->GetParentNode();
+ }
+ return true;
+}
+
+void TreeNode::SetVisible(bool state)
+{
+ BaseClass::SetVisible(state);
+
+ bool bChildrenVisible = state && m_bExpand;
+ int i;
+ for (i=0;i<GetChildrenCount();i++)
+ {
+ m_Children[i]->SetVisible(bChildrenVisible);
+ }
+}
+
+void TreeNode::Paint()
+{
+ if (GetChildrenCount() > 0 || m_bExpandableWithoutChildren)
+ {
+ m_pExpandImage->Paint();
+ }
+
+ // set image
+ int imageIndex = 0;
+ if (IsSelected())
+ {
+ imageIndex = m_pData->GetInt("SelectedImage", 0);
+ }
+ else
+ {
+ imageIndex = m_pData->GetInt("Image", 0);
+ }
+
+ if (imageIndex)
+ {
+ IImage *pImage = m_pTreeView->GetImage(imageIndex);
+ if (pImage)
+ {
+ m_pImagePanel->SetImage(pImage);
+ }
+ m_pImagePanel->Paint();
+ }
+
+ m_pText->Paint();
+}
+
+void TreeNode::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetBorder( NULL );
+ SetFgColor( m_pTreeView->GetFgColor() );
+ SetBgColor( m_pTreeView->GetBgColor() );
+ SetFont( m_pTreeView->GetFont() );
+}
+
+void TreeNode::SetSelectionTextColor( const Color& clr )
+{
+ if ( m_pText )
+ {
+ m_pText->SetSelectionTextColor( clr );
+ }
+}
+
+void TreeNode::SetSelectionBgColor( const Color& clr )
+{
+ if ( m_pText )
+ {
+ m_pText->SetSelectionBgColor( clr );
+ }
+}
+
+void TreeNode::SetSelectionUnfocusedBgColor( const Color& clr )
+{
+ if ( m_pText )
+ {
+ m_pText->SetSelectionUnfocusedBgColor( clr );
+ }
+}
+
+void TreeNode::SetBgColor( Color color )
+{
+ BaseClass::SetBgColor( color );
+ if ( m_pText )
+ {
+ m_pText->SetBgColor( color );
+ }
+
+}
+
+void TreeNode::SetFgColor( Color color )
+{
+ BaseClass::SetFgColor( color );
+ if ( m_pText )
+ {
+ m_pText->SetFgColor( color );
+ }
+}
+
+void TreeNode::OnSetFocus()
+{
+ m_pText->RequestFocus();
+}
+
+int TreeNode::GetPrevChildItemIndex( TreeNode *pCurrentChild )
+{
+ int i;
+ for (i=0;i<GetChildrenCount();i++)
+ {
+ if ( m_Children[i] == pCurrentChild )
+ {
+ if ( i <= 0 )
+ return -1;
+
+ TreeNode *pChild = m_Children[i-1];
+ return pChild->m_ItemIndex;
+ }
+ }
+ return -1;
+}
+
+int TreeNode::GetNextChildItemIndex( TreeNode *pCurrentChild )
+{
+ int i;
+ for (i=0;i<GetChildrenCount();i++)
+ {
+ if ( m_Children[i] == pCurrentChild )
+ {
+ if ( i >= GetChildrenCount() - 1 )
+ return -1;
+
+ TreeNode *pChild = m_Children[i+1];
+ return pChild->m_ItemIndex;
+ }
+ }
+ return -1;
+}
+
+void TreeNode::SelectPrevChild(TreeNode *pCurrentChild)
+{
+ int i;
+ for (i=0;i<GetChildrenCount();i++)
+ {
+ if (m_Children[i] == pCurrentChild)
+ break;
+ }
+
+ // this shouldn't happen
+ if (i == GetChildrenCount())
+ {
+ Assert(0);
+ return;
+ }
+
+ // were we on the first child?
+ if (i == 0)
+ {
+ // if so, then we take over!
+ m_pTreeView->AddSelectedItem( m_ItemIndex, true );
+ }
+ else
+ {
+ // see if we need to find a grandchild of the previous sibling
+ TreeNode *pChild = m_Children[i-1];
+
+ // if this child is expanded with children, then we have to find the last child
+ while (pChild->m_bExpand && pChild->GetChildrenCount()>0)
+ {
+ // find the last child
+ pChild = pChild->m_Children[pChild->GetChildrenCount()-1];
+ }
+ m_pTreeView->AddSelectedItem( pChild->m_ItemIndex, true );
+ }
+}
+
+void TreeNode::SelectNextChild(TreeNode *pCurrentChild)
+{
+ int i;
+ for (i=0;i<GetChildrenCount();i++)
+ {
+ if (m_Children[i] == pCurrentChild)
+ break;
+ }
+
+ // this shouldn't happen
+ if (i == GetChildrenCount())
+ {
+ Assert(0);
+ return;
+ }
+
+ // were we on the last child?
+ if (i == GetChildrenCount() - 1)
+ {
+ // tell our parent to get the next child
+ if (GetParentNode())
+ {
+ GetParentNode()->SelectNextChild(this);
+ }
+ }
+ else
+ {
+ m_pTreeView->AddSelectedItem( m_Children[i+1]->m_ItemIndex, true );
+ }
+}
+
+void TreeNode::ClosePreviousParents( TreeNode *pPreviousParent )
+{
+ // close up all the open nodes we've just stepped out of.
+ CUtlVector< int > selected;
+ m_pTreeView->GetSelectedItems( selected );
+ if ( selected.Count() == 0 )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ // Most recently clicked item
+ TreeNode *selectedItem = m_pTreeView->GetItem( selected[ 0 ] );
+ TreeNode *pNewParent = selectedItem->GetParentNode();
+ if ( pPreviousParent && pNewParent )
+ {
+ while ( pPreviousParent->m_ItemIndex > pNewParent->m_ItemIndex )
+ {
+ pPreviousParent->SetNodeExpanded(false);
+ pPreviousParent = pPreviousParent->GetParentNode();
+ }
+ }
+}
+
+void TreeNode::StepInto( bool bClosePrevious )
+{
+ if ( !m_bExpand )
+ {
+ SetNodeExpanded(true);
+ }
+
+ if ( ( GetChildrenCount() > 0 ) && m_bExpand )
+ {
+ m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true );
+ }
+ else if ( GetParentNode() )
+ {
+ TreeNode *pParent = GetParentNode();
+ pParent->SelectNextChild(this);
+
+ if ( bClosePrevious )
+ {
+ ClosePreviousParents( pParent );
+ }
+ }
+}
+
+void TreeNode::StepOut( bool bClosePrevious )
+{
+ TreeNode *pParent = GetParentNode();
+ if ( pParent )
+ {
+ m_pTreeView->AddSelectedItem( pParent->m_ItemIndex, true );
+ if ( pParent->GetParentNode() )
+ {
+ pParent->GetParentNode()->SelectNextChild(pParent);
+ }
+ if ( bClosePrevious )
+ {
+ ClosePreviousParents( pParent );
+ }
+ else
+ {
+ pParent->SetNodeExpanded(true);
+ }
+ }
+}
+
+void TreeNode::StepOver( bool bClosePrevious )
+{
+ TreeNode *pParent = GetParentNode();
+ if ( pParent )
+ {
+ GetParentNode()->SelectNextChild(this);
+ if ( bClosePrevious )
+ {
+ ClosePreviousParents( pParent );
+ }
+ }
+}
+
+void TreeNode::OnKeyCodeTyped(KeyCode code)
+{
+ switch (code)
+ {
+ case KEY_LEFT:
+ {
+ if (m_bExpand && GetChildrenCount() > 0)
+ {
+ SetNodeExpanded(false);
+ }
+ else
+ {
+ if (GetParentNode())
+ {
+ m_pTreeView->AddSelectedItem( GetParentNode()->m_ItemIndex, true );
+ }
+ }
+ break;
+ }
+ case KEY_RIGHT:
+ {
+ if (!m_bExpand)
+ {
+ SetNodeExpanded(true);
+ }
+ else if (GetChildrenCount() > 0)
+ {
+ m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true );
+ }
+ break;
+ }
+ case KEY_UP:
+ {
+ if (GetParentNode())
+ {
+ GetParentNode()->SelectPrevChild(this);
+ }
+ break;
+ }
+ case KEY_DOWN:
+ {
+ if (GetChildrenCount() > 0 && m_bExpand)
+ {
+ m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true );
+ }
+ else if (GetParentNode())
+ {
+ GetParentNode()->SelectNextChild(this);
+ }
+ break;
+ }
+ case KEY_SPACE:
+ {
+ bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+ bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+ bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
+ if ( shift )
+ {
+ StepOut( !ctrl );
+ }
+ else if ( alt )
+ {
+ StepOver( !ctrl );
+ }
+ else
+ {
+ StepInto( !ctrl );
+ }
+ break;
+ }
+ case KEY_I:
+ {
+ StepInto();
+ break;
+ }
+ case KEY_U:
+ {
+ StepOut();
+ break;
+ }
+ case KEY_O:
+ {
+ StepOver();
+ break;
+ }
+ case KEY_ESCAPE:
+ {
+ if ( m_pTreeView->GetSelectedItemCount() > 0 )
+ {
+ m_pTreeView->ClearSelection();
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped(code);
+ }
+ }
+ break;
+ case KEY_A:
+ {
+ bool ctrldown = input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL );
+ if ( ctrldown )
+ {
+ m_pTreeView->SelectAll();
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped(code);
+ }
+ }
+ break;
+ default:
+ BaseClass::OnKeyCodeTyped(code);
+ return;
+ }
+}
+
+void TreeNode::OnMouseWheeled(int delta)
+{
+ CallParentFunction(new KeyValues("MouseWheeled", "delta", delta));
+}
+
+void TreeNode::OnMouseDoublePressed( MouseCode code )
+{
+ int x, y;
+ input()->GetCursorPos(x, y);
+
+ if (code == MOUSE_LEFT)
+ {
+ ScreenToLocal(x, y);
+ if (x > TREE_INDENT_AMOUNT)
+ {
+ SetNodeExpanded(!m_bExpand);
+ }
+ }
+}
+
+bool TreeNode::IsDragEnabled() const
+{
+ int x, y;
+ input()->GetCursorPos(x, y);
+ ((TreeNode *)this)->ScreenToLocal(x, y);
+ if ( x < TREE_INDENT_AMOUNT )
+ return false;
+
+ return BaseClass::IsDragEnabled();
+}
+
+void TreeNode::OnMouseReleased(MouseCode code)
+{
+ BaseClass::OnMouseReleased( code );
+
+ if ( input()->GetMouseCapture() == GetVPanel() )
+ {
+ input()->SetMouseCapture( NULL );
+ return;
+ }
+ int x, y;
+ input()->GetCursorPos(x, y);
+ ScreenToLocal(x, y);
+
+ if ( x < TREE_INDENT_AMOUNT )
+ return;
+
+ bool ctrldown = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+ bool shiftdown = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+
+ if ( !ctrldown && !shiftdown && ( code == MOUSE_LEFT ) )
+ {
+ m_pTreeView->AddSelectedItem( m_ItemIndex, true );
+ }
+}
+
+void TreeNode::OnCursorMoved( int x, int y )
+{
+ if ( input()->GetMouseCapture() != GetVPanel() )
+ return;
+
+ LocalToScreen( x, y );
+ m_pTreeView->ScreenToLocal( x, y );
+ int newItem = m_pTreeView->FindItemUnderMouse( x, y );
+ if ( newItem == -1 )
+ {
+ // Fixme: Figure out best item
+ return;
+ }
+
+ int startItem = m_nClickedItem;
+ int endItem = newItem;
+ if ( startItem > endItem )
+ {
+ int temp = startItem;
+ startItem = endItem;
+ endItem = temp;
+ }
+
+ CUtlVector< TreeNode * > list;
+ m_pTreeView->m_pRootNode->FindNodesInRange( list, startItem, endItem );
+
+ int c = list.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ TreeNode *item = list[ i ];
+ if ( m_bClickedSelected )
+ {
+ m_pTreeView->AddSelectedItem( item->m_ItemIndex, false );
+ }
+ else
+ {
+ m_pTreeView->RemoveSelectedItem( item->m_ItemIndex );
+ }
+ }
+}
+
+void TreeNode::OnMousePressed( MouseCode code)
+{
+ BaseClass::OnMousePressed( code );
+
+ bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+ bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+ int x, y;
+ input()->GetCursorPos(x, y);
+
+ bool bExpandTree = m_pTreeView->m_bLeftClickExpandsTree;
+
+ if ( code == MOUSE_LEFT )
+ {
+ ScreenToLocal(x, y);
+ if ( x < TREE_INDENT_AMOUNT )
+ {
+ if ( bExpandTree )
+ {
+ SetNodeExpanded(!m_bExpand);
+ }
+ // m_pTreeView->SetSelectedItem(m_ItemIndex); // explorer doesn't actually select item when it expands an item
+ // purposely commented out in case we want to change the behavior
+ }
+ else
+ {
+ m_nClickedItem = m_ItemIndex;
+ if ( m_pTreeView->IsMultipleItemDragEnabled() )
+ {
+ input()->SetMouseCapture( GetVPanel() );
+ }
+
+ if ( shift )
+ {
+ m_pTreeView->RangeSelectItems( m_ItemIndex );
+ }
+ else
+ {
+ if ( !IsSelected() || ctrl )
+ {
+ if ( IsSelected() && ctrl )
+ {
+ m_pTreeView->RemoveSelectedItem( m_ItemIndex );
+ }
+ else
+ {
+ m_pTreeView->AddSelectedItem( m_ItemIndex, !ctrl );
+ }
+ }
+ else if ( IsSelected() && m_pTreeView->IsMultipleItemDragEnabled() )
+ {
+ m_pTreeView->AddSelectedItem( m_ItemIndex, !shift );
+ }
+ }
+
+ m_bClickedSelected = m_pTreeView->IsItemSelected( m_ItemIndex );
+ }
+ }
+ else if (code == MOUSE_RIGHT)
+ {
+ // context menu selection
+ // If the item was selected, leave selected items alone, otherwise make it the only selected item
+ if ( !m_pTreeView->IsItemSelected( m_ItemIndex ) )
+ {
+ m_pTreeView->AddSelectedItem( m_ItemIndex, true );
+ }
+
+ // ask parent to context menu
+ m_pTreeView->GenerateContextMenu(m_ItemIndex, x, y);
+ }
+}
+
+void TreeNode::RemoveChildren()
+{
+ int c = m_Children.Count();
+ for ( int i = c - 1 ; i >= 0 ; --i )
+ {
+ m_pTreeView->RemoveItem( m_Children[ i ]->m_ItemIndex, false, true );
+ }
+ m_Children.RemoveAll();
+}
+
+void TreeNode::FindNodesInRange( CUtlVector< TreeNode * >& list, int startIndex, int endIndex )
+{
+ list.RemoveAll();
+ bool finished = false;
+ bool foundstart = false;
+ FindNodesInRange_R( list, finished, foundstart, startIndex, endIndex );
+}
+
+void TreeNode::FindNodesInRange_R( CUtlVector< TreeNode * >& list, bool& finished, bool& foundStart, int startIndex, int endIndex )
+{
+ if ( finished )
+ return;
+ if ( foundStart == true )
+ {
+ list.AddToTail( this );
+
+ if ( m_ItemIndex == startIndex || m_ItemIndex == endIndex )
+ {
+ finished = true;
+ return;
+ }
+ }
+ else if ( m_ItemIndex == startIndex || m_ItemIndex == endIndex )
+ {
+ foundStart = true;
+ list.AddToTail( this );
+ if ( startIndex == endIndex )
+ {
+ finished = true;
+ return;
+ }
+ }
+
+ if ( !m_bExpand )
+ return;
+
+
+ int i;
+ int c = GetChildrenCount();
+ for (i=0;i<c;i++)
+ {
+ m_Children[i]->FindNodesInRange_R( list, finished, foundStart, startIndex, endIndex );
+ }
+}
+
+void TreeNode::PositionAndSetVisibleNodes(int &nStart, int &nCount, int x, int &y)
+{
+ // position ourselves
+ if (nStart == 0)
+ {
+ BaseClass::SetVisible(true);
+ SetPos(x, y);
+ y += m_pTreeView->GetRowHeight(); // m_nRowHeight
+ nCount--;
+ }
+ else // still looking for first element
+ {
+ nStart--;
+ BaseClass::SetVisible(false);
+ }
+
+ x += TREE_INDENT_AMOUNT;
+ int i;
+ for (i=0;i<GetChildrenCount();i++)
+ {
+ if (nCount > 0 && m_bExpand)
+ {
+ m_Children[i]->PositionAndSetVisibleNodes(nStart, nCount, x, y);
+ }
+ else
+ {
+ m_Children[i]->SetVisible(false); // this will make all grand children hidden as well
+ }
+ }
+}
+
+TreeNode *TreeNode::FindItemUnderMouse( int &nStart, int& nCount, int x, int &y, int mx, int my )
+{
+ // position ourselves
+ if (nStart == 0)
+ {
+ int posx, posy;
+ GetPos(posx, posy);
+ if ( my >= posy && my < posy + m_pTreeView->GetRowHeight() )
+ {
+ return this;
+ }
+ y += m_pTreeView->GetRowHeight();
+ nCount--;
+ }
+ else // still looking for first element
+ {
+ nStart--;
+ }
+
+ x += TREE_INDENT_AMOUNT;
+ int i;
+ for (i=0;i<GetChildrenCount();i++)
+ {
+ if (nCount > 0 && m_bExpand)
+ {
+ TreeNode *child = m_Children[i]->FindItemUnderMouse(nStart, nCount, x, y, mx, my);
+ if ( child != NULL )
+ {
+ return child;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+// counts items above this item including itself
+int TreeNode::CountVisibleIndex()
+{
+ int nCount = 1; // myself
+ if (GetParentNode())
+ {
+ int i;
+ for (i=0;i<GetParentNode()->GetChildrenCount();i++)
+ {
+ if (GetParentNode()->m_Children[i] == this)
+ break;
+
+ nCount += GetParentNode()->m_Children[i]->CountVisibleNodes();
+ }
+ return nCount + GetParentNode()->CountVisibleIndex();
+ }
+ else
+ return nCount;
+}
+
+
+}; // namespace vgui
+
+DECLARE_BUILD_FACTORY( TreeView );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+TreeView::TreeView(Panel *parent, const char *panelName) : Panel(parent, panelName)
+{
+ m_bScrollbarExternal[ 0 ] = m_bScrollbarExternal[ 1 ] = false;
+ m_nRowHeight = 20;
+ m_pRootNode = NULL;
+ m_pImageList = NULL;
+ m_pSortFunc = NULL;
+ m_Font = 0;
+
+ m_pSubPanel = new TreeViewSubPanel(this);
+ m_pSubPanel->SetVisible(true);
+ m_pSubPanel->SetPos(0,0);
+
+ m_pHorzScrollBar = new ScrollBar(this, "HorizScrollBar", false);
+ m_pHorzScrollBar->AddActionSignalTarget(this);
+ m_pHorzScrollBar->SetVisible(false);
+
+ m_pVertScrollBar = new ScrollBar(this, "VertScrollBar", true);
+ m_pVertScrollBar->SetVisible(false);
+ m_pVertScrollBar->AddActionSignalTarget(this);
+
+ m_bAllowLabelEditing = false;
+ m_bDragEnabledItems = false;
+ m_bDeleteImageListWhenDone = false;
+ m_bLabelBeingEdited = false;
+ m_bMultipleItemDragging = false;
+ m_bLeftClickExpandsTree = true;
+ m_bAllowMultipleSelections = false;
+ m_nMostRecentlySelectedItem = -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+TreeView::~TreeView()
+{
+ CleanUpImageList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Clean up the image list
+//-----------------------------------------------------------------------------
+void TreeView::CleanUpImageList( )
+{
+ if ( m_pImageList )
+ {
+ if ( m_bDeleteImageListWhenDone )
+ {
+ delete m_pImageList;
+ }
+ m_pImageList = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::SetSortFunc(TreeViewSortFunc_t pSortFunc)
+{
+ m_pSortFunc = pSortFunc;
+}
+
+HFont TreeView::GetFont()
+{
+ return m_Font;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::SetFont(HFont font)
+{
+ Assert( font );
+ if ( !font )
+ return;
+
+ m_Font = font;
+ m_nRowHeight = surface()->GetFontTall(font) + 2;
+
+ if (m_pRootNode)
+ {
+ m_pRootNode->SetFont(font);
+ }
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int TreeView::GetRowHeight()
+{
+ return m_nRowHeight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int TreeView::GetVisibleMaxWidth()
+{
+ if (m_pRootNode)
+ {
+ return m_pRootNode->GetVisibleMaxWidth();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int TreeView::AddItem(KeyValues *data, int parentItemIndex)
+{
+ Assert(parentItemIndex == -1 || m_NodeList.IsValidIndex(parentItemIndex));
+
+ TreeNode *pTreeNode = new TreeNode(m_pSubPanel, this);
+ pTreeNode->SetDragEnabled( m_bDragEnabledItems );
+ pTreeNode->m_ItemIndex = m_NodeList.AddToTail(pTreeNode);
+ pTreeNode->SetKeyValues(data);
+
+ if ( m_Font != 0 )
+ {
+ pTreeNode->SetFont( m_Font );
+ }
+ pTreeNode->SetBgColor( GetBgColor() );
+
+ if ( data->GetInt( "droppable", 0 ) != 0 )
+ {
+ float flContextDelay = data->GetFloat( "drophoverdelay" );
+ if ( flContextDelay )
+ {
+ pTreeNode->SetDropEnabled( true, flContextDelay );
+ }
+ else
+ {
+ pTreeNode->SetDropEnabled( true );
+ }
+ }
+
+ // there can be only one root
+ if (parentItemIndex == -1)
+ {
+ Assert(m_pRootNode == NULL);
+ m_pRootNode = pTreeNode;
+ pTreeNode->m_ParentIndex = -1;
+ }
+ else
+ {
+ pTreeNode->m_ParentIndex = parentItemIndex;
+
+ // add to parent list
+ pTreeNode->GetParentNode()->AddChild(pTreeNode);
+ }
+
+ SETUP_PANEL( pTreeNode );
+
+ return pTreeNode->m_ItemIndex;
+}
+
+
+int TreeView::GetRootItemIndex()
+{
+ if ( m_pRootNode )
+ return m_pRootNode->m_ItemIndex;
+ else
+ return -1;
+}
+
+
+int TreeView::GetNumChildren( int itemIndex )
+{
+ if ( itemIndex == -1 )
+ return 0;
+
+ return m_NodeList[itemIndex]->m_Children.Count();
+}
+
+
+int TreeView::GetChild( int iParentItemIndex, int iChild )
+{
+ return m_NodeList[iParentItemIndex]->m_Children[iChild]->m_ItemIndex;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : itemIndex -
+// Output : TreeNode
+//-----------------------------------------------------------------------------
+TreeNode *TreeView::GetItem( int itemIndex )
+{
+ if ( !m_NodeList.IsValidIndex( itemIndex ) )
+ {
+ Assert( 0 );
+ return NULL;
+ }
+
+ return m_NodeList[ itemIndex ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int TreeView::GetItemCount(void)
+{
+ return m_NodeList.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues* TreeView::GetItemData(int itemIndex)
+{
+ if (!m_NodeList.IsValidIndex(itemIndex))
+ return NULL;
+ else
+ return m_NodeList[itemIndex]->m_pData;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::RemoveItem(int itemIndex, bool bPromoteChildren, bool bFullDelete )
+{
+ // HACK: there's a bug with RemoveItem where panels are lingering. This gets around it temporarily.
+
+ // FIXME: Negative item indices is a bogus interface method!
+ // because what if you want to recursively remove everything under node 0?
+ // Use the bFullDelete parameter instead.
+ if ( itemIndex < 0 )
+ {
+ itemIndex = -itemIndex;
+ bFullDelete = true;
+ }
+
+ if (!m_NodeList.IsValidIndex(itemIndex))
+ return;
+
+ TreeNode *pNode = m_NodeList[itemIndex];
+ TreeNode *pParent = pNode->GetParentNode();
+
+ // are we promoting the children
+ if (bPromoteChildren && pParent)
+ {
+ int i;
+ for (i=0;i<pNode->GetChildrenCount();i++)
+ {
+ TreeNode *pChild = pNode->m_Children[i];
+ pChild->m_ParentIndex = pParent->m_ItemIndex;
+ }
+ }
+ else
+ {
+ // delete our children
+ if ( bFullDelete )
+ {
+ while ( pNode->GetChildrenCount() )
+ RemoveItem( -pNode->m_Children[0]->m_ItemIndex, false );
+ }
+ else
+ {
+ int i;
+ for (i=0;i<pNode->GetChildrenCount();i++)
+ {
+ TreeNode *pDeleteChild = pNode->m_Children[i];
+ RemoveItem(pDeleteChild->m_ItemIndex, false);
+ }
+ }
+ }
+
+ // remove from our parent's children list
+ if (pParent)
+ {
+ pParent->m_Children.FindAndRemove(pNode);
+ }
+
+ // finally get rid of ourselves from the main list
+ m_NodeList.Remove(itemIndex);
+
+ if ( bFullDelete )
+ delete pNode;
+ else
+ pNode->MarkForDeletion();
+
+ // Make sure we don't leave ourselves with an invalid selected item.
+ m_SelectedItems.FindAndRemove( pNode );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::RemoveAll()
+{
+ int i;
+ for (i=0;i<m_NodeList.MaxElementIndex();i++)
+ {
+ if (!m_NodeList.IsValidIndex(i))
+ continue;
+
+ m_NodeList[i]->MarkForDeletion();
+ }
+ m_NodeList.RemoveAll();
+ m_pRootNode = NULL;
+ ClearSelection();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool TreeView::ModifyItem(int itemIndex, KeyValues *data)
+{
+ if (!m_NodeList.IsValidIndex(itemIndex))
+ return false;
+
+ TreeNode *pNode = m_NodeList[itemIndex];
+ TreeNode *pParent = pNode->GetParentNode();
+ bool bReSort = ( m_pSortFunc && pParent );
+ int nChildIndex = -1;
+ if ( bReSort )
+ {
+ nChildIndex = pParent->FindChild( pNode );
+ }
+
+ pNode->SetKeyValues(data);
+
+ // Changing the data can cause it to re-sort
+ if ( bReSort )
+ {
+ int nChildren = pParent->GetChildrenCount();
+ bool bLeftBad = (nChildIndex > 0) && m_pSortFunc( pNode->m_pData, pParent->m_Children[nChildIndex-1]->m_pData );
+ bool bRightBad = (nChildIndex < nChildren - 1) && m_pSortFunc( pParent->m_Children[nChildIndex+1]->m_pData, pNode->m_pData );
+ if ( bLeftBad || bRightBad )
+ {
+ pParent->m_Children.Remove( nChildIndex );
+ pParent->AddChild( pNode );
+ }
+ }
+
+ InvalidateLayout();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set the selection colors of an element in the tree view
+//-----------------------------------------------------------------------------
+
+void TreeView::SetItemSelectionTextColor( int itemIndex, const Color& clr )
+{
+ Assert( m_NodeList.IsValidIndex(itemIndex) );
+ if ( !m_NodeList.IsValidIndex(itemIndex) )
+ return;
+
+ TreeNode *pNode = m_NodeList[itemIndex];
+ pNode->SetSelectionTextColor( clr );
+}
+
+void TreeView::SetItemSelectionBgColor( int itemIndex, const Color& clr )
+{
+ Assert( m_NodeList.IsValidIndex(itemIndex) );
+ if ( !m_NodeList.IsValidIndex(itemIndex) )
+ return;
+
+ TreeNode *pNode = m_NodeList[itemIndex];
+ pNode->SetSelectionBgColor( clr );
+}
+
+void TreeView::SetItemSelectionUnfocusedBgColor( int itemIndex, const Color& clr )
+{
+ Assert( m_NodeList.IsValidIndex(itemIndex) );
+ if ( !m_NodeList.IsValidIndex(itemIndex) )
+ return;
+
+ TreeNode *pNode = m_NodeList[itemIndex];
+ pNode->SetSelectionUnfocusedBgColor( clr );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set the fg color of an element in the tree view
+//-----------------------------------------------------------------------------
+void TreeView::SetItemFgColor(int itemIndex, const Color& color)
+{
+ Assert( m_NodeList.IsValidIndex(itemIndex) );
+ if ( !m_NodeList.IsValidIndex(itemIndex) )
+ return;
+
+ TreeNode *pNode = m_NodeList[itemIndex];
+ pNode->SetFgColor( color );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: set the bg color of an element in the tree view
+//-----------------------------------------------------------------------------
+void TreeView::SetItemBgColor(int itemIndex, const Color& color)
+{
+ Assert( m_NodeList.IsValidIndex(itemIndex) );
+ if ( !m_NodeList.IsValidIndex(itemIndex) )
+ return;
+
+ TreeNode *pNode = m_NodeList[itemIndex];
+ pNode->SetBgColor( color );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int TreeView::GetItemParent(int itemIndex)
+{
+ return m_NodeList[itemIndex]->m_ParentIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::SetImageList(ImageList *imageList, bool deleteImageListWhenDone)
+{
+ CleanUpImageList();
+ m_pImageList = imageList;
+ m_bDeleteImageListWhenDone = deleteImageListWhenDone;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+IImage *TreeView::GetImage(int index)
+{
+ return m_pImageList->GetImage(index);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::GetSelectedItems( CUtlVector< int >& list )
+{
+ list.RemoveAll();
+
+ int c = m_SelectedItems.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ list.AddToTail( m_SelectedItems[ i ]->m_ItemIndex );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::GetSelectedItemData( CUtlVector< KeyValues * >& list )
+{
+ list.RemoveAll();
+
+ int c = m_SelectedItems.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ list.AddToTail( m_SelectedItems[ i ]->m_pData );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool TreeView::IsItemIDValid(int itemIndex)
+{
+ return m_NodeList.IsValidIndex(itemIndex);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int TreeView::GetHighestItemID()
+{
+ return m_NodeList.MaxElementIndex();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::ExpandItem(int itemIndex, bool bExpand)
+{
+ if (!m_NodeList.IsValidIndex(itemIndex))
+ return;
+
+ m_NodeList[itemIndex]->SetNodeExpanded(bExpand);
+ InvalidateLayout();
+}
+
+bool TreeView::IsItemExpanded( int itemIndex )
+{
+ if (!m_NodeList.IsValidIndex(itemIndex))
+ return false;
+
+ return m_NodeList[itemIndex]->IsExpanded();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Scrolls the list according to the mouse wheel movement
+//-----------------------------------------------------------------------------
+void TreeView::OnMouseWheeled(int delta)
+{
+ if ( !m_pVertScrollBar->IsVisible() )
+ {
+ return;
+ }
+ int val = m_pVertScrollBar->GetValue();
+ val -= (delta * 3);
+ m_pVertScrollBar->SetValue(val);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::OnSizeChanged(int wide, int tall)
+{
+ BaseClass::OnSizeChanged(wide, tall);
+ InvalidateLayout();
+ Repaint();
+}
+
+void TreeView::GetScrollBarSize( bool vertical, int& w, int& h )
+{
+ int idx = vertical ? 0 : 1;
+
+ if ( m_bScrollbarExternal[ idx ] )
+ {
+ w = h = 0;
+ return;
+ }
+
+ if ( vertical )
+ {
+ m_pVertScrollBar->GetSize( w, h );
+ }
+ else
+ {
+ m_pHorzScrollBar->GetSize( w, h );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::PerformLayout()
+{
+ int wide, tall;
+ GetSize( wide, tall );
+
+ if ( !m_pRootNode )
+ {
+ m_pSubPanel->SetSize( wide, tall );
+ return;
+ }
+
+ int sbhw, sbhh;
+ GetScrollBarSize( false, sbhw, sbhh );
+ int sbvw, sbvh;
+ GetScrollBarSize( true, sbvw, sbvh );
+
+ bool vbarNeeded = false;
+ bool hbarNeeded = false;
+
+ // okay we have to check if we need either scroll bars, since if we need one
+ // it might make it necessary to have the other one
+ int nodesVisible = tall / m_nRowHeight;
+
+ // count the number of visible items
+ int visibleItemCount = m_pRootNode->CountVisibleNodes();
+ int maxWidth = m_pRootNode->GetVisibleMaxWidth() + 10; // 10 pixel buffer
+
+ vbarNeeded = visibleItemCount > nodesVisible;
+
+ if (!vbarNeeded)
+ {
+ if (maxWidth > wide)
+ {
+ hbarNeeded = true;
+
+ // recalculate if vbar is needed now
+ // double check that we really don't need it
+ nodesVisible = (tall - sbhh) / m_nRowHeight;
+ vbarNeeded = visibleItemCount > nodesVisible;
+ }
+ }
+ else
+ {
+ // we've got the vertical bar here, so shrink the width
+ hbarNeeded = maxWidth > (wide - (sbvw+2));
+
+ if (hbarNeeded)
+ {
+ nodesVisible = (tall - sbhh) / m_nRowHeight;
+ }
+ }
+
+ int subPanelWidth = wide;
+ int subPanelHeight = tall;
+
+ int vbarPos = 0;
+ if (vbarNeeded)
+ {
+ subPanelWidth -= (sbvw + 2);
+ int barSize = tall;
+ if (hbarNeeded)
+ {
+ barSize -= sbhh;
+ }
+
+ //!! need to make it recalculate scroll positions
+ m_pVertScrollBar->SetVisible(true);
+ m_pVertScrollBar->SetEnabled(false);
+ m_pVertScrollBar->SetRangeWindow( nodesVisible );
+ m_pVertScrollBar->SetRange( 0, visibleItemCount);
+ m_pVertScrollBar->SetButtonPressedScrollValue( 1 );
+
+ if ( !m_bScrollbarExternal[ 0 ] )
+ {
+ m_pVertScrollBar->SetPos(wide - (sbvw + WINDOW_BORDER_WIDTH), 0);
+ m_pVertScrollBar->SetSize(sbvw, barSize - 2);
+ }
+
+ // need to figure out
+ vbarPos = m_pVertScrollBar->GetValue();
+ }
+ else
+ {
+ m_pVertScrollBar->SetVisible(false);
+ m_pVertScrollBar->SetValue( 0 );
+ }
+
+ int hbarPos = 0;
+ if (hbarNeeded)
+ {
+ subPanelHeight -= (sbhh + 2);
+ int barSize = wide;
+ if (vbarNeeded)
+ {
+ barSize -= sbvw;
+ }
+ m_pHorzScrollBar->SetVisible(true);
+ m_pHorzScrollBar->SetEnabled(false);
+ m_pHorzScrollBar->SetRangeWindow( barSize );
+ m_pHorzScrollBar->SetRange( 0, maxWidth);
+ m_pHorzScrollBar->SetButtonPressedScrollValue( 10 );
+
+ if ( !m_bScrollbarExternal[ 1 ] )
+ {
+ m_pHorzScrollBar->SetPos(0, tall - (sbhh + WINDOW_BORDER_WIDTH));
+ m_pHorzScrollBar->SetSize(barSize - 2, sbhh);
+ }
+
+ hbarPos = m_pHorzScrollBar->GetValue();
+ }
+ else
+ {
+ m_pHorzScrollBar->SetVisible(false);
+ m_pHorzScrollBar->SetValue( 0 );
+ }
+
+ m_pSubPanel->SetSize(subPanelWidth, subPanelHeight);
+
+ int y = 0;
+ m_pRootNode->PositionAndSetVisibleNodes(vbarPos, visibleItemCount, -hbarPos, y);
+
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::MakeItemVisible(int itemIndex)
+{
+ // first make sure that all parents are expanded
+ TreeNode *pNode = m_NodeList[itemIndex];
+ TreeNode *pParent = pNode->GetParentNode();
+ while (pParent)
+ {
+ if (!pParent->m_bExpand)
+ {
+ pParent->SetNodeExpanded(true);
+ }
+ pParent = pParent->GetParentNode();
+ }
+
+ // recalculate scroll bar due to possible exapnsion
+ PerformLayout();
+
+ if (!m_pVertScrollBar->IsVisible())
+ return;
+
+ int visibleIndex = pNode->CountVisibleIndex()-1;
+ int range = m_pVertScrollBar->GetRangeWindow();
+ int vbarPos = m_pVertScrollBar->GetValue();
+
+ // do we need to scroll up or down?
+ if (visibleIndex < vbarPos)
+ {
+ m_pVertScrollBar->SetValue(visibleIndex);
+ }
+ else if (visibleIndex+1 > vbarPos+range)
+ {
+ m_pVertScrollBar->SetValue(visibleIndex+1-range);
+ }
+ InvalidateLayout();
+}
+
+void TreeView::GetVBarInfo( int &top, int &nItemsVisible, bool& hbarVisible )
+{
+ int wide, tall;
+ GetSize( wide, tall );
+ nItemsVisible = tall / m_nRowHeight;
+
+ if ( m_pVertScrollBar->IsVisible() )
+ {
+ top = m_pVertScrollBar->GetValue();
+ }
+ else
+ {
+ top = 0;
+ }
+ hbarVisible = m_pHorzScrollBar->IsVisible();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
+ SetBgColor(GetSchemeColor("TreeView.BgColor", GetSchemeColor("WindowDisabledBgColor", pScheme), pScheme));
+ SetFont( pScheme->GetFont( "Default", IsProportional() ) );
+ m_pSubPanel->SetBgColor( GetBgColor() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::SetBgColor( Color color )
+{
+ BaseClass::SetBgColor( color );
+ m_pSubPanel->SetBgColor( color );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::OnSliderMoved( int position )
+{
+ InvalidateLayout();
+ Repaint();
+}
+
+void TreeView::GenerateDragDataForItem( int itemIndex, KeyValues *msg )
+{
+ // Implemented by subclassed TreeView
+}
+
+void TreeView::SetDragEnabledItems( bool state )
+{
+ m_bDragEnabledItems = state;
+}
+
+void TreeView::OnLabelChanged( int itemIndex, char const *oldString, char const *newString )
+{
+}
+
+bool TreeView::IsLabelEditingAllowed() const
+{
+ return m_bAllowLabelEditing;
+}
+
+void TreeView::SetLabelBeingEdited( bool state )
+{
+ m_bLabelBeingEdited = state;
+}
+
+bool TreeView::IsLabelBeingEdited() const
+{
+ return m_bLabelBeingEdited;
+}
+
+void TreeView::SetAllowLabelEditing( bool state )
+{
+ m_bAllowLabelEditing = state;
+}
+
+void TreeView::EnableExpandTreeOnLeftClick( bool bEnable )
+{
+ m_bLeftClickExpandsTree = bEnable;
+}
+
+int TreeView::FindItemUnderMouse( int mx, int my )
+{
+ mx = clamp( mx, 0, GetWide() - 1 );
+ my = clamp( my, 0, GetTall() - 1 );
+ if ( mx >= TREE_INDENT_AMOUNT )
+ {
+ // Find what's under this position
+ // need to figure out
+ int vbarPos = m_pVertScrollBar->IsVisible() ? m_pVertScrollBar->GetValue() : 0;
+ int hbarPos = m_pHorzScrollBar->IsVisible() ? m_pHorzScrollBar->GetValue() : 0;
+ int count = m_pRootNode->CountVisibleNodes();
+
+ int y = 0;
+ TreeNode *item = m_pRootNode->FindItemUnderMouse( vbarPos, count, -hbarPos, y, mx, my );
+ if ( item )
+ {
+ return item->m_ItemIndex;
+ }
+ }
+
+ return -1;
+}
+
+void TreeView::OnMousePressed( MouseCode code )
+{
+ bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
+ bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
+
+ // Try to map mouse position to a row
+ if ( code == MOUSE_LEFT && m_pRootNode )
+ {
+ int mx, my;
+ input()->GetCursorPos( mx, my );
+ ScreenToLocal( mx, my );
+ if ( mx >= TREE_INDENT_AMOUNT )
+ {
+ // Find what's under this position
+ // need to figure out
+ int vbarPos = m_pVertScrollBar->IsVisible() ? m_pVertScrollBar->GetValue() : 0;
+ int hbarPos = m_pHorzScrollBar->IsVisible() ? m_pHorzScrollBar->GetValue() : 0;
+ int count = m_pRootNode->CountVisibleNodes();
+
+ int y = 0;
+ TreeNode *item = m_pRootNode->FindItemUnderMouse( vbarPos, count, -hbarPos, y, mx, my );
+ if ( item )
+ {
+ if ( !item->IsSelected() )
+ {
+ AddSelectedItem( item->m_ItemIndex, !ctrl && !shift );
+ }
+ return;
+ }
+ else
+ {
+ ClearSelection();
+ }
+ }
+ }
+
+ BaseClass::OnMousePressed( code );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : state -
+//-----------------------------------------------------------------------------
+void TreeView::SetAllowMultipleSelections( bool state )
+{
+ m_bAllowMultipleSelections = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool TreeView::IsMultipleSelectionAllowed() const
+{
+ return m_bAllowMultipleSelections;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : int
+//-----------------------------------------------------------------------------
+int TreeView::GetSelectedItemCount() const
+{
+ return m_SelectedItems.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void TreeView::ClearSelection()
+{
+ m_SelectedItems.RemoveAll();
+ m_nMostRecentlySelectedItem = -1;
+ PostActionSignal( new KeyValues( "TreeViewItemSelectionCleared" ) );
+}
+
+void TreeView::RangeSelectItems( int endItem )
+{
+ int startItem = m_nMostRecentlySelectedItem;
+ ClearSelection();
+ m_nMostRecentlySelectedItem = startItem;
+
+ if ( !m_NodeList.IsValidIndex( startItem ) )
+ {
+ AddSelectedItem( endItem, false );
+ return;
+ }
+
+ Assert( m_NodeList.IsValidIndex( endItem ) );
+
+ if ( !m_pRootNode )
+ {
+ return;
+ }
+
+ CUtlVector< TreeNode * > list;
+ m_pRootNode->FindNodesInRange( list, startItem, endItem );
+
+ int c = list.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ TreeNode *item = list[ i ];
+ AddSelectedItem( item->m_ItemIndex, false );
+ }
+}
+
+void TreeView::FindNodesInRange( int startItem, int endItem, CUtlVector< int >& itemIndices )
+{
+ CUtlVector< TreeNode * > nodes;
+ m_pRootNode->FindNodesInRange( nodes, startItem, endItem );
+
+ int c = nodes.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ TreeNode *item = nodes[ i ];
+ itemIndices.AddToTail( item->m_ItemIndex );
+ }
+}
+
+void TreeView::RemoveSelectedItem( int itemIndex )
+{
+ if ( !m_NodeList.IsValidIndex( itemIndex ) )
+ return;
+
+ TreeNode *sel = m_NodeList[ itemIndex ];
+ Assert( sel );
+ int slot = m_SelectedItems.Find( sel );
+ if ( slot != m_SelectedItems.InvalidIndex() )
+ {
+ m_SelectedItems.Remove( slot );
+ PostActionSignal( new KeyValues( "TreeViewItemDeselected", "itemIndex", itemIndex ) );
+
+ m_nMostRecentlySelectedItem = itemIndex;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TreeView::AddSelectedItem( int itemIndex, bool clearCurrentSelection, bool requestFocus /* = true */, bool bMakeItemVisible /*= true*/ )
+{
+ if ( clearCurrentSelection )
+ {
+ ClearSelection();
+ }
+
+ // Assume it's bogus
+ if ( !m_NodeList.IsValidIndex( itemIndex ) )
+ return;
+
+ TreeNode *sel = m_NodeList[ itemIndex ];
+ Assert( sel );
+ if ( requestFocus )
+ {
+ sel->RequestFocus();
+ }
+
+ // Item 0 is most recently selected!!!
+ int slot = m_SelectedItems.Find( sel );
+ if ( slot == m_SelectedItems.InvalidIndex() )
+ {
+ m_SelectedItems.AddToHead( sel );
+ }
+ else if ( slot != 0 )
+ {
+ m_SelectedItems.Remove( slot );
+ m_SelectedItems.AddToHead( sel );
+ }
+
+ if ( bMakeItemVisible )
+ {
+ MakeItemVisible( itemIndex );
+ }
+
+ PostActionSignal( new KeyValues( "TreeViewItemSelected", "itemIndex", itemIndex ) );
+ InvalidateLayout();
+
+ if ( clearCurrentSelection )
+ {
+ m_nMostRecentlySelectedItem = itemIndex;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : int
+//-----------------------------------------------------------------------------
+int TreeView::GetFirstSelectedItem() const
+{
+ if ( m_SelectedItems.Count() <= 0 )
+ return -1;
+ return m_SelectedItems[ 0 ]->m_ItemIndex;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : itemIndex -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool TreeView::IsItemSelected( int itemIndex )
+{
+ // Assume it's bogus
+ if ( !m_NodeList.IsValidIndex( itemIndex ) )
+ return false;
+
+ TreeNode *sel = m_NodeList[ itemIndex ];
+ return m_SelectedItems.Find( sel ) != m_SelectedItems.InvalidIndex();
+}
+
+void TreeView::SetLabelEditingAllowed( int itemIndex, bool state )
+{
+ if ( !m_NodeList.IsValidIndex( itemIndex ) )
+ return;
+
+ TreeNode *sel = m_NodeList[ itemIndex ];
+ sel->SetLabelEditingAllowed( state );
+}
+
+void TreeView::StartEditingLabel( int itemIndex )
+{
+ if ( !m_NodeList.IsValidIndex( itemIndex ) )
+ return;
+
+ Assert( IsLabelEditingAllowed() );
+
+ TreeNode *sel = m_NodeList[ itemIndex ];
+ Assert( sel->IsLabelEditingAllowed() );
+ if ( !sel->IsLabelEditingAllowed() )
+ return;
+
+ sel->EditLabel();
+}
+
+int TreeView::GetPrevChildItemIndex( int itemIndex )
+{
+ if ( !m_NodeList.IsValidIndex( itemIndex ) )
+ return -1;
+ TreeNode *sel = m_NodeList[ itemIndex ];
+ TreeNode *parent = sel->GetParentNode();
+ if ( !parent )
+ return -1;
+
+ return parent->GetPrevChildItemIndex( sel );
+}
+
+int TreeView::GetNextChildItemIndex( int itemIndex )
+{
+ if ( !m_NodeList.IsValidIndex( itemIndex ) )
+ return -1;
+ TreeNode *sel = m_NodeList[ itemIndex ];
+ TreeNode *parent = sel->GetParentNode();
+ if ( !parent )
+ return -1;
+
+ return parent->GetNextChildItemIndex( sel );
+}
+
+bool TreeView::IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist )
+{
+ // Derived classes should implement
+ return false;
+}
+
+void TreeView::OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist )
+{
+}
+
+bool TreeView::GetItemDropContextMenu( int itemIndex, Menu *menu, CUtlVector< KeyValues * >& msglist )
+{
+ return false;
+}
+
+HCursor TreeView::GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist )
+{
+ return dc_arrow;
+}
+
+void TreeView::RemoveChildrenOfNode( int itemIndex )
+{
+ if ( !m_NodeList.IsValidIndex( itemIndex ) )
+ return;
+
+ TreeNode *node = m_NodeList[ itemIndex ];
+ node->RemoveChildren();
+}
+
+ScrollBar *TreeView::SetScrollBarExternal( bool vertical, Panel *newParent )
+{
+ if ( vertical )
+ {
+ m_bScrollbarExternal[ 0 ] = true;
+ m_pVertScrollBar->SetParent( newParent );
+ return m_pVertScrollBar;
+ }
+ m_bScrollbarExternal[ 1 ] = true;
+ m_pHorzScrollBar->SetParent( newParent );
+ return m_pHorzScrollBar;
+}
+
+// if this is set, then clicking on one row and dragging will select a run or items, etc.
+void TreeView::SetMultipleItemDragEnabled( bool state )
+{
+ m_bMultipleItemDragging = state;
+}
+
+bool TreeView::IsMultipleItemDragEnabled() const
+{
+ return m_bMultipleItemDragging;
+}
+
+void TreeView::SelectAll()
+{
+ m_SelectedItems.RemoveAll();
+ FOR_EACH_LL( m_NodeList, i )
+ {
+ m_SelectedItems.AddToTail( m_NodeList[ i ] );
+ }
+
+ PostActionSignal( new KeyValues( "TreeViewItemSelected", "itemIndex", GetRootItemIndex() ) );
+ InvalidateLayout();
+}
diff --git a/mp/src/vgui2/vgui_controls/TreeViewListControl.cpp b/mp/src/vgui2/vgui_controls/TreeViewListControl.cpp
new file mode 100644
index 00000000..81575496
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/TreeViewListControl.cpp
@@ -0,0 +1,315 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include <assert.h>
+
+#define PROTECTED_THINGS_DISABLE
+
+#include <vgui/Cursor.h>
+#include <vgui/IScheme.h>
+#include <vgui/IInput.h>
+#include <vgui/IPanel.h>
+#include <vgui/ISurface.h>
+#include <vgui/KeyCode.h>
+#include <KeyValues.h>
+#include <vgui/MouseCode.h>
+#include <vgui/IBorder.h>
+
+#include <vgui_controls/TreeViewListControl.h>
+#include <vgui_controls/ScrollBar.h>
+#include <vgui_controls/TextEntry.h>
+#include <vgui_controls/TreeView.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/Button.h>
+#include <vgui_controls/TextImage.h>
+#include <vgui_controls/ImageList.h>
+#include <vgui_controls/ImagePanel.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+DECLARE_BUILD_FACTORY( CTreeViewListControl );
+
+CTreeViewListControl::CTreeViewListControl( vgui::Panel *pParent, const char *pName ) :
+ BaseClass( pParent, pName )
+{
+ m_pTree = NULL;
+ m_BorderColor.SetColor( 255, 255, 255, 255 );
+ m_TitleBarFont = NULL;
+ m_TitleBarHeight = 20;
+ SetPostChildPaintEnabled( true );
+}
+
+void CTreeViewListControl::SetTreeView( vgui::TreeView *pTree )
+{
+ m_pTree = pTree;
+ if ( m_pTree )
+ {
+ m_pTree->SetParent( this );
+ m_pTree->SetPaintBackgroundEnabled( false );
+ }
+
+ InvalidateLayout();
+}
+
+vgui::TreeView *CTreeViewListControl::GetTree()
+{
+ return m_pTree;
+}
+
+int CTreeViewListControl::GetTitleBarHeight()
+{
+ return m_TitleBarHeight;
+}
+
+void CTreeViewListControl::SetTitleBarInfo( vgui::HFont hFont, int titleBarHeight )
+{
+ m_TitleBarFont = hFont;
+ m_TitleBarHeight = titleBarHeight;
+
+ InvalidateLayout();
+}
+
+void CTreeViewListControl::SetBorderColor( Color clr )
+{
+ m_BorderColor = clr;
+}
+
+void CTreeViewListControl::SetNumColumns( int nColumns )
+{
+ m_Columns.Purge();
+ m_Columns.SetSize( nColumns );
+ InvalidateLayout();
+}
+
+int CTreeViewListControl::GetNumColumns() const
+{
+ return m_Columns.Count();
+}
+
+void CTreeViewListControl::SetColumnInfo( int iColumn, const char *pTitle, int width, int ciFlags )
+{
+ if ( iColumn < 0 || iColumn >= m_Columns.Count() )
+ {
+ Assert( false );
+ return;
+ }
+ CColumnInfo *pInfo = &m_Columns[iColumn];
+ pInfo->m_Title = pTitle;
+ pInfo->m_Width = width;
+ pInfo->m_ciFlags = ciFlags;
+
+ InvalidateLayout();
+}
+
+int CTreeViewListControl::GetNumRows()
+{
+ return m_Rows.Count();
+}
+
+int CTreeViewListControl::GetTreeItemAtRow( int iRow )
+{
+ if ( iRow < 0 || iRow >= m_Rows.Count() )
+ return -1;
+ else
+ return m_Rows[iRow];
+}
+
+void CTreeViewListControl::GetGridElementBounds( int iColumn, int iRow, int &left, int &top, int &right, int &bottom )
+{
+ left = m_Columns[iColumn].m_Left;
+ right = m_Columns[iColumn].m_Right;
+
+ // vgui doesn't seem to be drawing things exactly right. Like it you draw a line at (0,0) to (100,0),
+ // then a rectangle from (1,1) to (100,100), it'll overwrite the line at the top.
+ int treeTopBorder = 0;
+ IBorder *treeBorder = m_pTree->GetBorder();
+ if ( treeBorder )
+ {
+ int l, t, r, b;
+ treeBorder->GetInset( l, t, r, b );
+ treeTopBorder = t;
+ }
+ if ( iRow == -1 )
+ {
+ top = 1;
+ bottom = m_TitleBarHeight - 2;
+ }
+ else if ( m_pTree )
+ {
+ int x, y;
+ m_pTree->GetPos( x, y );
+
+ top = treeTopBorder + m_TitleBarHeight + ( iRow * m_pTree->GetRowHeight() );
+ bottom = top + m_pTree->GetRowHeight();
+ }
+ else
+ {
+ left = top = right = bottom = 0;
+ }
+}
+
+void CTreeViewListControl::PerformLayout()
+{
+ RecalculateRows();
+ RecalculateColumns();
+
+ // Reposition the tree view.
+ if ( m_pTree && m_Columns.Count() > 0 )
+ {
+ int left, top, right, bottom;
+ GetGridElementBounds( 0, -1, left, top, right, bottom );
+
+ top = m_TitleBarHeight;
+
+ m_pTree->SetBounds( left, top, right - left, GetTall() - top );
+ }
+
+ BaseClass::PerformLayout();
+}
+
+
+void CTreeViewListControl::RecalculateRows()
+{
+ m_Rows.Purge();
+
+ if ( !m_pTree || m_pTree->GetRootItemIndex() == -1 )
+ return;
+
+ int iRoot = m_pTree->GetRootItemIndex();
+ RecalculateRows_R( iRoot );
+}
+
+
+void CTreeViewListControl::RecalculateRows_R( int index )
+{
+ m_Rows.AddToTail( index );
+ if ( !m_pTree->IsItemExpanded( index ) )
+ return;
+
+ int nChildren = m_pTree->GetNumChildren( index );
+ for ( int i=0; i < nChildren; i++ )
+ {
+ int iChild = m_pTree->GetChild( index, i );
+ RecalculateRows_R( iChild );
+ }
+}
+
+int CTreeViewListControl::GetScrollBarSize()
+{
+ return 0;
+}
+
+void CTreeViewListControl::RecalculateColumns()
+{
+ int rightEdge = GetWide()-1 - GetScrollBarSize();
+
+ int x = 0;
+ int c = m_Columns.Count();
+ for ( int i=0; i < c; i++ )
+ {
+ m_Columns[i].m_Left = x + 1;
+ int cw = m_Columns[i].m_Width;
+ if ( i == c - 1 )
+ {
+ cw = rightEdge - x - 2;
+ }
+ m_Columns[i].m_Right = x + cw - 2;
+ x += cw;
+ }
+}
+
+void CTreeViewListControl::PostChildPaint()
+{
+ BaseClass::PostChildPaint();
+
+ // Draw the grid lines.
+ vgui::surface()->DrawSetColor( m_BorderColor );
+
+ if ( m_Columns.Count() <= 0 )
+ return;
+
+ // Draw the horizontal lines.
+ int endX = 0;
+ endX = m_Columns[m_Columns.Count()-1].m_Right + 1;
+
+ int bottomY = 0;
+ for ( int i=0; i < m_Rows.Count(); i++ )
+ {
+ int left, top, right, bottom;
+ GetGridElementBounds( 0, i, left, top, right, bottom );
+
+ bottomY = bottom;
+ vgui::surface()->DrawLine( 0, bottomY, endX, bottomY );
+ }
+
+ // Draw the vertical lines.
+ int curX = 0;
+ for ( int i=0; i < m_Columns.Count(); i++ )
+ {
+ vgui::surface()->DrawLine( curX, 0, curX, bottomY );
+ curX += m_Columns[i].m_Width;
+ }
+ vgui::surface()->DrawLine( curX, 0, curX, bottomY );
+}
+
+void CTreeViewListControl::Paint()
+{
+ BaseClass::Paint();
+
+ // Draw the title bars.
+ DrawTitleBars();
+}
+
+void CTreeViewListControl::DrawTitleBars()
+{
+ int rightEdge = GetWide();
+
+ for ( int i=0; i < m_Columns.Count(); i++ )
+ {
+ int left, top, right, bottom;
+ GetGridElementBounds( i, -1, left, top, right, bottom );
+
+ if ( left >= rightEdge )
+ continue;
+
+ vgui::surface()->DrawSetColor( 0, 0, 0, 255 );
+ vgui::surface()->DrawFilledRect( left, top, right, bottom );
+
+ vgui::surface()->DrawSetTextColor( 255, 255, 255, 255 );
+
+ const char *pTitleString = m_Columns[i].m_Title.String();
+
+ wchar_t unicodeString[1024];
+ g_pVGuiLocalize->ConvertANSIToUnicode( pTitleString, unicodeString, sizeof(unicodeString) );
+
+ int wide, tall;
+ surface()->GetTextSize( m_TitleBarFont, unicodeString, wide, tall );
+
+ surface()->DrawSetTextFont( m_TitleBarFont );
+
+ if ( m_Columns[i].m_ciFlags & CTreeViewListControl::CI_HEADER_LEFTALIGN )
+ {
+ int midy = (top+bottom)/2;
+ surface()->DrawSetTextPos( left, midy );
+ }
+ else
+ {
+ int textRight = min( right, rightEdge );
+
+ int midx = (left+textRight)/2;
+ int midy = (top+bottom)/2;
+
+ surface()->DrawSetTextPos( midx - wide/2, midy - tall/2 );
+ }
+
+ surface()->DrawPrintText( unicodeString, strlen( pTitleString ) );
+ }
+}
+
diff --git a/mp/src/vgui2/vgui_controls/URLLabel.cpp b/mp/src/vgui2/vgui_controls/URLLabel.cpp
new file mode 100644
index 00000000..349b2e71
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/URLLabel.cpp
@@ -0,0 +1,158 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdio.h>
+
+#include "vgui/ISurface.h"
+#include "vgui/ISystem.h"
+#include "vgui/MouseCode.h"
+#include "vgui/Cursor.h"
+#include "KeyValues.h"
+
+#include "vgui_controls/URLLabel.h"
+#include "vgui_controls/TextImage.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+vgui::Panel *URLLabel_Factory()
+{
+ return new URLLabel(NULL, NULL, "URLLabel", NULL);
+}
+
+DECLARE_BUILD_FACTORY_CUSTOM( URLLabel, URLLabel_Factory );
+//-----------------------------------------------------------------------------
+// Purpose: constructor
+//-----------------------------------------------------------------------------
+URLLabel::URLLabel(Panel *parent, const char *panelName, const char *text, const char *pszURL) : Label(parent, panelName, text)
+{
+ m_pszURL = NULL;
+ m_bUnderline = false;
+ m_iURLSize = 0;
+ if (pszURL && strlen(pszURL) > 0)
+ {
+ SetURL(pszURL);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: unicode constructor
+//-----------------------------------------------------------------------------
+URLLabel::URLLabel(Panel *parent, const char *panelName, const wchar_t *wszText, const char *pszURL) : Label(parent, panelName, wszText)
+{
+ m_pszURL = NULL;
+ m_bUnderline = false;
+ m_iURLSize = 0;
+ if (pszURL && strlen(pszURL) > 0)
+ {
+ SetURL(pszURL);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: destructor
+//-----------------------------------------------------------------------------
+URLLabel::~URLLabel()
+{
+ if (m_pszURL)
+ delete [] m_pszURL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the URL
+//-----------------------------------------------------------------------------
+void URLLabel::SetURL(const char *pszURL)
+{
+ int iNewURLSize = strlen(pszURL);
+ if (iNewURLSize > m_iURLSize || !m_pszURL)
+ {
+ delete [] m_pszURL;
+ m_pszURL = new char [iNewURLSize + 1];
+ }
+ strcpy(m_pszURL, pszURL);
+ m_iURLSize = iNewURLSize;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If we were left clicked on, launch the URL
+//-----------------------------------------------------------------------------
+void URLLabel::OnMousePressed(MouseCode code)
+{
+ if (code == MOUSE_LEFT)
+ {
+ if (m_pszURL)
+ {
+ system()->ShellExecute("open", m_pszURL);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies resouce settings
+//-----------------------------------------------------------------------------
+void URLLabel::ApplySettings(KeyValues *inResourceData)
+{
+ BaseClass::ApplySettings(inResourceData);
+
+ const char *pszURL = inResourceData->GetString("URLText", NULL);
+ if (pszURL)
+ {
+ if (pszURL[0] == '#')
+ {
+ // it's a localized url, look it up
+ const wchar_t *ws = g_pVGuiLocalize->Find(pszURL + 1);
+ if (ws)
+ {
+ char localizedUrl[512];
+ g_pVGuiLocalize->ConvertUnicodeToANSI(ws, localizedUrl, sizeof(localizedUrl));
+ SetURL(localizedUrl);
+ }
+ }
+ else
+ {
+ SetURL(pszURL);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: saves them to disk
+//-----------------------------------------------------------------------------
+void URLLabel::GetSettings( KeyValues *outResourceData )
+{
+ BaseClass::GetSettings(outResourceData);
+
+ if (m_pszURL)
+ {
+ outResourceData->SetString("URLText", m_pszURL);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a description of the label string
+//-----------------------------------------------------------------------------
+const char *URLLabel::GetDescription( void )
+{
+ static char buf[1024];
+ _snprintf(buf, sizeof(buf), "%s, string URLText", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: scheme settings
+//-----------------------------------------------------------------------------
+void URLLabel::ApplySchemeSettings(IScheme *pScheme)
+{
+ // set our font to be underlined by default
+ // the Label::ApplySchemeSettings() will override it if override set in scheme file
+ SetFont( pScheme->GetFont( "DefaultUnderline", IsProportional() ) );
+ BaseClass::ApplySchemeSettings(pScheme);
+ SetCursor(dc_hand);
+}
+
diff --git a/mp/src/vgui2/vgui_controls/WizardPanel.cpp b/mp/src/vgui2/vgui_controls/WizardPanel.cpp
new file mode 100644
index 00000000..9ce5f802
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/WizardPanel.cpp
@@ -0,0 +1,720 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <vgui/IVGui.h>
+#include <KeyValues.h>
+
+#include <vgui_controls/BuildGroup.h>
+#include <vgui_controls/Button.h>
+#include <vgui_controls/Controls.h>
+#include <vgui_controls/WizardPanel.h>
+#include <vgui_controls/WizardSubPanel.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+WizardPanel::WizardPanel(Panel *parent, const char *panelName) : Frame(parent, panelName)
+{
+ _currentSubPanel = NULL;
+ _currentData = new KeyValues("WizardData");
+ _showButtons = true;
+
+
+ SetSizeable(false);
+
+ CreateButtons();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+WizardPanel::~WizardPanel()
+{
+ if (_currentData)
+ {
+ _currentData->deleteThis();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ // resize the sub panel to fit in the Client area
+ int x, y, wide, tall;
+ GetClientArea(x, y, wide, tall);
+
+ if (_currentSubPanel && _currentSubPanel->isNonWizardPanel())
+ {
+ // just have the subpanel cover the full size
+ _currentSubPanel->SetBounds(x, y, wide, tall);
+ _cancelButton->SetVisible(false);
+ _prevButton->SetVisible(false);
+ _nextButton->SetVisible(false);
+ _finishButton->SetVisible(false);
+ }
+ else
+ {
+ // make room for the buttons at bottom
+ if (_currentSubPanel)
+ {
+ if( _showButtons )
+ {
+ _currentSubPanel->SetBounds(x, y, wide, tall - 35);
+ }
+ else
+ {
+ _currentSubPanel->SetBounds(x, y, wide, tall);
+ }
+ }
+
+ // align the buttons to the right hand side
+ GetSize(wide, tall);
+
+
+ int bwide, btall;
+ _cancelButton->GetSize(bwide, btall);
+
+ x = wide - (20 + bwide);
+ y = tall - (12 + btall);
+
+ _cancelButton->SetPos(x, y);
+ x -= (20 + bwide);
+
+ // only display one of the next or finish buttons (and only if both are visible)
+ if ( _showButtons )
+ {
+ if (_finishButton->IsEnabled() )
+ {
+ _nextButton->SetVisible(false);
+ _finishButton->SetVisible(true);
+ _finishButton->SetPos(x, y);
+ }
+ else
+ {
+ _nextButton->SetVisible(true);
+ _finishButton->SetVisible(false);
+ _nextButton->SetPos(x, y);
+ }
+ }
+
+ x -= (1 + bwide);
+ _prevButton->SetPos(x, y);
+
+ ResetDefaultButton();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: if we don't show buttons then let the sub panel occupy the whole screen
+//-----------------------------------------------------------------------------
+void WizardPanel::GetClientArea(int &x, int &y, int &wide, int &tall)
+{
+ if( _showButtons )
+ {
+ BaseClass::GetClientArea( x, y, wide, tall );
+ }
+ else
+ {
+ x = 0;
+ y = 0;
+ GetSize( wide, tall );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::Run(WizardSubPanel *startPanel)
+{
+ // skip over sub panels if they don't want to be displayed
+ startPanel = FindNextValidSubPanel(startPanel);
+
+ // show it
+ ActivateNextSubPanel(startPanel);
+
+ // make sure we're set up and Run the first panel
+ Activate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::ActivateBuildMode()
+{
+ // no subpanel, no build mode
+ if (!_currentSubPanel)
+ return;
+
+ _currentSubPanel->ActivateBuildMode();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::ResetDefaultButton()
+{
+ // work out which is the default button
+ if (_nextButton->IsEnabled())
+ {
+ _nextButton->SetAsDefaultButton(true);
+ }
+ else if (_finishButton->IsEnabled())
+ {
+ _finishButton->SetAsDefaultButton(true);
+ }
+ else if (_prevButton->IsEnabled())
+ {
+ _prevButton->SetAsDefaultButton(true);
+ }
+ /* Don't ever set the cancel button as the default, as it is too easy for users to quit the wizard without realizing
+ else if (_cancelButton->IsEnabled())
+ {
+ _cancelButton->SetAsDefaultButton(true);
+ }
+ */
+
+ // reset them all (this may not be necessary)
+ _nextButton->InvalidateLayout();
+ _prevButton->InvalidateLayout();
+ _cancelButton->InvalidateLayout();
+ _finishButton->InvalidateLayout();
+
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::ResetKeyFocus()
+{
+ // set the focus on the default
+ FocusNavGroup &navGroup = GetFocusNavGroup();
+ Panel *def = navGroup.GetDefaultPanel();
+ if (def)
+ {
+ if (def->IsEnabled() && def->IsVisible())
+ {
+ def->RequestFocus();
+ }
+ else
+ {
+ def->RequestFocusNext();
+ }
+ }
+
+ ResetDefaultButton();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::CreateButtons()
+{
+ _prevButton = new Button(this, "PrevButton", "");
+ _nextButton = new Button(this, "NextButton", "");
+ _cancelButton = new Button(this, "CancelButton", "");
+ _finishButton = new Button(this, "FinishButton", "");
+
+ _prevButton->SetCommand(new KeyValues("PrevButton"));
+ _nextButton->SetCommand(new KeyValues("NextButton"));
+ _cancelButton->SetCommand(new KeyValues("CancelButton"));
+ _finishButton->SetCommand(new KeyValues("FinishButton"));
+
+ SetNextButtonText(NULL);
+ SetPrevButtonText(NULL);
+ SetFinishButtonText(NULL);
+ SetCancelButtonText(NULL);
+
+ _prevButton->SetSize(82, 24);
+ _nextButton->SetSize(82, 24);
+ _cancelButton->SetSize(82, 24);
+ _finishButton->SetSize(82, 24);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: clears all previous history
+//-----------------------------------------------------------------------------
+void WizardPanel::ResetHistory()
+{
+ _subPanelStack.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::ActivateNextSubPanel(WizardSubPanel *subPanel)
+{
+ // get rid of previous panel
+ WizardSubPanel *prevPanel = _currentSubPanel;
+ if (prevPanel && prevPanel->ShouldDisplayPanel())
+ {
+ // hide
+ prevPanel->SetVisible(false);
+
+ // push onto history stack
+ _subPanelStack.AddElement(_currentSubPanel);
+ }
+
+ // reenable all buttons, returning them to their default state
+ _prevButton->SetEnabled(true);
+ _nextButton->SetEnabled(true);
+ _cancelButton->SetEnabled(true);
+ _finishButton->SetEnabled(true);
+ if ( _showButtons )
+ {
+ _prevButton->SetVisible(true);
+ _cancelButton->SetVisible(true);
+ }
+
+ // set up new subpanel
+ _currentSubPanel = subPanel;
+ _currentSubPanel->SetParent(this);
+ _currentSubPanel->SetVisible(true);
+
+ _currentSubPanel->SetWizardPanel(this);
+ _currentSubPanel->OnDisplayAsNext();
+ _currentSubPanel->OnDisplay();
+ _currentSubPanel->InvalidateLayout(false);
+
+ SETUP_PANEL( _currentSubPanel );
+ int wide, tall;
+ if ( _currentSubPanel->GetDesiredSize(wide, tall) )
+ {
+ SetSize(wide, tall);
+ }
+
+ if (!prevPanel)
+ {
+ // no previous panel, so disable the back button
+ _prevButton->SetEnabled(false);
+ }
+
+ _currentSubPanel->RequestFocus();
+
+ RecalculateTabOrdering();
+ InvalidateLayout(false);
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Pops the last panel off the stack and runs it
+//-----------------------------------------------------------------------------
+void WizardPanel::ActivatePrevSubPanel()
+{
+ _currentSubPanel->SetVisible(false);
+
+ WizardSubPanel *prevPanel = NULL;
+ if (_subPanelStack.GetCount())
+ {
+ // check to see if we need to jump back to a previous sub panel
+ WizardSubPanel *searchPanel = _currentSubPanel->GetPrevSubPanel();
+ if (searchPanel && _subPanelStack.HasElement(searchPanel))
+ {
+ // keep poping the stack till we find it
+ while (_subPanelStack.GetCount() && prevPanel != searchPanel)
+ {
+ prevPanel = _subPanelStack[_subPanelStack.GetCount() - 1];
+ _subPanelStack.RemoveElementAt(_subPanelStack.GetCount() - 1);
+ }
+ }
+ else
+ {
+ // just get the last one
+ prevPanel = _subPanelStack[_subPanelStack.GetCount() - 1];
+ _subPanelStack.RemoveElementAt(_subPanelStack.GetCount() - 1);
+ }
+ }
+
+ if (!prevPanel)
+ {
+ ivgui()->DPrintf2("Error: WizardPanel::ActivatePrevSubPanel(): no previous panel to go back to\n");
+ return;
+ }
+
+ // hide old panel
+ _currentSubPanel->SetVisible(false);
+
+ // reenable all buttons, returning them to their default state
+ _prevButton->SetEnabled(true);
+ _nextButton->SetEnabled(true);
+ _cancelButton->SetEnabled(true);
+ _finishButton->SetEnabled(true);
+
+ // Activate new panel
+ _currentSubPanel = prevPanel;
+ _currentSubPanel->RequestFocus();
+ _currentSubPanel->SetWizardPanel(this);
+ _currentSubPanel->OnDisplayAsPrev();
+ _currentSubPanel->OnDisplay();
+ _currentSubPanel->InvalidateLayout(false);
+
+ SETUP_PANEL( _currentSubPanel );
+ int wide, tall;
+ if ( _currentSubPanel->GetDesiredSize(wide, tall) )
+ {
+ SetSize(wide, tall);
+ }
+
+ // show the previous panel, but don't Activate it (since it should show just what it was previously)
+ _currentSubPanel->SetVisible(true);
+
+ if (!_subPanelStack.GetCount())
+ {
+ // no previous panel, so disable the back button
+ _prevButton->SetEnabled(false);
+ }
+
+ RecalculateTabOrdering();
+ InvalidateLayout(false);
+ Repaint();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets up the new tab ordering
+//-----------------------------------------------------------------------------
+void WizardPanel::RecalculateTabOrdering()
+{
+ if (_currentSubPanel)
+ {
+ _currentSubPanel->SetTabPosition(1);
+ }
+ _prevButton->SetTabPosition(2);
+ _nextButton->SetTabPosition(3);
+ _finishButton->SetTabPosition(4);
+ _cancelButton->SetTabPosition(5);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::SetNextButtonEnabled(bool state)
+{
+ if (_nextButton->IsEnabled() != state)
+ {
+ _nextButton->SetEnabled(state);
+ InvalidateLayout(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::SetPrevButtonEnabled(bool state)
+{
+ if (_prevButton->IsEnabled() != state)
+ {
+ _prevButton->SetEnabled(state);
+ InvalidateLayout(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::SetFinishButtonEnabled(bool state)
+{
+ if (_finishButton->IsEnabled() != state)
+ {
+ _finishButton->SetEnabled(state);
+ InvalidateLayout(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::SetCancelButtonEnabled(bool state)
+{
+ if (_cancelButton->IsEnabled() != state)
+ {
+ _cancelButton->SetEnabled(state);
+ InvalidateLayout(false);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::SetNextButtonVisible(bool state)
+{
+ _nextButton->SetVisible(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::SetPrevButtonVisible(bool state)
+{
+ _prevButton->SetVisible(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::SetFinishButtonVisible(bool state)
+{
+ _finishButton->SetVisible(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::SetCancelButtonVisible(bool state)
+{
+ _cancelButton->SetVisible(state);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::SetNextButtonText(const char *text)
+{
+ if (text)
+ {
+ _nextButton->SetText(text);
+ }
+ else
+ {
+ _nextButton->SetText("#WizardPanel_Next");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::SetPrevButtonText(const char *text)
+{
+ if (text)
+ {
+ _prevButton->SetText(text);
+ }
+ else
+ {
+ _prevButton->SetText("#WizardPanel_Back");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::SetFinishButtonText(const char *text)
+{
+ if (text)
+ {
+ _finishButton->SetText(text);
+ }
+ else
+ {
+ _finishButton->SetText("#WizardPanel_Finish");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::SetCancelButtonText(const char *text)
+{
+ if (text)
+ {
+ _cancelButton->SetText(text);
+ }
+ else
+ {
+ _cancelButton->SetText("#WizardPanel_Cancel");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the next panel that wants to be shown
+//-----------------------------------------------------------------------------
+WizardSubPanel *WizardPanel::FindNextValidSubPanel(WizardSubPanel *currentPanel)
+{
+ // skip over sub panels if they don't want to be displayed
+ while (currentPanel)
+ {
+ currentPanel->SetWizardPanel(this);
+ if (currentPanel->ShouldDisplayPanel())
+ break;
+
+ // ok the panel wants to be skipped, so skip ahead
+ currentPanel = currentPanel->GetNextSubPanel();
+ }
+
+ return currentPanel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Advances to the next panel
+//-----------------------------------------------------------------------------
+void WizardPanel::OnNextButton()
+{
+ if (_currentSubPanel)
+ {
+ bool shouldAdvance = _currentSubPanel->OnNextButton();
+ if (shouldAdvance)
+ {
+ WizardSubPanel *nextPanel = FindNextValidSubPanel(_currentSubPanel->GetNextSubPanel());
+
+ if (nextPanel)
+ {
+ KeyValues *kv = new KeyValues("ActivateNextSubPanel");
+ kv->SetPtr("panel", nextPanel);
+ ivgui()->PostMessage(GetVPanel(), kv, GetVPanel());
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Retreats to the previous panel
+//-----------------------------------------------------------------------------
+void WizardPanel::OnPrevButton()
+{
+ bool shouldRetreat = true;
+ if (_currentSubPanel)
+ {
+ shouldRetreat = _currentSubPanel->OnPrevButton();
+ }
+
+ if (shouldRetreat)
+ {
+ ActivatePrevSubPanel();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::OnFinishButton()
+{
+ if (_currentSubPanel && _currentSubPanel->OnFinishButton())
+ {
+ // hide ourselves away
+ BaseClass::OnClose();
+
+ // automatically delete ourselves if marked to do so
+ if (IsAutoDeleteSet())
+ {
+ MarkForDeletion();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardPanel::OnCancelButton()
+{
+ if (_currentSubPanel && _currentSubPanel->OnCancelButton())
+ {
+ // hide ourselves away
+ BaseClass::OnClose();
+ if (IsAutoDeleteSet())
+ {
+ MarkForDeletion();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: command handler for catching escape key presses
+//-----------------------------------------------------------------------------
+void WizardPanel::OnCommand(const char *command)
+{
+ if (!stricmp(command, "Cancel"))
+ {
+ if (_cancelButton->IsEnabled())
+ {
+ _cancelButton->DoClick();
+ }
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Maps close button to cancel button
+//-----------------------------------------------------------------------------
+void WizardPanel::OnClose()
+{
+ if (_cancelButton->IsEnabled())
+ {
+ _cancelButton->DoClick();
+ }
+ else if (_finishButton->IsEnabled())
+ {
+ _finishButton->DoClick();
+ }
+
+ // don't chain back
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues *WizardPanel::GetWizardData()
+{
+ return _currentData;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: whether to show the next,prev,finish and cancel buttons
+//-----------------------------------------------------------------------------
+void WizardPanel::ShowButtons(bool state)
+{
+ _showButtons = state; // hide the wizard panel buttons
+ SetNextButtonVisible( state );
+ SetPrevButtonVisible( state );
+ SetFinishButtonVisible( state );
+ SetCancelButtonVisible( state );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: filters close buttons
+//-----------------------------------------------------------------------------
+void WizardPanel::OnCloseFrameButtonPressed()
+{
+ // only allow close if the cancel button is enabled
+ if (_cancelButton->IsEnabled())
+ {
+ BaseClass::OnCloseFrameButtonPressed();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns a page by name
+//-----------------------------------------------------------------------------
+WizardSubPanel *WizardPanel::GetSubPanelByName(const char *pageName)
+{
+ return dynamic_cast<WizardSubPanel *>(FindChildByName(pageName));
+}
diff --git a/mp/src/vgui2/vgui_controls/WizardSubPanel.cpp b/mp/src/vgui2/vgui_controls/WizardSubPanel.cpp
new file mode 100644
index 00000000..52562a26
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/WizardSubPanel.cpp
@@ -0,0 +1,114 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vgui_controls/WizardPanel.h"
+#include "vgui_controls/WizardSubPanel.h"
+#include "vgui_controls/BuildGroup.h"
+
+#include "KeyValues.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+#include <stdio.h>
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+WizardSubPanel::WizardSubPanel(Panel *parent, const char *panelName) : EditablePanel(parent, panelName), _wizardPanel(NULL)
+{
+ SetVisible(false);
+ m_iDesiredWide = 0;
+ m_iDesiredTall = 0;
+ SetBuildGroup(GetBuildGroup());
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+WizardSubPanel::~WizardSubPanel()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardSubPanel::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+ SetBgColor(GetSchemeColor("WizardSubPanel.BgColor",pScheme));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardSubPanel::GetSettings( KeyValues *outResourceData )
+{
+ BaseClass::GetSettings(outResourceData);
+
+ outResourceData->SetInt("WizardWide", m_iDesiredWide);
+ outResourceData->SetInt("WizardTall", m_iDesiredTall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void WizardSubPanel::ApplySettings(KeyValues *inResourceData)
+{
+ // don't adjust visiblity during settings application (since it's our parent who really controls it)
+ bool bVisible = IsVisible();
+
+ BaseClass::ApplySettings(inResourceData);
+
+ m_iDesiredWide = inResourceData->GetInt("WizardWide", 0);
+ m_iDesiredTall = inResourceData->GetInt("WizardTall", 0);
+
+ if (GetWizardPanel() && m_iDesiredWide && m_iDesiredTall)
+ {
+ GetWizardPanel()->SetSize(m_iDesiredWide, m_iDesiredTall);
+ }
+
+ SetVisible(bVisible);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: build mode description
+//-----------------------------------------------------------------------------
+const char *WizardSubPanel::GetDescription()
+{
+ static char buf[1024];
+ _snprintf(buf, sizeof(buf), "%s, int WizardWide, int WizardTall", BaseClass::GetDescription());
+ return buf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: gets the size this subpanel would like the wizard to be
+//-----------------------------------------------------------------------------
+bool WizardSubPanel::GetDesiredSize(int &wide, int &tall)
+{
+ wide = m_iDesiredWide;
+ tall = m_iDesiredTall;
+
+ return (m_iDesiredWide && m_iDesiredTall);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues *WizardSubPanel::GetWizardData()
+{
+ return GetWizardPanel()->GetWizardData();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+WizardSubPanel *WizardSubPanel::GetSiblingSubPanelByName(const char *pageName)
+{
+ return GetWizardPanel()->GetSubPanelByName(pageName);
+}
diff --git a/mp/src/vgui2/vgui_controls/consoledialog.cpp b/mp/src/vgui2/vgui_controls/consoledialog.cpp
new file mode 100644
index 00000000..dba72dd8
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/consoledialog.cpp
@@ -0,0 +1,1256 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include "vgui_controls/consoledialog.h"
+
+#include "vgui/IInput.h"
+#include "vgui/IScheme.h"
+#include "vgui/IVGui.h"
+#include "vgui/ISurface.h"
+#include "vgui/ILocalize.h"
+#include "KeyValues.h"
+
+#include "vgui_controls/Button.h"
+#include "vgui/KeyCode.h"
+#include "vgui_controls/Menu.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui_controls/RichText.h"
+#include "tier1/convar.h"
+#include "tier1/convar_serverbounded.h"
+#include "icvar.h"
+#include "filesystem.h"
+
+#include <stdlib.h>
+
+#if defined( _X360 )
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// Used by the autocompletion system
+//-----------------------------------------------------------------------------
+class CNonFocusableMenu : public Menu
+{
+ DECLARE_CLASS_SIMPLE( CNonFocusableMenu, Menu );
+
+public:
+ CNonFocusableMenu( Panel *parent, const char *panelName )
+ : BaseClass( parent, panelName ),
+ m_pFocus( 0 )
+ {
+ }
+
+ void SetFocusPanel( Panel *panel )
+ {
+ m_pFocus = panel;
+ }
+
+ VPANEL GetCurrentKeyFocus()
+ {
+ if ( !m_pFocus )
+ return GetVPanel();
+
+ return m_pFocus->GetVPanel();
+ }
+
+private:
+ Panel *m_pFocus;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: forwards tab key presses up from the text entry so we can do autocomplete
+//-----------------------------------------------------------------------------
+class TabCatchingTextEntry : public TextEntry
+{
+public:
+ TabCatchingTextEntry(Panel *parent, const char *name, VPANEL comp) : TextEntry(parent, name), m_pCompletionList( comp )
+ {
+ SetAllowNonAsciiCharacters( true );
+ SetDragEnabled( true );
+ }
+
+ virtual void OnKeyCodeTyped(KeyCode code)
+ {
+ if (code == KEY_TAB)
+ {
+ GetParent()->OnKeyCodeTyped(code);
+ }
+ else if ( code == KEY_ENTER )
+ {
+ // submit is the default button whose click event will have been called already
+ }
+ else
+ {
+ TextEntry::OnKeyCodeTyped(code);
+ }
+ }
+
+ virtual void OnKillFocus()
+ {
+ if ( input()->GetFocus() != m_pCompletionList ) // if its not the completion window trying to steal our focus
+ {
+ PostMessage(GetParent(), new KeyValues("CloseCompletionList"));
+ }
+ }
+
+private:
+ VPANEL m_pCompletionList;
+};
+
+
+
+// Things the user typed in and hit submit/return with
+CHistoryItem::CHistoryItem( void )
+{
+ m_text = NULL;
+ m_extraText = NULL;
+ m_bHasExtra = false;
+}
+
+CHistoryItem::CHistoryItem( const char *text, const char *extra )
+{
+ Assert( text );
+ m_text = NULL;
+ m_extraText = NULL;
+ m_bHasExtra = false;
+ SetText( text , extra );
+}
+
+CHistoryItem::CHistoryItem( const CHistoryItem& src )
+{
+ m_text = NULL;
+ m_extraText = NULL;
+ m_bHasExtra = false;
+ SetText( src.GetText(), src.GetExtra() );
+}
+
+CHistoryItem::~CHistoryItem( void )
+{
+ delete[] m_text;
+ delete[] m_extraText;
+ m_text = NULL;
+}
+
+const char *CHistoryItem::GetText() const
+{
+ if ( m_text )
+ {
+ return m_text;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+const char *CHistoryItem::GetExtra() const
+{
+ if ( m_extraText )
+ {
+ return m_extraText;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void CHistoryItem::SetText( const char *text, const char *extra )
+{
+ delete[] m_text;
+ int len = strlen( text ) + 1;
+
+ m_text = new char[ len ];
+ Q_memset( m_text, 0x0, len );
+ Q_strncpy( m_text, text, len );
+
+ if ( extra )
+ {
+ m_bHasExtra = true;
+ delete[] m_extraText;
+ int elen = strlen( extra ) + 1;
+ m_extraText = new char[ elen ];
+ Q_memset( m_extraText, 0x0, elen);
+ Q_strncpy( m_extraText, extra, elen );
+ }
+ else
+ {
+ m_bHasExtra = false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Console page completion item starts here
+//
+//-----------------------------------------------------------------------------
+CConsolePanel::CompletionItem::CompletionItem( void )
+{
+ m_bIsCommand = true;
+ m_pCommand = NULL;
+ m_pText = NULL;
+}
+
+CConsolePanel::CompletionItem::CompletionItem( const CompletionItem& src )
+{
+ m_bIsCommand = src.m_bIsCommand;
+ m_pCommand = src.m_pCommand;
+ if ( src.m_pText )
+ {
+ m_pText = new CHistoryItem( (const CHistoryItem& )src.m_pText );
+ }
+ else
+ {
+ m_pText = NULL;
+ }
+}
+
+CConsolePanel::CompletionItem& CConsolePanel::CompletionItem::operator =( const CompletionItem& src )
+{
+ if ( this == &src )
+ return *this;
+
+ m_bIsCommand = src.m_bIsCommand;
+ m_pCommand = src.m_pCommand;
+ if ( src.m_pText )
+ {
+ m_pText = new CHistoryItem( (const CHistoryItem& )*src.m_pText );
+ }
+ else
+ {
+ m_pText = NULL;
+ }
+
+ return *this;
+}
+
+CConsolePanel::CompletionItem::~CompletionItem( void )
+{
+ if ( m_pText )
+ {
+ delete m_pText;
+ m_pText = NULL;
+ }
+}
+
+const char *CConsolePanel::CompletionItem::GetName() const
+{
+ if ( m_bIsCommand )
+ return m_pCommand->GetName();
+ return m_pCommand ? m_pCommand->GetName() : GetCommand();
+}
+
+const char *CConsolePanel::CompletionItem::GetItemText( void )
+{
+ static char text[256];
+ text[0] = 0;
+ if ( m_pText )
+ {
+ if ( m_pText->HasExtra() )
+ {
+ Q_snprintf( text, sizeof( text ), "%s %s", m_pText->GetText(), m_pText->GetExtra() );
+ }
+ else
+ {
+ Q_strncpy( text, m_pText->GetText(), sizeof( text ) );
+ }
+ }
+ return text;
+}
+
+const char *CConsolePanel::CompletionItem::GetCommand( void ) const
+{
+ static char text[256];
+ text[0] = 0;
+ if ( m_pText )
+ {
+ Q_strncpy( text, m_pText->GetText(), sizeof( text ) );
+ }
+ return text;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Console page starts here
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor, destuctor
+//-----------------------------------------------------------------------------
+CConsolePanel::CConsolePanel( vgui::Panel *pParent, const char *pName, bool bStatusVersion ) :
+ BaseClass( pParent, pName ), m_bStatusVersion( bStatusVersion )
+{
+ SetKeyBoardInputEnabled( true );
+
+ if ( !m_bStatusVersion )
+ {
+ SetMinimumSize(100,100);
+ }
+
+ // create controls
+ m_pHistory = new RichText(this, "ConsoleHistory");
+ m_pHistory->SetAllowKeyBindingChainToParent( false );
+ SETUP_PANEL( m_pHistory );
+ m_pHistory->SetVerticalScrollbar( !m_bStatusVersion );
+ if ( m_bStatusVersion )
+ {
+ m_pHistory->SetDrawOffsets( 3, 3 );
+ }
+ m_pHistory->GotoTextEnd();
+
+ m_pSubmit = new Button(this, "ConsoleSubmit", "#Console_Submit");
+ m_pSubmit->SetCommand("submit");
+ m_pSubmit->SetVisible( !m_bStatusVersion );
+
+ CNonFocusableMenu *pCompletionList = new CNonFocusableMenu( this, "CompletionList" );
+ m_pCompletionList = pCompletionList;
+ m_pCompletionList->SetVisible(false);
+
+ m_pEntry = new TabCatchingTextEntry(this, "ConsoleEntry", m_pCompletionList->GetVPanel() );
+ m_pEntry->AddActionSignalTarget(this);
+ m_pEntry->SendNewLine(true);
+ pCompletionList->SetFocusPanel( m_pEntry );
+
+ // need to set up default colors, since ApplySchemeSettings won't be called until later
+ m_PrintColor = Color(216, 222, 211, 255);
+ m_DPrintColor = Color(196, 181, 80, 255);
+
+ m_pEntry->SetTabPosition(1);
+
+ m_bAutoCompleteMode = false;
+ m_szPartialText[0] = 0;
+ m_szPreviousPartialText[0]=0;
+
+ // Add to global console list
+ g_pCVar->InstallConsoleDisplayFunc( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CConsolePanel::~CConsolePanel()
+{
+ ClearCompletionList();
+ m_CommandHistory.Purge();
+ g_pCVar->RemoveConsoleDisplayFunc( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Updates the completion list
+//-----------------------------------------------------------------------------
+void CConsolePanel::OnThink()
+{
+ BaseClass::OnThink();
+
+ if ( !IsVisible() )
+ return;
+
+ if ( !m_pCompletionList->IsVisible() )
+ return;
+
+ UpdateCompletionListPosition();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Clears the console
+//-----------------------------------------------------------------------------
+void CConsolePanel::Clear()
+{
+ m_pHistory->SetText("");
+ m_pHistory->GotoTextEnd();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: color text print
+//-----------------------------------------------------------------------------
+void CConsolePanel::ColorPrint( const Color& clr, const char *msg )
+{
+ if ( m_bStatusVersion )
+ {
+ Clear();
+ }
+
+ m_pHistory->InsertColorChange( clr );
+ m_pHistory->InsertString( msg );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: normal text print
+//-----------------------------------------------------------------------------
+void CConsolePanel::Print(const char *msg)
+{
+ ColorPrint( m_PrintColor, msg );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: debug text print
+//-----------------------------------------------------------------------------
+void CConsolePanel::DPrint( const char *msg )
+{
+ ColorPrint( m_DPrintColor, msg );
+}
+
+
+void CConsolePanel::ClearCompletionList()
+{
+ int c = m_CompletionList.Count();
+ int i;
+ for ( i = c - 1; i >= 0; i-- )
+ {
+ delete m_CompletionList[ i ];
+ }
+ m_CompletionList.Purge();
+}
+
+
+static ConCommand *FindAutoCompleteCommmandFromPartial( const char *partial )
+{
+ char command[ 256 ];
+ Q_strncpy( command, partial, sizeof( command ) );
+
+ char *space = Q_strstr( command, " " );
+ if ( space )
+ {
+ *space = 0;
+ }
+
+ ConCommand *cmd = g_pCVar->FindCommand( command );
+ if ( !cmd )
+ return NULL;
+
+ if ( !cmd->CanAutoComplete() )
+ return NULL;
+
+ return cmd;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: rebuilds the list of possible completions from the current entered text
+//-----------------------------------------------------------------------------
+void CConsolePanel::RebuildCompletionList(const char *text)
+{
+ ClearCompletionList();
+
+ // we need the length of the text for the partial string compares
+ int len = Q_strlen(text);
+ if ( len < 1 )
+ {
+ // Fill the completion list with history instead
+ for ( int i = 0 ; i < m_CommandHistory.Count(); i++ )
+ {
+ CHistoryItem *item = &m_CommandHistory[ i ];
+ CompletionItem *comp = new CompletionItem();
+ m_CompletionList.AddToTail( comp );
+ comp->m_bIsCommand = false;
+ comp->m_pCommand = NULL;
+ comp->m_pText = new CHistoryItem( *item );
+ }
+ return;
+ }
+
+ bool bNormalBuild = true;
+
+ // if there is a space in the text, and the command isn't of the type to know how to autocomplet, then command completion is over
+ const char *space = strstr( text, " " );
+ if ( space )
+ {
+ ConCommand *pCommand = FindAutoCompleteCommmandFromPartial( text );
+ if ( !pCommand )
+ return;
+
+ bNormalBuild = false;
+
+ CUtlVector< CUtlString > commands;
+ int count = pCommand->AutoCompleteSuggest( text, commands );
+ Assert( count <= COMMAND_COMPLETION_MAXITEMS );
+ int i;
+
+ for ( i = 0; i < count; i++ )
+ {
+ // match found, add to list
+ CompletionItem *item = new CompletionItem();
+ m_CompletionList.AddToTail( item );
+ item->m_bIsCommand = false;
+ item->m_pCommand = NULL;
+ item->m_pText = new CHistoryItem( commands[ i ].String() );
+ }
+ }
+
+ if ( bNormalBuild )
+ {
+ // look through the command list for all matches
+ ConCommandBase const *cmd = (ConCommandBase const *)cvar->GetCommands();
+ while (cmd)
+ {
+ if ( cmd->IsFlagSet( FCVAR_DEVELOPMENTONLY ) || cmd->IsFlagSet( FCVAR_HIDDEN ) )
+ {
+ cmd = cmd->GetNext();
+ continue;
+ }
+
+ if ( !strnicmp(text, cmd->GetName(), len))
+ {
+ // match found, add to list
+ CompletionItem *item = new CompletionItem();
+ m_CompletionList.AddToTail( item );
+ item->m_pCommand = (ConCommandBase *)cmd;
+ const char *tst = cmd->GetName();
+ if ( !cmd->IsCommand() )
+ {
+ item->m_bIsCommand = false;
+ ConVar *var = ( ConVar * )cmd;
+ ConVar_ServerBounded *pBounded = dynamic_cast<ConVar_ServerBounded*>( var );
+ if ( pBounded || var->IsFlagSet( FCVAR_NEVER_AS_STRING ) )
+ {
+ char strValue[512];
+
+ int intVal = pBounded ? pBounded->GetInt() : var->GetInt();
+ float floatVal = pBounded ? pBounded->GetFloat() : var->GetFloat();
+
+ if ( floatVal == intVal )
+ Q_snprintf( strValue, sizeof( strValue ), "%d", intVal );
+ else
+ Q_snprintf( strValue, sizeof( strValue ), "%f", floatVal );
+
+ item->m_pText = new CHistoryItem( var->GetName(), strValue );
+ }
+ else
+ {
+ item->m_pText = new CHistoryItem( var->GetName(), var->GetString() );
+ }
+ }
+ else
+ {
+ item->m_bIsCommand = true;
+ item->m_pText = new CHistoryItem( tst );
+ }
+ }
+
+ cmd = cmd->GetNext();
+ }
+
+ // Now sort the list by command name
+ if ( m_CompletionList.Count() >= 2 )
+ {
+ for ( int i = 0 ; i < m_CompletionList.Count(); i++ )
+ {
+ for ( int j = i + 1; j < m_CompletionList.Count(); j++ )
+ {
+ const CompletionItem *i1, *i2;
+ i1 = m_CompletionList[ i ];
+ i2 = m_CompletionList[ j ];
+
+ if ( Q_stricmp( i1->GetName(), i2->GetName() ) > 0 )
+ {
+ CompletionItem *temp = m_CompletionList[ i ];
+ m_CompletionList[ i ] = m_CompletionList[ j ];
+ m_CompletionList[ j ] = temp;
+ }
+ }
+ }
+ }
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: auto completes current text
+//-----------------------------------------------------------------------------
+void CConsolePanel::OnAutoComplete(bool reverse)
+{
+ if (!m_bAutoCompleteMode)
+ {
+ // we're not in auto-complete mode, Start
+ m_iNextCompletion = 0;
+ m_bAutoCompleteMode = true;
+ }
+
+ // if we're in reverse, move back to before the current
+ if (reverse)
+ {
+ m_iNextCompletion -= 2;
+ if (m_iNextCompletion < 0)
+ {
+ // loop around in reverse
+ m_iNextCompletion = m_CompletionList.Size() - 1;
+ }
+ }
+
+ // get the next completion
+ if (!m_CompletionList.IsValidIndex(m_iNextCompletion))
+ {
+ // loop completion list
+ m_iNextCompletion = 0;
+ }
+
+ // make sure everything is still valid
+ if (!m_CompletionList.IsValidIndex(m_iNextCompletion))
+ return;
+
+ // match found, set text
+ char completedText[256];
+ CompletionItem *item = m_CompletionList[m_iNextCompletion];
+ Assert( item );
+
+ if ( !item->m_bIsCommand && item->m_pCommand )
+ {
+ Q_strncpy(completedText, item->GetCommand(), sizeof(completedText) - 2 );
+ }
+ else
+ {
+ Q_strncpy(completedText, item->GetItemText(), sizeof(completedText) - 2 );
+ }
+
+ if ( !Q_strstr( completedText, " " ) )
+ {
+ Q_strncat(completedText, " ", sizeof(completedText), COPY_ALL_CHARACTERS );
+ }
+
+ m_pEntry->SetText(completedText);
+ m_pEntry->GotoTextEnd();
+ m_pEntry->SelectNone();
+
+ m_iNextCompletion++;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called whenever the user types text
+//-----------------------------------------------------------------------------
+void CConsolePanel::OnTextChanged(Panel *panel)
+{
+ if (panel != m_pEntry)
+ return;
+
+ Q_strncpy( m_szPreviousPartialText, m_szPartialText, sizeof( m_szPreviousPartialText ) );
+
+ // get the partial text the user type
+ m_pEntry->GetText(m_szPartialText, sizeof(m_szPartialText));
+
+ // see if they've hit the tilde key (which opens & closes the console)
+ int len = Q_strlen(m_szPartialText);
+
+ bool hitTilde = ( m_szPartialText[len - 1] == '~' || m_szPartialText[len - 1] == '`' ) ? true : false;
+
+ bool altKeyDown = ( vgui::input()->IsKeyDown( KEY_LALT ) || vgui::input()->IsKeyDown( KEY_RALT ) ) ? true : false;
+ bool ctrlKeyDown = ( vgui::input()->IsKeyDown( KEY_LCONTROL ) || vgui::input()->IsKeyDown( KEY_RCONTROL ) ) ? true : false;
+
+ // Alt-Tilde toggles Japanese IME on/off!!!
+ if ( ( len > 0 ) && hitTilde )
+ {
+ // Strip the last character (tilde)
+ m_szPartialText[ len - 1 ] = L'\0';
+
+ if( !altKeyDown && !ctrlKeyDown )
+ {
+ m_pEntry->SetText( "" );
+
+ // close the console
+ PostMessage( this, new KeyValues( "Close" ) );
+ PostActionSignal( new KeyValues( "ClosedByHittingTilde" ) );
+ }
+ else
+ {
+ m_pEntry->SetText( m_szPartialText );
+ }
+ return;
+ }
+
+ // clear auto-complete state since the user has typed
+ m_bAutoCompleteMode = false;
+
+ RebuildCompletionList(m_szPartialText);
+
+ // build the menu
+ if ( m_CompletionList.Count() < 1 )
+ {
+ m_pCompletionList->SetVisible(false);
+ }
+ else
+ {
+ m_pCompletionList->SetVisible(true);
+ m_pCompletionList->DeleteAllItems();
+ const int MAX_MENU_ITEMS = 10;
+
+ // add the first ten items to the list
+ for (int i = 0; i < m_CompletionList.Count() && i < MAX_MENU_ITEMS; i++)
+ {
+ char text[256];
+ text[0] = 0;
+ if (i == MAX_MENU_ITEMS - 1)
+ {
+ Q_strncpy(text, "...", sizeof( text ) );
+ }
+ else
+ {
+ Assert( m_CompletionList[i] );
+ Q_strncpy(text, m_CompletionList[i]->GetItemText(), sizeof( text ) );
+ }
+ KeyValues *kv = new KeyValues("CompletionCommand");
+ kv->SetString("command",text);
+ m_pCompletionList->AddMenuItem(text, kv, this);
+ }
+
+ UpdateCompletionListPosition();
+ }
+
+ RequestFocus();
+ m_pEntry->RequestFocus();
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: generic vgui command handler
+//-----------------------------------------------------------------------------
+void CConsolePanel::OnCommand(const char *command)
+{
+ if ( !Q_stricmp( command, "Submit" ) )
+ {
+ // submit the entry as a console commmand
+ char szCommand[256];
+ m_pEntry->GetText(szCommand, sizeof(szCommand));
+ PostActionSignal( new KeyValues( "CommandSubmitted", "command", szCommand ) );
+
+ // add to the history
+ Print("] ");
+ Print(szCommand);
+ Print("\n");
+
+ // clear the field
+ m_pEntry->SetText("");
+
+ // clear the completion state
+ OnTextChanged(m_pEntry);
+
+ // always go the end of the buffer when the user has typed something
+ m_pHistory->GotoTextEnd();
+
+ // Add the command to the history
+ char *extra = strchr(szCommand, ' ');
+ if ( extra )
+ {
+ *extra = '\0';
+ extra++;
+ }
+
+ if ( Q_strlen( szCommand ) > 0 )
+ {
+ AddToHistory( szCommand, extra );
+ }
+ m_pCompletionList->SetVisible(false);
+ }
+ else
+ {
+ BaseClass::OnCommand(command);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Focus related methods
+//-----------------------------------------------------------------------------
+bool CConsolePanel::TextEntryHasFocus() const
+{
+ return ( input()->GetFocus() == m_pEntry->GetVPanel() );
+}
+
+void CConsolePanel::TextEntryRequestFocus()
+{
+ m_pEntry->RequestFocus();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: swallows tab key pressed
+//-----------------------------------------------------------------------------
+void CConsolePanel::OnKeyCodeTyped(KeyCode code)
+{
+ BaseClass::OnKeyCodeTyped(code);
+
+ // check for processing
+ if ( TextEntryHasFocus() )
+ {
+ if (code == KEY_TAB)
+ {
+ bool reverse = false;
+ if (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT))
+ {
+ reverse = true;
+ }
+
+ // attempt auto-completion
+ OnAutoComplete(reverse);
+ m_pEntry->RequestFocus();
+ }
+ else if (code == KEY_DOWN)
+ {
+ OnAutoComplete(false);
+ // UpdateCompletionListPosition();
+ // m_pCompletionList->SetVisible(true);
+
+ m_pEntry->RequestFocus();
+ }
+ else if (code == KEY_UP)
+ {
+ OnAutoComplete(true);
+ m_pEntry->RequestFocus();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: lays out controls
+//-----------------------------------------------------------------------------
+void CConsolePanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ // setup tab ordering
+ GetFocusNavGroup().SetDefaultButton(m_pSubmit);
+
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+ m_pEntry->SetBorder(pScheme->GetBorder("DepressedButtonBorder"));
+ m_pHistory->SetBorder(pScheme->GetBorder("DepressedButtonBorder"));
+
+ // layout controls
+ int wide, tall;
+ GetSize(wide, tall);
+
+ if ( !m_bStatusVersion )
+ {
+ const int inset = 8;
+ const int entryHeight = 24;
+ const int topHeight = 4;
+ const int entryInset = 4;
+ const int submitWide = 64;
+ const int submitInset = 7; // x inset to pull the submit button away from the frame grab
+
+ m_pHistory->SetPos(inset, inset + topHeight);
+ m_pHistory->SetSize(wide - (inset * 2), tall - (entryInset * 2 + inset * 2 + topHeight + entryHeight));
+ m_pHistory->InvalidateLayout();
+
+ int nSubmitXPos = wide - ( inset + submitWide + submitInset );
+ m_pSubmit->SetPos( nSubmitXPos, tall - (entryInset * 2 + entryHeight));
+ m_pSubmit->SetSize( submitWide, entryHeight);
+
+ m_pEntry->SetPos( inset, tall - (entryInset * 2 + entryHeight) );
+ m_pEntry->SetSize( nSubmitXPos - entryInset - 2 * inset, entryHeight);
+ }
+ else
+ {
+ const int inset = 2;
+
+ int entryWidth = wide / 2;
+ if ( wide > 400 )
+ {
+ entryWidth = 200;
+ }
+
+ m_pEntry->SetBounds( inset, inset, entryWidth, tall - 2 * inset );
+
+ m_pHistory->SetBounds( inset + entryWidth + inset, inset, ( wide - entryWidth ) - inset, tall - 2 * inset );
+ }
+
+ UpdateCompletionListPosition();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the position of the completion list popup
+//-----------------------------------------------------------------------------
+void CConsolePanel::UpdateCompletionListPosition()
+{
+ int ex, ey;
+ m_pEntry->GetPos(ex, ey);
+
+ if ( !m_bStatusVersion )
+ {
+ // Position below text entry
+ ey += m_pEntry->GetTall();
+ }
+ else
+ {
+ // Position above text entry
+ int menuwide, menutall;
+ m_pCompletionList->GetSize( menuwide, menutall );
+ ey -= ( menutall + 4 );
+ }
+
+ LocalToScreen( ex, ey );
+ m_pCompletionList->SetPos( ex, ey );
+
+ if ( m_pCompletionList->IsVisible() )
+ {
+ m_pEntry->RequestFocus();
+ MoveToFront();
+ m_pCompletionList->MoveToFront();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Closes the completion list
+//-----------------------------------------------------------------------------
+void CConsolePanel::CloseCompletionList()
+{
+ m_pCompletionList->SetVisible(false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets up colors
+//-----------------------------------------------------------------------------
+void CConsolePanel::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ m_PrintColor = GetSchemeColor("Console.TextColor", pScheme);
+ m_DPrintColor = GetSchemeColor("Console.DevTextColor", pScheme);
+ m_pHistory->SetFont( pScheme->GetFont( "ConsoleText", IsProportional() ) );
+ m_pCompletionList->SetFont( pScheme->GetFont( "DefaultSmall", IsProportional() ) );
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles autocompletion menu input
+//-----------------------------------------------------------------------------
+void CConsolePanel::OnMenuItemSelected(const char *command)
+{
+ if ( strstr( command, "..." ) ) // stop the menu going away if you click on ...
+ {
+ m_pCompletionList->SetVisible( true );
+ }
+ else
+ {
+ m_pEntry->SetText(command);
+ m_pEntry->GotoTextEnd();
+ m_pEntry->InsertChar(' ');
+ m_pEntry->GotoTextEnd();
+ }
+}
+
+void CConsolePanel::Hide()
+{
+ OnClose();
+ m_iNextCompletion = 0;
+ RebuildCompletionList("");
+}
+
+void CConsolePanel::AddToHistory( const char *commandText, const char *extraText )
+{
+ // Newest at end, oldest at head
+ while ( m_CommandHistory.Count() >= MAX_HISTORY_ITEMS )
+ {
+ // Remove from head until size is reasonable
+ m_CommandHistory.Remove( 0 );
+ }
+
+ // strip the space off the end of the command before adding it to the history
+ // If this code gets cleaned up then we should remove the redundant calls to strlen,
+ // the check for whether _alloca succeeded, and should use V_strncpy instead of the
+ // error prone memset/strncpy sequence.
+ char *command = static_cast<char *>( _alloca( (strlen( commandText ) + 1 ) * sizeof( char ) ));
+ if ( command )
+ {
+ memset( command, 0x0, strlen( commandText ) + 1 );
+ strncpy( command, commandText, strlen( commandText ));
+ // There is no actual bug here, just some sloppy/odd code.
+ // src\vgui2\vgui_controls\consoledialog.cpp(974): warning C6053: The prior call to 'strncpy' might not zero-terminate string 'command'
+ ANALYZE_SUPPRESS( 6053 )
+ if ( command[ strlen( command ) -1 ] == ' ' )
+ {
+ command[ strlen( command ) -1 ] = '\0';
+ }
+ }
+
+ // strip the quotes off the extra text
+ char *extra = NULL;
+
+ if ( extraText )
+ {
+ extra = static_cast<char *>( malloc( (strlen( extraText ) + 1 ) * sizeof( char ) ));
+ if ( extra )
+ {
+ memset( extra, 0x0, strlen( extraText ) + 1 );
+ strncpy( extra, extraText, strlen( extraText )); // +1 to dodge the starting quote
+
+ // Strip trailing spaces
+ int i = strlen( extra ) - 1;
+ while ( i >= 0 && // Check I before referencing i == -1 into the extra array!
+ extra[ i ] == ' ' )
+ {
+ extra[ i ] = '\0';
+ i--;
+ }
+ }
+ }
+
+ // If it's already there, then remove since we'll add it to the end instead
+ CHistoryItem *item = NULL;
+ for ( int i = m_CommandHistory.Count() - 1; i >= 0; i-- )
+ {
+ item = &m_CommandHistory[ i ];
+ if ( !item )
+ continue;
+
+ if ( stricmp( item->GetText(), command ) )
+ continue;
+
+ if ( extra || item->GetExtra() )
+ {
+ if ( !extra || !item->GetExtra() )
+ continue;
+
+ // stricmp so two commands with the same starting text get added
+ if ( stricmp( item->GetExtra(), extra ) )
+ continue;
+ }
+ m_CommandHistory.Remove( i );
+ }
+
+ item = &m_CommandHistory[ m_CommandHistory.AddToTail() ];
+ Assert( item );
+ item->SetText( command, extra );
+
+ m_iNextCompletion = 0;
+ RebuildCompletionList( m_szPartialText );
+
+ free( extra );
+}
+
+void CConsolePanel::GetConsoleText( char *pchText, size_t bufSize ) const
+{
+ wchar_t *temp = new wchar_t[ bufSize ];
+ m_pHistory->GetText( 0, temp, bufSize * sizeof( wchar_t ) );
+ g_pVGuiLocalize->ConvertUnicodeToANSI( temp, pchText, bufSize );
+ delete[] temp;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: writes out console to disk
+//-----------------------------------------------------------------------------
+void CConsolePanel::DumpConsoleTextToFile()
+{
+ const int CONDUMP_FILES_MAX_NUM = 1000;
+
+ FileHandle_t handle;
+ bool found = false;
+ char szfile[ 512 ];
+
+ // we don't want to overwrite other condump.txt files
+ for ( int i = 0 ; i < CONDUMP_FILES_MAX_NUM ; ++i )
+ {
+ _snprintf( szfile, sizeof(szfile), "condump%03d.txt", i );
+ if ( !g_pFullFileSystem->FileExists(szfile) )
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if ( !found )
+ {
+ Print( "Can't condump! Too many existing condump output files in the gamedir!\n" );
+ return;
+ }
+
+ handle = g_pFullFileSystem->Open( szfile, "wb" );
+ if ( handle != FILESYSTEM_INVALID_HANDLE )
+ {
+ int pos = 0;
+ while (1)
+ {
+ wchar_t buf[512];
+ m_pHistory->GetText(pos, buf, sizeof(buf));
+ pos += sizeof(buf) / sizeof(wchar_t);
+
+ // don't continue if none left
+ if (buf[0] == 0)
+ break;
+
+ // convert to ansi
+ char ansi[512];
+ g_pVGuiLocalize->ConvertUnicodeToANSI(buf, ansi, sizeof(ansi));
+
+ // write to disk
+ int len = strlen(ansi);
+ for (int i = 0; i < len; i++)
+ {
+ // preceed newlines with a return
+ if (ansi[i] == '\n')
+ {
+ char ret = '\r';
+ g_pFullFileSystem->Write( &ret, 1, handle );
+ }
+
+ g_pFullFileSystem->Write( ansi + i, 1, handle );
+ }
+ }
+
+ g_pFullFileSystem->Close( handle );
+
+ Print( "console dumped to " );
+ Print( szfile );
+ Print( "\n" );
+ }
+ else
+ {
+ Print( "Unable to condump to " );
+ Print( szfile );
+ Print( "\n" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Console dialog starts here
+//
+//-----------------------------------------------------------------------------
+CConsoleDialog::CConsoleDialog( vgui::Panel *pParent, const char *pName, bool bStatusVersion ) :
+ BaseClass( pParent, pName )
+{
+ // initialize dialog
+ SetVisible( false );
+ SetTitle( "#Console_Title", true );
+ m_pConsolePanel = new CConsolePanel( this, "ConsolePage", bStatusVersion );
+ m_pConsolePanel->AddActionSignalTarget( this );
+}
+
+void CConsoleDialog::OnScreenSizeChanged( int iOldWide, int iOldTall )
+{
+ BaseClass::OnScreenSizeChanged( iOldWide, iOldTall );
+
+ int sx, sy;
+ surface()->GetScreenSize( sx, sy );
+
+ int w, h;
+ GetSize( w, h );
+ if ( w > sx || h > sy )
+ {
+ if ( w > sx )
+ {
+ w = sx;
+ }
+ if ( h > sy )
+ {
+ h = sy;
+ }
+
+ // Try and lower the size to match the screen bounds
+ SetSize( w, h );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: brings dialog to the fore
+//-----------------------------------------------------------------------------
+void CConsoleDialog::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int x, y, w, h;
+ GetClientArea( x, y, w, h );
+ m_pConsolePanel->SetBounds( x, y, w, h );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: brings dialog to the fore
+//-----------------------------------------------------------------------------
+void CConsoleDialog::Activate()
+{
+ BaseClass::Activate();
+ m_pConsolePanel->m_pEntry->RequestFocus();
+}
+
+
+//-----------------------------------------------------------------------------
+// Hides the dialog
+//-----------------------------------------------------------------------------
+void CConsoleDialog::Hide()
+{
+ OnClose();
+ m_pConsolePanel->Hide();
+}
+
+
+//-----------------------------------------------------------------------------
+// Close just hides the dialog
+//-----------------------------------------------------------------------------
+void CConsoleDialog::Close()
+{
+ Hide();
+}
+
+
+//-----------------------------------------------------------------------------
+// Submits commands
+//-----------------------------------------------------------------------------
+void CConsoleDialog::OnCommandSubmitted( const char *pCommand )
+{
+ PostActionSignal( new KeyValues( "CommandSubmitted", "command", pCommand ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Chain to the page
+//-----------------------------------------------------------------------------
+void CConsoleDialog::Print( const char *pMessage )
+{
+ m_pConsolePanel->Print( pMessage );
+}
+
+void CConsoleDialog::DPrint( const char *pMessage )
+{
+ m_pConsolePanel->DPrint( pMessage );
+}
+
+void CConsoleDialog::ColorPrint( const Color& clr, const char *msg )
+{
+ m_pConsolePanel->ColorPrint( clr, msg );
+}
+
+void CConsoleDialog::Clear()
+{
+ m_pConsolePanel->Clear( );
+}
+
+void CConsoleDialog::DumpConsoleTextToFile()
+{
+ m_pConsolePanel->DumpConsoleTextToFile( );
+}
+
+
+void CConsoleDialog::OnKeyCodePressed( vgui::KeyCode code )
+{
+ if ( code == KEY_XBUTTON_B )
+ {
+ Hide();
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed(code);
+ }
+} \ No newline at end of file
diff --git a/mp/src/vgui2/vgui_controls/controls.cpp b/mp/src/vgui2/vgui_controls/controls.cpp
new file mode 100644
index 00000000..2da48c2b
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/controls.cpp
@@ -0,0 +1,72 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include <vgui_controls/Controls.h>
+#include <locale.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern int g_nYou_Must_Add_Public_Vgui_Controls_Vgui_ControlsCpp_To_Your_Project;
+
+namespace vgui
+{
+
+static char g_szControlsModuleName[256];
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the controls
+//-----------------------------------------------------------------------------
+extern "C" { extern int _heapmin(); }
+
+bool VGui_InitInterfacesList( const char *moduleName, CreateInterfaceFn *factoryList, int numFactories )
+{
+ g_nYou_Must_Add_Public_Vgui_Controls_Vgui_ControlsCpp_To_Your_Project = 1;
+
+ // If you hit this error, then you need to include memoverride.cpp in the project somewhere or else
+ // you'll get crashes later when vgui_controls allocates KeyValues and vgui tries to delete them.
+#if !defined(NO_MALLOC_OVERRIDE) && defined( WIN32 )
+ if ( _heapmin() != 1 )
+ {
+ Assert( false );
+ Error( "Must include memoverride.cpp in your project." );
+ }
+#endif
+ // keep a record of this module name
+ strncpy(g_szControlsModuleName, moduleName, sizeof(g_szControlsModuleName));
+ g_szControlsModuleName[sizeof(g_szControlsModuleName) - 1] = 0;
+
+ // initialize our locale (must be done for every vgui dll/exe)
+ // "" makes it use the default locale, required to make iswprint() work correctly in different languages
+ setlocale(LC_CTYPE, "");
+ setlocale(LC_TIME, "");
+ setlocale(LC_COLLATE, "");
+ setlocale(LC_MONETARY, "");
+
+ // NOTE: Vgui expects to use these interfaces which are defined in tier3.lib
+ if ( !g_pVGui || !g_pVGuiInput || !g_pVGuiPanel ||
+ !g_pVGuiSurface || !g_pVGuiSchemeManager || !g_pVGuiSystem )
+ {
+ Warning( "vgui_controls is missing a required interface!\n" );
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the name of the module this has been compiled into
+//-----------------------------------------------------------------------------
+const char *GetControlsModuleName()
+{
+ return g_szControlsModuleName;
+}
+
+} // namespace vgui
+
+
+
diff --git a/mp/src/vgui2/vgui_controls/cvartogglecheckbutton.cpp b/mp/src/vgui2/vgui_controls/cvartogglecheckbutton.cpp
new file mode 100644
index 00000000..eb8a900b
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/cvartogglecheckbutton.cpp
@@ -0,0 +1,25 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "tier1/KeyValues.h"
+
+#include <vgui/ISurface.h>
+#include <vgui/IScheme.h>
+#include <vgui_controls/cvartogglecheckbutton.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+using namespace vgui;
+
+vgui::Panel *Create_CvarToggleCheckButton()
+{
+ return new CvarToggleCheckButton< ConVarRef >( NULL, NULL );
+}
+
+DECLARE_BUILD_FACTORY_CUSTOM_ALIAS( CvarToggleCheckButton<ConVarRef>, CvarToggleCheckButton, Create_CvarToggleCheckButton );
+
diff --git a/mp/src/vgui2/vgui_controls/perforcefilelistframe.cpp b/mp/src/vgui2/vgui_controls/perforcefilelistframe.cpp
new file mode 100644
index 00000000..1ba29d16
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/perforcefilelistframe.cpp
@@ -0,0 +1,623 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// List of perforce files and operations
+//
+//=============================================================================
+
+#include "vgui_controls/perforcefilelistframe.h"
+#include "tier1/KeyValues.h"
+#include "vgui_controls/Button.h"
+#include "vgui_controls/ListPanel.h"
+#include "vgui_controls/Splitter.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui_controls/MessageBox.h"
+#include "tier2/tier2.h"
+#include "p4lib/ip4.h"
+#include "filesystem.h"
+#include "vgui/IVGui.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Sort by asset name
+//-----------------------------------------------------------------------------
+static int __cdecl OperationSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 )
+{
+ const char *string1 = item1.kv->GetString("operation");
+ const char *string2 = item2.kv->GetString("operation");
+ int nRetVal = Q_stricmp( string1, string2 );
+ if ( nRetVal != 0 )
+ return nRetVal;
+
+ string1 = item1.kv->GetString("filename");
+ string2 = item2.kv->GetString("filename");
+ return Q_stricmp( string1, string2 );
+}
+
+static int __cdecl FileBrowserSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 )
+{
+ const char *string1 = item1.kv->GetString("filename");
+ const char *string2 = item2.kv->GetString("filename");
+ return Q_stricmp( string1, string2 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+COperationFileListFrame::COperationFileListFrame( vgui::Panel *pParent, const char *pTitle, const char *pColumnHeader, bool bShowDescription, bool bShowOkOnly, int nDialogID ) :
+ BaseClass( pParent, "PerforceFileList" )
+{
+ m_pText = NULL;
+ vgui::Panel *pBrowserParent = this;
+
+ m_pDescription = NULL;
+ m_pSplitter = NULL;
+ if ( bShowDescription )
+ {
+ m_pSplitter = new vgui::Splitter( this, "Splitter", vgui::SPLITTER_MODE_HORIZONTAL, 1 );
+
+ pBrowserParent = m_pSplitter->GetChild( 0 );
+ vgui::Panel *pDescParent = m_pSplitter->GetChild( 1 );
+
+ m_pDescription = new vgui::TextEntry( pDescParent, "Description" );
+ m_pDescription->SetMultiline( true );
+ m_pDescription->SetCatchEnterKey( true );
+ m_pDescription->SetText( "<enter description here>" );
+ }
+
+ // FIXME: Might be nice to have checkboxes per row
+ m_pFileBrowser = new vgui::ListPanel( pBrowserParent, "Browser" );
+ m_pFileBrowser->AddColumnHeader( 0, "operation", "Operation", 52, 0 );
+ m_pFileBrowser->AddColumnHeader( 1, "filename", pColumnHeader, 128, vgui::ListPanel::COLUMN_RESIZEWITHWINDOW );
+ m_pFileBrowser->SetSelectIndividualCells( false );
+ m_pFileBrowser->SetMultiselectEnabled( false );
+ m_pFileBrowser->SetEmptyListText( "No Perforce Operations" );
+ m_pFileBrowser->SetDragEnabled( true );
+ m_pFileBrowser->AddActionSignalTarget( this );
+ m_pFileBrowser->SetSortFunc( 0, OperationSortFunc );
+ m_pFileBrowser->SetSortFunc( 1, FileBrowserSortFunc );
+ m_pFileBrowser->SetSortColumn( 0 );
+
+ m_pYesButton = new vgui::Button( this, "YesButton", "Yes", this, "Yes" );
+ m_pNoButton = new vgui::Button( this, "NoButton", "No", this, "No" );
+
+ SetBlockDragChaining( true );
+ SetDeleteSelfOnClose( true );
+
+ if ( bShowDescription )
+ {
+ LoadControlSettingsAndUserConfig( "resource/perforcefilelistdescription.res", nDialogID );
+ }
+ else
+ {
+ LoadControlSettingsAndUserConfig( "resource/perforcefilelist.res", nDialogID );
+ }
+
+ if ( bShowOkOnly )
+ {
+ m_pYesButton->SetText( "#MessageBox_OK" );
+ m_pNoButton->SetVisible( false );
+ }
+
+ m_pContextKeyValues = NULL;
+
+ SetTitle( pTitle, false );
+}
+
+COperationFileListFrame::~COperationFileListFrame()
+{
+ SaveUserConfig();
+ CleanUpMessage();
+ if ( m_pText )
+ {
+ delete[] m_pText;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Deletes the message
+//-----------------------------------------------------------------------------
+void COperationFileListFrame::CleanUpMessage()
+{
+ if ( m_pContextKeyValues )
+ {
+ m_pContextKeyValues->deleteThis();
+ m_pContextKeyValues = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Performs layout
+//-----------------------------------------------------------------------------
+void COperationFileListFrame::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ if ( m_pSplitter )
+ {
+ int x, y, w, h;
+ GetClientArea( x, y, w, h );
+ y += 6;
+ h -= 36;
+ m_pSplitter->SetBounds( x, y, w, h );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds files to the frame
+//-----------------------------------------------------------------------------
+void COperationFileListFrame::ClearAllOperations()
+{
+ m_pFileBrowser->RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds the strings to the list panel
+//-----------------------------------------------------------------------------
+void COperationFileListFrame::AddOperation( const char *pOperation, const char *pFileName )
+{
+ KeyValues *kv = new KeyValues( "node", "filename", pFileName );
+ kv->SetString( "operation", pOperation );
+ m_pFileBrowser->AddItem( kv, 0, false, false );
+}
+
+void COperationFileListFrame::AddOperation( const char *pOperation, const char *pFileName, const Color& clr )
+{
+ KeyValues *kv = new KeyValues( "node", "filename", pFileName );
+ kv->SetString( "operation", pOperation );
+ kv->SetColor( "cellcolor", clr );
+ m_pFileBrowser->AddItem( kv, 0, false, false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Resizes the operation column to fit the operation text
+//-----------------------------------------------------------------------------
+void COperationFileListFrame::ResizeOperationColumnToContents()
+{
+ m_pFileBrowser->ResizeColumnToContents( 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the column header for the 'operation' column
+//-----------------------------------------------------------------------------
+void COperationFileListFrame::SetOperationColumnHeaderText( const char *pText )
+{
+ m_pFileBrowser->SetColumnHeaderText( 0, pText );
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds the strings to the list panel
+//-----------------------------------------------------------------------------
+void COperationFileListFrame::DoModal( KeyValues *pContextKeyValues, const char *pMessage )
+{
+ m_MessageName = pMessage ? pMessage : "OperationConfirmed";
+ CleanUpMessage();
+ m_pContextKeyValues = pContextKeyValues;
+ m_pFileBrowser->SortList();
+ if ( m_pNoButton->IsVisible() )
+ {
+ m_pYesButton->SetEnabled( m_pFileBrowser->GetItemCount() != 0 );
+ }
+ BaseClass::DoModal();
+}
+
+
+//-----------------------------------------------------------------------------
+// Retrieves the number of files, the file names, and operations
+//-----------------------------------------------------------------------------
+int COperationFileListFrame::GetOperationCount()
+{
+ return m_pFileBrowser->GetItemCount();
+}
+
+const char *COperationFileListFrame::GetFileName( int i )
+{
+ int nItemId = m_pFileBrowser->GetItemIDFromRow( i );
+ KeyValues *pKeyValues = m_pFileBrowser->GetItem( nItemId );
+ return pKeyValues->GetString( "filename" );
+}
+
+const char *COperationFileListFrame::GetOperation( int i )
+{
+ int nItemId = m_pFileBrowser->GetItemIDFromRow( i );
+ KeyValues *pKeyValues = m_pFileBrowser->GetItem( nItemId );
+ return pKeyValues->GetString( "operation" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Retreives the description (only if it was shown)
+//-----------------------------------------------------------------------------
+const char *COperationFileListFrame::GetDescription()
+{
+ return m_pText;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the message name
+//-----------------------------------------------------------------------------
+const char *COperationFileListFrame::CompletionMessage()
+{
+ return m_MessageName;
+}
+
+
+//-----------------------------------------------------------------------------
+// On command
+//-----------------------------------------------------------------------------
+void COperationFileListFrame::OnCommand( const char *pCommand )
+{
+ if ( !Q_stricmp( pCommand, "Yes" ) )
+ {
+ if ( m_pDescription )
+ {
+ int nLen = m_pDescription->GetTextLength() + 1;
+ m_pText = new char[ nLen ];
+ m_pDescription->GetText( m_pText, nLen );
+ }
+
+ KeyValues *pActionKeys;
+ if ( PerformOperation() )
+ {
+ pActionKeys = new KeyValues( CompletionMessage(), "operationPerformed", 1 );
+ }
+ else
+ {
+ pActionKeys = new KeyValues( CompletionMessage(), "operationPerformed", 0 );
+ }
+
+ if ( m_pContextKeyValues )
+ {
+ pActionKeys->AddSubKey( m_pContextKeyValues );
+ m_pContextKeyValues = NULL;
+ }
+ CloseModal();
+ PostActionSignal( pActionKeys );
+ return;
+ }
+
+ if ( !Q_stricmp( pCommand, "No" ) )
+ {
+ KeyValues *pActionKeys = new KeyValues( CompletionMessage(), "operationPerformed", 0 );
+ if ( m_pContextKeyValues )
+ {
+ pActionKeys->AddSubKey( m_pContextKeyValues );
+ m_pContextKeyValues = NULL;
+ }
+ CloseModal();
+ PostActionSignal( pActionKeys );
+ return;
+ }
+
+ BaseClass::OnCommand( pCommand );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Version that does the work of perforce actions
+//
+//-----------------------------------------------------------------------------
+CPerforceFileListFrame::CPerforceFileListFrame( vgui::Panel *pParent, const char *pTitle, const char *pColumnHeader, PerforceAction_t action ) :
+ BaseClass( pParent, pTitle, pColumnHeader, (action == PERFORCE_ACTION_FILE_SUBMIT), false, OPERATION_DIALOG_ID_PERFORCE )
+{
+ m_Action = action;
+}
+
+CPerforceFileListFrame::~CPerforceFileListFrame()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Activates the modal dialog
+//-----------------------------------------------------------------------------
+void CPerforceFileListFrame::DoModal( KeyValues *pContextKeys, const char *pMessage )
+{
+ BaseClass::DoModal( pContextKeys, pMessage ? pMessage : "PerforceActionConfirmed" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a file for open
+//-----------------------------------------------------------------------------
+void CPerforceFileListFrame::AddFileForOpen( const char *pFullPath )
+{
+ bool bIsInPerforce = p4->IsFileInPerforce( pFullPath );
+ bool bIsOpened = ( p4->GetFileState( pFullPath ) != P4FILE_UNOPENED );
+ switch( m_Action )
+ {
+ case PERFORCE_ACTION_FILE_ADD:
+ if ( !bIsInPerforce && !bIsOpened )
+ {
+ AddOperation( "Add", pFullPath );
+ }
+ break;
+
+ case PERFORCE_ACTION_FILE_EDIT:
+ if ( bIsInPerforce && !bIsOpened )
+ {
+ AddOperation( "Edit", pFullPath );
+ }
+ break;
+
+ case PERFORCE_ACTION_FILE_DELETE:
+ if ( bIsInPerforce && !bIsOpened )
+ {
+ AddOperation( "Delete", pFullPath );
+ }
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Add files to dialog for submit/revert dialogs
+//-----------------------------------------------------------------------------
+void CPerforceFileListFrame::AddFileForSubmit( const char *pFullPath, P4FileState_t state )
+{
+ if ( state == P4FILE_UNOPENED )
+ return;
+
+ char pBuf[128];
+ const char *pPrefix = (m_Action == PERFORCE_ACTION_FILE_REVERT) ? "Revert" : "Submit";
+ switch( state )
+ {
+ case P4FILE_OPENED_FOR_ADD:
+ Q_snprintf( pBuf, sizeof(pBuf), "%s Add", pPrefix );
+ AddOperation( pBuf, pFullPath );
+ break;
+
+ case P4FILE_OPENED_FOR_EDIT:
+ Q_snprintf( pBuf, sizeof(pBuf), "%s Edit", pPrefix );
+ AddOperation( pBuf, pFullPath );
+ break;
+
+ case P4FILE_OPENED_FOR_DELETE:
+ Q_snprintf( pBuf, sizeof(pBuf), "%s Delete", pPrefix );
+ AddOperation( pBuf, pFullPath );
+ break;
+
+ case P4FILE_OPENED_FOR_INTEGRATE:
+ Q_snprintf( pBuf, sizeof(pBuf), "%s Integrate", pPrefix );
+ AddOperation( pBuf, pFullPath );
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Version of AddFile that accepts full paths
+//-----------------------------------------------------------------------------
+void CPerforceFileListFrame::AddFile( const char *pFullPath )
+{
+ if ( m_Action < PERFORCE_ACTION_FILE_REVERT )
+ {
+ // If the file wasn't found on the disk, then abort
+ if ( g_pFullFileSystem->FileExists( pFullPath, NULL ) )
+ {
+ AddFileForOpen( pFullPath );
+ }
+ return;
+ }
+
+ // Deal with submit, revert
+ bool bFileExists = g_pFullFileSystem->FileExists( pFullPath, NULL );
+ P4FileState_t state = p4->GetFileState( pFullPath );
+ if ( bFileExists || (state == P4FILE_OPENED_FOR_DELETE) )
+ {
+ AddFileForSubmit( pFullPath, state );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Version of AddFile that accepts relative paths + search path ids
+//-----------------------------------------------------------------------------
+void CPerforceFileListFrame::AddFile( const char *pRelativePath, const char *pPathId )
+{
+ // Deal with add, open, edit
+ if ( m_Action < PERFORCE_ACTION_FILE_REVERT )
+ {
+ // If the file wasn't found on the disk, then abort
+ if ( g_pFullFileSystem->FileExists( pRelativePath, pPathId ) )
+ {
+ char pFullPath[MAX_PATH];
+ g_pFullFileSystem->RelativePathToFullPath( pRelativePath, pPathId, pFullPath, sizeof( pFullPath ) );
+ AddFileForOpen( pFullPath );
+ }
+ return;
+ }
+
+ // Deal with submit, revert
+
+ // First, handle the case where the file exists on the drive
+ char pFullPath[MAX_PATH];
+ if ( g_pFullFileSystem->FileExists( pRelativePath, pPathId ) )
+ {
+ g_pFullFileSystem->RelativePathToFullPath( pRelativePath, pPathId, pFullPath, sizeof( pFullPath ) );
+ P4FileState_t state = p4->GetFileState( pFullPath );
+ AddFileForSubmit( pFullPath, state );
+ return;
+ }
+
+ // Get the list of opened files, cache it off so we aren't continually reasking
+ if ( Q_stricmp( pPathId, m_LastOpenedFilePathId ) )
+ {
+ p4->GetOpenedFileListInPath( pPathId, m_OpenedFiles );
+ m_LastOpenedFilePathId = pPathId;
+ }
+
+ // If the file doesn't exist, it was opened for delete.
+ // Using the client spec of the path, we need to piece together
+ // the full path; the full path unfortunately is usually ambiguous:
+ // you can never exactly know which mod it came from.
+ char pTemp[MAX_PATH];
+ char pSearchString[MAX_PATH];
+ Q_strncpy( pSearchString, pRelativePath, sizeof(pSearchString) );
+ Q_FixSlashes( pSearchString );
+
+ int k;
+ int nOpenedFileCount = m_OpenedFiles.Count();
+ for ( k = 0; k < nOpenedFileCount; ++k )
+ {
+ if ( m_OpenedFiles[k].m_eOpenState != P4FILE_OPENED_FOR_DELETE )
+ continue;
+
+ // Check to see if the end of the local file matches the file
+ const char *pLocalFile = p4->String( m_OpenedFiles[k].m_sLocalFile );
+
+ // This ensures the full path lies under the search path
+ if ( !g_pFullFileSystem->FullPathToRelativePathEx( pLocalFile, pPathId, pTemp, sizeof(pTemp) ) )
+ continue;
+
+ // The relative paths had better be the same
+ if ( Q_stricmp( pTemp, pSearchString ) )
+ continue;
+
+ AddFileForSubmit( pLocalFile, m_OpenedFiles[k].m_eOpenState );
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Does the perforce operation
+//-----------------------------------------------------------------------------
+bool CPerforceFileListFrame::PerformOperation( )
+{
+ int nFileCount = GetOperationCount();
+ const char **ppFileNames = (const char**)_alloca( nFileCount * sizeof(char*) );
+ for ( int i = 0; i < nFileCount; ++i )
+ {
+ ppFileNames[i] = GetFileName( i );
+ }
+
+ bool bSuccess = false;
+
+ switch ( m_Action )
+ {
+ case PERFORCE_ACTION_FILE_ADD:
+ bSuccess = p4->OpenFilesForAdd( nFileCount, ppFileNames );
+ break;
+
+ case PERFORCE_ACTION_FILE_EDIT:
+ bSuccess = p4->OpenFilesForEdit( nFileCount, ppFileNames );
+ break;
+
+ case PERFORCE_ACTION_FILE_DELETE:
+ bSuccess = p4->OpenFilesForDelete( nFileCount, ppFileNames );
+ break;
+
+ case PERFORCE_ACTION_FILE_REVERT:
+ bSuccess = p4->RevertFiles( nFileCount, ppFileNames );
+ break;
+
+ case PERFORCE_ACTION_FILE_SUBMIT:
+ {
+ // Ensure a description was added
+ const char *pDescription = GetDescription();
+ if ( !pDescription[0] || !Q_stricmp( pDescription, "<enter description here>" ) )
+ {
+ vgui::MessageBox *pError = new vgui::MessageBox( "Submission Error!", "Description required for submission.", GetParent() );
+ pError->SetSmallCaption( true );
+ pError->DoModal();
+ return false;
+ }
+ else
+ {
+ bSuccess = p4->SubmitFiles( nFileCount, ppFileNames, pDescription );
+ }
+ }
+ break;
+ }
+
+ const char *pErrorString = p4->GetLastError();
+ if ( !bSuccess )
+ {
+ vgui::MessageBox *pError = new vgui::MessageBox( "Perforce Error!", pErrorString, GetParent() );
+ pError->SetSmallCaption( true );
+ pError->DoModal();
+ }
+#if 0
+ if ( *pErrorString )
+ {
+ if ( V_strstr( pErrorString, "opened for add" ) )
+ return bSuccess;
+ if ( V_strstr( pErrorString, "opened for edit" ) )
+ return bSuccess;
+ // TODO - figure out the rest of these...
+
+ const char *pPrefix = "Perforce has generated the following message which may or may not be an error.\n"
+ "Please email joe with the text of the message, whether you think it was an error, and what perforce operation you where performing.\n"
+ "To copy the message, hit ~ to enter the console, where you will find the message reprinted.\n"
+ "Select the lines of text in the message, right click, select Copy, and then paste into an email message.\n\n";
+ static int nPrefixLen = V_strlen( pPrefix );
+ int nErrorStringLength = V_strlen( pErrorString );
+ char *pMsg = (char*)_alloca( nPrefixLen + nErrorStringLength + 1 );
+ V_strcpy( pMsg, pPrefix );
+ V_strcpy( pMsg + nPrefixLen, pErrorString );
+
+ vgui::MessageBox *pError = new vgui::MessageBox( "Dubious Perforce Message", pMsg, GetParent() );
+ pError->SetSmallCaption( true );
+ pError->DoModal();
+ }
+#endif
+ return bSuccess;
+}
+
+
+//-----------------------------------------------------------------------------
+// Show the perforce query dialog
+//-----------------------------------------------------------------------------
+void ShowPerforceQuery( vgui::Panel *pParent, const char *pFileName, vgui::Panel *pActionSignalTarget, KeyValues *pKeyValues, PerforceAction_t actionFilter )
+{
+ // Refresh the current perforce settings
+ p4->RefreshActiveClient();
+
+ PerforceAction_t action = PERFORCE_ACTION_NONE;
+ const char *pTitle = NULL;
+ if ( !p4->IsFileInPerforce( pFileName ) )
+ {
+ // If the file isn't in perforce, ask to add it
+ action = PERFORCE_ACTION_FILE_ADD;
+ pTitle = "Add File to Perforce?";
+ }
+ else if ( p4->GetFileState( pFileName ) == P4FILE_UNOPENED )
+ {
+ // If the file isn't checked out yet, ask to check it out
+ action = PERFORCE_ACTION_FILE_EDIT;
+ pTitle = "Check Out File from Perforce?";
+ }
+
+ if ( ( action == PERFORCE_ACTION_NONE ) || ( ( actionFilter != PERFORCE_ACTION_NONE ) && ( actionFilter != action ) ) )
+ {
+ // Spoof a completion event
+ KeyValues *pSpoofKeys = new KeyValues( "PerforceQueryCompleted", "operationPerformed", 1 );
+ if ( pKeyValues )
+ {
+ pSpoofKeys->AddSubKey( pKeyValues );
+ }
+ vgui::ivgui()->PostMessage( pActionSignalTarget->GetVPanel(), pSpoofKeys, 0 );
+ return;
+ }
+
+ CPerforceFileListFrame *pQuery = new CPerforceFileListFrame( pParent, pTitle, "File", action );
+ pQuery->AddFile( pFileName );
+ if ( pActionSignalTarget )
+ {
+ pQuery->AddActionSignalTarget( pActionSignalTarget );
+ }
+ pQuery->DoModal( pKeyValues, "PerforceQueryCompleted" );
+}
diff --git a/mp/src/vgui2/vgui_controls/savedocumentquery.cpp b/mp/src/vgui2/vgui_controls/savedocumentquery.cpp
new file mode 100644
index 00000000..4efef551
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/savedocumentquery.cpp
@@ -0,0 +1,195 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Core Movie Maker UI API
+//
+//=============================================================================
+
+#include "vgui_controls/savedocumentquery.h"
+#include "vgui_controls/Button.h"
+#include "vgui_controls/Label.h"
+#include "vgui_controls/Frame.h"
+#include "vgui/ISurface.h"
+#include "vgui/IVGui.h"
+#include "tier1/KeyValues.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// This dialog asks if you want to save your work
+//-----------------------------------------------------------------------------
+class CSaveDocumentQuery : public vgui::Frame
+{
+ DECLARE_CLASS_SIMPLE( CSaveDocumentQuery, vgui::Frame );
+
+public:
+ CSaveDocumentQuery( vgui::Panel *pParent, const char *filename, const char *pFileType, int nContext,
+ vgui::Panel *pActionSignalTarget = 0, KeyValues *pKeyValues = 0 );
+ ~CSaveDocumentQuery();
+
+ // Inherited from vgui::Frame
+ virtual void OnCommand( char const *cmd );
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ // Put the message box into a modal state
+ void DoModal();
+
+private:
+ // Posts commands to the action signal target
+ void PostCommand( const char *pCommand );
+
+ vgui::Label *m_pMessageLabel;
+ vgui::Button *m_pYesButton;
+ vgui::Button *m_pNoButton;
+ vgui::Button *m_pCancelButton;
+ vgui::Panel *m_pActionSignalTarget;
+
+ char m_szFileName[ 256 ];
+ char m_szFileType[ 256 ];
+ int m_nContext;
+ KeyValues* m_pPostSaveKeyValues;
+};
+
+
+//-----------------------------------------------------------------------------
+// Show the save document query dialog
+//-----------------------------------------------------------------------------
+void ShowSaveDocumentQuery( vgui::Panel *pParent, const char *pFileName, const char *pFileType, int nContext, vgui::Panel *pActionSignalTarget, KeyValues *pPostSaveCommand )
+{
+ CSaveDocumentQuery *query = new CSaveDocumentQuery( pParent, pFileName, pFileType, nContext, pActionSignalTarget, pPostSaveCommand );
+ query->SetSmallCaption( true );
+ query->DoModal();
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CSaveDocumentQuery::CSaveDocumentQuery( vgui::Panel *pParent, char const *pFileName, const char *pFileType, int nContext, vgui::Panel *pActionSignalTarget, KeyValues *pPostSaveCommand ) :
+ BaseClass( pParent, "SaveDocumentQuery" ),
+ m_nContext( nContext ),
+ m_pActionSignalTarget( pActionSignalTarget )
+{
+ if ( !pFileName || !pFileName[0] )
+ {
+ pFileName = "<untitled>";
+ }
+ Q_strncpy( m_szFileName, pFileName, sizeof( m_szFileName ) );
+ Q_strncpy( m_szFileType, pFileType, sizeof( m_szFileType ) );
+ m_pPostSaveKeyValues = pPostSaveCommand;
+
+ SetDeleteSelfOnClose(true);
+
+ SetMenuButtonResponsive(false);
+ SetMinimizeButtonVisible(false);
+ SetCloseButtonVisible(false);
+ SetSizeable(false);
+
+ SetTitle( "Save Changes", true );
+
+ m_pMessageLabel = new Label( this, "FileNameLabel", "" );
+
+ m_pYesButton = new Button( this, "Yes", "Yes", this, "yes" );
+ m_pNoButton = new Button( this, "No", "No", this, "no" );
+ m_pCancelButton = new Button( this, "Cancel", "Cancel", this, "cancel" );
+
+ LoadControlSettings( "resource/ToolSaveDocumentQuery.res" );
+
+ m_pMessageLabel->SetText( m_szFileName );
+}
+
+CSaveDocumentQuery::~CSaveDocumentQuery()
+{
+ if ( m_pPostSaveKeyValues )
+ {
+ m_pPostSaveKeyValues->deleteThis();
+ m_pPostSaveKeyValues = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Posts commands to the action signal target
+//-----------------------------------------------------------------------------
+void CSaveDocumentQuery::PostCommand( const char *pCommand )
+{
+ KeyValues *kv = new KeyValues( pCommand );
+ vgui::ivgui()->PostMessage( m_pActionSignalTarget->GetVPanel(), kv, 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Process commands
+//-----------------------------------------------------------------------------
+void CSaveDocumentQuery::OnCommand( char const *cmd )
+{
+ if ( !Q_stricmp( cmd, "yes" ) )
+ {
+ KeyValues *kv = new KeyValues( "OnSaveFile" );
+ kv->SetString( "filename", m_szFileName );
+ kv->SetString( "filetype", m_szFileType );
+ kv->SetInt( "context", m_nContext );
+ kv->SetPtr( "actionTarget", m_pActionSignalTarget );
+ if ( m_pPostSaveKeyValues )
+ {
+ kv->AddSubKey( m_pPostSaveKeyValues->MakeCopy() );
+ }
+ vgui::ivgui()->PostMessage( m_pActionSignalTarget->GetVPanel(), kv, 0 );
+ MarkForDeletion();
+ }
+ else if ( !Q_stricmp( cmd, "no" ) )
+ {
+ PostCommand( "OnMarkNotDirty" );
+ if ( m_pPostSaveKeyValues )
+ {
+ vgui::ivgui()->PostMessage( m_pActionSignalTarget->GetVPanel(), m_pPostSaveKeyValues->MakeCopy(), 0 );
+ }
+ MarkForDeletion();
+ }
+ else if ( !Q_stricmp( cmd, "cancel" ) )
+ {
+ PostCommand( "OnCancelSaveDocument" );
+ MarkForDeletion();
+ }
+ else
+ {
+ BaseClass::OnCommand( cmd );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Deal with scheme
+//-----------------------------------------------------------------------------
+void CSaveDocumentQuery::ApplySchemeSettings(IScheme *pScheme)
+{
+ BaseClass::ApplySchemeSettings(pScheme);
+
+ int wide, tall;
+ GetSize( wide, tall );
+
+ int swide, stall;
+ surface()->GetScreenSize(swide, stall);
+
+ // put the dialog in the middle of the screen
+ SetPos((swide - wide) / 2, (stall - tall) / 2);
+}
+
+
+//-----------------------------------------------------------------------------
+// Put the message box into a modal state
+//-----------------------------------------------------------------------------
+void CSaveDocumentQuery::DoModal()
+{
+ SetVisible( true );
+ SetEnabled( true );
+ MoveToFront();
+
+ RequestFocus();
+
+ InvalidateLayout();
+}
diff --git a/mp/src/vgui2/vgui_controls/subrectimage.cpp b/mp/src/vgui2/vgui_controls/subrectimage.cpp
new file mode 100644
index 00000000..e7405049
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/subrectimage.cpp
@@ -0,0 +1,212 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "vgui_controls/subrectimage.h"
+#include "tier0/dbg.h"
+#include "vgui/ISurface.h"
+#include "vgui_controls/Controls.h"
+
+// NOTE: This has to be the last file included!
+#include "tier0/memdbgon.h"
+
+
+using namespace vgui;
+
+// Officially the invalid texture ID is zero, but -1 is used in many
+// places, and changing it carries some risk. Adding a named constant
+// for this file avoids warnings and makes future changes easier.
+const HTexture SUBRECT_INVALID_TEXTURE = (HTexture)-1;
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CSubRectImage::CSubRectImage( const char *filename, bool hardwareFiltered, int subx, int suby, int subw, int subh )
+{
+ SetSize( subw, subh );
+ sub[ 0 ] = subx;
+ sub[ 1 ] = suby;
+ sub[ 2 ] = subw;
+ sub[ 3 ] = subh;
+
+ _filtered = hardwareFiltered;
+ // HACKHACK - force VGUI materials to be in the vgui/ directory
+ // This needs to be revisited once GoldSRC is grandfathered off.
+ //!! need to make this work with goldsrc
+ int size = strlen(filename) + 1 + strlen("vgui/");
+ _filename = (char *)malloc( size );
+ Assert( _filename );
+
+ Q_snprintf( _filename, size, "vgui/%s", filename );
+
+ _id = SUBRECT_INVALID_TEXTURE;
+ _uploaded = false;
+ _color = Color(255, 255, 255, 255);
+ _pos[0] = _pos[1] = 0;
+ _valid = true;
+ _wide = subw;
+ _tall = subh;
+ ForceUpload();
+}
+
+CSubRectImage::~CSubRectImage()
+{
+ if ( vgui::surface() && _id != SUBRECT_INVALID_TEXTURE )
+ {
+ vgui::surface()->DestroyTextureID( _id );
+ _id = SUBRECT_INVALID_TEXTURE;
+ }
+
+ if ( _filename )
+ {
+ free( _filename );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void CSubRectImage::GetSize(int &wide, int &tall)
+{
+ wide = _wide;
+ tall = _tall;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: size of the bitmap
+//-----------------------------------------------------------------------------
+void CSubRectImage::GetContentSize(int &wide, int &tall)
+{
+ wide = 0;
+ tall = 0;
+
+ if (!_valid)
+ return;
+
+ if ( _id != SUBRECT_INVALID_TEXTURE )
+ {
+ surface()->DrawGetTextureSize(_id, wide, tall);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: ignored
+//-----------------------------------------------------------------------------
+void CSubRectImage::SetSize(int x, int y)
+{
+ _wide = x;
+ _tall = y;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void CSubRectImage::SetPos(int x, int y)
+{
+ _pos[0] = x;
+ _pos[1] = y;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+void CSubRectImage::SetColor(Color col)
+{
+ _color = col;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the file name of the bitmap
+//-----------------------------------------------------------------------------
+const char *CSubRectImage::GetName()
+{
+ return _filename;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Renders the loaded image, uploading it if necessary
+// Assumes a valid image is always returned from uploading
+//-----------------------------------------------------------------------------
+void CSubRectImage::Paint()
+{
+ if ( !_valid )
+ return;
+
+ // if we don't have an _id then lets make one
+ if ( _id == SUBRECT_INVALID_TEXTURE )
+ {
+ _id = surface()->CreateNewTextureID();
+ }
+
+ // if we have not uploaded yet, lets go ahead and do so
+ if ( !_uploaded )
+ {
+ ForceUpload();
+ }
+
+ // set the texture current, set the color, and draw the biatch
+ surface()->DrawSetColor( _color[0], _color[1], _color[2], _color[3] );
+ surface()->DrawSetTexture( _id );
+
+ if ( _wide == 0 || _tall == 0 )
+ return;
+
+ int cw, ch;
+ GetContentSize( cw, ch );
+ if ( cw == 0 || ch == 0 )
+ return;
+
+ float s[ 2 ];
+ float t[ 2 ];
+
+ s[ 0 ] = (float)sub[ 0 ] / (float)cw;
+ s[ 1 ] = (float)(sub[ 0 ]+sub[ 2 ]) / (float)cw;
+ t[ 0 ] = (float)sub[ 1 ] / (float)ch;
+ t[ 1 ] = (float)(sub[ 1 ]+sub[ 3 ]) / (float)ch;
+ surface()->DrawTexturedSubRect(
+ _pos[0],
+ _pos[1],
+ _pos[0] + _wide,
+ _pos[1] + _tall,
+ s[ 0 ],
+ t[ 0 ],
+ s[ 1 ],
+ t[ 1 ] );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: ensures the bitmap has been uploaded
+//-----------------------------------------------------------------------------
+void CSubRectImage::ForceUpload()
+{
+ if ( !_valid || _uploaded )
+ return;
+
+ if ( _id == SUBRECT_INVALID_TEXTURE )
+ {
+ _id = surface()->CreateNewTextureID( false );
+ }
+
+ surface()->DrawSetTextureFile( _id, _filename, _filtered, false );
+
+ _uploaded = true;
+
+ _valid = surface()->IsTextureIDValid( _id );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: data accessor
+//-----------------------------------------------------------------------------
+HTexture CSubRectImage::GetID()
+{
+ return _id;
+}
+
+bool CSubRectImage::IsValid()
+{
+ return _valid;
+}
+
diff --git a/mp/src/vgui2/vgui_controls/vgui_controls-2010.vcxproj b/mp/src/vgui2/vgui_controls/vgui_controls-2010.vcxproj
new file mode 100644
index 00000000..8ae3a595
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/vgui_controls-2010.vcxproj
@@ -0,0 +1,362 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>vgui_controls</ProjectName>
+ <ProjectGuid>{F69B3672-C5E8-CD1A-257F-253A25B5B939}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vgui_controls</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vgui_controls</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\lib\public\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\lib\public\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;generated_proto;..\..\thirdparty\protobuf-2.3.0\src;..\..\thirdparty;..\..\thirdparty\cef;generated_proto</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;COMPILER_MSVC32;LIBNAME=vgui_controls;RAD_TELEMETRY_DISABLED;BINK_VIDEO;AVI_VIDEO;WMV_VIDEO;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\vgui2\vgui_controls;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <GenerateXMLDocumentationFiles>false</GenerateXMLDocumentationFiles>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Lib>
+ <UseUnicodeResponseFiles>false</UseUnicodeResponseFiles>
+ <OutputFile>..\..\lib\public\vgui_controls.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ <AdditionalOptions> /ignore:4221</AdditionalOptions>
+ </Lib>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vgui_controls.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;generated_proto;..\..\thirdparty\protobuf-2.3.0\src;..\..\thirdparty;..\..\thirdparty\cef;generated_proto</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;COMPILER_MSVC32;LIBNAME=vgui_controls;RAD_TELEMETRY_DISABLED;BINK_VIDEO;AVI_VIDEO;WMV_VIDEO;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\vgui2\vgui_controls;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <GenerateXMLDocumentationFiles>false</GenerateXMLDocumentationFiles>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Lib>
+ <UseUnicodeResponseFiles>false</UseUnicodeResponseFiles>
+ <OutputFile>..\..\lib\public\vgui_controls.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ <AdditionalOptions> /ignore:4221</AdditionalOptions>
+ </Lib>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vgui_controls.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="generated_proto\htmlmessages.pb.h" />
+ <ClInclude Include="..\..\public\vgui_controls\AnalogBar.h" />
+ <ClInclude Include="..\..\public\vgui_controls\AnimatingImagePanel.h" />
+ <ClInclude Include="..\..\public\vgui_controls\AnimationController.h" />
+ <ClInclude Include="..\..\public\vgui_controls\BitmapImagePanel.h" />
+ <ClInclude Include="..\..\public\vgui_controls\BuildGroup.h" />
+ <ClInclude Include="..\..\public\vgui_controls\BuildModeDialog.h" />
+ <ClInclude Include="..\..\public\vgui_controls\Button.h" />
+ <ClInclude Include="..\..\public\vgui_controls\CheckButton.h" />
+ <ClInclude Include="..\..\public\vgui_controls\CheckButtonList.h" />
+ <ClInclude Include="..\..\public\vgui_controls\CircularProgressBar.h" />
+ <ClInclude Include="..\..\public\Color.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ComboBox.h" />
+ <ClInclude Include="..\..\public\vgui_controls\consoledialog.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ControllerMap.h" />
+ <ClInclude Include="..\..\public\vgui_controls\Controls.h" />
+ <ClInclude Include="..\..\public\vgui_controls\CvarToggleCheckButton.h" />
+ <ClInclude Include="..\..\public\vgui_controls\DialogManager.h" />
+ <ClInclude Include="..\..\public\vgui_controls\DirectorySelectDialog.h" />
+ <ClInclude Include="..\..\public\vgui_controls\Divider.h" />
+ <ClInclude Include="..\..\public\vgui_controls\EditablePanel.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ExpandButton.h" />
+ <ClInclude Include="..\..\public\vgui_controls\FileOpenDialog.h" />
+ <ClInclude Include="..\..\public\vgui_controls\FileOpenStateMachine.h" />
+ <ClInclude Include="..\..\public\filesystem.h" />
+ <ClInclude Include="..\..\public\filesystem_helpers.h" />
+ <ClInclude Include="..\..\public\vgui_controls\FocusNavGroup.h" />
+ <ClInclude Include="..\..\public\vgui_controls\Frame.h" />
+ <ClInclude Include="..\..\public\vgui_controls\GraphPanel.h" />
+ <ClInclude Include="..\..\public\vgui_controls\HTML.h" />
+ <ClInclude Include="..\..\public\vgui_controls\Image.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ImageList.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ImagePanel.h" />
+ <ClInclude Include="..\..\public\vgui_controls\InputDialog.h" />
+ <ClInclude Include="..\..\public\tier1\interface.h" />
+ <ClInclude Include="..\..\public\vgui_controls\KeyBindingHelpDialog.h" />
+ <ClInclude Include="..\..\public\vgui_controls\KeyBindingMap.h" />
+ <ClInclude Include="..\..\public\vgui_controls\KeyBoardEditorDialog.h" />
+ <ClInclude Include="..\..\public\vgui_controls\KeyRepeat.h" />
+ <ClInclude Include="..\..\public\tier1\KeyValues.h" />
+ <ClInclude Include="..\..\public\vgui_controls\Label.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ListPanel.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ListViewPanel.h" />
+ <ClInclude Include="..\..\public\tier0\memdbgoff.h" />
+ <ClInclude Include="..\..\public\tier0\memdbgon.h" />
+ <ClInclude Include="..\..\public\tier1\mempool.h" />
+ <ClInclude Include="..\..\public\vgui_controls\Menu.h" />
+ <ClInclude Include="..\..\public\vgui_controls\MenuBar.h" />
+ <ClInclude Include="..\..\public\vgui_controls\MenuButton.h" />
+ <ClInclude Include="..\..\public\vgui_controls\MenuItem.h" />
+ <ClInclude Include="..\..\public\vgui_controls\MessageBox.h" />
+ <ClInclude Include="..\..\public\vgui_controls\MessageDialog.h" />
+ <ClInclude Include="..\..\public\vgui_controls\MessageMap.h" />
+ <ClInclude Include="..\..\public\vgui_controls\Panel.h" />
+ <ClInclude Include="..\..\public\vgui_controls\PanelAnimationVar.h" />
+ <ClInclude Include="..\..\public\vgui_controls\PanelListPanel.h" />
+ <ClInclude Include="..\..\public\vgui_controls\PerforceFileExplorer.h" />
+ <ClInclude Include="..\..\public\vgui_controls\PerforceFileList.h" />
+ <ClInclude Include="..\..\public\vgui_controls\perforcefilelistframe.h" />
+ <ClInclude Include="..\..\public\vgui_controls\PHandle.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ProgressBar.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ProgressBox.h" />
+ <ClInclude Include="..\..\public\vgui_controls\PropertyDialog.h" />
+ <ClInclude Include="..\..\public\vgui_controls\PropertyPage.h" />
+ <ClInclude Include="..\..\public\vgui_controls\PropertySheet.h" />
+ <ClInclude Include="..\..\public\vgui_controls\QueryBox.h" />
+ <ClInclude Include="..\..\public\vgui_controls\RadioButton.h" />
+ <ClInclude Include="..\..\public\vgui_controls\RichText.h" />
+ <ClInclude Include="..\..\public\vgui_controls\RotatingProgressBar.h" />
+ <ClInclude Include="..\..\public\vgui_controls\savedocumentquery.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ScalableImagePanel.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ScrollableEditablePanel.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ScrollBar.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ScrollBarSlider.h" />
+ <ClInclude Include="..\..\public\vgui_controls\SectionedListPanel.h" />
+ <ClInclude Include="..\..\public\vgui_controls\Slider.h" />
+ <ClInclude Include="..\..\public\vgui_controls\Splitter.h" />
+ <ClInclude Include="..\..\public\vgui_controls\subrectimage.h" />
+ <ClInclude Include="..\..\public\vgui_controls\TextEntry.h" />
+ <ClInclude Include="..\..\public\vgui_controls\TextImage.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ToggleButton.h" />
+ <ClInclude Include="..\..\public\vgui_controls\Tooltip.h" />
+ <ClInclude Include="..\..\public\vgui_controls\ToolWindow.h" />
+ <ClInclude Include="..\..\public\vgui_controls\TreeView.h" />
+ <ClInclude Include="..\..\public\vgui_controls\TreeViewListControl.h" />
+ <ClInclude Include="..\..\public\vgui_controls\URLLabel.h" />
+ <ClInclude Include="..\..\public\tier1\utlmemory.h" />
+ <ClInclude Include="..\..\public\tier1\utlrbtree.h" />
+ <ClInclude Include="..\..\public\tier1\utlvector.h" />
+ <ClInclude Include="..\..\public\vgui_controls\WizardPanel.h" />
+ <ClInclude Include="..\..\public\vgui_controls\WizardSubPanel.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="generated_proto\htmlmessages.pb.cc">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="AnalogBar.cpp" />
+ <ClCompile Include="AnimatingImagePanel.cpp" />
+ <ClCompile Include="AnimationController.cpp" />
+ <ClCompile Include="BitmapImagePanel.cpp" />
+ <ClCompile Include="BuildFactoryHelper.cpp" />
+ <ClCompile Include="BuildGroup.cpp" />
+ <ClCompile Include="BuildModeDialog.cpp" />
+ <ClCompile Include="Button.cpp" />
+ <ClCompile Include="CheckButton.cpp" />
+ <ClCompile Include="CheckButtonList.cpp" />
+ <ClCompile Include="CircularProgressBar.cpp" />
+ <ClCompile Include="ComboBox.cpp" />
+ <ClCompile Include="consoledialog.cpp" />
+ <ClCompile Include="ControllerMap.cpp" />
+ <ClCompile Include="controls.cpp" />
+ <ClCompile Include="CvarToggleCheckButton.cpp" />
+ <ClCompile Include="DirectorySelectDialog.cpp" />
+ <ClCompile Include="Divider.cpp" />
+ <ClCompile Include="EditablePanel.cpp" />
+ <ClCompile Include="ExpandButton.cpp" />
+ <ClCompile Include="FileOpenDialog.cpp" />
+ <ClCompile Include="FileOpenStateMachine.cpp" />
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp" />
+ <ClCompile Include="FocusNavGroup.cpp" />
+ <ClCompile Include="Frame.cpp" />
+ <ClCompile Include="GraphPanel.cpp" />
+ <ClCompile Include="HTML.cpp" />
+ <ClCompile Include="..\..\public\html\htmlprotobuf.cpp" />
+ <ClCompile Include="Image.cpp" />
+ <ClCompile Include="ImageList.cpp" />
+ <ClCompile Include="ImagePanel.cpp" />
+ <ClCompile Include="InputDialog.cpp" />
+ <ClCompile Include="KeyBindingHelpDialog.cpp" />
+ <ClCompile Include="KeyBoardEditorDialog.cpp" />
+ <ClCompile Include="KeyRepeat.cpp" />
+ <ClCompile Include="Label.cpp" />
+ <ClCompile Include="ListPanel.cpp" />
+ <ClCompile Include="ListViewPanel.cpp" />
+ <ClCompile Include="Menu.cpp" />
+ <ClCompile Include="MenuBar.cpp" />
+ <ClCompile Include="MenuButton.cpp" />
+ <ClCompile Include="MenuItem.cpp" />
+ <ClCompile Include="MessageBox.cpp" />
+ <ClCompile Include="MessageDialog.cpp" />
+ <ClCompile Include="Panel.cpp" />
+ <ClCompile Include="PanelListPanel.cpp" />
+ <ClCompile Include="PerforceFileExplorer.cpp" />
+ <ClCompile Include="PerforceFileList.cpp" />
+ <ClCompile Include="perforcefilelistframe.cpp" />
+ <ClCompile Include="ProgressBar.cpp" />
+ <ClCompile Include="ProgressBox.cpp" />
+ <ClCompile Include="PropertyDialog.cpp" />
+ <ClCompile Include="PropertyPage.cpp" />
+ <ClCompile Include="PropertySheet.cpp" />
+ <ClCompile Include="QueryBox.cpp" />
+ <ClCompile Include="RadioButton.cpp" />
+ <ClCompile Include="RichText.cpp" />
+ <ClCompile Include="RotatingProgressBar.cpp" />
+ <ClCompile Include="savedocumentquery.cpp" />
+ <ClCompile Include="ScalableImagePanel.cpp" />
+ <ClCompile Include="ScrollableEditablePanel.cpp" />
+ <ClCompile Include="ScrollBar.cpp" />
+ <ClCompile Include="ScrollBarSlider.cpp" />
+ <ClCompile Include="SectionedListPanel.cpp" />
+ <ClCompile Include="Slider.cpp" />
+ <ClCompile Include="Splitter.cpp" />
+ <ClCompile Include="subrectimage.cpp" />
+ <ClCompile Include="TextEntry.cpp" />
+ <ClCompile Include="TextImage.cpp" />
+ <ClCompile Include="ToggleButton.cpp" />
+ <ClCompile Include="Tooltip.cpp" />
+ <ClCompile Include="ToolWindow.cpp" />
+ <ClCompile Include="TreeView.cpp" />
+ <ClCompile Include="TreeViewListControl.cpp" />
+ <ClCompile Include="URLLabel.cpp" />
+ <ClCompile Include="..\..\vgui2\src\vgui_key_translation.cpp" />
+ <ClCompile Include="WizardPanel.cpp" />
+ <ClCompile Include="WizardSubPanel.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\vgui2\chromehtml\htmlmessages.proto">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Running Protocol Buffer Compiler on %(Filename)%(Extension)...</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">if not exist generated_proto mkdir generated_proto&#x0D;&#x0A;..\..\gcsdk\bin\protoc.exe --proto_path=..\..\thirdparty\protobuf-2.3.0\src --proto_path=%(RootDir)%(Directory) --proto_path=..\..\gcsdk --cpp_out=generated_proto %(FullPath)</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">generated_proto\%(Filename).pb.cc;generated_proto\%(Filename).pb.h</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Running Protocol Buffer Compiler on %(Filename)%(Extension)...</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">if not exist generated_proto mkdir generated_proto&#x0D;&#x0A;..\..\gcsdk\bin\protoc.exe --proto_path=..\..\thirdparty\protobuf-2.3.0\src --proto_path=%(RootDir)%(Directory) --proto_path=..\..\gcsdk --cpp_out=generated_proto %(FullPath)</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">generated_proto\%(Filename).pb.cc;generated_proto\%(Filename).pb.h</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/vgui2/vgui_controls/vgui_controls-2010.vcxproj.filters b/mp/src/vgui2/vgui_controls/vgui_controls-2010.vcxproj.filters
new file mode 100644
index 00000000..9bf969d3
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/vgui_controls-2010.vcxproj.filters
@@ -0,0 +1,539 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Protobuf Files">
+ <UniqueIdentifier>{14744026-5703-4552-1949-4929EDF94F84}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Public Header Files">
+ <UniqueIdentifier>{680EF60A-F852-B6F6-8E56-5693F8167FE5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="generated_proto\htmlmessages.pb.h">
+ <Filter>Protobuf Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\AnalogBar.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\AnimatingImagePanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\AnimationController.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\BitmapImagePanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\BuildGroup.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\BuildModeDialog.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\Button.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\CheckButton.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\CheckButtonList.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\CircularProgressBar.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\Color.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ComboBox.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\consoledialog.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ControllerMap.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\Controls.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\CvarToggleCheckButton.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\DialogManager.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\DirectorySelectDialog.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\Divider.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\EditablePanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ExpandButton.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\FileOpenDialog.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\FileOpenStateMachine.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\filesystem.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\filesystem_helpers.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\FocusNavGroup.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\Frame.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\GraphPanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\HTML.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\Image.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ImageList.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ImagePanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\InputDialog.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\interface.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\KeyBindingHelpDialog.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\KeyBindingMap.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\KeyBoardEditorDialog.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\KeyRepeat.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\KeyValues.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\Label.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ListPanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ListViewPanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\memdbgoff.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\memdbgon.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\mempool.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\Menu.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\MenuBar.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\MenuButton.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\MenuItem.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\MessageBox.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\MessageDialog.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\MessageMap.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\Panel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\PanelAnimationVar.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\PanelListPanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\PerforceFileExplorer.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\PerforceFileList.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\perforcefilelistframe.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\PHandle.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ProgressBar.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ProgressBox.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\PropertyDialog.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\PropertyPage.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\PropertySheet.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\QueryBox.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\RadioButton.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\RichText.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\RotatingProgressBar.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\savedocumentquery.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ScalableImagePanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ScrollableEditablePanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ScrollBar.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ScrollBarSlider.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\SectionedListPanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\Slider.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\Splitter.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\subrectimage.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\TextEntry.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\TextImage.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ToggleButton.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\Tooltip.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\ToolWindow.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\TreeView.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\TreeViewListControl.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\URLLabel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlmemory.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlrbtree.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlvector.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\WizardPanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vgui_controls\WizardSubPanel.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="generated_proto\htmlmessages.pb.cc">
+ <Filter>Protobuf Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AnalogBar.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AnimatingImagePanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AnimationController.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="BitmapImagePanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="BuildFactoryHelper.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="BuildGroup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="BuildModeDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Button.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CheckButton.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CheckButtonList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CircularProgressBar.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ComboBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="consoledialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ControllerMap.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="controls.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CvarToggleCheckButton.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="DirectorySelectDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Divider.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="EditablePanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ExpandButton.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FileOpenDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FileOpenStateMachine.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FocusNavGroup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Frame.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="GraphPanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="HTML.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\html\htmlprotobuf.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Image.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ImageList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ImagePanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="InputDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="KeyBindingHelpDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="KeyBoardEditorDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="KeyRepeat.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Label.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ListPanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ListViewPanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Menu.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MenuBar.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MenuButton.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MenuItem.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MessageBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MessageDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Panel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PanelListPanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PerforceFileExplorer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PerforceFileList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="perforcefilelistframe.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ProgressBar.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ProgressBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PropertyDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PropertyPage.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PropertySheet.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="QueryBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RadioButton.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RichText.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RotatingProgressBar.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="savedocumentquery.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ScalableImagePanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ScrollableEditablePanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ScrollBar.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ScrollBarSlider.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SectionedListPanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Slider.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Splitter.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="subrectimage.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TextEntry.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TextImage.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ToggleButton.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Tooltip.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ToolWindow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TreeView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TreeViewListControl.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="URLLabel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\vgui2\src\vgui_key_translation.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WizardPanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WizardSubPanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\vgui2\chromehtml\htmlmessages.proto">
+ <Filter>Protobuf Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/vgui2/vgui_controls/vgui_controls_linux32.mak b/mp/src/vgui2/vgui_controls/vgui_controls_linux32.mak
new file mode 100644
index 00000000..356a708c
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/vgui_controls_linux32.mak
@@ -0,0 +1,782 @@
+NAME=vgui_controls
+SRCROOT=../..
+TARGET_PLATFORM=linux32
+TARGET_PLATFORM_EXT=
+USE_VALVE_BINDIR=0
+PWD := $(shell pwd)
+# If no configuration is specified, "release" will be used.
+ifeq "$(CFG)" ""
+ CFG = release
+endif
+
+GCC_ExtraCompilerFlags= -U_FORTIFY_SOURCE
+GCC_ExtraLinkerFlags =
+SymbolVisibility = hidden
+OptimizerLevel = -gdwarf-2 -g $(OptimizerLevel_CompilerSpecific)
+SystemLibraries =
+DLL_EXT = .so
+SYM_EXT = .dbg
+FORCEINCLUDES=
+ifeq "$(CFG)" "debug"
+DEFINES += -DDEBUG -D_DEBUG -DPOSIX -DGNUC -DLINUX -D_LINUX -DLIBNAME=vgui_controls -DRAD_TELEMETRY_DISABLED -DBINK_VIDEO -DGL_GLEXT_PROTOTYPES -DDX_TO_GL_ABSTRACTION -DUSE_SDL -D_EXTERNAL_DLL_EXT=.so -DVPCGAMECAPS=VALVE -DPROJECTDIR=/home/VALVE/joe/p4clients/linuxsdk/hl2/src/vgui2/vgui_controls -D_DLL_EXT=.so -DVPCGAME=valve -D_LINUX=1 -D_POSIX=1 -DLINUX=1 -DPOSIX=1
+else
+DEFINES += -DNDEBUG -DPOSIX -DGNUC -DLINUX -D_LINUX -DLIBNAME=vgui_controls -DRAD_TELEMETRY_DISABLED -DBINK_VIDEO -DGL_GLEXT_PROTOTYPES -DDX_TO_GL_ABSTRACTION -DUSE_SDL -D_EXTERNAL_DLL_EXT=.so -DVPCGAMECAPS=VALVE -DPROJECTDIR=/home/VALVE/joe/p4clients/linuxsdk/hl2/src/vgui2/vgui_controls -D_DLL_EXT=.so -DVPCGAME=valve -D_LINUX=1 -D_POSIX=1 -DLINUX=1 -DPOSIX=1
+endif
+INCLUDEDIRS += ../../common ../../public ../../public/tier0 ../../public/tier1 ../../thirdparty/SDL2 generated_proto ../../thirdparty/protobuf-2.3.0/src ../../thirdparty ../../thirdparty/cef generated_proto
+CONFTYPE = lib
+OUTPUTFILE=../../lib/public/linux32/vgui_controls.a
+THIS_MAKEFILE = $(PWD)/vgui_controls_linux32.mak
+MAKEFILE_BASE = $(SRCROOT)/devtools/makefile_base_posix.mak
+
+
+POSTBUILDCOMMAND = true
+
+
+
+CPPFILES= \
+ ../../public/filesystem_helpers.cpp \
+ ../../public/html/htmlprotobuf.cpp \
+ ../../vgui2/src/vgui_key_translation.cpp \
+ AnalogBar.cpp \
+ AnimatingImagePanel.cpp \
+ AnimationController.cpp \
+ BitmapImagePanel.cpp \
+ BuildFactoryHelper.cpp \
+ BuildGroup.cpp \
+ BuildModeDialog.cpp \
+ Button.cpp \
+ CheckButton.cpp \
+ CheckButtonList.cpp \
+ CircularProgressBar.cpp \
+ ComboBox.cpp \
+ consoledialog.cpp \
+ ControllerMap.cpp \
+ controls.cpp \
+ cvartogglecheckbutton.cpp \
+ DirectorySelectDialog.cpp \
+ Divider.cpp \
+ EditablePanel.cpp \
+ ExpandButton.cpp \
+ FileOpenDialog.cpp \
+ FileOpenStateMachine.cpp \
+ FocusNavGroup.cpp \
+ Frame.cpp \
+ generated_proto/htmlmessages.pb.cc \
+ GraphPanel.cpp \
+ HTML.cpp \
+ Image.cpp \
+ ImageList.cpp \
+ ImagePanel.cpp \
+ InputDialog.cpp \
+ KeyBindingHelpDialog.cpp \
+ KeyBoardEditorDialog.cpp \
+ KeyRepeat.cpp \
+ Label.cpp \
+ ListPanel.cpp \
+ ListViewPanel.cpp \
+ Menu.cpp \
+ MenuBar.cpp \
+ MenuButton.cpp \
+ MenuItem.cpp \
+ MessageBox.cpp \
+ MessageDialog.cpp \
+ Panel.cpp \
+ PanelListPanel.cpp \
+ PerforceFileExplorer.cpp \
+ PerforceFileList.cpp \
+ perforcefilelistframe.cpp \
+ ProgressBar.cpp \
+ ProgressBox.cpp \
+ PropertyDialog.cpp \
+ PropertyPage.cpp \
+ PropertySheet.cpp \
+ QueryBox.cpp \
+ RadioButton.cpp \
+ RichText.cpp \
+ RotatingProgressBar.cpp \
+ savedocumentquery.cpp \
+ ScalableImagePanel.cpp \
+ ScrollableEditablePanel.cpp \
+ ScrollBar.cpp \
+ ScrollBarSlider.cpp \
+ SectionedListPanel.cpp \
+ Slider.cpp \
+ Splitter.cpp \
+ subrectimage.cpp \
+ TextEntry.cpp \
+ TextImage.cpp \
+ ToggleButton.cpp \
+ Tooltip.cpp \
+ ToolWindow.cpp \
+ TreeView.cpp \
+ TreeViewListControl.cpp \
+ URLLabel.cpp \
+ WizardPanel.cpp \
+ WizardSubPanel.cpp \
+
+
+LIBFILES = \
+
+
+LIBFILENAMES = \
+
+
+# Include the base makefile now.
+include $(SRCROOT)/devtools/makefile_base_posix.mak
+
+
+
+OTHER_DEPENDENCIES = \
+ generated_proto/htmlmessages.pb.cc \
+ generated_proto/htmlmessages.pb.h
+
+
+$(OBJ_DIR)/_other_deps.P : $(OTHER_DEPENDENCIES)
+ $(GEN_OTHER_DEPS)
+
+-include $(OBJ_DIR)/_other_deps.P
+
+
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/filesystem_helpers.P
+endif
+
+$(OBJ_DIR)/filesystem_helpers.o : $(PWD)/../../public/filesystem_helpers.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/htmlprotobuf.P
+endif
+
+$(OBJ_DIR)/htmlprotobuf.o : $(PWD)/../../public/html/htmlprotobuf.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+generated_proto/htmlmessages.pb.cc generated_proto/htmlmessages.pb.h : ../../vgui2/chromehtml/htmlmessages.proto
+ @echo "Running Protocol Buffer Compiler on htmlmessages.proto...";mkdir -p $(OBJ_DIR) 2> /dev/null;mkdir generated_proto 2> /dev/null;../../gcsdk/bin/linux/protoc --proto_path=../../thirdparty/protobuf-2.3.0/src --proto_path=../../vgui2/chromehtml/ --proto_path=../../gcsdk --cpp_out=generated_proto ../../vgui2/chromehtml/htmlmessages.proto
+
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/vgui_key_translation.P
+endif
+
+$(OBJ_DIR)/vgui_key_translation.o : $(PWD)/../../vgui2/src/vgui_key_translation.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/AnalogBar.P
+endif
+
+$(OBJ_DIR)/AnalogBar.o : $(PWD)/AnalogBar.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/AnimatingImagePanel.P
+endif
+
+$(OBJ_DIR)/AnimatingImagePanel.o : $(PWD)/AnimatingImagePanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/AnimationController.P
+endif
+
+$(OBJ_DIR)/AnimationController.o : $(PWD)/AnimationController.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/BitmapImagePanel.P
+endif
+
+$(OBJ_DIR)/BitmapImagePanel.o : $(PWD)/BitmapImagePanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/BuildFactoryHelper.P
+endif
+
+$(OBJ_DIR)/BuildFactoryHelper.o : $(PWD)/BuildFactoryHelper.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/BuildGroup.P
+endif
+
+$(OBJ_DIR)/BuildGroup.o : $(PWD)/BuildGroup.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/BuildModeDialog.P
+endif
+
+$(OBJ_DIR)/BuildModeDialog.o : $(PWD)/BuildModeDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Button.P
+endif
+
+$(OBJ_DIR)/Button.o : $(PWD)/Button.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/CheckButton.P
+endif
+
+$(OBJ_DIR)/CheckButton.o : $(PWD)/CheckButton.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/CheckButtonList.P
+endif
+
+$(OBJ_DIR)/CheckButtonList.o : $(PWD)/CheckButtonList.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/CircularProgressBar.P
+endif
+
+$(OBJ_DIR)/CircularProgressBar.o : $(PWD)/CircularProgressBar.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ComboBox.P
+endif
+
+$(OBJ_DIR)/ComboBox.o : $(PWD)/ComboBox.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/consoledialog.P
+endif
+
+$(OBJ_DIR)/consoledialog.o : $(PWD)/consoledialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ControllerMap.P
+endif
+
+$(OBJ_DIR)/ControllerMap.o : $(PWD)/ControllerMap.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/controls.P
+endif
+
+$(OBJ_DIR)/controls.o : $(PWD)/controls.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/cvartogglecheckbutton.P
+endif
+
+$(OBJ_DIR)/cvartogglecheckbutton.o : $(PWD)/cvartogglecheckbutton.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/DirectorySelectDialog.P
+endif
+
+$(OBJ_DIR)/DirectorySelectDialog.o : $(PWD)/DirectorySelectDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Divider.P
+endif
+
+$(OBJ_DIR)/Divider.o : $(PWD)/Divider.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/EditablePanel.P
+endif
+
+$(OBJ_DIR)/EditablePanel.o : $(PWD)/EditablePanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ExpandButton.P
+endif
+
+$(OBJ_DIR)/ExpandButton.o : $(PWD)/ExpandButton.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/FileOpenDialog.P
+endif
+
+$(OBJ_DIR)/FileOpenDialog.o : $(PWD)/FileOpenDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/FileOpenStateMachine.P
+endif
+
+$(OBJ_DIR)/FileOpenStateMachine.o : $(PWD)/FileOpenStateMachine.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/FocusNavGroup.P
+endif
+
+$(OBJ_DIR)/FocusNavGroup.o : $(PWD)/FocusNavGroup.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Frame.P
+endif
+
+$(OBJ_DIR)/Frame.o : $(PWD)/Frame.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/htmlmessages.pb.P
+endif
+
+$(OBJ_DIR)/htmlmessages.pb.o : $(PWD)/generated_proto/htmlmessages.pb.cc $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/GraphPanel.P
+endif
+
+$(OBJ_DIR)/GraphPanel.o : $(PWD)/GraphPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/HTML.P
+endif
+
+$(OBJ_DIR)/HTML.o : $(PWD)/HTML.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Image.P
+endif
+
+$(OBJ_DIR)/Image.o : $(PWD)/Image.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ImageList.P
+endif
+
+$(OBJ_DIR)/ImageList.o : $(PWD)/ImageList.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ImagePanel.P
+endif
+
+$(OBJ_DIR)/ImagePanel.o : $(PWD)/ImagePanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/InputDialog.P
+endif
+
+$(OBJ_DIR)/InputDialog.o : $(PWD)/InputDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/KeyBindingHelpDialog.P
+endif
+
+$(OBJ_DIR)/KeyBindingHelpDialog.o : $(PWD)/KeyBindingHelpDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/KeyBoardEditorDialog.P
+endif
+
+$(OBJ_DIR)/KeyBoardEditorDialog.o : $(PWD)/KeyBoardEditorDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/KeyRepeat.P
+endif
+
+$(OBJ_DIR)/KeyRepeat.o : $(PWD)/KeyRepeat.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Label.P
+endif
+
+$(OBJ_DIR)/Label.o : $(PWD)/Label.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ListPanel.P
+endif
+
+$(OBJ_DIR)/ListPanel.o : $(PWD)/ListPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ListViewPanel.P
+endif
+
+$(OBJ_DIR)/ListViewPanel.o : $(PWD)/ListViewPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Menu.P
+endif
+
+$(OBJ_DIR)/Menu.o : $(PWD)/Menu.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/MenuBar.P
+endif
+
+$(OBJ_DIR)/MenuBar.o : $(PWD)/MenuBar.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/MenuButton.P
+endif
+
+$(OBJ_DIR)/MenuButton.o : $(PWD)/MenuButton.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/MenuItem.P
+endif
+
+$(OBJ_DIR)/MenuItem.o : $(PWD)/MenuItem.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/MessageBox.P
+endif
+
+$(OBJ_DIR)/MessageBox.o : $(PWD)/MessageBox.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/MessageDialog.P
+endif
+
+$(OBJ_DIR)/MessageDialog.o : $(PWD)/MessageDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Panel.P
+endif
+
+$(OBJ_DIR)/Panel.o : $(PWD)/Panel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/PanelListPanel.P
+endif
+
+$(OBJ_DIR)/PanelListPanel.o : $(PWD)/PanelListPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/PerforceFileExplorer.P
+endif
+
+$(OBJ_DIR)/PerforceFileExplorer.o : $(PWD)/PerforceFileExplorer.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/PerforceFileList.P
+endif
+
+$(OBJ_DIR)/PerforceFileList.o : $(PWD)/PerforceFileList.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/perforcefilelistframe.P
+endif
+
+$(OBJ_DIR)/perforcefilelistframe.o : $(PWD)/perforcefilelistframe.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ProgressBar.P
+endif
+
+$(OBJ_DIR)/ProgressBar.o : $(PWD)/ProgressBar.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ProgressBox.P
+endif
+
+$(OBJ_DIR)/ProgressBox.o : $(PWD)/ProgressBox.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/PropertyDialog.P
+endif
+
+$(OBJ_DIR)/PropertyDialog.o : $(PWD)/PropertyDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/PropertyPage.P
+endif
+
+$(OBJ_DIR)/PropertyPage.o : $(PWD)/PropertyPage.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/PropertySheet.P
+endif
+
+$(OBJ_DIR)/PropertySheet.o : $(PWD)/PropertySheet.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/QueryBox.P
+endif
+
+$(OBJ_DIR)/QueryBox.o : $(PWD)/QueryBox.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/RadioButton.P
+endif
+
+$(OBJ_DIR)/RadioButton.o : $(PWD)/RadioButton.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/RichText.P
+endif
+
+$(OBJ_DIR)/RichText.o : $(PWD)/RichText.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/RotatingProgressBar.P
+endif
+
+$(OBJ_DIR)/RotatingProgressBar.o : $(PWD)/RotatingProgressBar.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/savedocumentquery.P
+endif
+
+$(OBJ_DIR)/savedocumentquery.o : $(PWD)/savedocumentquery.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ScalableImagePanel.P
+endif
+
+$(OBJ_DIR)/ScalableImagePanel.o : $(PWD)/ScalableImagePanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ScrollableEditablePanel.P
+endif
+
+$(OBJ_DIR)/ScrollableEditablePanel.o : $(PWD)/ScrollableEditablePanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ScrollBar.P
+endif
+
+$(OBJ_DIR)/ScrollBar.o : $(PWD)/ScrollBar.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ScrollBarSlider.P
+endif
+
+$(OBJ_DIR)/ScrollBarSlider.o : $(PWD)/ScrollBarSlider.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/SectionedListPanel.P
+endif
+
+$(OBJ_DIR)/SectionedListPanel.o : $(PWD)/SectionedListPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Slider.P
+endif
+
+$(OBJ_DIR)/Slider.o : $(PWD)/Slider.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Splitter.P
+endif
+
+$(OBJ_DIR)/Splitter.o : $(PWD)/Splitter.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/subrectimage.P
+endif
+
+$(OBJ_DIR)/subrectimage.o : $(PWD)/subrectimage.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/TextEntry.P
+endif
+
+$(OBJ_DIR)/TextEntry.o : $(PWD)/TextEntry.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/TextImage.P
+endif
+
+$(OBJ_DIR)/TextImage.o : $(PWD)/TextImage.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ToggleButton.P
+endif
+
+$(OBJ_DIR)/ToggleButton.o : $(PWD)/ToggleButton.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Tooltip.P
+endif
+
+$(OBJ_DIR)/Tooltip.o : $(PWD)/Tooltip.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ToolWindow.P
+endif
+
+$(OBJ_DIR)/ToolWindow.o : $(PWD)/ToolWindow.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/TreeView.P
+endif
+
+$(OBJ_DIR)/TreeView.o : $(PWD)/TreeView.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/TreeViewListControl.P
+endif
+
+$(OBJ_DIR)/TreeViewListControl.o : $(PWD)/TreeViewListControl.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/URLLabel.P
+endif
+
+$(OBJ_DIR)/URLLabel.o : $(PWD)/URLLabel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/WizardPanel.P
+endif
+
+$(OBJ_DIR)/WizardPanel.o : $(PWD)/WizardPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/WizardSubPanel.P
+endif
+
+$(OBJ_DIR)/WizardSubPanel.o : $(PWD)/WizardSubPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+# Uncomment this, and set FILENAME to file you want built without optimizations enabled.
+# $(OBJ_DIR)/FILENAME.o : CFLAGS := $(subst -O2,-O0,$(CFLAGS))
+
+# Uncomment this to disable optimizations for the entire project.
+# $(OBJ_DIR)/%.o : CFLAGS := $(subst -O2,-O0,$(CFLAGS))
+
+
diff --git a/mp/src/vgui2/vgui_controls/vgui_controls_osx32.mak b/mp/src/vgui2/vgui_controls/vgui_controls_osx32.mak
new file mode 100644
index 00000000..4083ed3c
--- /dev/null
+++ b/mp/src/vgui2/vgui_controls/vgui_controls_osx32.mak
@@ -0,0 +1,786 @@
+NAME=vgui_controls
+SRCROOT=../..
+TARGET_PLATFORM=osx32
+TARGET_PLATFORM_EXT=
+USE_VALVE_BINDIR=0
+PWD := $(shell pwd)
+# If no configuration is specified, "release" will be used.
+ifeq "$(CFG)" ""
+ CFG = release
+endif
+
+GCC_ExtraCompilerFlags=
+GCC_ExtraLinkerFlags =
+SymbolVisibility = hidden
+OptimizerLevel = -gdwarf-2 -g $(OptimizerLevel_CompilerSpecific)
+SystemLibraries =
+DLL_EXT = .dylib
+SYM_EXT = .dSYM
+FORCEINCLUDES=
+ifeq "$(CFG)" "debug"
+DEFINES += -DDEBUG -D_DEBUG -DGNUC -DPOSIX -D_OSX -DOSX -D_DARWIN_UNLIMITED_SELECT -DFD_SETSIZE=10240 -DQUICKTIME_VIDEO -DFORCE_QUICKTIME -DGL_GLEXT_PROTOTYPES -DDX_TO_GL_ABSTRACTION -DVPCGAMECAPS=VALVE -DPROJECTDIR=/Users/joe/p4/ValveGames/rel/hl2/src/vgui2/vgui_controls -D_DLL_EXT=.dylib -DVPCGAME=valve -D_POSIX=1
+else
+DEFINES += -DNDEBUG -DGNUC -DPOSIX -D_OSX -DOSX -D_DARWIN_UNLIMITED_SELECT -DFD_SETSIZE=10240 -DQUICKTIME_VIDEO -DFORCE_QUICKTIME -DGL_GLEXT_PROTOTYPES -DDX_TO_GL_ABSTRACTION -DVPCGAMECAPS=VALVE -DPROJECTDIR=/Users/joe/p4/ValveGames/rel/hl2/src/vgui2/vgui_controls -D_DLL_EXT=.dylib -DVPCGAME=valve -D_POSIX=1
+endif
+INCLUDEDIRS += ../../common ../../public ../../public/tier0 ../../public/tier1 generated_proto ../../thirdparty/protobuf-2.3.0/src ../../thirdparty ../../thirdparty/cef generated_proto
+CONFTYPE = lib
+OUTPUTFILE=../../lib/osx32/vgui_controls.a
+THIS_MAKEFILE = $(PWD)/vgui_controls_osx32.mak
+MAKEFILE_BASE = $(SRCROOT)/devtools/makefile_base_posix.mak
+
+
+POSTBUILDCOMMAND = true
+
+
+
+CPPFILES= \
+ ../../public/filesystem_helpers.cpp \
+ ../../public/html/htmlprotobuf.cpp \
+ ../../vgui2/src/vgui_key_translation.cpp \
+ AnalogBar.cpp \
+ AnimatingImagePanel.cpp \
+ AnimationController.cpp \
+ BitmapImagePanel.cpp \
+ BuildFactoryHelper.cpp \
+ BuildGroup.cpp \
+ BuildModeDialog.cpp \
+ Button.cpp \
+ CheckButton.cpp \
+ CheckButtonList.cpp \
+ CircularProgressBar.cpp \
+ ComboBox.cpp \
+ consoledialog.cpp \
+ ControllerMap.cpp \
+ controls.cpp \
+ CvarToggleCheckButton.cpp \
+ DirectorySelectDialog.cpp \
+ Divider.cpp \
+ EditablePanel.cpp \
+ ExpandButton.cpp \
+ FileOpenDialog.cpp \
+ FileOpenStateMachine.cpp \
+ FocusNavGroup.cpp \
+ Frame.cpp \
+ generated_proto/htmlmessages.pb.cc \
+ GraphPanel.cpp \
+ HTML.cpp \
+ Image.cpp \
+ ImageList.cpp \
+ ImagePanel.cpp \
+ InputDialog.cpp \
+ KeyBindingHelpDialog.cpp \
+ KeyBoardEditorDialog.cpp \
+ KeyRepeat.cpp \
+ Label.cpp \
+ ListPanel.cpp \
+ ListViewPanel.cpp \
+ Menu.cpp \
+ MenuBar.cpp \
+ MenuButton.cpp \
+ MenuItem.cpp \
+ MessageBox.cpp \
+ MessageDialog.cpp \
+ Panel.cpp \
+ PanelListPanel.cpp \
+ PerforceFileExplorer.cpp \
+ PerforceFileList.cpp \
+ perforcefilelistframe.cpp \
+ ProgressBar.cpp \
+ ProgressBox.cpp \
+ PropertyDialog.cpp \
+ PropertyPage.cpp \
+ PropertySheet.cpp \
+ QueryBox.cpp \
+ RadioButton.cpp \
+ RichText.cpp \
+ RotatingProgressBar.cpp \
+ savedocumentquery.cpp \
+ ScalableImagePanel.cpp \
+ ScrollableEditablePanel.cpp \
+ ScrollBar.cpp \
+ ScrollBarSlider.cpp \
+ SectionedListPanel.cpp \
+ Slider.cpp \
+ Splitter.cpp \
+ subrectimage.cpp \
+ TextEntry.cpp \
+ TextImage.cpp \
+ ToggleButton.cpp \
+ Tooltip.cpp \
+ ToolWindow.cpp \
+ TreeView.cpp \
+ TreeViewListControl.cpp \
+ URLLabel.cpp \
+ WizardPanel.cpp \
+ WizardSubPanel.cpp \
+
+
+LIBFILES = \
+ ../../lib/osx32/libvstdlib.dylib \
+ ../../lib/osx32/tier1.a \
+
+
+LIBFILENAMES = \
+ ../../lib/osx32/libvstdlib.dylib \
+ ../../lib/osx32/tier1.a \
+
+
+# Include the base makefile now.
+include $(SRCROOT)/devtools/makefile_base_posix.mak
+
+
+
+OTHER_DEPENDENCIES = \
+ generated_proto/htmlmessages.pb.cc \
+ generated_proto/htmlmessages.pb.h
+
+
+$(OBJ_DIR)/_other_deps.P : $(OTHER_DEPENDENCIES)
+ $(GEN_OTHER_DEPS)
+
+-include $(OBJ_DIR)/_other_deps.P
+
+
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/filesystem_helpers.P
+endif
+
+$(OBJ_DIR)/filesystem_helpers.o : $(PWD)/../../public/filesystem_helpers.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/htmlprotobuf.P
+endif
+
+$(OBJ_DIR)/htmlprotobuf.o : $(PWD)/../../public/html/htmlprotobuf.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+generated_proto/htmlmessages.pb.cc generated_proto/htmlmessages.pb.h : ../../vgui2/chromehtml/htmlmessages.proto
+ @echo "Running Protocol Buffer Compiler on htmlmessages.proto...";mkdir -p $(OBJ_DIR) 2> /dev/null;mkdir generated_proto 2> /dev/null;../../devtools/bin/osx32/protoc --proto_path=../../thirdparty/protobuf-2.3.0/src --proto_path=../../vgui2/chromehtml/ --proto_path=../../gcsdk --cpp_out=generated_proto ../../vgui2/chromehtml/htmlmessages.proto
+
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/vgui_key_translation.P
+endif
+
+$(OBJ_DIR)/vgui_key_translation.o : $(PWD)/../../vgui2/src/vgui_key_translation.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/AnalogBar.P
+endif
+
+$(OBJ_DIR)/AnalogBar.o : $(PWD)/AnalogBar.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/AnimatingImagePanel.P
+endif
+
+$(OBJ_DIR)/AnimatingImagePanel.o : $(PWD)/AnimatingImagePanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/AnimationController.P
+endif
+
+$(OBJ_DIR)/AnimationController.o : $(PWD)/AnimationController.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/BitmapImagePanel.P
+endif
+
+$(OBJ_DIR)/BitmapImagePanel.o : $(PWD)/BitmapImagePanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/BuildFactoryHelper.P
+endif
+
+$(OBJ_DIR)/BuildFactoryHelper.o : $(PWD)/BuildFactoryHelper.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/BuildGroup.P
+endif
+
+$(OBJ_DIR)/BuildGroup.o : $(PWD)/BuildGroup.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/BuildModeDialog.P
+endif
+
+$(OBJ_DIR)/BuildModeDialog.o : $(PWD)/BuildModeDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Button.P
+endif
+
+$(OBJ_DIR)/Button.o : $(PWD)/Button.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/CheckButton.P
+endif
+
+$(OBJ_DIR)/CheckButton.o : $(PWD)/CheckButton.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/CheckButtonList.P
+endif
+
+$(OBJ_DIR)/CheckButtonList.o : $(PWD)/CheckButtonList.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/CircularProgressBar.P
+endif
+
+$(OBJ_DIR)/CircularProgressBar.o : $(PWD)/CircularProgressBar.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ComboBox.P
+endif
+
+$(OBJ_DIR)/ComboBox.o : $(PWD)/ComboBox.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/consoledialog.P
+endif
+
+$(OBJ_DIR)/consoledialog.o : $(PWD)/consoledialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ControllerMap.P
+endif
+
+$(OBJ_DIR)/ControllerMap.o : $(PWD)/ControllerMap.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/controls.P
+endif
+
+$(OBJ_DIR)/controls.o : $(PWD)/controls.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/CvarToggleCheckButton.P
+endif
+
+$(OBJ_DIR)/CvarToggleCheckButton.o : $(PWD)/CvarToggleCheckButton.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/DirectorySelectDialog.P
+endif
+
+$(OBJ_DIR)/DirectorySelectDialog.o : $(PWD)/DirectorySelectDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Divider.P
+endif
+
+$(OBJ_DIR)/Divider.o : $(PWD)/Divider.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/EditablePanel.P
+endif
+
+$(OBJ_DIR)/EditablePanel.o : $(PWD)/EditablePanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ExpandButton.P
+endif
+
+$(OBJ_DIR)/ExpandButton.o : $(PWD)/ExpandButton.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/FileOpenDialog.P
+endif
+
+$(OBJ_DIR)/FileOpenDialog.o : $(PWD)/FileOpenDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/FileOpenStateMachine.P
+endif
+
+$(OBJ_DIR)/FileOpenStateMachine.o : $(PWD)/FileOpenStateMachine.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/FocusNavGroup.P
+endif
+
+$(OBJ_DIR)/FocusNavGroup.o : $(PWD)/FocusNavGroup.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Frame.P
+endif
+
+$(OBJ_DIR)/Frame.o : $(PWD)/Frame.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/htmlmessages.pb.P
+endif
+
+$(OBJ_DIR)/htmlmessages.pb.o : $(PWD)/generated_proto/htmlmessages.pb.cc $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/GraphPanel.P
+endif
+
+$(OBJ_DIR)/GraphPanel.o : $(PWD)/GraphPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/HTML.P
+endif
+
+$(OBJ_DIR)/HTML.o : $(PWD)/HTML.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Image.P
+endif
+
+$(OBJ_DIR)/Image.o : $(PWD)/Image.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ImageList.P
+endif
+
+$(OBJ_DIR)/ImageList.o : $(PWD)/ImageList.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ImagePanel.P
+endif
+
+$(OBJ_DIR)/ImagePanel.o : $(PWD)/ImagePanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/InputDialog.P
+endif
+
+$(OBJ_DIR)/InputDialog.o : $(PWD)/InputDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/KeyBindingHelpDialog.P
+endif
+
+$(OBJ_DIR)/KeyBindingHelpDialog.o : $(PWD)/KeyBindingHelpDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/KeyBoardEditorDialog.P
+endif
+
+$(OBJ_DIR)/KeyBoardEditorDialog.o : $(PWD)/KeyBoardEditorDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/KeyRepeat.P
+endif
+
+$(OBJ_DIR)/KeyRepeat.o : $(PWD)/KeyRepeat.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Label.P
+endif
+
+$(OBJ_DIR)/Label.o : $(PWD)/Label.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ListPanel.P
+endif
+
+$(OBJ_DIR)/ListPanel.o : $(PWD)/ListPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ListViewPanel.P
+endif
+
+$(OBJ_DIR)/ListViewPanel.o : $(PWD)/ListViewPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Menu.P
+endif
+
+$(OBJ_DIR)/Menu.o : $(PWD)/Menu.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/MenuBar.P
+endif
+
+$(OBJ_DIR)/MenuBar.o : $(PWD)/MenuBar.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/MenuButton.P
+endif
+
+$(OBJ_DIR)/MenuButton.o : $(PWD)/MenuButton.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/MenuItem.P
+endif
+
+$(OBJ_DIR)/MenuItem.o : $(PWD)/MenuItem.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/MessageBox.P
+endif
+
+$(OBJ_DIR)/MessageBox.o : $(PWD)/MessageBox.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/MessageDialog.P
+endif
+
+$(OBJ_DIR)/MessageDialog.o : $(PWD)/MessageDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Panel.P
+endif
+
+$(OBJ_DIR)/Panel.o : $(PWD)/Panel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/PanelListPanel.P
+endif
+
+$(OBJ_DIR)/PanelListPanel.o : $(PWD)/PanelListPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/PerforceFileExplorer.P
+endif
+
+$(OBJ_DIR)/PerforceFileExplorer.o : $(PWD)/PerforceFileExplorer.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/PerforceFileList.P
+endif
+
+$(OBJ_DIR)/PerforceFileList.o : $(PWD)/PerforceFileList.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/perforcefilelistframe.P
+endif
+
+$(OBJ_DIR)/perforcefilelistframe.o : $(PWD)/perforcefilelistframe.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ProgressBar.P
+endif
+
+$(OBJ_DIR)/ProgressBar.o : $(PWD)/ProgressBar.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ProgressBox.P
+endif
+
+$(OBJ_DIR)/ProgressBox.o : $(PWD)/ProgressBox.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/PropertyDialog.P
+endif
+
+$(OBJ_DIR)/PropertyDialog.o : $(PWD)/PropertyDialog.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/PropertyPage.P
+endif
+
+$(OBJ_DIR)/PropertyPage.o : $(PWD)/PropertyPage.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/PropertySheet.P
+endif
+
+$(OBJ_DIR)/PropertySheet.o : $(PWD)/PropertySheet.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/QueryBox.P
+endif
+
+$(OBJ_DIR)/QueryBox.o : $(PWD)/QueryBox.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/RadioButton.P
+endif
+
+$(OBJ_DIR)/RadioButton.o : $(PWD)/RadioButton.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/RichText.P
+endif
+
+$(OBJ_DIR)/RichText.o : $(PWD)/RichText.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/RotatingProgressBar.P
+endif
+
+$(OBJ_DIR)/RotatingProgressBar.o : $(PWD)/RotatingProgressBar.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/savedocumentquery.P
+endif
+
+$(OBJ_DIR)/savedocumentquery.o : $(PWD)/savedocumentquery.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ScalableImagePanel.P
+endif
+
+$(OBJ_DIR)/ScalableImagePanel.o : $(PWD)/ScalableImagePanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ScrollableEditablePanel.P
+endif
+
+$(OBJ_DIR)/ScrollableEditablePanel.o : $(PWD)/ScrollableEditablePanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ScrollBar.P
+endif
+
+$(OBJ_DIR)/ScrollBar.o : $(PWD)/ScrollBar.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ScrollBarSlider.P
+endif
+
+$(OBJ_DIR)/ScrollBarSlider.o : $(PWD)/ScrollBarSlider.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/SectionedListPanel.P
+endif
+
+$(OBJ_DIR)/SectionedListPanel.o : $(PWD)/SectionedListPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Slider.P
+endif
+
+$(OBJ_DIR)/Slider.o : $(PWD)/Slider.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Splitter.P
+endif
+
+$(OBJ_DIR)/Splitter.o : $(PWD)/Splitter.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/subrectimage.P
+endif
+
+$(OBJ_DIR)/subrectimage.o : $(PWD)/subrectimage.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/TextEntry.P
+endif
+
+$(OBJ_DIR)/TextEntry.o : $(PWD)/TextEntry.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/TextImage.P
+endif
+
+$(OBJ_DIR)/TextImage.o : $(PWD)/TextImage.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ToggleButton.P
+endif
+
+$(OBJ_DIR)/ToggleButton.o : $(PWD)/ToggleButton.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/Tooltip.P
+endif
+
+$(OBJ_DIR)/Tooltip.o : $(PWD)/Tooltip.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/ToolWindow.P
+endif
+
+$(OBJ_DIR)/ToolWindow.o : $(PWD)/ToolWindow.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/TreeView.P
+endif
+
+$(OBJ_DIR)/TreeView.o : $(PWD)/TreeView.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/TreeViewListControl.P
+endif
+
+$(OBJ_DIR)/TreeViewListControl.o : $(PWD)/TreeViewListControl.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/URLLabel.P
+endif
+
+$(OBJ_DIR)/URLLabel.o : $(PWD)/URLLabel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/WizardPanel.P
+endif
+
+$(OBJ_DIR)/WizardPanel.o : $(PWD)/WizardPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
+-include $(OBJ_DIR)/WizardSubPanel.P
+endif
+
+$(OBJ_DIR)/WizardSubPanel.o : $(PWD)/WizardSubPanel.cpp $(THIS_MAKEFILE) $(MAKEFILE_BASE)
+ $(PRE_COMPILE_FILE)
+ $(COMPILE_FILE) $(POST_COMPILE_FILE)
+
+# Uncomment this, and set FILENAME to file you want built without optimizations enabled.
+# $(OBJ_DIR)/FILENAME.o : CFLAGS := $(subst -O2,-O0,$(CFLAGS))
+
+# Uncomment this to disable optimizations for the entire project.
+# $(OBJ_DIR)/%.o : CFLAGS := $(subst -O2,-O0,$(CFLAGS))
+
+