/*
** Command & Conquer Generals Zero Hour(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) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: TextEntry.cpp ////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: TextEntry.cpp
//
// Created: Colin Day, June 2001
//
// Desc: Text entry GUI gadget
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "Common/Language.h"
#include "GameClient/DisplayStringManager.h"
#include "GameClient/GameWindow.h"
#include "GameClient/Gadget.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/IMEManager.h"
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
///////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static Byte drawCnt = 0;
// static TbIME *ourIME = NULL; ///< @todo need this for IME kanji support
static GameWindow *curWindow = NULL; /**< so we can keep track of the input
window when using IME */
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// GadgetTextEntryInput =======================================================
/** Handle input for text entry field */
//=============================================================================
WindowMsgHandledType GadgetTextEntryInput( GameWindow *window, UnsignedInt msg,
WindowMsgData mData1, WindowMsgData mData2 )
{
EntryData *e = (EntryData *)window->winGetUserData();
WinInstanceData *instData = window->winGetInstanceData();
if ( TheIMEManager && TheIMEManager->isAttachedTo( window) && TheIMEManager->isComposing())
{
// ignore input while IME has focus
return MSG_HANDLED;
}
switch( msg )
{
// ------------------------------------------------------------------------
case GWM_IME_CHAR:
{
WideChar ch = (WideChar) mData1;
// --------------------------------------------------------------------
if ( ch == VK_RETURN )
{
// Done with this edit
TheWindowManager->winSendSystemMsg( window->winGetOwner(),
GEM_EDIT_DONE,
(WindowMsgData)window,
0 );
return MSG_HANDLED;
};
if( ch )
{
// Constrain keys based on rules for entry box.
if( e->numericalOnly )
{
if( TheWindowManager->winIsDigit( ch ) == 0 )
return MSG_HANDLED;
}
if( e->alphaNumericalOnly )
{
if( TheWindowManager->winIsAlNum( ch ) == 0 )
return MSG_HANDLED;
}
if ( e->aSCIIOnly )
{
if ( TheWindowManager->winIsAscii( ch ) == 0 )
{
return MSG_HANDLED;
}
}
if( e->charPos < e->maxTextLen-1 )
{
e->text->appendChar( ch );
e->sText->appendChar( L'*' );
e->charPos++;
TheWindowManager->winSendSystemMsg( window->winGetOwner(),
GEM_UPDATE_TEXT,
(WindowMsgData)window,
0 );
}
}
break;
}
// ------------------------------------------------------------------------
case GWM_CHAR:
if ( BitTest( mData2, KEY_STATE_DOWN ) && BitTest( mData2, KEY_STATE_ALT | KEY_STATE_CONTROL ) )
{
return MSG_IGNORED; // text extries shouldn't care about CTRL+* or ALT+*
}
switch( mData1 )
{
/*
// --------------------------------------------------------------------
case KEY_KPENTER:
case KEY_ENTER:
// Done with this edit
if( BitTest( mData2, KEY_STATE_DOWN ) )
{
if( e->receivedUnichar == FALSE )
{
TheWindowManager->winSendSystemMsg( window->winGetOwner(),
GEM_EDIT_DONE,
(WindowMsgData)window,
0 );
}
}
break;
*/
// --------------------------------------------------------------------
// Don't process these keys
case KEY_ESC:
case KEY_PGUP:
case KEY_PGDN:
case KEY_HOME:
case KEY_END:
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_CAPS:
case KEY_DEL:
return MSG_IGNORED;
// --------------------------------------------------------------------
case KEY_DOWN:
case KEY_RIGHT:
case KEY_TAB:
if( BitTest( mData2, KEY_STATE_DOWN ) )
{
GameWindow *parent;
parent = window->winGetParent();
if(parent && !BitTest(parent->winGetStyle(), GWS_COMBO_BOX))
parent = NULL;
if(parent)
TheWindowManager->winNextTab(parent);
else
TheWindowManager->winNextTab(window);
}
break;
// --------------------------------------------------------------------
case KEY_UP:
case KEY_LEFT:
if( BitTest( mData2, KEY_STATE_DOWN ) )
{
GameWindow *parent;
parent = window->winGetParent();
if(parent && !BitTest(parent->winGetStyle(), GWS_COMBO_BOX))
parent = NULL;
if(parent)
TheWindowManager->winPrevTab(parent);
else
TheWindowManager->winPrevTab(window);
}
break;
// --------------------------------------------------------------------
case KEY_BACKSPACE:
if( BitTest( mData2, KEY_STATE_DOWN ) )
{
// if conCharPos != 0 this will fall through to next case.
// it should be noted that conCharPos can only != 0 in Jap & Kor
if( e->conCharPos == 0 )
{
if( e->charPos > 0 )
{
e->text->removeLastChar();
e->sText->removeLastChar();
e->charPos--;
TheWindowManager->winSendSystemMsg( window->winGetOwner(),
GEM_UPDATE_TEXT,
(WindowMsgData)window,
0 );
} // end if
}
}
break;
} // end switch( mData1 )
break;
// ------------------------------------------------------------------------
case GWM_LEFT_DOWN:
BitSet( instData->m_state, WIN_STATE_HILITED );
TheWindowManager->winSetFocus( window );
break;
// ------------------------------------------------------------------------
case GWM_MOUSE_ENTERING:
if (BitTest( instData->getStyle(), GWS_MOUSE_TRACK ) )
{
BitSet( instData->m_state, WIN_STATE_HILITED );
TheWindowManager->winSendSystemMsg( window->winGetOwner(),
GBM_MOUSE_ENTERING,
(WindowMsgData)window, 0 );
//TheWindowManager->winSetFocus( window );
}
break;
// ------------------------------------------------------------------------
case GWM_MOUSE_LEAVING:
if( BitTest( instData->getStyle(), GWS_MOUSE_TRACK ) )
{
BitClear( instData->m_state, WIN_STATE_HILITED );
TheWindowManager->winSendSystemMsg( window->winGetOwner(),
GBM_MOUSE_LEAVING,
(WindowMsgData)window, 0 );
}
break;
// ------------------------------------------------------------------------
case GWM_LEFT_DRAG:
if( BitTest( instData->getStyle(), GWS_MOUSE_TRACK ) )
TheWindowManager->winSendSystemMsg( window->winGetOwner(),
GGM_LEFT_DRAG,
(WindowMsgData)window, 0 );
break;
// ------------------------------------------------------------------------
default:
return MSG_IGNORED;
} // end switch( msg )
return MSG_HANDLED;
} // end GadgetTextEntryInput
// GadgetTextEntrySystem ======================================================
/** Handle system messages for entry field */
//=============================================================================
WindowMsgHandledType GadgetTextEntrySystem( GameWindow *window, UnsignedInt msg,
WindowMsgData mData1, WindowMsgData mData2 )
{
EntryData *e = (EntryData *)window->winGetUserData();
WinInstanceData *instData = window->winGetInstanceData();
switch( msg )
{
// ------------------------------------------------------------------------
case GEM_GET_TEXT:
*(UnicodeString*)mData2 = e->text->getText();
break;
// ------------------------------------------------------------------------
case GEM_SET_TEXT:
{
const UnicodeString* ustr = (const UnicodeString*)mData1;
e->text->setText( *ustr );
e->charPos = ustr->getLength();
e->constructText->setText( UnicodeString::TheEmptyString );
e->conCharPos = 0;
// set our secret text string to be filled with '*' the same length
e->sText->setText( UnicodeString::TheEmptyString );
Int len = ustr->getLength();
for( Int i = 0; i < len; i++ )
e->sText->appendChar( L'*' );
break;
} // end set text
// ------------------------------------------------------------------------
case GWM_CREATE:
break;
// ------------------------------------------------------------------------
case GWM_DESTROY:
// delete the edit display string
TheDisplayStringManager->freeDisplayString( e->text );
TheDisplayStringManager->freeDisplayString( e->sText );
TheDisplayStringManager->freeDisplayString( e->constructText );
// delete construct list
if( e->constructList )
TheWindowManager->winDestroy( e->constructList );
// free all edit data
delete( (EntryData *)window->winGetUserData() );
break;
// ------------------------------------------------------------------------
case GWM_INPUT_FOCUS:
if( mData1 == FALSE )
{
// If we're losing focus
/// @todo need to enable this for IME support
// ourIME->UnActivate();
curWindow = NULL;
BitClear( instData->m_state, WIN_STATE_SELECTED );
BitClear( instData->m_state, WIN_STATE_HILITED );
if( e->constructList )
e->constructList->winHide( TRUE );
e->constructText->setText( UnicodeString::TheEmptyString );
e->conCharPos = 0;
if(TheIMEManager && TheIMEManager->isAttachedTo(window))
TheIMEManager->attach(NULL);
//TheIMEManager->detatch();
}
else
{
curWindow = window;
/// @todo need to enable this for IME support
if (TheIMEManager)
TheIMEManager->attach( window );
// ourIME->Activate( (void *)ApplicationHWnd );
BitSet( instData->m_state, WIN_STATE_SELECTED );
BitSet( instData->m_state, WIN_STATE_HILITED );
}
TheWindowManager->winSendSystemMsg( window->winGetOwner(),
GGM_FOCUS_CHANGE,
mData1,
window->winGetWindowId() );
*(Bool*)mData2 = TRUE;
break;
default:
return MSG_IGNORED;
} // end switch( msg )
return MSG_HANDLED;
} // end GadgetTextEntrySystem
/** @todo we might want to do something like this if we use IME for language
* support in this product */
/*
// used to create interface to IME
BoolCode InitializeEntryGadget( void )
{
ourIME = NEW TbIME;
ourIME->Composition_SetMaxLength( 11 );
return TRUE;
}
// used to destroy interface to IME
BoolCode ShutdownEntryGadget( void )
{
delete ourIME;
ourIME = NULL;
return TRUE;
}
void InformEntry( WideChar c )
{
Int i, listCount = 0;
EntryData *e;
if( ourIME == NULL || curWindow == NULL )
return;
e = (EntryData *)curWindow->winGetUserData();
if( ( (OurLanguage == LANGUAGE_ID_JAPANESE) ||
(OurLanguage == LANGUAGE_ID_KOREAN) ) &&
( (e->aSCIIOnly == FALSE ) &&
(e->alphaNumericalOnly == FALSE ) &&
(e->numericalOnly == FALSE ) ) )
{
e->receivedUnichar = TRUE;
// we must eat the following keys
switch( c )
{
case L'\a':
case L'\b':
case L'\f':
case L'\t':
case L'\v':
return;
// we must completely ignore the return key
case L'\r':
case L'\n':
e->receivedUnichar = FALSE;
return;
}
if( e->charPos < e->maxTextLen-1 )
{
e->text[ e->charPos++ ] = c;
e->text[ e->charPos ] = 0;
}
// always update the construction buffer after a key has come through here.
TheWindowManager->winStrcpy( e->constructText, (WideChar *)ourIME->Composition_Get() );
e->conCharPos = NoxStrlen( e->constructText );
// we might need to update our listbox
listCount = ourIME->CandidateList_GetSize();
if( TRUE ) //listCount == 0)
{
// if no entries just hide it and leave
if( e->constructList )
e->constructList->winHide( TRUE );
}
else
{
Int maxWidth = 0;
ListboxData list = NULL;
ICoord2D constructSize, sliderSize;
WinHide( e->constructList, FALSE );
list = (ListBoxData)e->constructList->winGetUserData();
e->constructList->winGetSize( &constructSize.x, &constructSize.y );
list->slider->winGetSize( &sliderSize.x, &sliderSize.y );
TheWindowManager->winSendSystemMsg( e->constructList, GLM_DEL_ALL, 0, 0 );
for( i=0; iCandidateList_GetItem( i );
TheWindowManager->winGetTextSize( e->constructList->instData.font,
text, NULL, &tempWidth, 0 );
if( tempWidth > maxWidth )
maxWidth = tempWidth;
UnicodeString tmp(text);
TheWindowManager->winSendSystemMsg( e->constructList, GLM_ADD_ENTRY,
(WindowMsgData)&tmp, -1 );
}
e->constructList->winSetSize( maxWidth + sliderSize.y,
constructSize.y );
}
}
}
*/
// GadgetTextEntrySetFont =====================================================
/** Set the font for a text entry control, we need to set the window
* text font, the tooltip font, and the edit text display strings for
* the text data itself and the secret text */
//=============================================================================
void GadgetTextEntrySetFont( GameWindow *g, GameFont *font )
{
EntryData *entryData = (EntryData *)g->winGetUserData();
DisplayString *dString;
// set the font for the display strings all windows have
dString = g->winGetInstanceData()->getTextDisplayString();
if( dString )
dString->setFont( font );
dString = g->winGetInstanceData()->getTooltipDisplayString();
if( dString )
dString->setFont( font );
// text entry specific
if( entryData )
{
dString = entryData->text;
if( dString )
dString->setFont( font );
dString = entryData->sText;
if( dString )
dString->setFont( font );
} // end if
} // end GadgetTextEntrySetFont
// GadgetTextEntryGetText =======================================================
/** Get the text for a Text entry */
//=============================================================================
UnicodeString GadgetTextEntryGetText( GameWindow *textentry )
{
// sanity
if( textentry == NULL )
return UnicodeString::TheEmptyString;
// verify that this is a list box
if( BitTest( textentry->winGetStyle(), GWS_ENTRY_FIELD ) == FALSE )
return UnicodeString::TheEmptyString;
UnicodeString result;
TheWindowManager->winSendSystemMsg( textentry, GEM_GET_TEXT, 0, (WindowMsgData)&result );
return result;
} // end GadgetListBoxGetText