From 3289f089c3b6f6196fe9508e7d3d48b32a42d10d Mon Sep 17 00:00:00 2001
From: Tim Angus <tim@ngus.net>
Date: Fri, 2 Nov 2007 20:42:47 +0000
Subject: * s/ui_loading/ui_hideCursor/ * Hide cursor when using edit fields
 and binding keys * Remove client side chat handling, moving it to UI module *
 Remove target and last attacker message modes * Add ui_developer cvar * Fix
 edit field bug where cvar changes under it * Add onTextEntry UI script event
 * Give the user some feedback when binding keys * Stop doing a UI refresh
 when primed (and remove cgame ui_loading sets) * Move cg_drawSnapshot to
 bottom of the screen

---
 src/cgame/cg_consolecmds.c |  4 +++
 src/cgame/cg_local.h       |  1 -
 src/cgame/cg_main.c        | 14 +-------
 src/cgame/cg_public.h      |  6 ----
 src/client/cl_console.c    | 85 ----------------------------------------------
 src/client/cl_keys.c       | 61 ---------------------------------
 src/client/cl_scrn.c       |  6 ----
 src/qcommon/q_shared.h     |  1 -
 src/ui/ui_atoms.c          | 28 +++++++++++++--
 src/ui/ui_local.h          |  5 +++
 src/ui/ui_main.c           | 21 +++++++++---
 src/ui/ui_shared.c         | 74 +++++++++++++++++++++++++++-------------
 src/ui/ui_shared.h         |  1 +
 ui/ingame_options.menu     | 48 +++-----------------------
 ui/say.menu                | 46 +++++++++++++++++++++++++
 ui/tremulous.txt           |  1 +
 ui/tremulous_common_hud.h  |  2 +-
 17 files changed, 155 insertions(+), 249 deletions(-)
 create mode 100644 ui/say.menu

diff --git a/src/cgame/cg_consolecmds.c b/src/cgame/cg_consolecmds.c
index 65177875..a44a5497 100644
--- a/src/cgame/cg_consolecmds.c
+++ b/src/cgame/cg_consolecmds.c
@@ -279,6 +279,10 @@ void CG_InitConsoleCommands( void )
   // forwarded to the server after they are not recognized locally
   //
   trap_AddCommand( "kill" );
+  trap_AddCommand( "messagemode" );
+  trap_AddCommand( "messagemode2" );
+  trap_AddCommand( "messagemode3" );
+  trap_AddCommand( "messagemode4" );
   trap_AddCommand( "say" );
   trap_AddCommand( "say_team" );
   trap_AddCommand( "tell" );
diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h
index 3816218b..012a29ed 100644
--- a/src/cgame/cg_local.h
+++ b/src/cgame/cg_local.h
@@ -1499,7 +1499,6 @@ extern  vmCvar_t    ui_currentClass;
 extern  vmCvar_t    ui_carriage;
 extern  vmCvar_t    ui_stages;
 extern  vmCvar_t    ui_dialog;
-extern  vmCvar_t    ui_loading;
 extern  vmCvar_t    ui_voteActive;
 extern  vmCvar_t    ui_alienTeamVoteActive;
 extern  vmCvar_t    ui_humanTeamVoteActive;
diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c
index 0d668286..618762e3 100644
--- a/src/cgame/cg_main.c
+++ b/src/cgame/cg_main.c
@@ -68,12 +68,6 @@ intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3,
       CG_DrawActiveFrame( arg0, arg1, arg2 );
       return 0;
 
-    case CG_CROSSHAIR_PLAYER:
-      return CG_CrosshairPlayer( );
-
-    case CG_LAST_ATTACKER:
-      return CG_LastAttacker( );
-
     case CG_KEY_EVENT:
       CG_KeyEvent( arg0, arg1 );
       return 0;
@@ -224,7 +218,6 @@ vmCvar_t  ui_currentClass;
 vmCvar_t  ui_carriage;
 vmCvar_t  ui_stages;
 vmCvar_t  ui_dialog;
-vmCvar_t  ui_loading;
 vmCvar_t  ui_voteActive;
 vmCvar_t  ui_alienTeamVoteActive;
 vmCvar_t  ui_humanTeamVoteActive;
@@ -339,7 +332,6 @@ static cvarTable_t cvarTable[ ] =
   { &ui_carriage, "ui_carriage", "", 0 },
   { &ui_stages, "ui_stages", "0 0", 0 },
   { &ui_dialog, "ui_dialog", "Text not set", 0 },
-  { &ui_loading, "ui_loading", "0", 0 },
   { &ui_voteActive, "ui_voteActive", "0", 0 },
   { &ui_humanTeamVoteActive, "ui_humanTeamVoteActive", "0", 0 },
   { &ui_alienTeamVoteActive, "ui_alienTeamVoteActive", "0", 0 },
@@ -507,6 +499,7 @@ int CG_LastAttacker( void )
   return cg.snap->ps.persistant[ PERS_ATTACKER ];
 }
 
+
 /*
 =================
 CG_RemoveNotifyLine
@@ -1760,9 +1753,6 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )
   cgs.media.charsetShader   = trap_R_RegisterShader( "gfx/2d/bigchars" );
   cgs.media.outlineShader   = trap_R_RegisterShader( "outline" );
 
-  //inform UI to repress cursor whilst loading
-  trap_Cvar_Set( "ui_loading", "1" );
-
   // load overrides
   BG_InitClassOverrides( );
   BG_InitBuildableOverrides( );
@@ -1846,8 +1836,6 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )
   CG_ShaderStateChanged( );
 
   trap_S_ClearLoopingSounds( qtrue );
-
-  trap_Cvar_Set( "ui_loading", "0" );
 }
 
 /*
diff --git a/src/cgame/cg_public.h b/src/cgame/cg_public.h
index 4d9e965a..905b1db2 100644
--- a/src/cgame/cg_public.h
+++ b/src/cgame/cg_public.h
@@ -232,12 +232,6 @@ typedef enum
   // Generates and draws a game scene and status information at the given time.
   // If demoPlayback is set, local movement prediction will not be enabled
 
-  CG_CROSSHAIR_PLAYER,
-  // int (*CG_CrosshairPlayer)( void );
-
-  CG_LAST_ATTACKER,
-  // int (*CG_LastAttacker)( void );
-
   CG_KEY_EVENT,
   // void  (*CG_KeyEvent)( int key, qboolean down );
 
diff --git a/src/client/cl_console.c b/src/client/cl_console.c
index d740696d..0ad397b9 100644
--- a/src/client/cl_console.c
+++ b/src/client/cl_console.c
@@ -80,67 +80,6 @@ void Con_ToggleConsole_f (void) {
 	Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_CONSOLE );
 }
 
-/*
-================
-Con_MessageMode_f
-================
-*/
-void Con_MessageMode_f (void) {
-	chat_playerNum = -1;
-	chat_team = qfalse;
-	Field_Clear( &chatField );
-	chatField.widthInChars = 30;
-
-	Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_MESSAGE );
-}
-
-/*
-================
-Con_MessageMode2_f
-================
-*/
-void Con_MessageMode2_f (void) {
-	chat_playerNum = -1;
-	chat_team = qtrue;
-	Field_Clear( &chatField );
-	chatField.widthInChars = 25;
-	Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_MESSAGE );
-}
-
-/*
-================
-Con_MessageMode3_f
-================
-*/
-void Con_MessageMode3_f (void) {
-	chat_playerNum = VM_Call( cgvm, CG_CROSSHAIR_PLAYER );
-	if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) {
-		chat_playerNum = -1;
-		return;
-	}
-	chat_team = qfalse;
-	Field_Clear( &chatField );
-	chatField.widthInChars = 30;
-	Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_MESSAGE );
-}
-
-/*
-================
-Con_MessageMode4_f
-================
-*/
-void Con_MessageMode4_f (void) {
-	chat_playerNum = VM_Call( cgvm, CG_LAST_ATTACKER );
-	if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) {
-		chat_playerNum = -1;
-		return;
-	}
-	chat_team = qfalse;
-	Field_Clear( &chatField );
-	chatField.widthInChars = 30;
-	Key_SetCatcher( Key_GetCatcher( ) ^ KEYCATCH_MESSAGE );
-}
-
 /*
 ================
 Con_Clear_f
@@ -314,10 +253,6 @@ void Con_Init (void) {
 	CL_LoadConsoleHistory( );
 
 	Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
-	Cmd_AddCommand ("messagemode", Con_MessageMode_f);
-	Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
-	Cmd_AddCommand ("messagemode3", Con_MessageMode3_f);
-	Cmd_AddCommand ("messagemode4", Con_MessageMode4_f);
 	Cmd_AddCommand ("clear", Con_Clear_f);
 	Cmd_AddCommand ("condump", Con_Dump_f);
 }
@@ -606,26 +541,6 @@ void Con_DrawConsole( void ) {
 
 	if( Key_GetCatcher( ) & ( KEYCATCH_UI | KEYCATCH_CGAME ) )
 		return;
-
-	// draw the chat line
-	if( Key_GetCatcher( ) & KEYCATCH_MESSAGE )
-	{
-		int skip;
-
-		if( chat_team )
-		{
-			SCR_DrawBigString( 8, 232, "Team Say:", 1.0f, qfalse );
-			skip = 11;
-		}
-		else
-		{ 
-			SCR_DrawBigString( 8, 232, "Say:", 1.0f, qfalse );
-			skip = 5;
-		}
-
-		Field_BigDraw( &chatField, skip * BIGCHAR_WIDTH, 232,
-				SCREEN_WIDTH - ( skip + 1 ) * BIGCHAR_WIDTH, qtrue, qtrue );
-	}
 }
 
 //================================================================
diff --git a/src/client/cl_keys.c b/src/client/cl_keys.c
index b23247bd..66597427 100644
--- a/src/client/cl_keys.c
+++ b/src/client/cl_keys.c
@@ -35,10 +35,6 @@ int			historyLine;	// the line being displayed from history buffer
 							// will be <= nextHistoryLine
 
 field_t		g_consoleField;
-field_t		chatField;
-qboolean	chat_team;
-
-int			chat_playerNum;
 
 
 qboolean	key_overstrikeMode;
@@ -705,51 +701,6 @@ void Console_Key (int key) {
 	Field_KeyDownEvent( &g_consoleField, key );
 }
 
-//============================================================================
-
-
-/*
-================
-Message_Key
-
-In game talk message
-================
-*/
-void Message_Key( int key ) {
-
-	char	buffer[MAX_STRING_CHARS];
-
-
-	if (key == K_ESCAPE) {
-		Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_MESSAGE );
-		Field_Clear( &chatField );
-		return;
-	}
-
-	if ( key == K_ENTER || key == K_KP_ENTER )
-	{
-		if ( chatField.buffer[0] && cls.state == CA_ACTIVE ) {
-			if (chat_playerNum != -1 )
-
-				Com_sprintf( buffer, sizeof( buffer ), "tell %i \"%s\"\n", chat_playerNum, chatField.buffer );
-
-			else if (chat_team)
-
-				Com_sprintf( buffer, sizeof( buffer ), "say_team \"%s\"\n", chatField.buffer );
-			else
-				Com_sprintf( buffer, sizeof( buffer ), "say \"%s\"\n", chatField.buffer );
-
-
-
-			CL_AddReliableCommand( buffer );
-		}
-		Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_MESSAGE );
-		Field_Clear( &chatField );
-		return;
-	}
-
-	Field_KeyDownEvent( &chatField, key );
-}
 
 //============================================================================
 
@@ -1186,12 +1137,6 @@ void CL_KeyEvent (int key, qboolean down, unsigned time) {
 
 	// escape is always handled special
 	if ( key == K_ESCAPE && down ) {
-		if ( Key_GetCatcher( ) & KEYCATCH_MESSAGE ) {
-			// clear message mode
-			Message_Key( key );
-			return;
-		}
-
 		// escape always gets out of CGAME stuff
 		if (Key_GetCatcher( ) & KEYCATCH_CGAME) {
 			Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_CGAME );
@@ -1249,8 +1194,6 @@ void CL_KeyEvent (int key, qboolean down, unsigned time) {
 		if ( cgvm ) {
 			VM_Call( cgvm, CG_KEY_EVENT, key, down );
 		} 
-	} else if ( Key_GetCatcher( ) & KEYCATCH_MESSAGE ) {
-		Message_Key( key );
 	} else if ( cls.state == CA_DISCONNECTED ) {
 		Console_Key( key );
 	} else {
@@ -1325,10 +1268,6 @@ void CL_CharEvent( int key ) {
 	{
 		VM_Call( uivm, UI_KEY_EVENT, key | K_CHAR_FLAG, qtrue );
 	}
-	else if ( Key_GetCatcher( ) & KEYCATCH_MESSAGE ) 
-	{
-		Field_CharEvent( &chatField, key );
-	}
 	else if ( cls.state == CA_DISCONNECTED )
 	{
 		Field_CharEvent( &g_consoleField, key );
diff --git a/src/client/cl_scrn.c b/src/client/cl_scrn.c
index 25217072..f0a64bb4 100644
--- a/src/client/cl_scrn.c
+++ b/src/client/cl_scrn.c
@@ -453,12 +453,6 @@ void SCR_DrawScreenField( stereoFrame_t stereoFrame ) {
 		case CA_PRIMED:
 			// draw the game information screen and loading progress
 			CL_CGameRendering( stereoFrame );
-
-			// also draw the connection information, so it doesn't
-			// flash away too briefly on local or lan games
-			// refresh to update the time
-			VM_Call( uivm, UI_REFRESH, cls.realtime );
-			VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qtrue );
 			break;
 		case CA_ACTIVE:
 			CL_CGameRendering( stereoFrame );
diff --git a/src/qcommon/q_shared.h b/src/qcommon/q_shared.h
index 3622097b..65be0891 100644
--- a/src/qcommon/q_shared.h
+++ b/src/qcommon/q_shared.h
@@ -935,7 +935,6 @@ typedef struct {
 // if none of the catchers are active, bound key strings will be executed
 #define KEYCATCH_CONSOLE		0x0001
 #define	KEYCATCH_UI					0x0002
-#define	KEYCATCH_MESSAGE		0x0004
 #define	KEYCATCH_CGAME			0x0008
 
 
diff --git a/src/ui/ui_atoms.c b/src/ui/ui_atoms.c
index 64efa914..d2bb6b26 100644
--- a/src/ui/ui_atoms.c
+++ b/src/ui/ui_atoms.c
@@ -52,8 +52,6 @@ void QDECL Com_Printf( const char *msg, ... ) {
   trap_Print( va("%s", text) );
 }
 
-qboolean newUI = qfalse;
-
 
 /*
 =================
@@ -298,6 +296,8 @@ static void UI_CalcPostGameStats( void ) {
 /*
 =================
 UI_ConsoleCommand
+
+FIXME: lookup table
 =================
 */
 qboolean UI_ConsoleCommand( int realTime )
@@ -353,6 +353,30 @@ qboolean UI_ConsoleCommand( int realTime )
     return qtrue;
   }
 
+  if ( Q_strncmp( cmd, "messagemode", 11 ) == 0 ) {
+    trap_Cvar_Set( "ui_sayBuffer", "" );
+
+    switch( cmd[ 11 ] )
+    {
+      default:
+      case '\0':
+        // Global
+        uiInfo.chatTeam             = qfalse;
+        uiInfo.chatTargetClientNum  = -1;
+        break;
+
+      case '2':
+        // Team
+        uiInfo.chatTeam             = qtrue;
+        uiInfo.chatTargetClientNum  = -1;
+        break;
+    }
+
+    trap_Key_SetCatcher( KEYCATCH_UI );
+    Menus_ActivateByName( "say" );
+    return qtrue;
+  }
+
   if( Q_stricmp ( cmd, "menu" ) == 0 )
   {
     arg1 = UI_Argv( 1 );
diff --git a/src/ui/ui_local.h b/src/ui/ui_local.h
index 5f5ea862..450a4e44 100644
--- a/src/ui/ui_local.h
+++ b/src/ui/ui_local.h
@@ -128,6 +128,8 @@ extern vmCvar_t  ui_scoreTime;
 extern vmCvar_t  ui_smallFont;
 extern vmCvar_t  ui_bigFont;
 extern vmCvar_t  ui_serverStatusTimeOut;
+extern vmCvar_t  ui_textWrapCache;
+extern vmCvar_t  ui_developer;
 
 
 
@@ -896,6 +898,9 @@ typedef struct {
   int effectsColor;
 
   qboolean inGameLoad;
+
+  qboolean  chatTeam;
+  int       chatTargetClientNum;
 }  uiInfo_t;
 
 extern uiInfo_t uiInfo;
diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c
index edecd280..39e6c1b5 100644
--- a/src/ui/ui_main.c
+++ b/src/ui/ui_main.c
@@ -29,9 +29,6 @@ USER INTERFACE MAIN
 =======================================================================
 */
 
-// use this to get a demo build without an explicit demo build, i.e. to get the demo ui files to build
-//#define PRE_RELEASE_TADEMO
-
 #include "ui_local.h"
 
 uiInfo_t uiInfo;
@@ -681,7 +678,7 @@ void _UI_Refresh( int realtime )
   UI_SetColor( NULL );
 
   // don't draw the cursor whilst loading
-  if( Menu_Count( ) > 0 && !trap_Cvar_VariableValue( "ui_loading" ) )
+  if( Menu_Count( ) > 0 && !trap_Cvar_VariableValue( "ui_hideCursor" ) )
     UI_DrawHandlePic( uiInfo.uiDC.cursorx-16, uiInfo.uiDC.cursory-16, 32, 32, uiInfo.uiDC.Assets.cursor);
 
 #ifndef NDEBUG
@@ -3518,6 +3515,7 @@ static void UI_Update(const char *name) {
   }
 }
 
+//FIXME: lookup table
 static void UI_RunMenuScript(char **args) {
   const char *name, *name2;
   char buff[1024];
@@ -3727,6 +3725,18 @@ static void UI_RunMenuScript(char **args) {
         trap_Cmd_ExecuteText( EXEC_APPEND, command );
       }
     }
+    else if( Q_stricmp( name, "Say" ) == 0 )
+    {
+      char buffer[ MAX_CVAR_VALUE_STRING ];
+      trap_Cvar_VariableStringBuffer( "ui_sayBuffer", buffer, sizeof( buffer ) );
+
+      if( uiInfo.chatTargetClientNum != -1 )
+        trap_Cmd_ExecuteText( EXEC_APPEND, va( "tell %i \"%s\"\n", uiInfo.chatTargetClientNum, buffer  ) );
+      else if( uiInfo.chatTeam )
+        trap_Cmd_ExecuteText( EXEC_APPEND, va( "say_team \"%s\"\n", buffer ) );
+      else
+        trap_Cmd_ExecuteText( EXEC_APPEND, va( "say \"%s\"\n", buffer ) );
+    }
     else if (Q_stricmp(name, "playMovie") == 0) {
       if (uiInfo.previewMovie >= 0) {
         trap_CIN_StopCinematic(uiInfo.previewMovie);
@@ -5365,7 +5375,6 @@ void _UI_SetActiveMenu( uiMenuCommand_t menu ) {
       }
       Menus_CloseAll();
       Menus_ActivateByName("main");
-      trap_Cvar_Set( "ui_loading", "0" );
       trap_Cvar_VariableStringBuffer("com_errorMessage", buf, sizeof(buf));
       if (strlen(buf)) {
         if (!ui_singlePlayerActive.integer) {
@@ -5801,6 +5810,7 @@ vmCvar_t  ui_realCaptureLimit;
 vmCvar_t  ui_realWarmUp;
 vmCvar_t  ui_serverStatusTimeOut;
 vmCvar_t  ui_textWrapCache;
+vmCvar_t  ui_developer;
 
 vmCvar_t  ui_winner;
 
@@ -5928,6 +5938,7 @@ static cvarTable_t    cvarTable[] = {
   { &ui_realCaptureLimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART},
   { &ui_serverStatusTimeOut, "ui_serverStatusTimeOut", "7000", CVAR_ARCHIVE},
   { &ui_textWrapCache, "ui_textWrapCache", "1", CVAR_ARCHIVE },
+  { &ui_developer, "ui_developer", "0", CVAR_ARCHIVE | CVAR_CHEAT },
 };
 
 static int    cvarTableSize = sizeof(cvarTable) / sizeof(cvarTable[0]);
diff --git a/src/ui/ui_shared.c b/src/ui/ui_shared.c
index 3224b697..ec6a07eb 100644
--- a/src/ui/ui_shared.c
+++ b/src/ui/ui_shared.c
@@ -64,8 +64,6 @@ int menuCount = 0;               // how many
 menuDef_t *menuStack[MAX_OPEN_MENUS];
 int openMenuCount = 0;
 
-static qboolean debugMode = qfalse;
-
 #define DOUBLE_CLICK_DELAY 300
 static int lastListBoxClickTime = 0;
 
@@ -895,7 +893,7 @@ static void Window_Paint(Window *w, float fadeAmount, float fadeClamp, float fad
   rectDef_t fillRect = w->rect;
 
 
-  if (debugMode) {
+  if ( DC->getCVarValue( "ui_developer" ) ) {
     color[0] = color[1] = color[2] = color[3] = 1;
     DC->drawRect(w->rect.x, w->rect.y, w->rect.w, w->rect.h, 1, color);
   }
@@ -1467,12 +1465,19 @@ void Script_SetFocus(itemDef_t *item, char **args) {
 
   if (String_Parse(args, &name)) {
     focusItem = Menu_FindItemByName(item->parent, name);
-    if (focusItem && !(focusItem->window.flags & WINDOW_DECORATION) && !(focusItem->window.flags & WINDOW_HASFOCUS)) {
+    if (focusItem && !(focusItem->window.flags & WINDOW_DECORATION)) {
       Menu_ClearFocus(item->parent);
       focusItem->window.flags |= WINDOW_HASFOCUS;
       if (focusItem->onFocus) {
         Item_RunScript(focusItem, focusItem->onFocus);
       }
+
+      // Edit fields get activated too
+      if ( focusItem->type == ITEM_TYPE_EDITFIELD || focusItem->type == ITEM_TYPE_NUMERICFIELD) {
+        g_editingField = qtrue;
+        g_editItem = focusItem;
+      }
+
       if (DC->Assets.itemFocusSound) {
         DC->startLocalSound( DC->Assets.itemFocusSound, CHAN_LOCAL_SOUND );
       }
@@ -2400,6 +2405,9 @@ qboolean Item_TextField_HandleKey(itemDef_t *item, int key)
     DC->getCVarString( item->cvar, buff, sizeof( buff ) );
     len = strlen( buff );
 
+    if( len < item->cursorPos )
+      item->cursorPos = len;
+
     if( editPtr->maxChars && len > editPtr->maxChars )
       len = editPtr->maxChars;
 
@@ -2988,11 +2996,13 @@ void Menu_HandleKey(menuDef_t *menu, int key, qboolean down) {
   if (g_editingField && down) {
     if (!Item_TextField_HandleKey(g_editItem, key)) {
       g_editingField = qfalse;
+      Item_RunScript( g_editItem, g_editItem->onTextEntry );
       g_editItem = NULL;
       inHandler = qfalse;
       return;
     } else if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_MOUSE3) {
       g_editingField = qfalse;
+      Item_RunScript( g_editItem, g_editItem->onTextEntry );
       g_editItem = NULL;
       Display_MouseMove(NULL, DC->cursorx, DC->cursory);
     } else if (key == K_TAB || key == K_UPARROW || key == K_DOWNARROW) {
@@ -3040,12 +3050,6 @@ void Menu_HandleKey(menuDef_t *menu, int key, qboolean down) {
   // default handling
   switch ( key ) {
 
-    case K_F11:
-      if (DC->getCVarValue("developer")) {
-        debugMode ^= 1;
-      }
-      break;
-
     case K_F12:
       if (DC->getCVarValue("developer")) {
         DC->executeText(EXEC_APPEND, "screenshot\n");
@@ -3552,7 +3556,7 @@ void Item_Text_Wrapped_Paint( itemDef_t *item )
         lineItem.window.border      = item->window.border;
         lineItem.window.borderSize  = item->window.borderSize;
 
-        if( debugMode )
+        if( DC->getCVarValue( "ui_developer" ) )
         {
           vec4_t color;
           color[ 0 ] = color[ 2 ] = color[ 3 ] = 1.0f;
@@ -3949,7 +3953,7 @@ void Controls_SetConfig(qboolean restart)
   //if ( s_controls.invertmouse.curvalue )
   //  DC->setCVar("m_pitch", va("%f),-fabs( DC->getCVarValue( "m_pitch" ) ) );
   //else
-  //  trap_Cvar_SetValue( "m_pitch", fabs( trap_Cvar_VariableValue( "m_pitch" ) ) );
+  //  trap_Cvar_SetValue( "m_pitch", fabs( DC->getCVarValue( "m_pitch" ) ) );
 
   //trap_Cvar_SetValue( "m_filter", s_controls.smoothmouse.curvalue );
   //trap_Cvar_SetValue( "cl_run", s_controls.alwaysrun.curvalue );
@@ -4080,26 +4084,34 @@ void Item_Bind_Paint(itemDef_t *item) {
 
   if (item->window.flags & WINDOW_HASFOCUS) {
     if (g_bindItem == item) {
-      lowLight[0] = 0.8f * 1.0f;
-      lowLight[1] = 0.8f * 0.0f;
-      lowLight[2] = 0.8f * 0.0f;
-      lowLight[3] = 0.8f * 1.0f;
-    } else {
       lowLight[0] = 0.8f * parent->focusColor[0];
       lowLight[1] = 0.8f * parent->focusColor[1];
       lowLight[2] = 0.8f * parent->focusColor[2];
       lowLight[3] = 0.8f * parent->focusColor[3];
-    }
 
-    memcpy(newColor, &parent->focusColor, sizeof(vec4_t));
+      LerpColor( parent->focusColor, lowLight, newColor,
+                 0.5 + 0.5 * sin( DC->realTime / PULSE_DIVISOR ) );
+    } else {
+      memcpy(&newColor, &parent->focusColor, sizeof(vec4_t));
+    }
   } else {
     memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t));
   }
 
   if (item->text) {
     Item_Text_Paint(item);
-    BindingFromName(item->cvar);
-    DC->drawText(item->textRect.x + item->textRect.w + ITEM_VALUE_OFFSET, item->textRect.y, item->textscale, newColor, g_nameBind1, 0, maxChars, item->textStyle);
+
+    if( g_bindItem == item && g_waitingForKey )
+    {
+      DC->drawText( item->textRect.x + item->textRect.w + ITEM_VALUE_OFFSET, item->textRect.y,
+                    item->textscale, newColor, "Press key", 0, maxChars, item->textStyle);
+    }
+    else
+    {
+      BindingFromName(item->cvar);
+      DC->drawText( item->textRect.x + item->textRect.w + ITEM_VALUE_OFFSET, item->textRect.y,
+                    item->textscale, newColor, g_nameBind1, 0, maxChars, item->textStyle);
+    }
   } else {
     DC->drawText(item->textRect.x, item->textRect.y, item->textscale, newColor, (value != 0) ? "FIXME" : "FIXME", 0, maxChars, item->textStyle);
   }
@@ -4710,7 +4722,7 @@ void Item_Paint(itemDef_t *item) {
 
   Window_Paint(&item->window, parent->fadeAmount , parent->fadeClamp, parent->fadeCycle);
 
-  if (debugMode) {
+  if (DC->getCVarValue( "ui_developer" )) {
     vec4_t color;
     rectDef_t *r = Item_CorrectedTextRect(item);
     color[1] = color[3] = 1;
@@ -4982,7 +4994,7 @@ void Menu_Paint(menuDef_t *menu, qboolean forcePaint) {
     Item_Paint(menu->items[i]);
   }
 
-  if (debugMode) {
+  if (DC->getCVarValue( "ui_developer" )) {
     vec4_t color;
     color[0] = color[2] = color[3] = 1;
     color[1] = 0;
@@ -5547,6 +5559,13 @@ qboolean ItemParse_mouseExitText( itemDef_t *item, int handle ) {
   return qtrue;
 }
 
+qboolean ItemParse_onTextEntry( itemDef_t *item, int handle ) {
+  if (!PC_Script_Parse(handle, &item->onTextEntry)) {
+    return qfalse;
+  }
+  return qtrue;
+}
+
 qboolean ItemParse_action( itemDef_t *item, int handle ) {
   if (!PC_Script_Parse(handle, &item->action)) {
     return qfalse;
@@ -5853,6 +5872,7 @@ keywordHash_t itemParseKeywords[] = {
   {"mouseExit", ItemParse_mouseExit, NULL},
   {"mouseEnterText", ItemParse_mouseEnterText, NULL},
   {"mouseExitText", ItemParse_mouseExitText, NULL},
+  {"onTextEntry", ItemParse_onTextEntry, NULL},
   {"action", ItemParse_action, NULL},
   {"special", ItemParse_special, NULL},
   {"cvar", ItemParse_cvar, NULL},
@@ -6357,6 +6377,12 @@ int Menu_Count( void ) {
 
 void Menu_PaintAll( void ) {
   int i;
+
+  if( g_editingField || g_waitingForKey )
+    DC->setCVar( "ui_hideCursor", "1" );
+  else
+    DC->setCVar( "ui_hideCursor", "0" );
+
   if ( captureFunc != voidFunction ) {
     if( captureFuncExpiry > 0 && DC->realTime > captureFuncExpiry ) {
       UI_RemoveCaptureFunc( );
@@ -6369,7 +6395,7 @@ void Menu_PaintAll( void ) {
     Menu_Paint(&Menus[i], qfalse);
   }
 
-  if (debugMode) {
+  if (DC->getCVarValue( "ui_developer" )) {
     vec4_t v = {1, 1, 1, 1};
     DC->drawText(5, 25, .5, v, va("fps: %f", DC->FPS), 0, 0, 0);
   }
diff --git a/src/ui/ui_shared.h b/src/ui/ui_shared.h
index 28b24482..504d0ddf 100644
--- a/src/ui/ui_shared.h
+++ b/src/ui/ui_shared.h
@@ -242,6 +242,7 @@ typedef struct itemDef_s {
   const char *action;            // select script
   const char *onFocus;           // select script
   const char *leaveFocus;        // select script
+  const char *onTextEntry;       // called when text entered
   const char *cvar;              // associated cvar
   const char *cvarTest;          // associated cvar for enable actions
   const char *enableCvar;         // enable, disable, show, or hide based on value, this can contain a list
diff --git a/ui/ingame_options.menu b/ui/ingame_options.menu
index 74a42f76..3f2978a4 100644
--- a/ui/ingame_options.menu
+++ b/ui/ingame_options.menu
@@ -1177,46 +1177,6 @@
       }
     }
 
-    itemDef
-    {
-      name misc
-      group optionsGrp
-      type ITEM_TYPE_BIND
-      text "Target Chat:"
-      cvar "messagemode3"
-      rect SCONTENT_X (SCONTENT_Y+(8*ELEM_H)) SCONTENT_W ELEM_H
-      textalign ITEM_ALIGN_RIGHT
-      textvalign ITEM_VALIGN_CENTER
-      textalignx SCONTENT_OFF
-      textscale .25
-      forecolor 1 1 1 1
-      visible MENU_FALSE
-      action
-      {
-        play "sound/misc/menu1.wav";
-      }
-    }
-
-    itemDef
-    {
-      name misc
-      group optionsGrp
-      type ITEM_TYPE_BIND
-      text "Attack Chat:"
-      cvar "messagemode4"
-      rect SCONTENT_X (SCONTENT_Y+(9*ELEM_H)) SCONTENT_W ELEM_H
-      textalign ITEM_ALIGN_RIGHT
-      textvalign ITEM_VALIGN_CENTER
-      textalignx SCONTENT_OFF
-      textscale .25
-      forecolor 1 1 1 1
-      visible MENU_FALSE
-      action
-      {
-        play "sound/misc/menu1.wav";
-      }
-    }
-
     itemDef
     {
       name misc
@@ -1224,7 +1184,7 @@
       type ITEM_TYPE_BIND
       text "Vote Yes:"
       cvar "vote yes"
-      rect SCONTENT_X (SCONTENT_Y+(10*ELEM_H)) SCONTENT_W ELEM_H
+      rect SCONTENT_X (SCONTENT_Y+(8*ELEM_H)) SCONTENT_W ELEM_H
       textalign ITEM_ALIGN_RIGHT
       textvalign ITEM_VALIGN_CENTER
       textalignx SCONTENT_OFF
@@ -1244,7 +1204,7 @@
       type ITEM_TYPE_BIND
       text "Vote No:"
       cvar "vote no"
-      rect SCONTENT_X (SCONTENT_Y+(11*ELEM_H)) SCONTENT_W ELEM_H
+      rect SCONTENT_X (SCONTENT_Y+(9*ELEM_H)) SCONTENT_W ELEM_H
       textalign ITEM_ALIGN_RIGHT
       textvalign ITEM_VALIGN_CENTER
       textalignx SCONTENT_OFF
@@ -1264,7 +1224,7 @@
       type ITEM_TYPE_BIND
       text "Team Vote Yes:"
       cvar "teamvote yes"
-      rect SCONTENT_X (SCONTENT_Y+(12*ELEM_H)) SCONTENT_W ELEM_H
+      rect SCONTENT_X (SCONTENT_Y+(10*ELEM_H)) SCONTENT_W ELEM_H
       textalign ITEM_ALIGN_RIGHT
       textvalign ITEM_VALIGN_CENTER
       textalignx SCONTENT_OFF
@@ -1284,7 +1244,7 @@
       type ITEM_TYPE_BIND
       text "Team Vote No:"
       cvar "teamvote no"
-      rect SCONTENT_X (SCONTENT_Y+(13*ELEM_H)) SCONTENT_W ELEM_H
+      rect SCONTENT_X (SCONTENT_Y+(11*ELEM_H)) SCONTENT_W ELEM_H
       textalign ITEM_ALIGN_RIGHT
       textvalign ITEM_VALIGN_CENTER
       textalignx SCONTENT_OFF
diff --git a/ui/say.menu b/ui/say.menu
new file mode 100644
index 00000000..089b1fea
--- /dev/null
+++ b/ui/say.menu
@@ -0,0 +1,46 @@
+#include "ui/menudef.h"
+
+{
+
+#define BORDER    10
+
+#define X         BORDER
+#define Y         200
+#define W         (640-(2*BORDER))
+#define H         40
+
+  menuDef
+  {
+    name say
+    fullScreen MENU_FALSE
+    visible MENU_TRUE
+    rect X Y W H
+    focusColor 1 1 1 1
+    style WINDOW_STYLE_EMPTY
+    onOpen
+    {
+      setfocus say_field
+    }
+
+    itemDef
+    {
+      name say_field
+      type ITEM_TYPE_EDITFIELD
+      style WINDOW_STYLE_EMPTY
+      text "Say:"
+      cvar "ui_sayBuffer"
+      maxchars 128
+      rect 0 0 W H
+      textalign ITEM_ALIGN_LEFT
+      textvalign ITEM_VALIGN_CENTER
+      textscale .5
+      forecolor 1 1 1 1
+      visible MENU_TRUE
+      onTextEntry
+      {
+        uiScript Say;
+        close say
+      }
+    }
+  }
+}
diff --git a/ui/tremulous.txt b/ui/tremulous.txt
index deb04d9a..e7732024 100644
--- a/ui/tremulous.txt
+++ b/ui/tremulous.txt
@@ -16,4 +16,5 @@
   loadMenu { "ui/tremulous_alienupgrade.menu" }
 
   loadMenu { "ui/ptrc.menu" }
+  loadMenu { "ui/say.menu" }
 }
diff --git a/ui/tremulous_common_hud.h b/ui/tremulous_common_hud.h
index 025bc68e..ae85d7e5 100644
--- a/ui/tremulous_common_hud.h
+++ b/ui/tremulous_common_hud.h
@@ -134,7 +134,7 @@ itemDef
 itemDef
 {
   name "snapshot"
-  rect BORDER 196 MAIN_W STAT_H
+  rect BORDER (H-(BORDER+STAT_H)) MAIN_W STAT_H
   style WINDOW_STYLE_EMPTY
   visible MENU_TRUE
   decoration
-- 
cgit