aboutsummaryrefslogtreecommitdiff
path: root/mp/src/vgui2/vgui_controls/TextEntry.cpp
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/TextEntry.cpp
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/TextEntry.cpp')
-rw-r--r--mp/src/vgui2/vgui_controls/TextEntry.cpp4279
1 files changed, 4279 insertions, 0 deletions
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;
+}