/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Combat *
* *
* $Archive:: /Commando/Code/wwui/editctrl.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 1/17/02 1:26p $*
* *
* $Revision:: 25 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "editctrl.h"
#include "assetmgr.h"
#include "refcount.h"
#include "font3d.h"
#include "mousemgr.h"
#include "ww3d.h"
#include "dialogmgr.h"
#include "stylemgr.h"
#include "dialogbase.h"
////////////////////////////////////////////////////////////////
//
// EditCtrlClass
//
////////////////////////////////////////////////////////////////
EditCtrlClass::EditCtrlClass (void) :
CaretPos (0),
ScrollPos (0),
NumChars(0),
TextLimit(0xFFFF),
CaretBlinkDelay (500),
LastCaretBlink (0),
HilightStartPos (-1),
HilightEndPos (-1),
HilightAnchorPos (-1),
WasButtonPressedOnMe (false),
IsCaretDisplayed (false),
mInComposition(false)
#ifdef SHOW_IME_TYPING
, mShowIMETypingText(false)
#endif
{
//
// Set the font for the text renderers
//
StyleMgrClass::Assign_Font (&TextRenderer, StyleMgrClass::FONT_CONTROLS);
StyleMgrClass::Configure_Renderer (&ControlRenderer);
StyleMgrClass::Configure_Renderer (&CaretRenderer);
StyleMgrClass::Configure_Renderer (&HilightRenderer);
mIME = DialogMgrClass::Get_IME();
}
////////////////////////////////////////////////////////////////
//
// ~EditCtrlClass
//
////////////////////////////////////////////////////////////////
EditCtrlClass::~EditCtrlClass (void)
{
if (mIME) {
mIME->Release_Ref();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Create_Text_Renderers
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Create_Text_Renderers (void)
{
HilightRenderer.Reset ();
HilightRenderer.Set_Coordinate_Range (Render2DClass::Get_Screen_Resolution());
StyleMgrClass::Configure_Hilighter (&HilightRenderer);
//
// Get the string we'll be displaying
//
WideStringClass display_string(0, true);
Get_Display_Text(display_string);
//
// Index into the text buffer
//
const WCHAR* text = display_string.Peek_Buffer();
if (ScrollPos >= 0 && ScrollPos < display_string.Get_Length()) {
text += ScrollPos;
}
//
// Start fresh
//
TextRenderer.Reset();
//
// Draw the text
//
uint32 color = StyleMgrClass::Get_Text_Color();
uint32 shadowColor = StyleMgrClass::Get_Text_Shadow_Color();
if (IsEnabled == false) {
color = StyleMgrClass::Get_Disabled_Text_Color();
shadowColor = StyleMgrClass::Get_Disabled_Text_Shadow_Color();
}
StyleMgrClass::Render_Text(text, &TextRenderer, color, shadowColor,
ClientRect, true, true, StyleMgrClass::LEFT_JUSTIFY, IsEnabled);
// Draw the composition markers
if (mIME && mInComposition) {
const wchar_t* compString = mIME->GetCompositionString();
Vector2 compSize = TextRenderer.Get_Text_Extents(compString);
if (compSize.X > 0.0f) {
// Underline the composition string
Vector2 startPos;
startPos.X = Pos_From_Character(CaretPos);
startPos.Y = ClientRect.Bottom - 2;
Vector2 stopPos(startPos);
stopPos.X += compSize.X;
unsigned long underlineColor = StyleMgrClass::Get_Disabled_Line_Color();
ControlRenderer.Add_Line(startPos, stopPos, 1.0f, underlineColor);
// Hilight the conversion target
unsigned long start = 0;
unsigned long end = 0;
mIME->GetTargetClause(start, end);
if (end > 0) {
HilightStartPos = (CaretPos + start);
HilightEndPos = (CaretPos + end);
}
}
}
//
// Do the hilight
//
if (HilightStartPos >= 0 && HilightStartPos != HilightEndPos && IsEnabled) {
RectClass hilight_rect;
hilight_rect.Top = ClientRect.Top + 1;
hilight_rect.Bottom = ClientRect.Bottom - 1;
hilight_rect.Left = Pos_From_Character (HilightStartPos);
hilight_rect.Right = Pos_From_Character (HilightEndPos);
StyleMgrClass::Render_Hilight (&HilightRenderer, hilight_rect);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Create_Control_Renderers
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Create_Control_Renderers (void)
{
//
// Configure this renderer
//
ControlRenderer.Reset ();
ControlRenderer.Enable_Texturing (false);
//
// Determine which color to draw the outline in
//
int color = StyleMgrClass::Get_Line_Color ();
int bkcolor = StyleMgrClass::Get_Bk_Color ();
if (IsEnabled == false) {
color = StyleMgrClass::Get_Disabled_Line_Color ();
bkcolor = StyleMgrClass::Get_Disabled_Bk_Color ();
}
//
// Draw the control outline
//
RectClass rect = Rect;
ControlRenderer.Add_Outline (rect, 1.0F, color);
//
// Now draw the client area
//
rect.Right -= 1;
rect.Bottom -= 1;
ControlRenderer.Add_Quad (rect, bkcolor);
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Set_Cursor
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::On_Set_Cursor (const Vector2 &mouse_pos)
{
//
// Change the mouse cursor if necessary
//
if (ClientRect.Contains (mouse_pos)) {
MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_TEXT);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Update_Client_Rect
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Update_Client_Rect (void)
{
//
// Determine what one character spacing would be
//
float char_width = TextRenderer.Get_Text_Extents (L"I").X;
//
// Shrink the client area
//
ClientRect = Rect;
ClientRect.Inflate (Vector2 (-char_width, -2));
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Render
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Render (void)
{
//
// Recreate the renderers (if necessary)
//
if (IsDirty) {
Create_Control_Renderers ();
Create_Text_Renderers ();
Create_Caret_Renderer ();
}
//
// Render the background and text for the current state
//
ControlRenderer.Render ();
TextRenderer.Render ();
HilightRenderer.Render ();
//
// Render the caret (if necessary)
//
Update_Caret ();
if (HasFocus && IsCaretDisplayed) {
CaretRenderer.Render ();
}
#ifdef SHOW_IME_TYPING
if (mShowIMETypingText) {
mIMETypingTip.Render();
}
#endif
DialogControlClass::Render ();
return ;
}
////////////////////////////////////////////////////////////////
// Get_Display_Text
////////////////////////////////////////////////////////////////
void EditCtrlClass::Get_Display_Text(WideStringClass &text)
{
// Resize to accomodate entire string
int length = Title.Get_Length();
if (mIME && mInComposition) {
const wchar_t* compString = mIME->GetCompositionString();
if (compString) {
length += wcslen(compString);
}
}
text.Get_Buffer(length);
// If password then replace text with astrisks
if ((Style & ES_PASSWORD) != 0) {
int len = Title.Get_Length();
wchar_t* buffer = text.Peek_Buffer();
for (int index = 0; index < len; ++index) {
buffer[index] = L'*';
}
buffer[index] = 0;
} else {
text = Title;
}
// Insert IME composition at cursor position
if (mIME && mInComposition) {
const wchar_t* compString = mIME->GetCompositionString();
if (compString) {
WideStringClass temp(length, true);
temp = (text.Peek_Buffer() + CaretPos);
text.Erase(CaretPos, (text.Get_Length() - CaretPos));
text += compString;
text += temp;
}
}
}
////////////////////////////////////////////////////////////////
//
// On_LButton_Down
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::On_LButton_Down (const Vector2 &mouse_pos)
{
if (mInComposition) {
return;
}
Set_Capture ();
if (HasFocus) {
//
// Update the caret position
//
Set_Caret_Pos (Character_From_Pos (mouse_pos));
HilightAnchorPos = CaretPos;
HilightStartPos = -1;
HilightEndPos = -1;
//
// Force a repaint
//
Set_Dirty ();
WasButtonPressedOnMe = true;
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_LButton_Up
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::On_LButton_Up (const Vector2 &mouse_pos)
{
if (mInComposition) {
return;
}
Release_Capture ();
//
// Reset our flags
//
WasButtonPressedOnMe = false;
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Mouse_Move
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::On_Mouse_Move (const Vector2 &mouse_pos)
{
if (mInComposition) {
return;
}
//
// Is the user "dragging" inside the control
//
if (HasFocus && WasButtonPressedOnMe) {
//
// Has the caret position changed?
//
int new_caret_pos = Character_From_Pos (mouse_pos);
if (new_caret_pos != CaretPos) {
//
// Calculate the new caret and hilight positions
//
Set_Caret_Pos (new_caret_pos);
HilightStartPos = min (HilightAnchorPos, CaretPos);
HilightEndPos = max (HilightAnchorPos, CaretPos);
//
// Force a repaint
//
Set_Dirty ();
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Update_Caret
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Update_Caret (void)
{
//
// Blink the caret if necessary
//
uint32 curr_time = DialogMgrClass::Get_Time ();
if ((curr_time - LastCaretBlink) > CaretBlinkDelay) {
IsCaretDisplayed = !IsCaretDisplayed;
LastCaretBlink = curr_time;
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Create_Caret_Renderer
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Create_Caret_Renderer (void)
{
CaretRenderer.Reset ();
//
// Configure this renderer
//
CaretRenderer.Enable_Texturing (false);
CaretRenderer.Set_Coordinate_Range (Render2DClass::Get_Screen_Resolution());
//
// Determine how many characters we should render the caret after
//
WideStringClass temp_copy(0, true);
Get_Display_Text(temp_copy);
WCHAR *text = temp_copy.Peek_Buffer();
int caretPos = Get_Caret_Pos();
text[caretPos] = 0;
text = &text[ScrollPos];
//
// Calculate where in the client area we should build the caret
//
Vector2 text_extent = TextRenderer.Get_Text_Extents (text);
float space_extent = 1;
//
// Create a bounding rectangle for the caret
//
RectClass rect;
rect.Left = int(ClientRect.Left + text_extent.X);
rect.Top = int(ClientRect.Top + 1);
rect.Right = int(rect.Left + max (space_extent, 1.0F));
rect.Bottom = int(ClientRect.Bottom - 1);
//
// Draw the caret
//
CaretRenderer.Add_Quad(rect, StyleMgrClass::Get_Text_Color());
}
////////////////////////////////////////////////////////////////
//
// Character_From_Pos
//
////////////////////////////////////////////////////////////////
int
EditCtrlClass::Character_From_Pos (const Vector2 &mouse_pos)
{
//
// Get the text we will be displaying
//
WideStringClass display_text(0, true);
Get_Display_Text(display_text);
//
// Index into the buffer
//
const WCHAR *text = display_text.Peek_Buffer () + ScrollPos;
int char_index = display_text.Get_Length ();
float x_pos = mouse_pos.X - ClientRect.Left;
float curr_x_pos = 0;
//
// Loop over all the characters in the remainder of the string until
// we've moved past the x-position we we're looking for.
//
int count = ::wcslen (text);
for (int index = 0; index < count; index ++) {
//
// Get the width of the character
//
WCHAR char_string[2] = { text[index], 0 };
float char_width = TextRenderer.Get_Text_Extents (char_string).X;
//
// Did we move past the position we were looking for?
//
if (curr_x_pos + (char_width / 2) >= x_pos) {
char_index = index + ScrollPos;
break;
}
//
// Increment our x-position
//
curr_x_pos += char_width;
}
//
// Return the character index
//
return char_index;
}
////////////////////////////////////////////////////////////////
//
// Pos_From_Character
//
////////////////////////////////////////////////////////////////
float
EditCtrlClass::Pos_From_Character (int char_index)
{
//
// Ensure the character isn't scrolled off the screen
//
char_index = max (ScrollPos, char_index);
//
// Get the width of the string up to this character index
//
WideStringClass temp_copy(0, true);
Get_Display_Text (temp_copy);
WCHAR *text = temp_copy.Peek_Buffer ();
text[char_index] = 0;
text = &text[ScrollPos];
float width = TextRenderer.Get_Text_Extents (text).X;
//
// Clip the position to the right side of the control
//
float position = min (ClientRect.Left + width, ClientRect.Right);
return position;
}
////////////////////////////////////////////////////////////////
//
// On_Set_Focus
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::On_Set_Focus (void)
{
//
// Hilight the contents of the window
//
Set_Caret_Pos (Title.Get_Length ());
HilightAnchorPos = 0;
HilightStartPos = 0;
HilightEndPos = CaretPos;
Set_Dirty ();
if (mIME) {
if (IsIMEAllowed()) {
Observer::NotifyMe(*mIME);
Observer::NotifyMe(*mIME);
mIME->Activate();
} else {
mIME->Disable();
}
}
DialogControlClass::On_Set_Focus ();
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Kill_Focus
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::On_Kill_Focus (DialogControlClass *focus)
{
if (mIME) {
if (IsIMEAllowed()) {
mIME->Deactivate();
Observer::StopObserving();
Observer::StopObserving();
} else {
mIME->Enable();
}
}
WasButtonPressedOnMe = false;
//
// Remove any hilight
//
Set_Caret_Pos (0);
HilightStartPos = -1;
HilightEndPos = -1;
Set_Dirty ();
DialogControlClass::On_Kill_Focus (focus);
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Key_Down
//
////////////////////////////////////////////////////////////////
bool
EditCtrlClass::On_Key_Down (uint32 key_id, uint32 key_data)
{
bool handled = false;
//
// Check to see if the advise sinks are processing this input...
//
if (AdviseSink != NULL) {
handled = AdviseSink->On_EditCtrl_Key_Down (this, key_id, key_data);
}
if (handled == false) {
handled = Parent->On_EditCtrl_Key_Down (this, key_id, key_data);
}
if (handled) {
return true;
}
handled = true;
bool changed = false;
bool is_dirty = true;
int old_caret = CaretPos;
bool update_hilight = ((DialogMgrClass::Get_VKey_State (VK_SHIFT) & VKEY_PRESSED) == VKEY_PRESSED);
switch (key_id)
{
case VK_BACK:
if (HilightStartPos >= 0) {
changed = Delete_Selection ();
} else if (CaretPos > 0) {
//
// Delete the previous character
//
Title.Erase (CaretPos - 1, 1);
Set_Caret_Pos (CaretPos - 1);
assert(NumChars > 0);
NumChars--;
changed = true;
}
update_hilight = false;
break;
case VK_DELETE:
if (HilightStartPos >= 0) {
changed = Delete_Selection ();
} else if (CaretPos < Title.Get_Length ()) {
//
// Delete the next character
//
Title.Erase (CaretPos, 1);
assert(NumChars > 0);
NumChars--;
changed = true;
}
update_hilight = false;
break;
case VK_HOME:
Set_Caret_Pos (0);
break;
case VK_END:
Set_Caret_Pos (Title.Get_Length ());
break;
case VK_LEFT:
//
// Should snap the caret to words, or just increment it?
//
if (DialogMgrClass::Get_VKey_State (VK_CONTROL) & VKEY_PRESSED) {
Set_Caret_Pos (Find_Word_Start (CaretPos, -1));
} else {
Set_Caret_Pos (max (0, CaretPos - 1));
}
break;
case VK_RIGHT:
//
// Should snap the caret to words, or just increment it?
//
if (DialogMgrClass::Get_VKey_State (VK_CONTROL) & VKEY_PRESSED) {
Set_Caret_Pos (Find_Word_Start (CaretPos, 1));
} else {
Set_Caret_Pos (min (Title.Get_Length (), CaretPos + 1));
}
break;
case VK_RETURN:
//
// Notify the parent that the user pressed enter
//
ADVISE_NOTIFY (On_EditCtrl_Enter_Pressed (this, Get_ID ()));
is_dirty = false;
break;
case VK_UP:
case VK_DOWN:
//
// Don't do anything
//
is_dirty = false;
handled = false;
break;
default:
is_dirty = false;
handled = false;
break;
}
if (update_hilight) {
//
// Update the hilight position
//
Update_Hilight (CaretPos, old_caret);
} else if (handled) {
//
// Reset the hilight position
//
HilightStartPos = -1;
HilightAnchorPos = -1;
}
if (changed) {
ADVISE_NOTIFY(On_EditCtrl_Change(this, Get_ID()));
}
//
// Repaint the control if necessary
//
if (is_dirty) {
Set_Dirty ();
}
return handled;
}
void EditCtrlClass::On_Unicode_Char(WCHAR unicode)
{
if (unicode >= 32) {
// Delete the old selection
Delete_Selection ();
if (NumChars < TextLimit) {
NumChars++;
// Build the new string
WideStringClass new_title = Title;
new_title.Erase (CaretPos, new_title.Get_Length () - CaretPos);
new_title += unicode;
new_title += Title.Peek_Buffer () + CaretPos;
Title = new_title;
// Move the caret to the end of the text the user just typed
Set_Caret_Pos (CaretPos + 1);
//
// Reset the hilight position
//
HilightStartPos = -1;
HilightAnchorPos = -1;
ADVISE_NOTIFY(On_EditCtrl_Change(this, Get_ID()));
// Force a repaint
Set_Dirty ();
}
}
}
void EditCtrlClass::Insert_String(const WCHAR* string)
{
int count = wcslen(string);
if (count > 0) {
// Delete the old selection
Delete_Selection();
WideStringClass new_title = Title;
new_title.Erase(CaretPos, new_title.Get_Length () - CaretPos);
// If the string exceed the text limit then only insert characters upto
// the text limit.
if ((NumChars + count) > TextLimit) {
// Truncate the insert string so that it will fit.
WideStringClass insert(count, true);
insert = string;
count = (TextLimit - NumChars);
insert.Erase(count, insert.Get_Length() - count);
new_title += insert;
} else {
new_title += string;
}
new_title += Title.Peek_Buffer() + CaretPos;
Title = new_title;
NumChars += count;
// Move the caret to the end of the text the user just typed
Set_Caret_Pos(CaretPos + count);
ADVISE_NOTIFY(On_EditCtrl_Change(this, Get_ID()));
// Force a repaint
Set_Dirty();
}
}
////////////////////////////////////////////////////////////////
//
// Delete_Selection
//
////////////////////////////////////////////////////////////////
bool
EditCtrlClass::Delete_Selection (void)
{
if (HilightStartPos >= 0) {
//
// Delete the characters and update the caret
//
int count = (HilightEndPos - HilightStartPos);
Title.Erase (HilightStartPos, count);
Set_Caret_Pos (HilightStartPos);
assert(NumChars >= count);
NumChars -= count;
//
// Remove any hilighting
//
HilightStartPos = -1;
HilightEndPos = -1;
//
// Force a repaint
//
Set_Dirty ();
return true;
}
return false;
}
////////////////////////////////////////////////////////////////
//
// On_Create
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::On_Create (void)
{
//TextColor.Set (0.35F, 1.0F, 0.35F);
//Set_Text (L"This is a test...");
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Caret_Pos
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Set_Caret_Pos (int new_pos)
{
//
// Remove the hilight
//
HilightStartPos = -1;
//
// Did the caret position change?
//
if (CaretPos != new_pos) {
CaretPos = new_pos;
//
// Force the caret to be displayed (i.e. no blink)
//
IsCaretDisplayed = true;
LastCaretBlink = DialogMgrClass::Get_Time ();
//
// Handle scrolling
//
Update_Scroll_Pos ();
//
// Force a repaint
//
Set_Dirty ();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Update_Hilight
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Update_Hilight (int new_pos, int anchor_pos)
{
HilightAnchorPos = (HilightAnchorPos >= 0) ? HilightAnchorPos : anchor_pos;
HilightStartPos = min (new_pos, HilightAnchorPos);
HilightEndPos = max (new_pos, HilightAnchorPos);
return ;
}
////////////////////////////////////////////////////////////////
//
// Find_Word_Start
//
////////////////////////////////////////////////////////////////
int
EditCtrlClass::Find_Word_Start (int pos, int increment)
{
int count = Title.Get_Length ();
//
// Determine what the extreme end posiiton should be
//
int extreme = pos + increment * 1000;
int retval = 0;
if (extreme > count) {
retval = count;
}
//
// Loop over all the characters until we've found the word break
//
for (int index = pos + increment; index < count && index >= 0; index += increment) {
//
// Is this a space character?
//
bool is_space = (Title[index] == L' ');
//
// If we've already found the word break and this is the
// start of a new word, then return its index to the caller
//
if (!is_space && ((index == 0) || (Title[index - 1] == L' '))) {
retval = index;
break;
}
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Update_Scroll_Pos
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Update_Scroll_Pos (void)
{
int old_scroll = ScrollPos;
int caretPos = Get_Caret_Pos();
//
// Find the current x-position of the caret
//
float x_pos = Pos_From_Character(caretPos);
if (x_pos <= ClientRect.Left) {
//
// Simply ensure the caret is scrolled into view
//
ScrollPos = caretPos - 2;
} else if (x_pos >= ClientRect.Right) {
//
// Make a temporary copy of the text so we can
// modify it
//
WideStringClass temp_string(0, true);
Get_Display_Text(temp_string);
WCHAR *text = temp_string.Peek_Buffer();
text[caretPos] = 0;
//
// Find how far we'd have to scroll in order to bring the
// caret back into view.
//
for (int index = 0; index < caretPos; ++index) {
float width = TextRenderer.Get_Text_Extents (text + index).X;
if (width < ClientRect.Width ()) {
ScrollPos = index + 2;
break;
}
}
}
//
// Adjust the scroll range to stay within our boundaries
//
ScrollPos = min (ScrollPos, Title.Get_Length () - 1);
ScrollPos = max (ScrollPos, 0);
//
// Repaint if necessary
//
if (old_scroll != ScrollPos) {
Set_Dirty ();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Int
//
////////////////////////////////////////////////////////////////
int
EditCtrlClass::Get_Int (void)
{
return _wtoi (Get_Text ());
}
////////////////////////////////////////////////////////////////
//
// Set_Int
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Set_Int (int value)
{
WideStringClass text(64, true);
text.Format (L"%d", value);
Set_Text (text);
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Text
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Set_Text (const WCHAR *title)
{
int count = wcslen(title);
// If the string is too long then truncate it so that it will fit.
if (count > TextLimit) {
WideStringClass text(count, true);
text = title;
text.Erase(TextLimit, text.Get_Length() - TextLimit);
DialogControlClass::Set_Text (text);
count = TextLimit;
} else {
DialogControlClass::Set_Text (title);
}
NumChars = count;
Set_Caret_Pos (Title.Get_Length ());
HilightAnchorPos = CaretPos;
HilightStartPos = -1;
HilightEndPos = -1;
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Text_Length
//
////////////////////////////////////////////////////////////////
int EditCtrlClass::Get_Text_Length(void) const
{
return NumChars;
}
////////////////////////////////////////////////////////////////
//
// Set_Text_Limit
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Set_Text_Limit (int numChars)
{
if (numChars > 0) {
TextLimit = numChars;
} else {
TextLimit = 0xFFFF;
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Text_Limit
//
////////////////////////////////////////////////////////////////
int
EditCtrlClass::Get_Text_Limit (void) const
{
return TextLimit;
}
////////////////////////////////////////////////////////////////
//
// Get_Caret_Pos
//
////////////////////////////////////////////////////////////////
int
EditCtrlClass::Get_Caret_Pos (void) const
{
if (mIME && mInComposition) {
return (CaretPos + mIME->GetCompositionCursorPos());
}
return CaretPos;
}
////////////////////////////////////////////////////////////////
//
// Set_Sel
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Set_Sel (int start_index, int end_index)
{
HilightAnchorPos = start_index;
HilightStartPos = start_index;
HilightEndPos = end_index;
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Sel
//
////////////////////////////////////////////////////////////////
void
EditCtrlClass::Get_Sel (int &start_index, int &end_index) const
{
start_index = HilightStartPos;
end_index = HilightEndPos;
return ;
}
/******************************************************************************
*
* NAME
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
*
******************************************************************************/
bool EditCtrlClass::IsIMEAllowed(void) const
{
// We use the ES_OEMCONVERT style to indicate that IME conversion
// is not allowed for this control. This style bit is set in the
// resource editor.
return ((Style & (ES_NUMBER|ES_OEMCONVERT)) == 0);
}
#ifdef SHOW_IME_TYPING
/******************************************************************************
*
* NAME
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
*
******************************************************************************/
void EditCtrlClass::Set_IME_Typing_Text_Pos(void)
{
Vector2 charExtent = TextRenderer.Get_Text_Extents(L"W");
int caretPos = Get_Caret_Pos();
Vector2 pos;
pos.X = (Pos_From_Character(caretPos) + (charExtent.X * 0.25f));
pos.Y = (ClientRect.Top + ((ClientRect.Bottom - ClientRect.Top) * 0.25f));
mIMETypingTip.Set_Position(pos);
}
/******************************************************************************
*
* NAME
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
*
******************************************************************************/
void EditCtrlClass::Show_IME_Typing_Text(const wchar_t* text)
{
mShowIMETypingText = (text && wcslen(text) > 0);
if (mShowIMETypingText) {
Set_IME_Typing_Text_Pos();
mIMETypingTip.Set_Text(text);
} else {
mIMETypingTip.Set_Text(L"");
}
}
/******************************************************************************
*
* NAME
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
*
******************************************************************************/
void EditCtrlClass::Hide_IME_Typing_Text(void)
{
mShowIMETypingText = false;
mIMETypingTip.Set_Text(L"");
}
#endif
/******************************************************************************
*
* NAME
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
*
******************************************************************************/
void EditCtrlClass::PositionCandidateList(void)
{
if (mIME) {
//-------------------------------------------------------------------------
// Position the candidate window under the edit control
//-------------------------------------------------------------------------
unsigned long start = 0;
unsigned long end = 0;
mIME->GetTargetClause(start, end);
int caretPos = CaretPos + start;
Vector2 pos;
pos.X = Pos_From_Character(caretPos);
pos.Y = (Rect.Bottom + 2.0f);
mCandidateList.Set_Window_Pos(pos);
//-------------------------------------------------------------------------
// Reposition the candidate list if it will go off the screen.
//-------------------------------------------------------------------------
bool reposition = false;
const RectClass& screen = Render2DClass::Get_Screen_Resolution();
const RectClass& ctrlRect = mCandidateList.Get_Window_Rect();
// If the list will go off the bottom of the screen move it above
// the edit control.
if (ctrlRect.Bottom > screen.Bottom) {
pos.Y = ((Rect.Top - 2.0f) - ctrlRect.Height());
WWASSERT((pos.Y >= 0.0f) && "CandidateCtrl off the top of the screen");
reposition = true;
}
// Do not allow the control to go off the right of the screen.
if (ctrlRect.Right > screen.Right) {
pos.X -= ((ctrlRect.Right - screen.Right) - 1);
WWASSERT((pos.X >= 0.0f) && "CandidateCtrl of the left of the screen");
reposition = true;
}
if (reposition) {
mCandidateList.Set_Window_Pos(pos);
}
}
}
/******************************************************************************
*
* NAME
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
*
******************************************************************************/
void EditCtrlClass::HandleNotification(IME::CompositionEvent& imeEvent)
{
switch (imeEvent.GetAction()) {
#ifdef SHOW_IME_TYPING
case IME::COMPOSITION_TYPING:
Show_IME_Typing_Text(imeEvent.Subject()->GetTypingString());
break;
#endif
case IME::COMPOSITION_START:
mInComposition = true;
Delete_Selection();
#ifdef SHOW_IME_TYPING
Show_IME_Typing_Text(imeEvent.Subject()->GetTypingString());
#endif
Set_Dirty(true);
break;
case IME::COMPOSITION_CHANGE:
Update_Scroll_Pos();
Set_Dirty(true);
break;
case IME::COMPOSITION_CANCEL:
case IME::COMPOSITION_END:
mInComposition = false;
#ifdef SHOW_IME_TYPING
Hide_IME_Typing_Text();
#endif
// Remove hilight
HilightStartPos = -1;
HilightEndPos = -1;
Set_Dirty(true);
break;
case IME::COMPOSITION_RESULT:
HilightStartPos = -1;
HilightEndPos = -1;
Insert_String(imeEvent.Subject()->GetResultString());
Update_Scroll_Pos();
break;
default:
break;
}
}
/******************************************************************************
*
* NAME
*
* DESCRIPTION
*
* INPUTS
*
* RESULT
*
******************************************************************************/
void EditCtrlClass::HandleNotification(IME::CandidateEvent& imeEvent)
{
switch (imeEvent.GetAction()) {
case IME::CANDIDATE_OPEN: {
#ifdef SHOW_IME_TYPING
Hide_IME_Typing_Text();
#endif
mCandidateList.Init(imeEvent.Subject());
PositionCandidateList();
Parent->Add_Control(&mCandidateList);
}
break;
case IME::CANDIDATE_CHANGE:
mCandidateList.Changed(imeEvent.Subject());
break;
case IME::CANDIDATE_CLOSE:
mCandidateList.Reset();
Parent->Remove_Control(&mCandidateList);
break;
default:
break;
}
}