about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2009-06-13 08:22:37 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2009-06-13 08:22:37 +0000
commit69511102de9b0f3ec6ad555baf2a01d9ee1c3dfd (patch)
treeebef66e5ab0cfd16e96026d8ac8d5a708eb68219 /src
parent9254ca61f1e459cbaf137ce4511332365da9c225 (diff)
downloadzcatch-69511102de9b0f3ec6ad555baf2a01d9ee1c3dfd.tar.gz
zcatch-69511102de9b0f3ec6ad555baf2a01d9ee1c3dfd.zip
improved the font system even futher. utf8 is now used everywhere. it uses less memory as well
Diffstat (limited to 'src')
-rw-r--r--src/base/system.c133
-rw-r--r--src/base/system.h63
-rw-r--r--src/engine/client/ec_gfx_text.c157
-rw-r--r--src/engine/client/ec_inp.c8
-rw-r--r--src/engine/e_if_gfx.h1
-rw-r--r--src/engine/e_if_inp.h2
-rw-r--r--src/game/client/components/menus.cpp46
-rw-r--r--src/game/client/components/menus.hpp2
-rw-r--r--src/game/client/lineinput.cpp49
-rw-r--r--src/game/client/lineinput.hpp6
-rw-r--r--src/game/editor/ed_editor.cpp40
11 files changed, 333 insertions, 174 deletions
diff --git a/src/base/system.c b/src/base/system.c
index 40d53e8a..c94e4cf9 100644
--- a/src/base/system.c
+++ b/src/base/system.c
@@ -1188,6 +1188,139 @@ void gui_messagebox(const char *title, const char *message)
 int str_isspace(char c) { return c == ' ' || c == '\n' || c == '\t'; }
 
 
+
+static int str_utf8_isstart(char c)
+{
+	if((c&0xC0) == 0x80)  /* 10xxxxxx */
+		return 0;
+	return 1;
+}
+
+int str_utf8_rewind(const char *str, int cursor)
+{
+	while(cursor)
+	{
+		cursor--;
+		if(str_utf8_isstart(*(str + cursor)))
+			break;
+	}
+	return cursor;
+}
+
+int str_utf8_forward(const char *str, int cursor)
+{
+	const char *buf = str + cursor;
+	if(!buf[0])
+		return cursor;
+	
+	if((*buf&0x80) == 0x0)  /* 0xxxxxxx */
+		return cursor+1;
+	else if((*buf&0xE0) == 0xC0) /* 110xxxxx */
+	{
+		if(!buf[1]) return cursor+1;
+		return cursor+2;
+	}
+	else  if((*buf & 0xF0) == 0xE0)	/* 1110xxxx */
+	{
+		if(!buf[1]) return cursor+1;
+		if(!buf[2]) return cursor+2;
+		return cursor+2;
+	}
+	else if((*buf & 0xF8) == 0xF0)	/* 11110xxx */
+	{
+		if(!buf[1]) return cursor+1;
+		if(!buf[2]) return cursor+2;
+		if(!buf[3]) return cursor+3;
+		return cursor+3;
+	}
+	
+	/* invalid */
+	return cursor+1;
+}
+
+int str_utf8_encode(char *ptr, int chr)
+{
+	/* encode */
+	if(chr <= 0x7F)
+	{
+		ptr[0] = (char)chr;
+		return 1;
+	}
+	else if(chr <= 0x7FF)
+	{
+		ptr[0] = 0xC0|((chr>>6)&0x1F);
+		ptr[1] = 0x80|(chr&0x3F);
+		return 2;
+	}
+	else if(chr <= 0xFFFF)
+	{
+		ptr[0] = 0xE0|((chr>>12)&0x0F);
+		ptr[1] = 0xC0|((chr>>6)&0x3F);
+		ptr[2] = 0xC0|(chr&0x3F);
+		return 3;
+	}
+	else if(chr <= 0x10FFFF)
+	{
+		ptr[0] = 0xF0|((chr>>18)&0x07);
+		ptr[1] = 0xC0|((chr>>12)&0x3F);
+		ptr[2] = 0xC0|((chr>>6)&0x3F);
+		ptr[3] = 0xC0|(chr&0x3F);
+		return 4;
+	}
+	
+	return 0;
+}
+
+int str_utf8_decode(const char **ptr)
+{
+	const char *buf = *ptr;
+	int ch = 0;
+	
+	do
+	{
+		if((*buf&0x80) == 0x0)  /* 0xxxxxxx */
+		{
+			ch = *buf;
+			buf++;
+		}
+		else if((*buf&0xE0) == 0xC0) /* 110xxxxx */
+		{
+			ch  = (*buf++ & 0x3F) << 6; if(!(*buf)) break;
+			ch += (*buf++ & 0x3F);
+			if(ch == 0) ch = -1;
+		}
+		else  if((*buf & 0xF0) == 0xE0)	/* 1110xxxx */
+		{
+			ch  = (*buf++ & 0x1F) << 12; if(!(*buf)) break;
+			ch += (*buf++ & 0x3F) <<  6; if(!(*buf)) break;
+			ch += (*buf++ & 0x3F);
+			if(ch == 0) ch = -1;
+		}
+		else if((*buf & 0xF8) == 0xF0)	/* 11110xxx */
+		{
+			ch  = (*buf++ & 0x0F) << 18; if(!(*buf)) break;
+			ch += (*buf++ & 0x3F) << 12; if(!(*buf)) break;
+			ch += (*buf++ & 0x3F) <<  6; if(!(*buf)) break;
+			ch += (*buf++ & 0x3F);
+			if(ch == 0) ch = -1;
+		}
+		else
+		{
+			/* invalid */
+			buf++;
+			break;
+		}
+		
+		*ptr = buf;
+		return ch;
+	} while(0);
+
+	/* out of bounds */
+	*ptr = buf;
+	return -1;
+	
+}
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/src/base/system.h b/src/base/system.h
index 3aa01cab..87cae4a0 100644
--- a/src/base/system.h
+++ b/src/base/system.h
@@ -947,6 +947,69 @@ int str_isspace(char c);
 */
 void gui_messagebox(const char *title, const char *message);
 
+
+/*
+	Function: str_utf8_rewind
+		Moves a cursor backwards in an utf8 string
+	
+	Parameters:
+		str - utf8 string
+		cursor - position in the string
+
+	Returns:
+		New cursor position.
+
+	Remarks:
+		- Won't move the cursor less then 0
+*/
+int str_utf8_rewind(const char *str, int cursor);
+
+/*
+	Function: str_utf8_forward
+		Moves a cursor forwards in an utf8 string
+	
+	Parameters:
+		str - utf8 string
+		cursor - position in the string
+		
+	Returns:
+		New cursor position.
+
+	Remarks:
+		- Won't move the cursor beyond the zero termination marker
+*/
+int str_utf8_forward(const char *str, int cursor);
+
+/*
+	Function: str_utf8_decode
+		Decodes an utf8 character
+	
+	Parameters:
+		ptr - pointer to an utf8 string. this pointer will be moved forward
+		
+	Returns:
+		Unicode value for the character. -1 for invalid characters and 0 for end of string.
+
+	Remarks:
+		- This function will also move the pointer forward.
+*/
+int str_utf8_decode(const char **ptr);
+
+/*
+	Function: str_utf8_encode
+		Encode an utf8 character
+	
+	Parameters:
+		ptr - Pointer to a buffer that should recive the data. Should be able to hold at least 4 bytes.
+		
+	Returns:
+		Number of bytes put into the buffer.
+
+	Remarks:
+		- Does not do zero termination of the string.
+*/
+int str_utf8_encode(char *ptr, int chr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/engine/client/ec_gfx_text.c b/src/engine/client/ec_gfx_text.c
index 2dedd032..a38e800b 100644
--- a/src/engine/client/ec_gfx_text.c
+++ b/src/engine/client/ec_gfx_text.c
@@ -121,6 +121,9 @@ typedef struct FONTSIZEDATA
 	int num_x_chars;
 	int num_y_chars;
 	
+	int char_max_width;
+	int char_max_height;
+	
 	FONTCHAR characters[MAX_CHARACTERS*MAX_CHARACTERS];
 	
 	int current_character;	
@@ -198,38 +201,90 @@ static void grow(unsigned char *in, unsigned char *out, int w, int h)
 				}
 
 			out[y*w+x] = c;
-		} 
+		}
 }
 
-static void font_init_texture(FONTSIZEDATA *font, int width, int height)
+static void font_init_texture(FONTSIZEDATA *sizedata, int charwidth, int charheight, int xchars, int ychars)
 {
+	static int font_memory_usage = 0;
 	int i;
+	int width = charwidth*xchars;
+	int height = charheight*ychars;
 	void *mem = mem_alloc(width*height, 1);
 	mem_zero(mem, width*height);
 	
-	font->texture_width = width;
-	font->texture_height = height;
-	font->num_x_chars = 32;
-	font->num_y_chars = 32;
-	font->current_character = 0;
-	glGenTextures(2, font->textures);
+	if(sizedata->textures[0] == 0)
+		glGenTextures(2, sizedata->textures);
+	else
+		font_memory_usage -= sizedata->texture_width*sizedata->texture_height*2;
+	
+	sizedata->num_x_chars = xchars;
+	sizedata->num_y_chars = ychars;
+	sizedata->texture_width = width;
+	sizedata->texture_height = height;
+	sizedata->current_character = 0;
 	
 	for(i = 0; i < 2; i++)
 	{
-		glBindTexture(GL_TEXTURE_2D, font->textures[i]);
+		glBindTexture(GL_TEXTURE_2D, sizedata->textures[i]);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 		glTexImage2D(GL_TEXTURE_2D, 0, font_texture_format, width, height, 0, font_texture_format, GL_UNSIGNED_BYTE, mem);
+		font_memory_usage += width*height;
 	}
 	
+	dbg_msg("", "font memory usage: %d", font_memory_usage);
+	
 	mem_free(mem);
 }
 
+static void font_increase_texture_size(FONTSIZEDATA *sizedata)
+{
+	if(sizedata->texture_width < sizedata->texture_height)
+		sizedata->num_x_chars <<= 1;
+	else
+		sizedata->num_y_chars <<= 1;
+	font_init_texture(sizedata, sizedata->char_max_width, sizedata->char_max_height, sizedata->num_x_chars, sizedata->num_y_chars);		
+}
+
 static void font_init_index(FONT *font, int index)
 {
-	font->sizes[index].font_size = font_sizes[index];
+	int outline_thickness = 1;
+	FONTSIZEDATA *sizedata = &font->sizes[index];
+	
+	sizedata->font_size = font_sizes[index];
+	FT_Set_Pixel_Sizes(font->ft_face, 0, sizedata->font_size);
+	
+	if(sizedata->font_size >= 18)
+		outline_thickness = 2;
+		
+	{
+		unsigned glyph_index;
+		int charcode;
+		int max_h = 0;
+		int max_w = 0;
+		
+		charcode = FT_Get_First_Char(font->ft_face, &glyph_index);
+		while(glyph_index != 0)
+		{   
+			/* do stuff */
+			FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT);
+			
+			if(font->ft_face->glyph->metrics.width > max_w) max_w = font->ft_face->glyph->metrics.width;
+			if(font->ft_face->glyph->metrics.height > max_h) max_h = font->ft_face->glyph->metrics.height;
+			charcode = FT_Get_Next_Char(font->ft_face, charcode, &glyph_index);
+		}
+		
+		max_w = (max_w>>6)+2+outline_thickness*2;
+		max_h = (max_h>>6)+2+outline_thickness*2;
+		
+		for(sizedata->char_max_width = 1; sizedata->char_max_width < max_w; sizedata->char_max_width <<= 1);
+		for(sizedata->char_max_height = 1; sizedata->char_max_height < max_h; sizedata->char_max_height <<= 1);
+	}
+	
+	//dbg_msg("font", "init size %d, texture size %d %d", font->sizes[index].font_size, w, h);
 	//FT_New_Face(ft_library, "data/fonts/vera.ttf", 0, &font->ft_face);
-	font_init_texture(&font->sizes[index], 1024*2, 1024*2);
+	font_init_texture(sizedata, sizedata->char_max_width, sizedata->char_max_height, 8, 8);
 }
 
 static FONTSIZEDATA *font_get_size(FONT *font, int pixelsize)
@@ -279,6 +334,13 @@ static int font_get_slot(FONTSIZEDATA *sizedata)
 			if(sizedata->characters[i].touch_time < sizedata->characters[oldest].touch_time)
 				oldest = i;
 		}
+		
+		if(time_get()-sizedata->characters[oldest].touch_time < time_freq())
+		{
+			font_increase_texture_size(sizedata);
+			return font_get_slot(sizedata);
+		}
+		
 		return oldest;
 	}
 }
@@ -286,7 +348,7 @@ static int font_get_slot(FONTSIZEDATA *sizedata)
 static int font_render_glyph(FONT *font, FONTSIZEDATA *sizedata, int chr)
 {
 	FT_Bitmap *bitmap;
-	int slot_id = sizedata->current_character;
+	int slot_id = 0;
 	int slot_w = sizedata->texture_width / sizedata->num_x_chars;
 	int slot_h = sizedata->texture_height / sizedata->num_y_chars;
 	int slot_size = slot_w*slot_h;
@@ -297,7 +359,7 @@ static int font_render_glyph(FONT *font, FONTSIZEDATA *sizedata, int chr)
 
 	FT_Set_Pixel_Sizes(font->ft_face, 0, sizedata->font_size);
 
-	if(FT_Load_Char(font->ft_face, chr, FT_LOAD_RENDER))
+	if(FT_Load_Char(font->ft_face, chr, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP))
 	{
 		dbg_msg("font", "error loading glyph %d", chr);
 		return -1;
@@ -412,7 +474,7 @@ static FONTCHAR *font_get_char(FONT *font, FONTSIZEDATA *sizedata, int chr)
 }
 
 /* must only be called from the rendering function as the font must be set to the correct size */
-static void font_setsize(FONT *font, int size)
+static void font_render_setup(FONT *font, int size)
 {
 	FT_Set_Pixel_Sizes(font->ft_face, 0, size);
 }
@@ -420,57 +482,10 @@ static void font_setsize(FONT *font, int size)
 static float font_kerning(FONT *font, int left, int right)
 {
 	FT_Vector kerning = {0,0};
-	FT_Get_Kerning(font->ft_face, left, right, FT_KERNING_DEFAULT, &kerning );
+	FT_Get_Kerning(font->ft_face, left, right, FT_KERNING_DEFAULT, &kerning);
 	return (kerning.x>>6);
 }
 
-static int utf8_decode(const unsigned char **ptr)
-{
-	const unsigned char *buf = *ptr;
-	int ch = 0;
-	
-	do
-	{
-		if((*buf&0x80) == 0x0)  /* 0xxxxxxx */
-		{
-			ch = *buf;
-			buf++;
-		}
-		else if((*buf&0xE0) == 0xC0) /* 110xxxxx */
-		{
-			ch  = (*buf++ & 0x3F) << 6; if(!(*buf)) break;
-			ch += (*buf++ & 0x3F);
-			if(ch == 0) ch = -1;
-		}
-		else  if((*buf & 0xF0) == 0xE0)	/* 1110xxxx */
-		{
-			ch  = (*buf++ & 0x1F) << 12; if(!(*buf)) break;
-			ch += (*buf++ & 0x3F) <<  6; if(!(*buf)) break;
-			ch += (*buf++ & 0x3F);
-			if(ch == 0) ch = -1;
-		}
-		else if((*buf & 0xF8) == 0xF0)	/* 11110xxx */
-		{
-			ch  = (*buf++ & 0x0F) << 18; if(!(*buf)) break;
-			ch += (*buf++ & 0x3F) << 12; if(!(*buf)) break;
-			ch += (*buf++ & 0x3F) <<  6; if(!(*buf)) break;
-			ch += (*buf++ & 0x3F);
-			if(ch == 0) ch = -1;
-		}
-		else
-			break;
-		
-		/* valid */
-		*ptr = buf;
-		return ch;
-	} while(0);
-
-	/* invalid */
-	*ptr = buf;
-	return -1;
-	
-}
-
 void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
 {
 	FONT *font = cursor->font;
@@ -512,7 +527,7 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
 		return;
 
 	sizedata = font_get_size(font, actual_size);
-	font_setsize(font, actual_size);
+	font_render_setup(font, actual_size);
 	
 	/* set length */
 	if(length < 0)
@@ -527,8 +542,8 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
 
 	for(;i < 2; i++)
 	{
-		const unsigned char *current = (unsigned char *)text;
-		const unsigned char *end = current+length;
+		const char *current = (char *)text;
+		const char *end = current+length;
 		//int to_render = length;
 		draw_x = cursor_x;
 		draw_y = cursor_y;
@@ -553,7 +568,7 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
 		{
 			int new_line = 0;
 			//int this_batch = (int)(end-current);
-			const unsigned char *batch_end = end;
+			const char *batch_end = end;
 			if(cursor->line_width > 0 && !(cursor->flags&TEXTFLAG_STOP_AT_END))
 			{
 				int wlen = word_length((char *)current);
@@ -599,19 +614,23 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
 
 			while(current < batch_end)
 			{
+				const char *tmp;
 				float advance = 0;
 				int character = 0;
+				int nextcharacter = 0;
 				FONTCHAR *chr;
 
 				// TODO: UTF-8 decode
-				character = utf8_decode(&current);
+				character = str_utf8_decode(&current);
+				tmp = current;
+				nextcharacter = str_utf8_decode(&tmp);
+				
 				if(character == '\n')
 				{
 					draw_x = cursor->start_x;
 					draw_y += size;
 					draw_x = (int)(draw_x * fake_to_screen_x) / fake_to_screen_x; /* realign */
 					draw_y = (int)(draw_y * fake_to_screen_y) / fake_to_screen_y;
-					/* current++; */
 					continue;
 				}
 
@@ -625,7 +644,7 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
 						gfx_quads_drawTL(draw_x+chr->offset_x*size, draw_y+chr->offset_y*size, chr->width*size, chr->height*size);
 					}
 
-					advance = chr->advance_x; /* + font_kerning(font, character, *(current+1));*/
+					advance = chr->advance_x + font_kerning(font, character, nextcharacter)/size;
 				}
 								
 				if(cursor->flags&TEXTFLAG_STOP_AT_END && draw_x+advance*size-cursor->start_x > cursor->line_width)
diff --git a/src/engine/client/ec_inp.c b/src/engine/client/ec_inp.c
index 4aea840a..495614d6 100644
--- a/src/engine/client/ec_inp.c
+++ b/src/engine/client/ec_inp.c
@@ -49,11 +49,11 @@ enum
 static INPUT_EVENT input_events[INPUT_BUFFER_SIZE];
 static int num_events = 0;
 
-static void add_event(char c, int key, int flags)
+static void add_event(int unicode, int key, int flags)
 {
 	if(num_events != INPUT_BUFFER_SIZE)
 	{
-		input_events[num_events].ch = c;
+		input_events[num_events].unicode = unicode;
 		input_events[num_events].key = key;
 		input_events[num_events].flags = flags;
 		num_events++;
@@ -178,8 +178,8 @@ void inp_update()
 			{
 				/* handle keys */
 				case SDL_KEYDOWN:
-					if(event.key.keysym.unicode < 255)
-						add_event(event.key.keysym.unicode, 0, 0);
+					/*if(event.key.keysym.unicode < 255) */
+					add_event(event.key.keysym.unicode, 0, 0);
 					key = event.key.keysym.sym;
 					break;
 				case SDL_KEYUP:
diff --git a/src/engine/e_if_gfx.h b/src/engine/e_if_gfx.h
index 61a76bad..3d048e85 100644
--- a/src/engine/e_if_gfx.h
+++ b/src/engine/e_if_gfx.h
@@ -671,5 +671,4 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length);
 struct FONT *gfx_font_load(const char *filename);
 void gfx_font_destroy(struct FONT *font);
 
-
 #endif
diff --git a/src/engine/e_if_inp.h b/src/engine/e_if_inp.h
index 00f44ba1..919ac0eb 100644
--- a/src/engine/e_if_inp.h
+++ b/src/engine/e_if_inp.h
@@ -20,7 +20,7 @@ enum
 typedef struct
 {
 	int flags;
-	unsigned char ch;
+	int unicode;
 	int key;
 } INPUT_EVENT;
 
diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp
index 050c7056..0fd024c3 100644
--- a/src/game/client/components/menus.cpp
+++ b/src/game/client/components/menus.cpp
@@ -18,6 +18,7 @@
 
 #include <game/generated/gc_data.hpp>
 #include <game/client/gameclient.hpp>
+#include <game/client/lineinput.hpp>
 #include <mastersrv/mastersrv.h>
 
 vec4 MENUS::gui_color;
@@ -199,7 +200,7 @@ void MENUS::ui_draw_checkbox_number(const void *id, const char *text, int checke
 	ui_draw_checkbox_common(id, text, buf, r);
 }
 
-int MENUS::ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float font_size, bool hidden)
+int MENUS::ui_do_edit_box(void *id, const RECT *rect, char *str, unsigned str_size, float font_size, bool hidden)
 {
     int inside = ui_mouse_inside(rect);
 	int r = 0;
@@ -229,48 +230,7 @@ int MENUS::ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, f
 		for(int i = 0; i < num_inputevents; i++)
 		{
 			len = strlen(str);
-			INPUT_EVENT e = inputevents[i];
-			char c = e.ch;
-			int k = e.key;
-
-			if (at_index > len)
-				at_index = len;
-			
-			if (!(c >= 0 && c < 32) && c != 127)
-			{
-				if (len < str_size - 1 && at_index < str_size - 1)
-				{
-					memmove(str + at_index + 1, str + at_index, len - at_index + 1);
-					str[at_index] = c;
-					at_index++;
-					r = 1;
-				}
-			}
-			
-			if(e.flags&INPFLAG_PRESS)
-			{
-				if (k == KEY_BACKSPACE && at_index > 0)
-				{
-					memmove(str + at_index - 1, str + at_index, len - at_index + 1);
-					at_index--;
-					r = 1;
-				}
-				else if (k == KEY_DELETE && at_index < len)
-				{
-					memmove(str + at_index, str + at_index + 1, len - at_index);
-					r = 1;
-				}
-				else if (k == KEY_RETURN)
-					ui_clear_last_active_item();
-				else if (k == KEY_LEFT && at_index > 0)
-					at_index--;
-				else if (k == KEY_RIGHT && at_index < len)
-					at_index++;
-				else if (k == KEY_HOME)
-					at_index = 0;
-				else if (k == KEY_END)
-					at_index = len;
-			}
+			LINEINPUT::manipulate(inputevents[i], str, str_size, &len, &at_index);
 		}
 	}
 
diff --git a/src/game/client/components/menus.hpp b/src/game/client/components/menus.hpp
index 8a50c122..27423490 100644
--- a/src/game/client/components/menus.hpp
+++ b/src/game/client/components/menus.hpp
@@ -40,7 +40,7 @@ class MENUS : public COMPONENT
 	static void ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const RECT *r);
 	static void ui_draw_checkbox(const void *id, const char *text, int checked, const RECT *r, const void *extra);
 	static void ui_draw_checkbox_number(const void *id, const char *text, int checked, const RECT *r, const void *extra);
-	static int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float font_size, bool hidden=false);
+	static int ui_do_edit_box(void *id, const RECT *rect, char *str, unsigned str_size, float font_size, bool hidden=false);
 
 	static float ui_do_scrollbar_v(const void *id, const RECT *rect, float current);
 	static float ui_do_scrollbar_h(const void *id, const RECT *rect, float current);
diff --git a/src/game/client/lineinput.cpp b/src/game/client/lineinput.cpp
index 40c47d41..f8c9d7e7 100644
--- a/src/game/client/lineinput.cpp
+++ b/src/game/client/lineinput.cpp
@@ -21,23 +21,30 @@ void LINEINPUT::set(const char *string)
 	cursor_pos = len;
 }
 
-void LINEINPUT::process_input(INPUT_EVENT e)
+void LINEINPUT::manipulate(INPUT_EVENT e, char *str, int str_max_size, int *str_len_ptr, int *cursor_pos_ptr)
 {
+	int cursor_pos = *cursor_pos_ptr;
+	int len = *str_len_ptr;
+	
 	if(cursor_pos > len)
 		cursor_pos = len;
 	
-	char c = e.ch;
+	int code = e.unicode;
 	int k = e.key;
 	
 	// 127 is produced on Mac OS X and corresponds to the delete key
-	if (!(c >= 0 && c < 32) && c != 127)
+	if (!(code >= 0 && code < 32) && code != 127)
 	{
-		if (len < sizeof(str) - 1 && cursor_pos < sizeof(str) - 1)
+		char tmp[8];
+		int charsize = str_utf8_encode(tmp, code);
+		
+		if (len < str_max_size - charsize && cursor_pos < str_max_size - charsize)
 		{
-			memmove(str + cursor_pos + 1, str + cursor_pos, len - cursor_pos + 1);
-			str[cursor_pos] = c;
-			cursor_pos++;
-			len++;
+			memmove(str + cursor_pos + charsize, str + cursor_pos, len - cursor_pos + charsize);
+			for(int i = 0; i < charsize; i++)
+				str[cursor_pos+i] = tmp[i];
+			cursor_pos += charsize;
+			len += charsize;
 		}
 	}
 	
@@ -45,22 +52,34 @@ void LINEINPUT::process_input(INPUT_EVENT e)
 	{
 		if (k == KEY_BACKSPACE && cursor_pos > 0)
 		{
-			memmove(str + cursor_pos - 1, str + cursor_pos, len - cursor_pos + 1);
-			cursor_pos--;
-			len--;
+			int new_cursor_pos = str_utf8_rewind(str, cursor_pos);
+			int charsize = cursor_pos-new_cursor_pos;
+			memmove(str+new_cursor_pos, str+cursor_pos, len - charsize + 1); // +1 == null term
+			cursor_pos = new_cursor_pos;
+			len -= charsize;
 		}
 		else if (k == KEY_DELETE && cursor_pos < len)
 		{
-			memmove(str + cursor_pos, str + cursor_pos + 1, len - cursor_pos);
-			len--;
+			int p = str_utf8_forward(str, cursor_pos);
+			int charsize = p-cursor_pos;
+			memmove(str + cursor_pos, str + cursor_pos + charsize, len - cursor_pos - charsize + 1); // +1 == null term
+			len -= charsize;
 		}
 		else if (k == KEY_LEFT && cursor_pos > 0)
-			cursor_pos--;
+			cursor_pos = str_utf8_rewind(str, cursor_pos);
 		else if (k == KEY_RIGHT && cursor_pos < len)
-			cursor_pos++;
+			cursor_pos = str_utf8_forward(str, cursor_pos);
 		else if (k == KEY_HOME)
 			cursor_pos = 0;
 		else if (k == KEY_END)
 			cursor_pos = len;
 	}
+	
+	*cursor_pos_ptr = cursor_pos;
+	*str_len_ptr = len;
+}
+
+void LINEINPUT::process_input(INPUT_EVENT e)
+{
+	manipulate(e, str, sizeof(str), &len, &cursor_pos);
 }
diff --git a/src/game/client/lineinput.hpp b/src/game/client/lineinput.hpp
index 80ef3c52..75b2bd1d 100644
--- a/src/game/client/lineinput.hpp
+++ b/src/game/client/lineinput.hpp
@@ -5,9 +5,11 @@
 class LINEINPUT
 {
 	char str[256];
-	unsigned len;
-	unsigned cursor_pos;
+	int len;
+	int cursor_pos;
 public:
+	static void manipulate(INPUT_EVENT e, char *str, int str_max_size, int *str_len, int *cursor_pos);
+
 	class CALLBACK
 	{
 	public:
diff --git a/src/game/editor/ed_editor.cpp b/src/game/editor/ed_editor.cpp
index 7a8a1ae7..d56a0506 100644
--- a/src/game/editor/ed_editor.cpp
+++ b/src/game/editor/ed_editor.cpp
@@ -18,6 +18,7 @@ extern "C" {
 #include <game/client/render.hpp>
 
 #include "ed_editor.hpp"
+#include <game/client/lineinput.hpp>
 
 static int checker_texture = 0;
 static int background_texture = 0;
@@ -209,44 +210,7 @@ int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float fo
 		for(int i = 0; i < inp_num_events(); i++)
 		{
 			len = strlen(str);
-			
-			INPUT_EVENT e = inp_get_event(i);
-			char c = e.ch;
-			int k = e.key;
-
-			if (at_index > len)
-				at_index = len;
-			
-			if (!(c >= 0 && c < 32) && c != 127)
-			{
-				if (len < str_size - 1 && at_index < str_size - 1)
-				{
-					memmove(str + at_index + 1, str + at_index, len - at_index + 1);
-					str[at_index] = c;
-					at_index++;
-				}
-			}
-			
-			if(e.flags&INPFLAG_PRESS)
-			{
-				if (k == KEY_BACKSPACE && at_index > 0)
-				{
-					memmove(str + at_index - 1, str + at_index, len - at_index + 1);
-					at_index--;
-				}
-				else if (k == KEY_DELETE && at_index < len)
-					memmove(str + at_index, str + at_index + 1, len - at_index);
-				else if (k == KEY_RETURN)
-					ui_clear_last_active_item();
-				else if (k == KEY_LEFT && at_index > 0)
-					at_index--;
-				else if (k == KEY_RIGHT && at_index < len)
-					at_index++;
-				else if (k == KEY_HOME)
-					at_index = 0;
-				else if (k == KEY_END)
-					at_index = len;
-			}
+			LINEINPUT::manipulate(inp_get_event(i), str, str_size, &len, &at_index);
 		}
 		
 		r = 1;