aboutsummaryrefslogtreecommitdiff
path: root/mp/src/vgui2/vgui_controls/TextEntry.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/vgui2/vgui_controls/TextEntry.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/vgui2/vgui_controls/TextEntry.cpp')
-rw-r--r--mp/src/vgui2/vgui_controls/TextEntry.cpp8558
1 files changed, 4279 insertions, 4279 deletions
diff --git a/mp/src/vgui2/vgui_controls/TextEntry.cpp b/mp/src/vgui2/vgui_controls/TextEntry.cpp
index cecf3e04..7a4f2d8c 100644
--- a/mp/src/vgui2/vgui_controls/TextEntry.cpp
+++ b/mp/src/vgui2/vgui_controls/TextEntry.cpp
@@ -1,4279 +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(), buf.Count());
- }
-
- // 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;
-}
+//========= 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(), buf.Count());
+ }
+
+ // 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;
+}