changeset 13263:d65c47851aab draft

(svn r17772) -Fix [FS#3264]: CJK languages don't have spaces, so for adding newlines (multi line strings) we need to (properly) handle the case when there are no spaces instead of truncating the string.
author rubidium <rubidium@openttd.org>
date Tue, 13 Oct 2009 20:19:34 +0000
parents 4738f109dfbb
children fd77863ff104
files src/gfx.cpp src/gfx_func.h src/network/network_chat_gui.cpp
diffstat 3 files changed, 47 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/src/gfx.cpp
+++ b/src/gfx.cpp
@@ -619,12 +619,13 @@
  * starting with index 0 is the real string end.
  *
  * @param str string to check and correct for length restrictions
+ * @param last the last valid location (for '\0') in the buffer of str
  * @param maxw the maximum width the string can have on one line
  * @return return a 32bit wide number consisting of 2 packed values:
  *  0 - 15 the number of lines ADDED to the string
  * 16 - 31 the fontsize in which the length calculation was done at
  */
-uint32 FormatStringLinebreaks(char *str, int maxw)
+uint32 FormatStringLinebreaks(char *str, const char *last, int maxw)
 {
 	FontSize size = _cur_fontsize;
 	int num = 0;
@@ -632,6 +633,7 @@
 	assert(maxw > 0);
 
 	for (;;) {
+		/* The character *after* the last space. */
 		char *last_space = NULL;
 		int w = 0;
 
@@ -641,18 +643,49 @@
 			if (IsWhitespace(c)) last_space = str;
 
 			if (IsPrintable(c)) {
-				w += GetCharacterWidth(size, c);
-				/* string is longer than maximum width so we need to decide what to
-				 * do. We can do two things:
-				 * 1. If no whitespace was found at all up until now (on this line) then
-				 *    we will truncate the string and bail out.
-				 * 2. In all other cases force a linebreak at the last seen whitespace */
+				int char_w = GetCharacterWidth(size, c);
+				w += char_w;
 				if (w > maxw) {
-					if (last_space == NULL) {
-						*Utf8PrevChar(str) = '\0';
+					/* The string is longer than maximum width so we need to decide
+					 * what to do with it. */
+					if (w == char_w) {
+						/* The character is wider than allowed width; don't know
+						 * what to do with this case... bail out! */
 						return num + (size << 16);
 					}
-					str = last_space;
+					if (last_space == NULL) {
+						/* No space has been found. Just terminate at our current
+						 * location. This usually happens for languages that do not
+						 * require spaces in strings, like Chinese, Japanese and
+						 * Korean. For other languages terminating mid-word might
+						 * not be the best, but terminating the whole string instead
+						 * of continuing the word at the next line is worse. */
+						str = Utf8PrevChar(str);
+						size_t len = strlen(str);
+						char *terminator = str + len;
+
+						/* The string location + length of the string + 1 for '\0'
+						 * always fits; otherwise there's no trailing '\0' and it
+						 * it not a valid string. */
+						assert(terminator <= last);
+						assert(*terminator == '\0');
+
+						/* If the string is too long we have to terminate it earlier. */
+						if (terminator == last) {
+							/* Get the 'begin' of the previous character and make that
+							 * the terminator of the string; we truncate it 'early'. */
+							*Utf8PrevChar(terminator) = '\0';
+							len = strlen(str);
+						}
+						/* Also move the terminator! */
+						memmove(str + 1, str, len + 1);
+						*str = '\0';
+						/* str needs to point to the character *after* the last space */
+						str++;
+					} else {
+						/* A space is found; perfect place to terminate */
+						str = last_space;
+					}
 					break;
 				}
 			} else {
@@ -721,7 +754,7 @@
 
 	GetString(buffer, str, lastof(buffer));
 
-	uint32 tmp = FormatStringLinebreaks(buffer, maxw);
+	uint32 tmp = FormatStringLinebreaks(buffer, lastof(buffer), maxw);
 
 	return GetMultilineStringHeight(buffer, GB(tmp, 0, 16));
 }
@@ -761,7 +794,7 @@
 	char buffer[DRAW_STRING_BUFFER];
 	GetString(buffer, str, lastof(buffer));
 
-	uint32 tmp = FormatStringLinebreaks(buffer, maxw);
+	uint32 tmp = FormatStringLinebreaks(buffer, lastof(buffer), maxw);
 	int num = GB(tmp, 0, 16);
 
 	int mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16));
--- a/src/gfx_func.h
+++ b/src/gfx_func.h
@@ -115,7 +115,7 @@
 
 Dimension GetStringBoundingBox(const char *str);
 Dimension GetStringBoundingBox(StringID strid);
-uint32 FormatStringLinebreaks(char *str, int maxw);
+uint32 FormatStringLinebreaks(char *str, const char *last, int maxw);
 int GetStringHeight(StringID str, int maxw);
 Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion);
 void LoadStringWidthTable();
--- a/src/network/network_chat_gui.cpp
+++ b/src/network/network_chat_gui.cpp
@@ -86,7 +86,7 @@
 	Utf8TrimString(buf, DRAW_STRING_BUFFER);
 
 	/* Force linebreaks for strings that are too long */
-	lines = GB(FormatStringLinebreaks(buf, _chatmsg_box.width - 8), 0, 16) + 1;
+	lines = GB(FormatStringLinebreaks(buf, lastof(buf), _chatmsg_box.width - 8), 0, 16) + 1;
 	if (lines >= MAX_CHAT_MESSAGES) return;
 
 	msg_count = GetChatMessageCount();