Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Rework in-game console & UI text editing.

The cursor position is now a character count, not a byte count.
However, the maximum length remains in bytes.
  • Loading branch information...
commit 263e3667c3badfc254f963928b1e34b5274b9ccf 1 parent 336e188
Darren Salt authored
View
2  CMakeLists.txt
@@ -724,6 +724,7 @@ set( CGAMEGPP
${GPP_DIR}/src/cgame/cg_particles.c
${GPP_DIR}/src/cgame/cg_tutorial.c
${GPP_DIR}/src/ui/ui_shared.c
+ ${GPP_DIR}/src/ui/ui_utf8.c
${MOUNT_DIR}/engine/qcommon/q_math.c
${MOUNT_DIR}/engine/qcommon/q_shared.c
${GPP_DIR}/src/cgame/cg_api.c
@@ -771,6 +772,7 @@ set( UIGPP
${GPP_DIR}/src/game/bg_lib.c
${MOUNT_DIR}/engine/qcommon/q_math.c
${MOUNT_DIR}/engine/qcommon/q_shared.c
+ ${GPP_DIR}/src/ui/ui_utf8.c
${GPP_DIR}/src/ui/ui_api.c
)
View
141 src/engine/client/cl_keys.c
@@ -514,69 +514,50 @@ void Field_KeyDownEvent( field_t *edit, int key )
}
key = tolower( key );
- len = strlen( edit->buffer );
- s = &edit->buffer[ edit->cursor ];
- width = Q_UTF8Width( edit->buffer + edit->cursor );
+ len = Q_UTF8Strlen( edit->buffer );
+ s = &edit->buffer[ Field_CursorToOffset( edit ) ];
+ width = Q_UTF8Width( s );
switch ( key )
{
case K_DEL:
- if ( edit->cursor + width < len )
+ if ( *s )
{
- memmove( edit->buffer + edit->cursor,
- edit->buffer + edit->cursor + width, len - edit->cursor );
+ memmove( s, s + width, strlen( s + width ) + 1 );
}
break;
case K_RIGHTARROW:
- if ( edit->cursor + width < len )
+ if ( edit->cursor < len )
{
- edit->cursor += width;
+ edit->cursor++;
}
break;
case K_LEFTARROW:
- while ( edit->cursor > 0 )
+ if ( edit->cursor > 0 )
{
edit->cursor--;
- if( !Q_UTF8ContByte( edit->buffer[ edit->cursor ] ) )
- {
- break;
- }
}
break;
- case K_HOME:
- edit->cursor = 0;
-
case 'a':
if ( keys[ K_CTRL ].down )
{
+ case K_HOME:
edit->cursor = 0;
}
break;
- case K_END:
- while( s > edit->buffer && edit->cursor > 0 && Q_UTF8ContByte( *s ) )
- {
- edit->cursor--;
- s--;
- }
- break;
-
case 'e':
if ( keys[ K_CTRL ].down )
{
- while( s > edit->buffer && edit->cursor > 0 && Q_UTF8ContByte( *s ) )
- {
- edit->cursor--;
- s--;
- }
- break;
+ case K_END:
+ edit->cursor = len;
}
break;
@@ -591,13 +572,9 @@ void Field_KeyDownEvent( field_t *edit, int key )
{
edit->scroll = edit->cursor;
}
- else if ( edit->cursor >= edit->scroll + edit->widthInChars + Q_UTF8Width( edit->buffer + edit->scroll ) && edit->cursor <= len )
+ else if ( edit->cursor >= edit->scroll + edit->widthInChars )
{
edit->scroll = edit->cursor - edit->widthInChars + 1;
- while( Q_UTF8ContByte( edit->buffer[ edit->scroll ] && edit->scroll > 0 ) )
- {
- edit->scroll--;
- }
}
}
@@ -608,7 +585,7 @@ Field_CharEvent
*/
void Field_CharEvent( field_t *edit, const char *s )
{
- int len, width;
+ int len, width, oldWidth, offset;
if ( *s == 'v' - 'a' + 1 ) // ctrl-v is paste
{
@@ -627,21 +604,20 @@ void Field_CharEvent( field_t *edit, const char *s )
if ( *s == 'h' - 'a' + 1 ) // ctrl-h is backspace
{
- while ( edit->cursor > 0 )
+ if ( edit->cursor )
{
- qboolean isContinue = Q_UTF8ContByte( edit->buffer[ edit->cursor - 1 ] );
- memmove( edit->buffer + edit->cursor - 1,
- edit->buffer + edit->cursor, len + 1 - edit->cursor );
- edit->cursor--;
+ int posFrom, posTo;
+
+ posFrom = Field_CursorToOffset( edit );
+ --edit->cursor;
+ posTo = Field_CursorToOffset( edit );
+
+ memmove( edit->buffer + posTo, edit->buffer + posFrom, len + 1 - posFrom );
if ( edit->cursor < edit->scroll )
{
edit->scroll--;
}
- if( !isContinue )
- {
- break;
- }
}
return;
@@ -656,7 +632,7 @@ void Field_CharEvent( field_t *edit, const char *s )
if ( *s == 'e' - 'a' + 1 ) // ctrl-e is end
{
- edit->cursor = len;
+ edit->cursor = Field_OffsetToCursor( edit, len );
edit->scroll = edit->cursor - edit->widthInChars;
return;
}
@@ -669,47 +645,27 @@ void Field_CharEvent( field_t *edit, const char *s )
return;
}
- if ( key_overstrikeMode )
- {
- if ( edit->cursor == MAX_EDIT_LINE - 1 )
- {
- return;
- }
+ width = Q_UTF8Width( s );
+ offset = Field_CursorToOffset( edit );
- edit->buffer[ edit->cursor ] = *s;
- edit->cursor++;
- }
- else // insert mode
+ // if overstrike, adjust the width according to what's being replaced
+ // (at end-of-string, just insert)
+ oldWidth = ( key_overstrikeMode && edit->buffer[ offset ] ) ? Q_UTF8Width( edit->buffer + offset ) : 0;
+
+ if ( len + width - oldWidth >= MAX_EDIT_LINE )
{
- width = Q_UTF8Width( s );
- if ( len == MAX_EDIT_LINE - 1 )
- {
- return; // all full
- }
+ return;
+ }
- if( edit->cursor + width >= MAX_EDIT_LINE )
- {
- return;
- }
- memmove( edit->buffer + edit->cursor + width,
- edit->buffer + edit->cursor, len + 1 - edit->cursor );
+ memmove( edit->buffer + offset + width,
+ edit->buffer + offset + oldWidth, len + 1 - offset - oldWidth );
+ Com_Memcpy( edit->buffer + offset, s, width );
+ ++edit->cursor;
- Com_Memcpy( edit->buffer + edit->cursor, s, width );
- edit->cursor += width;
- }
- if ( edit->cursor >= edit->widthInChars )
- {
do
{
edit->scroll++;
- } while( Q_UTF8ContByte( edit->buffer[ edit->scroll ] ) && edit->scroll < edit->cursor );
-
- }
-
- if ( edit->cursor == len + 1 )
- {
- edit->buffer[ edit->cursor ] = 0;
- }
+ } while ( edit->cursor >= edit->scroll +edit->widthInChars );
}
/*
@@ -969,18 +925,7 @@ void Console_Key( int key )
if ( ( key == K_MWHEELUP && keys[ K_SHIFT ].down ) || ( key == K_UPARROW ) || ( key == K_KP_UPARROW ) ||
( ( tolower( key ) == 'p' ) && keys[ K_CTRL ].down ) )
{
- Q_strncpyz( g_consoleField.buffer, Hist_Prev(), sizeof( g_consoleField.buffer ) );
- g_consoleField.cursor = strlen( g_consoleField.buffer );
-
- if ( g_consoleField.cursor >= g_consoleField.widthInChars )
- {
- g_consoleField.scroll = g_consoleField.cursor - g_consoleField.widthInChars + 1;
- }
- else
- {
- g_consoleField.scroll = 0;
- }
-
+ Field_Set( &g_consoleField, Hist_Prev() );
con.acLength = 0;
return;
}
@@ -993,17 +938,7 @@ void Console_Key( int key )
if ( history )
{
- Q_strncpyz( g_consoleField.buffer, history, sizeof( g_consoleField.buffer ) );
- g_consoleField.cursor = strlen( g_consoleField.buffer );
-
- if ( g_consoleField.cursor >= g_consoleField.widthInChars )
- {
- g_consoleField.scroll = g_consoleField.cursor - g_consoleField.widthInChars + 1;
- }
- else
- {
- g_consoleField.scroll = 0;
- }
+ Field_Set( &g_consoleField, history );
}
else if ( g_consoleField.buffer[ 0 ] )
{
View
77 src/engine/qcommon/common.c
@@ -3993,6 +3993,17 @@ void Field_Clear( field_t *edit )
/*
==================
+Field_SetCursor
+==================
+*/
+void Field_SetCursor( field_t *edit, int cursor )
+{
+ edit->cursor = cursor;
+ edit->scroll = ( cursor > edit->widthInChars ) ? ( cursor - edit->widthInChars ) : 0;
+}
+
+/*
+==================
Field_Set
==================
*/
@@ -4000,16 +4011,42 @@ void Field_Set( field_t *edit, const char *content )
{
memset( edit->buffer, 0, MAX_EDIT_LINE );
strncpy( edit->buffer, content, MAX_EDIT_LINE );
- edit->cursor = strlen( edit->buffer );
+ Field_SetCursor( edit, Q_UTF8Strlen( edit->buffer ) );
+}
- if ( edit->cursor > edit->widthInChars )
+/*
+==================
+Field_CursorToOffset
+==================
+*/
+int Field_CursorToOffset( field_t *edit )
+{
+ int i = -1, j = 0;
+
+ while ( ++i < edit->cursor )
{
- edit->scroll = edit->cursor - edit->widthInChars;
+ j += Q_UTF8Width( edit->buffer + j );
}
- else
+
+ return j;
+}
+
+/*
+==================
+Field_OffsetToCursor
+==================
+*/
+int Field_OffsetToCursor( field_t *edit, int offset )
+{
+ int i = 0, j = 0;
+
+ while ( i < offset )
{
- edit->scroll = 0;
+ i += Q_UTF8Width( edit->buffer + i );
+ ++j;
}
+
+ return j;
}
/*
@@ -4019,23 +4056,19 @@ Field_WordDelete
*/
void Field_WordDelete( field_t *edit )
{
- while ( edit->cursor )
- {
- if ( edit->buffer[ edit->cursor - 1 ] != ' ' )
- {
- edit->buffer[ edit->cursor - 1 ] = 0;
- edit->cursor--;
- }
- else
- {
- edit->cursor--;
+ int index = Field_CursorToOffset( edit );
+ int start = index;
- if ( edit->buffer[ edit->cursor - 1 ] != ' ' )
- {
- return;
- }
- }
- }
+ // search back past spaces
+ while ( --index >= 0 && edit->buffer[ index ] == ' ' ) {}
+
+ // search back past non-spaces
+ if ( index ) while ( --index >= 0 && edit->buffer[ index ] != ' ' ) {}
+
+ // strcpy would probably do, but possible overlap
+ memmove( edit->buffer + index, edit->buffer + start, strlen( edit->buffer + start ) + 1 );
+
+ Field_SetCursor( edit, Field_OffsetToCursor( edit, index ) );
}
static const char *completionString;
@@ -4215,7 +4248,7 @@ static qboolean Field_Complete( void )
Q_strncpyz( &completionField->buffer[ completionOffset ], shortestMatch,
sizeof( completionField->buffer ) - completionOffset );
- completionField->cursor = strlen( completionField->buffer );
+ completionField->cursor = Q_UTF8Strlen( completionField->buffer );
if ( matchCount == 1 )
{
View
3  src/engine/qcommon/qcommon.h
@@ -927,6 +927,9 @@ void Field_CompleteDelay( void );
void Field_CompleteCommand( char *cmd,
qboolean doCommands, qboolean doCvars );
+int Field_OffsetToCursor( field_t *edit, int offset );
+int Field_CursorToOffset( field_t *edit );
+
/*
==============================================================
View
8 src/gamelogic/gpp/src/cgame/cg_draw.c
@@ -4074,9 +4074,9 @@ void CG_EventHandling( int type )
}
}
-void CG_KeyEvent( int key, qboolean down )
+void CG_KeyEvent( int key, int chr, int flags )
{
- if ( !down )
+ if ( !( flags & ( 1 << KEYEVSTATE_DOWN ) ) )
{
return;
}
@@ -4090,7 +4090,7 @@ void CG_KeyEvent( int key, qboolean down )
return;
}
- Display_HandleKey( key, down, cgs.cursorX, cgs.cursorY );
+ Display_HandleKey( key, chr, qtrue, cgs.cursorX, cgs.cursorY );
if ( cgs.capturedItem )
{
@@ -4098,7 +4098,7 @@ void CG_KeyEvent( int key, qboolean down )
}
else
{
- if ( key == K_MOUSE2 && down )
+ if ( key == K_MOUSE2 /*&& down*/ )
{
cgs.capturedItem = Display_CaptureItem( cgs.cursorX, cgs.cursorY );
}
View
2  src/gamelogic/gpp/src/cgame/cg_local.h
@@ -1652,7 +1652,7 @@ void CG_UpdateCvars( void );
int CG_CrosshairPlayer( void );
int CG_LastAttacker( void );
void CG_LoadMenus( const char *menuFile );
-void CG_KeyEvent( int key, qboolean down );
+void CG_KeyEvent( int key, int chr, int flags );
void CG_MouseEvent( int x, int y );
void CG_EventHandling( int type );
void CG_SetScoreSelection( void *menu );
View
11 src/gamelogic/gpp/src/cgame/cg_main.c
@@ -73,7 +73,16 @@ Q_EXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3,
return CG_LastAttacker();
case CG_KEY_EVENT:
- CG_KeyEvent( arg0, arg1 );
+ if ( arg1 & ( 1 << KEYEVSTATE_CHAR ) )
+ {
+ arg0 &= ~K_CHAR_FLAG;
+ arg0 |= ( !!( arg1 & ( 1 << KEYEVSTATE_BIT ) ) ) << ( K_CHAR_BIT - 1 );
+ CG_KeyEvent( 0, arg0, arg1 );
+ }
+ else
+ {
+ CG_KeyEvent( arg0, 0, arg1 );
+ }
return 0;
case CG_MOUSE_EVENT:
View
19 src/gamelogic/gpp/src/ui/ui_main.c
@@ -154,7 +154,7 @@ This must be the very first function compiled into the .qvm file
*/
void UI_Init( qboolean );
void UI_Shutdown( void );
-void UI_KeyEvent( int key, qboolean down );
+void UI_KeyEvent( int key, int chr, int flags );
void UI_MouseEvent( int dx, int dy );
int UI_MousePosition( void );
void UI_SetMousePosition( int x, int y );
@@ -180,7 +180,16 @@ Q_EXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3,
return 0;
case UI_KEY_EVENT:
- UI_KeyEvent( arg0, arg1 );
+ if ( arg1 & ( 1 << KEYEVSTATE_CHAR ) )
+ {
+ arg0 &= ~K_CHAR_FLAG;
+ arg0 |= ( arg1 & ( 1 << KEYEVSTATE_BIT ) ) ? K_CHAR_FLAG : 0;
+ UI_KeyEvent( 0, arg0, arg1 );
+ }
+ else
+ {
+ UI_KeyEvent( arg0, 0, arg1 );
+ }
return 0;
case UI_MOUSE_EVENT:
@@ -4934,7 +4943,7 @@ void UI_Init( qboolean inGameLoad )
UI_KeyEvent
=================
*/
-void UI_KeyEvent( int key, qboolean down )
+void UI_KeyEvent( int key, int chr, int flags )
{
if ( Menu_Count() > 0 )
{
@@ -4942,13 +4951,13 @@ void UI_KeyEvent( int key, qboolean down )
if ( menu )
{
- if ( key == K_ESCAPE && down && !Menus_AnyFullScreenVisible() )
+ if ( key == K_ESCAPE && ( flags & ( 1 << KEYEVSTATE_DOWN ) ) && !Menus_AnyFullScreenVisible() )
{
Menus_CloseAll();
}
else
{
- Menu_HandleKey( menu, key, down );
+ Menu_HandleKey( menu, key, chr, !!( flags & ( 1 << KEYEVSTATE_DOWN ) ) );
}
}
else
View
105 src/gamelogic/gpp/src/ui/ui_shared.c
@@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ui_shared.h"
+#include "ui_utf8.h"
#define SCROLL_TIME_START 500
#define SCROLL_TIME_ADJUST 150
@@ -2433,6 +2434,12 @@ static void UI_Text_Paint_Generic( float x, float y, float scale, float gapAdjus
len = limit;
}
+ // if drawing a cursor, convert the cursor index to an offset into the string
+ if ( cursorPos > 0 )
+ {
+ cursorPos = ui_CursorToOffset( text, cursorPos );
+ }
+
DC->setColor( color );
memcpy( &newColor[ 0 ], &color[ 0 ], sizeof( vec4_t ) );
@@ -3625,15 +3632,15 @@ static void Item_TextField_CalcPaintOffset( itemDef_t *item, char *buff )
else
{
// If there is a maximum field width
-
if ( editPtr->maxFieldWidth > 0 )
{
+ int offset;
// If the cursor is at the end of the string, maximise the amount of the
// string that's visible
- if ( buff[ item->cursorPos + 1 ] == '\0' )
+ if ( item->cursorPos == ui_UTF8Strlen( buff ) )
{
- while ( UI_Text_Width( &buff[ editPtr->paintOffset ], item->textscale ) <=
+ while ( UI_Text_Width( buff + ui_CursorToOffset( buff, editPtr->paintOffset ), item->textscale ) <=
( editPtr->maxFieldWidth - EDIT_CURSOR_WIDTH ) && editPtr->paintOffset > 0 )
{
editPtr->paintOffset--;
@@ -3643,20 +3650,22 @@ static void Item_TextField_CalcPaintOffset( itemDef_t *item, char *buff )
buff[ item->cursorPos + 1 ] = '\0';
// Shift paintOffset so that the cursor is visible
+ offset = ui_CursorToOffset( buff, editPtr->paintOffset );
- while ( UI_Text_Width( &buff[ editPtr->paintOffset ], item->textscale ) >
+ while ( UI_Text_Width( buff + offset, item->textscale ) >
( editPtr->maxFieldWidth - EDIT_CURSOR_WIDTH ) )
{
editPtr->paintOffset++;
+ offset += ui_UTF8Width( buff );
}
}
}
}
-qboolean Item_TextField_HandleKey( itemDef_t *item, int key )
+qboolean Item_TextField_HandleKey( itemDef_t *item, int key, int chr )
{
char buff[ 1024 ];
- int len;
+ int len, lenChars;
itemDef_t *newItem = NULL;
editFieldDef_t *editPtr = item->typeData.edit;
qboolean releaseFocus = qtrue;
@@ -3665,41 +3674,45 @@ qboolean Item_TextField_HandleKey( itemDef_t *item, int key )
{
Com_Memset( buff, 0, sizeof( buff ) );
DC->getCVarString( item->cvar, buff, sizeof( buff ) );
- len = strlen( buff );
- if ( len < item->cursorPos )
- {
- item->cursorPos = len;
- }
+ len = strlen( buff );
if ( editPtr->maxChars && len > editPtr->maxChars )
{
len = editPtr->maxChars;
+ while ( len && ui_UTF8ContByte( buff[len] ) ) { --len; } // don't leave a partial multibyte character!
+ buff[ len ] = 0;
}
- if ( key & K_CHAR_FLAG )
+ lenChars = ui_UTF8Strlen( buff );
+
+ if ( lenChars < item->cursorPos )
{
- key &= ~K_CHAR_FLAG;
+ item->cursorPos = ui_OffsetToCursor( buff, len );
+ }
- if ( key == 'h' - 'a' + 1 )
+ if ( chr )
+ {
+ if ( chr == 'h' - 'a' + 1 )
{
// ctrl-h is backspace
if ( item->cursorPos > 0 )
{
- memmove( &buff[ item->cursorPos - 1 ], &buff[ item->cursorPos ], len + 1 - item->cursorPos );
- item->cursorPos--;
+ int index = ui_CursorToOffset( buff, --item->cursorPos );
+ int width = ui_UTF8Width( buff + index );
+ memmove( buff + index, buff + index + width, len + 1 - index - width );
}
DC->setCVar( item->cvar, buff );
}
- else if ( key < 32 || key == 127 || !item->cvar )
+ else if ( chr < 32 || chr == 127 || !item->cvar )
{
// Ignore any non printable chars
releaseFocus = qfalse;
goto exit;
}
- else if ( item->type == ITEM_TYPE_NUMERICFIELD && ( key < '0' || key > '9' ) )
+ else if ( item->type == ITEM_TYPE_NUMERICFIELD && ( chr < '0' || chr > '9' ) )
{
// Ignore non-numeric characters
releaseFocus = qfalse;
@@ -3707,35 +3720,25 @@ qboolean Item_TextField_HandleKey( itemDef_t *item, int key )
}
else
{
- if ( !DC->getOverstrikeMode() )
- {
- if ( ( len == MAX_EDITFIELD - 1 ) || ( editPtr->maxChars && len >= editPtr->maxChars ) )
- {
- // Reached maximum field length
- releaseFocus = qfalse;
- goto exit;
- }
+ int index = ui_CursorToOffset( buff, item->cursorPos );
+ int width = ui_UTF8WidthCP( chr );
+ int oldWidth = ( DC->getOverstrikeMode() && buff[ index ] ) ? ui_UTF8Width( buff + index ) : 0;
+ int max = min( editPtr->maxChars, MAX_EDITFIELD - 1 );
+ max = max ? max : MAX_EDITFIELD - 1;
- memmove( &buff[ item->cursorPos + 1 ], &buff[ item->cursorPos ], len + 1 - item->cursorPos );
- }
- else
+ if ( len + width - oldWidth > max )
{
// Reached maximum field length
- if ( editPtr->maxChars && item->cursorPos >= editPtr->maxChars )
- {
- releaseFocus = qfalse;
- goto exit;
- }
+ releaseFocus = qfalse;
+ goto exit;
}
- buff[ item->cursorPos ] = key;
+ memmove( buff + index + width, buff + index + oldWidth, len + 1 - index - oldWidth );
+ memcpy( buff + index, ui_UTF8Encode( chr ), width );
DC->setCVar( item->cvar, buff );
- if ( item->cursorPos < len + 1 )
- {
- item->cursorPos++;
- }
+ item->cursorPos++;
}
}
else
@@ -3744,9 +3747,11 @@ qboolean Item_TextField_HandleKey( itemDef_t *item, int key )
{
case K_DEL:
case K_KP_DEL:
- if ( item->cursorPos < len )
+ if ( item->cursorPos < lenChars )
{
- memmove( buff + item->cursorPos, buff + item->cursorPos + 1, len - item->cursorPos );
+ int index = ui_CursorToOffset( buff, item->cursorPos );
+ int width = ui_UTF8Width( buff + index );
+ memmove( buff + index, buff + index + width, len + 1 - index - width );
DC->setCVar( item->cvar, buff );
}
@@ -3754,7 +3759,7 @@ qboolean Item_TextField_HandleKey( itemDef_t *item, int key )
case K_RIGHTARROW:
case K_KP_RIGHTARROW:
- if ( item->cursorPos < len )
+ if ( item->cursorPos < lenChars )
{
item->cursorPos++;
}
@@ -3778,7 +3783,7 @@ qboolean Item_TextField_HandleKey( itemDef_t *item, int key )
case K_END:
case K_KP_END:
- item->cursorPos = len;
+ item->cursorPos = lenChars;
break;
@@ -4405,7 +4410,7 @@ int Display_VisibleMenuCount( void )
return count;
}
-void Menus_HandleOOBClick( menuDef_t *menu, int key, qboolean down )
+void Menus_HandleOOBClick( menuDef_t *menu, int key, int chr, qboolean down )
{
if ( menu )
{
@@ -4426,7 +4431,7 @@ void Menus_HandleOOBClick( menuDef_t *menu, int key, qboolean down )
Menus_Close( menu );
Menus_Activate( &Menus[ i ] );
Menu_HandleMouseMove( &Menus[ i ], DC->cursorx, DC->cursory );
- Menu_HandleKey( &Menus[ i ], key, down );
+ Menu_HandleKey( &Menus[ i ], key, chr, down );
}
}
@@ -4460,7 +4465,7 @@ static rectDef_t *Item_CorrectedTextRect( itemDef_t *item )
return &rect;
}
-void Menu_HandleKey( menuDef_t *menu, int key, qboolean down )
+void Menu_HandleKey( menuDef_t *menu, int key, int chr, qboolean down )
{
static qboolean shift = qfalse;
@@ -4495,7 +4500,7 @@ void Menu_HandleKey( menuDef_t *menu, int key, qboolean down )
if ( g_editingField && down )
{
- if ( !Item_TextField_HandleKey( g_editItem, key ) )
+ if ( !Item_TextField_HandleKey( g_editItem, key, chr ) )
{
g_editingField = qfalse;
Item_RunScript( g_editItem, g_editItem->onTextEntry );
@@ -4524,7 +4529,7 @@ void Menu_HandleKey( menuDef_t *menu, int key, qboolean down )
if ( !inHandleKey && ( key == K_MOUSE1 || key == K_MOUSE2 || key == K_MOUSE3 ) )
{
inHandleKey = qtrue;
- Menus_HandleOOBClick( menu, key, down );
+ Menus_HandleOOBClick( menu, key, chr, down );
inHandleKey = qfalse;
inHandler = qfalse;
return;
@@ -8973,7 +8978,7 @@ int Display_CursorType( int x, int y )
return CURSOR_ARROW;
}
-void Display_HandleKey( int key, qboolean down, int x, int y )
+void Display_HandleKey( int key, int chr, qboolean down, int x, int y )
{
menuDef_t *menu = Display_CaptureItem( x, y );
@@ -8984,7 +8989,7 @@ void Display_HandleKey( int key, qboolean down, int x, int y )
if ( menu )
{
- Menu_HandleKey( menu, key, down );
+ Menu_HandleKey( menu, key, chr, down );
}
}
View
4 src/gamelogic/gpp/src/ui/ui_shared.h
@@ -492,7 +492,7 @@ void Menu_Init( menuDef_t *menu );
void Item_Init( itemDef_t *item );
void Menu_PostParse( menuDef_t *menu );
menuDef_t *Menu_GetFocused( void );
-void Menu_HandleKey( menuDef_t *menu, int key, qboolean down );
+void Menu_HandleKey( menuDef_t *menu, int key, int chr, qboolean down );
void Menu_HandleMouseMove( menuDef_t *menu, float x, float y );
void Menu_ScrollFeeder( menuDef_t *menu, int feeder, qboolean down );
qboolean Float_Parse( char **p, float *f );
@@ -525,7 +525,7 @@ int Display_CursorType( int x, int y );
qboolean Display_KeyBindPending( void );
menuDef_t *Menus_FindByName( const char *p );
void Menus_CloseByName( const char *p );
-void Display_HandleKey( int key, qboolean down, int x, int y );
+void Display_HandleKey( int key, int chr, qboolean down, int x, int y );
void LerpColor( vec4_t a, vec4_t b, vec4_t c, float t );
void Menus_CloseAll( void );
void Menu_Update( menuDef_t *menu );
View
272 src/gamelogic/gpp/src/ui/ui_utf8.c
@@ -0,0 +1,272 @@
+/*
+===========================================================================
+
+Daemon GPL Source Code
+Copyright (C) 2012 Unvanquished Developers
+
+Daemon 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 2 of the License, or
+(at your option) any later version.
+
+Daemon 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 <http://www.gnu.org/licenses/>.
+
+===========================================================================
+*/
+
+#include "ui_local.h"
+#include "ui_utf8.h"
+
+int ui_CursorToOffset( const char *buf, int cursor )
+{
+ int i = -1, j = 0;
+
+ while ( ++i < cursor )
+ {
+ j += ui_UTF8Width( buf + j );
+ }
+
+ return j;
+}
+
+int ui_OffsetToCursor( const char *buf, int offset )
+{
+ int i = 0, j = 0;
+
+ while ( i < offset )
+ {
+ i += ui_UTF8Width( buf + i );
+ ++j;
+ }
+
+ return j;
+}
+
+// Copied from src/engine/common/q_shared.c
+
+int ui_UTF8Width( const char *str )
+{
+ int ewidth;
+ const unsigned char *s = (const unsigned char *)str;
+
+ if( !str )
+ return 0;
+
+ if ( 0x00 <= *s && *s <= 0x7F )
+ ewidth = 0;
+ else if( 0xC2 <= *s && *s <= 0xDF )
+ ewidth = 1;
+ else if( 0xE0 <= *s && *s <= 0xEF )
+ ewidth = 2;
+ else if( 0xF0 <= *s && *s <= 0xF4 )
+ ewidth = 3;
+ else
+ ewidth = 0;
+
+ for( ; *s && ewidth > 0; s++, ewidth-- );
+
+ return s - (const unsigned char *)str + 1;
+}
+
+int ui_UTF8WidthCP( int ch )
+{
+ if ( ch <= 0x007F ) { return 1; }
+ if ( ch <= 0x07FF ) { return 2; }
+ if ( ch <= 0xFFFF ) { return 3; }
+ if ( ch <= 0x10FFFF ) { return 4; }
+ return 0;
+}
+
+int ui_UTF8Strlen( const char *str )
+{
+ int l = 0;
+
+ while( *str )
+ {
+ l++;
+
+ str += ui_UTF8Width( str );
+ }
+
+ return l;
+}
+
+qboolean ui_UTF8ContByte( char c )
+{
+ return (unsigned char )0x80 <= (unsigned char)c && (unsigned char)c <= (unsigned char )0xBF;
+}
+
+static qboolean getbit(const unsigned char *p, int pos)
+{
+ p += pos / 8;
+ pos %= 8;
+
+ return (*p & (1 << (7 - pos))) != 0;
+}
+
+static void setbit(unsigned char *p, int pos, qboolean on)
+{
+ p += pos / 8;
+ pos %= 8;
+
+ if( on )
+ *p |= 1 << (7 - pos);
+ else
+ *p &= ~(1 << (7 - pos));
+}
+
+static void shiftbitsright(unsigned char *p, unsigned long num, unsigned long by)
+{
+ int step, off;
+ unsigned char *e;
+
+ if( by >= num )
+ {
+ for( ; num > 8; p++, num -= 8 )
+ *p = 0;
+
+ *p &= (~0x00) >> num;
+
+ return;
+ }
+
+ step = by / 8;
+ off = by % 8;
+
+ for( e = p + (num + 7) / 8 - 1; e > p + step; e-- )
+ *e = (*(e - step) >> off) | (*(e - step - 1) << (8 - off));
+
+ *e = *(e - step) >> off;
+
+ for( e = p; e < p + step; e++ )
+ *e = 0;
+}
+
+unsigned long ui_UTF8CodePoint( const char *str )
+{
+ int i, j;
+ int n = 0;
+ int size = ui_UTF8Width( str );
+ unsigned long codepoint = 0;
+ unsigned char *p = (unsigned char *) &codepoint;
+
+ if( size > sizeof( codepoint ) )
+ size = sizeof( codepoint );
+ else if( size < 1 )
+ size = 1;
+
+ for( i = (size > 1 ? size + 1 : 1); i < 8; i++ )
+ setbit(p, n++, getbit((const unsigned char *)str, i));
+ for( i = 1; i < size; i++ )
+ for( j = 2; j < 8; j++ )
+ setbit(p, n++, getbit(((const unsigned char *)str) + i, j));
+
+ /*
+ if( n > 8 * sizeof(codepoint) )
+ {
+ Com_Error( ERR_DROP, "ui_UTF8CodePoint: overflow caught" );
+
+ return 0;
+ }
+ */
+
+ shiftbitsright(p, 8 * sizeof(codepoint), 8 * sizeof(codepoint) - n);
+
+#ifndef Q3_BIG_ENDIAN
+ for( i = 0; i < sizeof(codepoint) / 2; i++ )
+ {
+ p[i] ^= p[sizeof(codepoint) - 1 - i];
+ p[sizeof(codepoint) - 1 - i] ^= p[i];
+ p[i] ^= p[sizeof(codepoint) - 1 - i];
+ }
+#endif
+
+ return codepoint;
+}
+
+char *ui_UTF8Encode( unsigned long codepoint )
+{
+ static char sbuf[2][5];
+ static int index = 0;
+ char *buf = sbuf[index++ & 1];
+
+ if ( codepoint <= 0x007F )
+ {
+ buf[0] = codepoint;
+ buf[1] = 0;
+ }
+ else if( 0x0080 <= codepoint && codepoint <= 0x07FF )
+ {
+ buf[0] = 0xC0 | ((codepoint & 0x0700) >> 6) | ((codepoint & 0x00C0) >> 6);
+ buf[1] = 0x80 | (codepoint & 0x003F);
+ buf[2] = 0;
+ }
+ else if( 0x0800 <= codepoint && codepoint <= 0xFFFF )
+ {
+ buf[0] = 0xE0 | ((codepoint & 0xF000) >> 12);
+ buf[1] = 0x80 | ((codepoint & 0x0F00) >> 6) | ((codepoint & 0x00C0) >> 6);
+ buf[2] = 0x80 | (codepoint & 0x003F);
+ buf[3] = 0;
+ }
+ else if( 0x010000 <= codepoint && codepoint <= 0x10FFFF )
+ {
+ buf[0] = 0xF0 | ((codepoint & 0x1C0000 >> 18));
+ buf[1] = 0x80 | ((codepoint & 0x030000 >> 16)) | ((codepoint & 0x00F000) >> 12);
+ buf[2] = 0x80 | ((codepoint & 0x000F00) >> 6) | ((codepoint & 0x0000C0) >> 6);
+ buf[3] = 0x80 | (codepoint & 0x00003F);
+ buf[4] = 0;
+ }
+ else
+ {
+ buf[0] = 0;
+ }
+
+ return buf;
+}
+
+// s needs to have at least sizeof(int) allocated
+int ui_UTF8Store( const char *s )
+{
+#ifdef Q3_BIG_ENDIAN
+ int r = *(int *)s, i;
+ unsigned char *p = (unsigned char *) &r;
+ for( i = 0; i < sizeof(r) / 2; i++ )
+ {
+ p[i] ^= p[sizeof(r) - 1 - i];
+ p[sizeof(r) - 1 - i] ^= p[i];
+ p[i] ^= p[sizeof(r) - 1 - i];
+ }
+ return r;
+#else
+ return *(int *)s;
+#endif
+}
+
+char *ui_UTF8Unstore( int e )
+{
+ static char sbuf[2][5];
+ static int index = 0;
+ char *buf = sbuf[index++ & 1];
+
+#ifdef Q3_BIG_ENDIAN
+ int i;
+ unsigned char *p = (unsigned char *) buf;
+ *(int *)buf = e;
+ for( i = 0; i < sizeof(e) / 2; i++ )
+ {
+ p[i] ^= p[sizeof(e) - 1 - i];
+ p[sizeof(e) - 1 - i] ^= p[i];
+ p[i] ^= p[sizeof(e) - 1 - i];
+ }
+#else
+ *(int *)buf = e;
+#endif
+
+ return buf;
+}
View
41 src/gamelogic/gpp/src/ui/ui_utf8.h
@@ -0,0 +1,41 @@
+/*
+===========================================================================
+
+Daemon GPL Source Code
+Copyright (C) 2012 Unvanquished Developers
+
+This file is part of the Daemon GPL Source Code (Daemon Source Code).
+
+Daemon Source Code 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.
+
+Daemon Source Code 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 Daemon Source Code. If not, see <http://www.gnu.org/licenses/>.
+
+===========================================================================
+*/
+
+#ifndef __UI_UTF8_H
+#define __UI_UTF8_H
+
+int ui_CursorToOffset( const char *buf, int cursor );
+int ui_OffsetToCursor( const char *buf, int offset );
+
+// Copied from src/engine/common/q_shared.c
+int ui_UTF8Width( const char *str );
+int ui_UTF8WidthCP( int ch );
+int ui_UTF8Strlen( const char *str );
+qboolean ui_UTF8ContByte( char c );
+unsigned long ui_UTF8CodePoint( const char *str );
+char *ui_UTF8Encode( unsigned long codepoint );
+int ui_UTF8Store( const char *s );
+char *ui_UTF8Unstore( int e );
+
+#endif
Please sign in to comment.
Something went wrong with that request. Please try again.