Changeset - r20614:4513b7ad6147
[Not reviewed]
master
0 9 1
michi_cc - 11 years ago 2013-08-05 20:35:27
michi_cc@openttd.org
(svn r25652) -Fix: Improve text caret movement for complex scripts.
10 files changed with 238 insertions and 13 deletions:
0 comments (0 inline, 0 general)
projects/openttd_vs100.vcxproj
Show inline comments
 
@@ -595,6 +595,7 @@
 
    <ClInclude Include="..\src\story_base.h" />
 
    <ClInclude Include="..\src\story_type.h" />
 
    <ClInclude Include="..\src\strgen\strgen.h" />
 
    <ClInclude Include="..\src\string_base.h" />
 
    <ClInclude Include="..\src\string_func.h" />
 
    <ClInclude Include="..\src\string_type.h" />
 
    <ClInclude Include="..\src\stringfilter_type.h" />
projects/openttd_vs100.vcxproj.filters
Show inline comments
 
@@ -1014,6 +1014,9 @@
 
    <ClInclude Include="..\src\strgen\strgen.h">
 
      <Filter>Header Files</Filter>
 
    </ClInclude>
 
    <ClInclude Include="..\src\string_base.h">
 
      <Filter>Header Files</Filter>
 
    </ClInclude>
 
    <ClInclude Include="..\src\string_func.h">
 
      <Filter>Header Files</Filter>
 
    </ClInclude>
projects/openttd_vs80.vcproj
Show inline comments
 
@@ -1655,6 +1655,10 @@
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\string_base.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\string_func.h"
 
				>
 
			</File>
projects/openttd_vs90.vcproj
Show inline comments
 
@@ -1652,6 +1652,10 @@
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\string_base.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\..\src\string_func.h"
 
				>
 
			</File>
source.list
Show inline comments
 
@@ -328,6 +328,7 @@ stdafx.h
 
story_base.h
 
story_type.h
 
strgen/strgen.h
 
string_base.h
 
string_func.h
 
string_type.h
 
stringfilter_type.h
src/string.cpp
Show inline comments
 
@@ -14,6 +14,7 @@
 
#include "core/alloc_func.hpp"
 
#include "core/math_func.hpp"
 
#include "string_func.h"
 
#include "string_base.h"
 

	
 
#include "table/control_codes.h"
 

	
 
@@ -650,3 +651,123 @@ int strnatcmp(const char *s1, const char
 
	/* Do a normal comparison if ICU is missing or if we cannot create a collator. */
 
	return strcasecmp(s1, s2);
 
}
 

	
 
#ifdef WITH_ICU
 

	
 
#include <unicode/utext.h>
 
#include <unicode/brkiter.h>
 

	
 
/** String iterator using ICU as a backend. */
 
class IcuStringIterator : public StringIterator
 
{
 
	icu::BreakIterator *char_itr; ///< ICU iterator for characters.
 
	const char *string;           ///< Iteration string in UTF-8.
 

	
 
public:
 
	IcuStringIterator() : char_itr(NULL)
 
	{
 
		UErrorCode status = U_ZERO_ERROR;
 
		this->char_itr = icu::BreakIterator::createCharacterInstance(icu::Locale(_current_language != NULL ? _current_language->isocode : "en"), status);
 
	}
 

	
 
	virtual ~IcuStringIterator()
 
	{
 
		delete this->char_itr;
 
	}
 

	
 
	virtual void SetString(const char *s)
 
	{
 
		this->string = s;
 

	
 
		UText text = UTEXT_INITIALIZER;
 
		UErrorCode status = U_ZERO_ERROR;
 
		utext_openUTF8(&text, s, -1, &status);
 
		this->char_itr->setText(&text, status);
 
		this->char_itr->first();
 
	}
 

	
 
	virtual size_t SetCurPosition(size_t pos)
 
	{
 
		/* isBoundary has the documented side-effect of setting the current
 
		 * position to the first valid boundary equal to or greater than
 
		 * the passed value. */
 
		this->char_itr->isBoundary((int32_t)pos);
 
		return this->char_itr->current();
 
	}
 

	
 
	virtual size_t Next()
 
	{
 
		int32_t pos = this->char_itr->next();
 
		return pos == icu::BreakIterator::DONE ? END : pos;
 
	}
 

	
 
	virtual size_t Prev()
 
	{
 
		int32_t pos = this->char_itr->previous();
 
		return pos == icu::BreakIterator::DONE ? END : pos;
 
	}
 
};
 

	
 
/* static */ StringIterator *StringIterator::Create()
 
{
 
	return new IcuStringIterator();
 
}
 

	
 
#else
 

	
 
/** Fallback simple string iterator. */
 
class DefaultStringIterator : public StringIterator
 
{
 
	const char *string; ///< Current string.
 
	size_t len;         ///< String length.
 
	size_t cur_pos;     ///< Current iteration position.
 

	
 
public:
 
	DefaultStringIterator() : string(NULL)
 
	{
 
	}
 

	
 
	virtual void SetString(const char *s)
 
	{
 
		this->string = s;
 
		this->len = strlen(s);
 
		this->cur_pos = 0;
 
	}
 

	
 
	virtual size_t SetCurPosition(size_t pos)
 
	{
 
		assert(this->string != NULL && pos <= this->len);
 
		/* Sanitize in case we get a position inside an UTF-8 sequence. */
 
		while (pos > 0 && IsUtf8Part(this->string[pos])) pos--;
 
		return this->cur_pos = pos;
 
	}
 

	
 
	virtual size_t Next()
 
	{
 
		assert(this->string != NULL);
 

	
 
		/* Already at the end? */
 
		if (this->cur_pos >= this->len) return END;
 

	
 
		WChar c;
 
		this->cur_pos += Utf8Decode(&c, this->string + this->cur_pos);
 
		return this->cur_pos;
 
	}
 

	
 
	virtual size_t Prev()
 
	{
 
		assert(this->string != NULL);
 

	
 
		/* Already at the beginning? */
 
		if (this->cur_pos == 0) return END;
 

	
 
		return this->cur_pos = Utf8PrevChar(this->string + this->cur_pos) - this->string;
 
	}
 
};
 

	
 
/* static */ StringIterator *StringIterator::Create()
 
{
 
	return new DefaultStringIterator();
 
}
 

	
 
#endif
src/string_base.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD 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, version 2.
 
 * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
#ifndef STRING_BASE_H
 
#define STRING_BASE_H
 

	
 
#include "string_type.h"
 

	
 
/** Class for iterating over different kind of parts of a string. */
 
class StringIterator {
 
public:
 
	/** Sentinel to indicate end-of-iteration. */
 
	static const size_t END = SIZE_MAX;
 

	
 
	/**
 
	 * Create a new iterator instance.
 
	 * @return New iterator instance.
 
	 */
 
	static StringIterator *Create();
 

	
 
	virtual ~StringIterator() {}
 

	
 
	/**
 
	 * Set a new iteration string. Must also be called if the string contents
 
	 * changed. The cursor is reset to the start of the string.
 
	 * @param s New string.
 
	 */
 
	virtual void SetString(const char *s) = 0;
 

	
 
	/**
 
	 * Change the current string cursor.
 
	 * @param p New cursor position.
 
	 * @return Actual new cursor position at the next valid character boundary.
 
	 * @pre p has to be inside the current string.
 
	 */
 
	virtual size_t SetCurPosition(size_t pos) = 0;
 

	
 
	/**
 
	 * Advance the cursor by one iteration unit.
 
	 * @return New cursor position (in bytes) or #END if the cursor is already at the end of the string.
 
	 */
 
	virtual size_t Next() = 0;
 

	
 
	/**
 
	 * Move the cursor back by one iteration unit.
 
	 * @return New cursor position (in bytes) or #END if the cursor is already at the start of the string.
 
	 */
 
	virtual size_t Prev() = 0;
 

	
 
protected:
 
	StringIterator() {}
 
};
 

	
 
#endif /* STRING_BASE_H */
src/string_func.h
Show inline comments
 
@@ -147,6 +147,13 @@ static inline char *Utf8PrevChar(char *s
 
	return ret;
 
}
 

	
 
static inline const char *Utf8PrevChar(const char *s)
 
{
 
	const char *ret = s;
 
	while (IsUtf8Part(*--ret)) {}
 
	return ret;
 
}
 

	
 
size_t Utf8StringLength(const char *s);
 

	
 
/**
src/textbuf.cpp
Show inline comments
 
@@ -87,11 +87,11 @@ void Textbuf::DelChar(bool backspace)
 
	this->bytes -= len;
 
	this->chars--;
 

	
 
	if (backspace) this->caretpos -= len;
 

	
 
	this->UpdateStringIter();
 
	this->UpdateWidth();
 
	if (backspace) {
 
		this->caretpos -= len;
 
		this->UpdateCaretPosition();
 
	}
 
	this->UpdateCaretPosition();
 
}
 

	
 
/**
 
@@ -147,6 +147,7 @@ void Textbuf::DeleteAll()
 
	memset(this->buf, 0, this->max_bytes);
 
	this->bytes = this->chars = 1;
 
	this->pixels = this->caretpos = this->caretxoffs = 0;
 
	this->UpdateStringIter();
 
}
 

	
 
/**
 
@@ -163,10 +164,11 @@ bool Textbuf::InsertChar(WChar key)
 
		memmove(this->buf + this->caretpos + len, this->buf + this->caretpos, this->bytes - this->caretpos);
 
		Utf8Encode(this->buf + this->caretpos, key);
 
		this->chars++;
 
		this->bytes  += len;
 
		this->bytes    += len;
 
		this->caretpos += len;
 

	
 
		this->UpdateStringIter();
 
		this->UpdateWidth();
 

	
 
		this->caretpos   += len;
 
		this->UpdateCaretPosition();
 
		return true;
 
	}
 
@@ -210,6 +212,7 @@ bool Textbuf::InsertClipboard()
 
	assert(this->chars <= this->max_chars);
 
	this->buf[this->bytes - 1] = '\0'; // terminating zero
 

	
 
	this->UpdateStringIter();
 
	this->UpdateWidth();
 
	this->UpdateCaretPosition();
 

	
 
@@ -234,11 +237,14 @@ WChar Textbuf::MoveCaretLeft()
 
{
 
	assert(this->CanMoveCaretLeft());
 

	
 
	size_t pos = this->char_iter->Prev();
 
	if (pos == StringIterator::END) pos = 0;
 

	
 
	this->caretpos = (uint16)pos;
 
	this->UpdateCaretPosition();
 

	
 
	WChar c;
 
	const char *s = Utf8PrevChar(this->buf + this->caretpos);
 
	Utf8Decode(&c, s);
 
	this->caretpos    = s - this->buf;
 
	this->UpdateCaretPosition();
 
	Utf8Decode(&c, this->buf + this->caretpos);
 

	
 
	return c;
 
}
 
@@ -261,14 +267,24 @@ WChar Textbuf::MoveCaretRight()
 
{
 
	assert(this->CanMoveCaretRight());
 

	
 
	WChar c;
 
	this->caretpos   += (uint16)Utf8Decode(&c, this->buf + this->caretpos);
 
	size_t pos = this->char_iter->Next();
 
	if (pos == StringIterator::END) pos = this->bytes - 1;
 

	
 
	this->caretpos = (uint16)pos;
 
	this->UpdateCaretPosition();
 

	
 
	WChar c;
 
	Utf8Decode(&c, this->buf + this->caretpos);
 
	return c;
 
}
 

	
 
/** Update the character iter after the text has changed. */
 
void Textbuf::UpdateStringIter()
 
{
 
	this->char_iter->SetString(this->buf);
 
	this->caretpos = (uint16)this->char_iter->SetCurPosition(this->caretpos);
 
}
 

	
 
/** Update pixel width of the text. */
 
void Textbuf::UpdateWidth()
 
{
 
@@ -372,6 +388,8 @@ Textbuf::Textbuf(uint16 max_bytes, uint1
 
	assert(max_bytes != 0);
 
	assert(max_chars != 0);
 

	
 
	this->char_iter = StringIterator::Create();
 

	
 
	this->afilter    = CS_ALPHANUMERAL;
 
	this->max_bytes  = max_bytes;
 
	this->max_chars  = max_chars == UINT16_MAX ? max_bytes : max_chars;
 
@@ -381,6 +399,7 @@ Textbuf::Textbuf(uint16 max_bytes, uint1
 

	
 
Textbuf::~Textbuf()
 
{
 
	delete this->char_iter;
 
	free(this->buf);
 
}
 

	
 
@@ -437,6 +456,7 @@ void Textbuf::UpdateSize()
 
	assert(this->chars <= this->max_chars);
 

	
 
	this->caretpos = this->bytes - 1;
 
	this->UpdateStringIter();
 
	this->UpdateWidth();
 

	
 
	this->UpdateCaretPosition();
src/textbuf_type.h
Show inline comments
 
@@ -14,6 +14,7 @@
 

	
 
#include "string_type.h"
 
#include "strings_type.h"
 
#include "string_base.h"
 

	
 
/**
 
 * Return values for Textbuf::HandleKeypress
 
@@ -61,6 +62,8 @@ struct Textbuf {
 
	void UpdateSize();
 

	
 
private:
 
	StringIterator *char_iter;
 

	
 
	bool CanDelChar(bool backspace);
 
	WChar GetNextDelChar(bool backspace);
 
	void DelChar(bool backspace);
 
@@ -69,6 +72,7 @@ private:
 
	bool CanMoveCaretRight();
 
	WChar MoveCaretRight();
 

	
 
	void UpdateStringIter();
 
	void UpdateWidth();
 
	void UpdateCaretPosition();
 
};
0 comments (0 inline, 0 general)