Add utf-8 conversion utilities and re-add intlGUIEditBox
authorest31 <MTest31@outlook.com>
Tue, 9 Jun 2015 22:35:21 +0000 (00:35 +0200)
committerest31 <MTest31@outlook.com>
Sat, 13 Jun 2015 17:49:55 +0000 (19:49 +0200)
build/android/Makefile
build/android/jni/Android.mk
build/android/libiconv_android.patch [new file with mode: 0644]
build/android/libiconv_android_mk.patch [new file with mode: 0644]
build/android/libiconv_stdio.patch [new file with mode: 0644]
src/CMakeLists.txt
src/intlGUIEditBox.cpp [new file with mode: 0644]
src/intlGUIEditBox.h [new file with mode: 0644]
src/util/string.cpp
src/util/string.h

index 1b33a45995dae370e161d567a5a9a12f0989d764..798d56037d23976a156910ab2ea082af10d42d4a 100644 (file)
@@ -131,6 +131,13 @@ FREETYPE_TIMESTAMP = $(FREETYPE_DIR)timestamp
 FREETYPE_TIMESTAMP_INT = $(ROOT)/deps/freetype_timestamp
 FREETYPE_URL_GIT = https://github.com/cdave1/freetype2-android
 
+ICONV_VERSION = 1.14
+ICONV_DIR = $(ROOT)/deps/libiconv/
+ICONV_LIB = $(ICONV_DIR)/iconv.so
+ICONV_TIMESTAMP = $(ICONV_DIR)timestamp
+ICONV_TIMESTAMP_INT = $(ROOT)/deps/iconv_timestamp
+ICONV_URL_HTTP = http://ftp.gnu.org/pub/gnu/libiconv/libiconv-$(ICONV_VERSION).tar.gz
+
 SQLITE3_FOLDER = sqlite-amalgamation-3080704
 SQLITE3_URL = http://www.sqlite.org/2014/$(SQLITE3_FOLDER).zip
 
@@ -431,6 +438,64 @@ $(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP)
 clean_freetype :
        $(RM) -rf ${FREETYPE_DIR}
 
+$(ICONV_TIMESTAMP) : iconv_download
+       @LAST_MODIF=$$(find ${ICONV_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+       if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then                     \
+               touch ${ICONV_TIMESTAMP};                                           \
+       fi
+
+iconv_download :
+       @if [ ! -d ${ICONV_DIR} ] ; then                                        \
+               echo "iconv sources missing, downloading...";                       \
+               mkdir -p ${ROOT}/deps;                                                 \
+               cd ${ROOT}/deps;                                                               \
+               wget ${ICONV_URL_HTTP} || exit 1;                                    \
+               tar -xzf libiconv-${ICONV_VERSION}.tar.gz || exit 1;                     \
+               rm libiconv-${ICONV_VERSION}.tar.gz;                                     \
+               ln -s libiconv-${ICONV_VERSION} libiconv;                                      \
+               cd ${ICONV_DIR};                                                                   \
+               patch -p1 < ${ROOT}/libiconv_android.patch;                                                     \
+               patch -p1 < ${ROOT}/libiconv_android_mk.patch;                                                     \
+               patch -p1 < ${ROOT}/libiconv_stdio.patch;                                                     \
+               cd jni;                                                                       \
+               ln -s .. src;                                                                         \
+       fi
+
+iconv : $(ICONV_LIB)
+
+$(ICONV_LIB) : $(ICONV_TIMESTAMP)
+       @REFRESH=0;                                                                \
+       if [ ! -e ${ICONV_TIMESTAMP_INT} ] ; then                               \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ! -e ${ICONV_LIB} ] ; then                                         \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ ${ICONV_TIMESTAMP} -nt ${ICONV_TIMESTAMP_INT} ] ; then          \
+               REFRESH=1;                                                             \
+       fi;                                                                        \
+       if [ $$REFRESH -ne 0 ] ; then                                              \
+       mkdir -p ${ICONV_DIR};                                                  \
+       export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK};             \
+       echo "changed timestamp for iconv detected building...";                \
+       cd ${ICONV_DIR};                                            \
+       ./configure;                                            \
+       make;                                            \
+       export NDK_PROJECT_PATH=${ICONV_DIR};                                \
+       ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH}              \
+       APP_PLATFORM=${APP_PLATFORM} APP_ABI=${TARGET_ABI}                         \
+       TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}"                                    \
+       TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}"                                  \
+       TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1;                     \
+       touch ${ICONV_TIMESTAMP};                                               \
+       touch ${ICONV_TIMESTAMP_INT};                                           \
+       else                                                                       \
+               echo "nothing to be done for iconv";                                \
+       fi
+
+clean_iconv :
+       $(RM) -rf ${ICONV_DIR}
+
 #Note: Texturehack patch is required for gpu's not supporting color format
 #      correctly. Known bad GPU:
 #      -geforce on emulator
@@ -736,7 +801,7 @@ assets : $(ASSETS_TIMESTAMP)
 clean_assets :
        @$(RM) -r assets
 
-apk: $(PATHCFGFILE) assets $(IRRLICHT_LIB) $(CURL_LIB) $(GMP_LIB) $(LEVELDB_TARGET)       \
+apk: $(PATHCFGFILE) assets $(ICONV_LIB) $(IRRLICHT_LIB) $(CURL_LIB) $(GMP_LIB) $(LEVELDB_TARGET)       \
        $(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ROOT)/jni/src/android_version.h     \
        sqlite3_download
        @export NDEBUG=$$NDEBUG; $(MAKE) manifest;                                 \
@@ -778,7 +843,7 @@ envpaths :
 
 clean_all :
        @$(MAKE) clean_apk;                                                        \
-       $(MAKE) clean_assets clean_irrlicht clean_leveldb clean_curl clean_openssl \
+       $(MAKE) clean_assets clean_iconv clean_irrlicht clean_leveldb clean_curl clean_openssl \
        clean_openal clean_ogg clean_gmp clean_manifest;                           \
        sleep 1;                                                                   \
        $(RM) -r gen libs obj deps bin Debug and_env
index 2c574bc4167fe9d6a9e34cf6f8eae47348def12b..d62698ce4b70cb7faa321df4a109da2d3587fc9d 100644 (file)
@@ -24,6 +24,11 @@ LOCAL_MODULE := freetype
 LOCAL_SRC_FILES := deps/freetype2-android/Android/obj/local/$(TARGET_ARCH_ABI)/libfreetype2-static.a
 include $(PREBUILT_STATIC_LIBRARY)
 
+include $(CLEAR_VARS)
+LOCAL_MODULE := iconv
+LOCAL_SRC_FILES := deps/libiconv/obj/local/$(TARGET_ARCH_ABI)/libiconv.a
+include $(PREBUILT_STATIC_LIBRARY)
+
 include $(CLEAR_VARS)
 LOCAL_MODULE := openal
 LOCAL_SRC_FILES := deps/openal-soft/libs/$(TARGET_LIBDIR)/libopenal.so
@@ -97,6 +102,7 @@ LOCAL_C_INCLUDES :=                               \
                jni/src/json                              \
                jni/src/cguittfont                        \
                deps/irrlicht/include                     \
+               deps/libiconv/include                     \
                deps/freetype2-android/include            \
                deps/curl/include                         \
                deps/openal-soft/jni/OpenAL/include       \
@@ -153,6 +159,7 @@ LOCAL_SRC_FILES :=                                \
                jni/src/httpfetch.cpp                     \
                jni/src/hud.cpp                           \
                jni/src/imagefilters.cpp                  \
+               jni/src/intlGUIEditBox.cpp                \
                jni/src/inventory.cpp                     \
                jni/src/inventorymanager.cpp              \
                jni/src/itemdef.cpp                       \
@@ -346,7 +353,7 @@ LOCAL_SRC_FILES +=                                \
 LOCAL_SRC_FILES += jni/src/json/jsoncpp.cpp
 
 LOCAL_SHARED_LIBRARIES := openal ogg vorbis gmp
-LOCAL_STATIC_LIBRARIES := Irrlicht freetype curl ssl crypto android_native_app_glue $(PROFILER_LIBS)
+LOCAL_STATIC_LIBRARIES := Irrlicht iconv freetype curl ssl crypto android_native_app_glue $(PROFILER_LIBS)
 
 ifeq ($(HAVE_LEVELDB), 1)
        LOCAL_STATIC_LIBRARIES += LevelDB
diff --git a/build/android/libiconv_android.patch b/build/android/libiconv_android.patch
new file mode 100644 (file)
index 0000000..4eca0a4
--- /dev/null
@@ -0,0 +1,39 @@
+--- a/libcharset/lib/localcharset.c    2015-06-10 11:55:25.933870724 +0200
++++ b/libcharset/lib/localcharset.c    2015-06-10 11:55:39.578063493 +0200
+@@ -47,7 +47,7 @@
+ #if !defined WIN32_NATIVE
+ # include <unistd.h>
+-# if HAVE_LANGINFO_CODESET
++# if HAVE_LANGINFO_CODESET && !(defined __ANDROID__)
+ #  include <langinfo.h>
+ # else
+ #  if 0 /* see comment below */
+@@ -124,7 +124,7 @@ get_charset_aliases (void)
+   cp = charset_aliases;
+   if (cp == NULL)
+     {
+-#if !(defined DARWIN7 || defined VMS || defined WIN32_NATIVE || defined __CYGWIN__)
++#if !(defined DARWIN7 || defined VMS || defined WIN32_NATIVE || defined __CYGWIN__ || defined __ANDROID__)
+       const char *dir;
+       const char *base = "charset.alias";
+       char *file_name;
+@@ -338,6 +338,9 @@ get_charset_aliases (void)
+            "CP54936" "\0" "GB18030" "\0"
+            "CP65001" "\0" "UTF-8" "\0";
+ # endif
++# if defined __ANDROID__
++      cp = "*" "\0" "UTF-8" "\0";
++# endif
+ #endif
+       charset_aliases = cp;
+@@ -361,7 +364,7 @@ locale_charset (void)
+   const char *codeset;
+   const char *aliases;
+-#if !(defined WIN32_NATIVE || defined OS2)
++#if !(defined WIN32_NATIVE || defined OS2 || defined __ANDROID__)
+ # if HAVE_LANGINFO_CODESET
diff --git a/build/android/libiconv_android_mk.patch b/build/android/libiconv_android_mk.patch
new file mode 100644 (file)
index 0000000..9eb7a19
--- /dev/null
@@ -0,0 +1,51 @@
+From fe27aae178d65b06d5f4104158343b0d2d33e3f0 Mon Sep 17 00:00:00 2001
+From: Pierre Zurek <pierrezurek@gmail.com>
+Date: Sat, 2 Apr 2011 23:11:57 +0200
+Subject: [PATCH] Added Android.mk.
+
+This makefile first executes the configure script, that will
+generate the config.h files necessary to build iconv.
+---
+ Android.mk | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+ create mode 100644 Android.mk
+
+diff --git a/jni/Android.mk b/jni/Android.mk
+new file mode 100644
+index 0000000..799b22d
+--- /dev/null
++++ b/jni/Android.mk
+@@ -0,0 +1,32 @@
++LOCAL_PATH := $(call my-dir)
++include $(CLEAR_VARS)
++
++LOCAL_ARM_MODE := arm
++
++LOCAL_SRC_FILES := src/lib/iconv.c \
++                   src/libcharset/lib/localcharset.c \
++                   src/lib/relocatable.c
++
++LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/include \
++                    $(LOCAL_PATH)/src/libcharset \
++                    $(LOCAL_PATH)/src/libcharset/include
++
++LOCAL_CFLAGS := \
++    -DLIBDIR="\"c\"" \
++    -D_ANDROID \
++    -DBUILDING_LIBCHARSET \
++    -DBUILDING_LIBICONV \
++    -DBUILDING_LIBICONV \
++    -DIN_LIBRARY
++
++LOCAL_MODULE:= iconv
++
++$(info Configuring iconv...)
++COMMAND := $(shell \
++           export PATH=$(TOOLCHAIN_INSTALL_DIR)/bin:$$PATH; \
++           cd $(LOCAL_PATH); \
++           make distclean; \
++           ./configure --host="arm-linux-androideabi")
++$(info iconv configured.)
++
++include $(BUILD_STATIC_LIBRARY)
++
diff --git a/build/android/libiconv_stdio.patch b/build/android/libiconv_stdio.patch
new file mode 100644 (file)
index 0000000..19cb6aa
--- /dev/null
@@ -0,0 +1,10 @@
+--- a/srclib/stdio.in.h        2011-08-07 15:42:06.000000000 +0200
++++ b/srclib/stdio.in.h        2015-06-10 09:27:58.129056262 +0200
+@@ -695,7 +696,8 @@ _GL_CXXALIASWARN (gets);
+ /* It is very rare that the developer ever has full control of stdin,
+    so any use of gets warrants an unconditional warning.  Assume it is
+    always declared, since it is required by C89.  */
+-_GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");
++/*_GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");*/
++#define gets(a) fgets( a, sizeof(*(a)), stdin)
+ #endif
\ No newline at end of file
index 190f4e9bd9d079056093975522d387e3ff3a7a0c..ef508d9b89228246564419b4ae99d6de9d367edf 100644 (file)
@@ -417,6 +417,7 @@ set(client_SRCS
        guiVolumeChange.cpp
        hud.cpp
        imagefilters.cpp
+       intlGUIEditBox.cpp
        keycode.cpp
        localplayer.cpp
        main.cpp
diff --git a/src/intlGUIEditBox.cpp b/src/intlGUIEditBox.cpp
new file mode 100644 (file)
index 0000000..33bf8a1
--- /dev/null
@@ -0,0 +1,1509 @@
+// 11.11.2011 11:11 ValkaTR
+//
+// This is a copy of intlGUIEditBox from the irrlicht, but with a
+// fix in the OnEvent function, which doesn't allowed input of
+// other keyboard layouts than latin-1
+//
+// Characters like: ä ö ü õ ы й ю я ъ № € ° ...
+//
+// This fix is only needed for linux, because of a bug
+// in the CIrrDeviceLinux.cpp:1014-1015 of the irrlicht
+//
+// Also locale in the programm should not be changed to
+// a "C", "POSIX" or whatever, it should be set to "",
+// or XLookupString will return nothing for the international
+// characters.
+//
+// From the "man setlocale":
+//
+// On startup of the main program, the portable "C" locale
+// is selected as default.  A  program  may  be  made
+// portable to all locales by calling:
+//
+//           setlocale(LC_ALL, "");
+//
+//       after  program initialization....
+//
+
+// Copyright (C) 2002-2013 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#include "intlGUIEditBox.h"
+
+#if defined(_IRR_COMPILE_WITH_GUI_) && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
+
+#include "IGUISkin.h"
+#include "IGUIEnvironment.h"
+#include "IGUIFont.h"
+#include "IVideoDriver.h"
+//#include "rect.h"
+//#include "irrlicht/os.cpp"
+#include "porting.h"
+//#include "Keycodes.h"
+#include "log.h"
+
+/*
+       todo:
+       optional scrollbars
+       ctrl+left/right to select word
+       double click/ctrl click: word select + drag to select whole words, triple click to select line
+       optional? dragging selected text
+       numerical
+*/
+
+namespace irr
+{
+namespace gui
+{
+
+//! constructor
+intlGUIEditBox::intlGUIEditBox(const wchar_t* text, bool border,
+               IGUIEnvironment* environment, IGUIElement* parent, s32 id,
+               const core::rect<s32>& rectangle)
+       : IGUIEditBox(environment, parent, id, rectangle), MouseMarking(false),
+       Border(border), OverrideColorEnabled(false), MarkBegin(0), MarkEnd(0),
+       OverrideColor(video::SColor(101,255,255,255)), OverrideFont(0), LastBreakFont(0),
+       Operator(0), BlinkStartTime(0), CursorPos(0), HScrollPos(0), VScrollPos(0), Max(0),
+       WordWrap(false), MultiLine(false), AutoScroll(true), PasswordBox(false),
+       PasswordChar(L'*'), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER),
+       CurrentTextRect(0,0,1,1), FrameRect(rectangle)
+{
+       #ifdef _DEBUG
+       setDebugName("intlintlGUIEditBox");
+       #endif
+
+       Text = text;
+
+       if (Environment)
+               Operator = Environment->getOSOperator();
+
+       if (Operator)
+               Operator->grab();
+
+       // this element can be tabbed to
+       setTabStop(true);
+       setTabOrder(-1);
+
+       IGUISkin *skin = 0;
+       if (Environment)
+               skin = Environment->getSkin();
+       if (Border && skin)
+       {
+               FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+               FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+               FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+               FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+       }
+
+       breakText();
+
+       calculateScrollPos();
+}
+
+
+//! destructor
+intlGUIEditBox::~intlGUIEditBox()
+{
+       if (OverrideFont)
+               OverrideFont->drop();
+
+       if (Operator)
+               Operator->drop();
+}
+
+
+//! Sets another skin independent font.
+void intlGUIEditBox::setOverrideFont(IGUIFont* font)
+{
+       if (OverrideFont == font)
+               return;
+
+       if (OverrideFont)
+               OverrideFont->drop();
+
+       OverrideFont = font;
+
+       if (OverrideFont)
+               OverrideFont->grab();
+
+       breakText();
+}
+
+IGUIFont * intlGUIEditBox::getOverrideFont() const
+{
+       return OverrideFont;
+}
+
+//! Get the font which is used right now for drawing
+IGUIFont* intlGUIEditBox::getActiveFont() const
+{
+       if ( OverrideFont )
+               return OverrideFont;
+       IGUISkin* skin = Environment->getSkin();
+       if (skin)
+               return skin->getFont();
+       return 0;
+}
+
+//! Sets another color for the text.
+void intlGUIEditBox::setOverrideColor(video::SColor color)
+{
+       OverrideColor = color;
+       OverrideColorEnabled = true;
+}
+
+video::SColor intlGUIEditBox::getOverrideColor() const
+{
+       return OverrideColor;
+}
+
+//! Turns the border on or off
+void intlGUIEditBox::setDrawBorder(bool border)
+{
+       Border = border;
+}
+
+//! Sets whether to draw the background
+void intlGUIEditBox::setDrawBackground(bool draw)
+{
+}
+
+//! Sets if the text should use the overide color or the color in the gui skin.
+void intlGUIEditBox::enableOverrideColor(bool enable)
+{
+       OverrideColorEnabled = enable;
+}
+
+bool intlGUIEditBox::isOverrideColorEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return OverrideColorEnabled;
+}
+
+//! Enables or disables word wrap
+void intlGUIEditBox::setWordWrap(bool enable)
+{
+       WordWrap = enable;
+       breakText();
+}
+
+
+void intlGUIEditBox::updateAbsolutePosition()
+{
+    core::rect<s32> oldAbsoluteRect(AbsoluteRect);
+       IGUIElement::updateAbsolutePosition();
+       if ( oldAbsoluteRect != AbsoluteRect )
+       {
+        breakText();
+       }
+}
+
+
+//! Checks if word wrap is enabled
+bool intlGUIEditBox::isWordWrapEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return WordWrap;
+}
+
+
+//! Enables or disables newlines.
+void intlGUIEditBox::setMultiLine(bool enable)
+{
+       MultiLine = enable;
+}
+
+
+//! Checks if multi line editing is enabled
+bool intlGUIEditBox::isMultiLineEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return MultiLine;
+}
+
+
+void intlGUIEditBox::setPasswordBox(bool passwordBox, wchar_t passwordChar)
+{
+       PasswordBox = passwordBox;
+       if (PasswordBox)
+       {
+               PasswordChar = passwordChar;
+               setMultiLine(false);
+               setWordWrap(false);
+               BrokenText.clear();
+       }
+}
+
+
+bool intlGUIEditBox::isPasswordBox() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return PasswordBox;
+}
+
+
+//! Sets text justification
+void intlGUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
+{
+       HAlign = horizontal;
+       VAlign = vertical;
+}
+
+
+//! called if an event happened.
+bool intlGUIEditBox::OnEvent(const SEvent& event)
+{
+       if (IsEnabled)
+       {
+
+               switch(event.EventType)
+               {
+               case EET_GUI_EVENT:
+                       if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
+                       {
+                               if (event.GUIEvent.Caller == this)
+                               {
+                                       MouseMarking = false;
+                                       setTextMarkers(0,0);
+                               }
+                       }
+                       break;
+               case EET_KEY_INPUT_EVENT:
+        {
+#if (defined(linux) || defined(__linux) || defined(__FreeBSD__))
+            // ################################################################
+                       // ValkaTR:
+            // This part is the difference from the original intlGUIEditBox
+            // It converts UTF-8 character into a UCS-2 (wchar_t)
+            wchar_t wc = L'_';
+            mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
+
+            //printf( "char: %lc (%u)  \r\n", wc, wc );
+
+            SEvent irrevent(event);
+            irrevent.KeyInput.Char = wc;
+            // ################################################################
+
+                       if (processKey(irrevent))
+                               return true;
+#else
+                       if (processKey(event))
+                               return true;
+#endif // defined(linux)
+
+                       break;
+        }
+               case EET_MOUSE_INPUT_EVENT:
+                       if (processMouse(event))
+                               return true;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return IGUIElement::OnEvent(event);
+}
+
+
+bool intlGUIEditBox::processKey(const SEvent& event)
+{
+       if (!event.KeyInput.PressedDown)
+               return false;
+
+       bool textChanged = false;
+       s32 newMarkBegin = MarkBegin;
+       s32 newMarkEnd = MarkEnd;
+
+       // control shortcut handling
+
+       if (event.KeyInput.Control)
+       {
+               // german backlash '\' entered with control + '?'
+               if ( event.KeyInput.Char == '\\' )
+               {
+                       inputChar(event.KeyInput.Char);
+                       return true;
+               }
+
+               switch(event.KeyInput.Key)
+               {
+               case KEY_KEY_A:
+                       // select all
+                       newMarkBegin = 0;
+                       newMarkEnd = Text.size();
+                       break;
+               case KEY_KEY_C:
+                       // copy to clipboard
+                       if (!PasswordBox && Operator && MarkBegin != MarkEnd)
+                       {
+                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+                               core::stringc s;
+                               s = Text.subString(realmbgn, realmend - realmbgn).c_str();
+                               Operator->copyToClipboard(s.c_str());
+                       }
+                       break;
+               case KEY_KEY_X:
+                       // cut to the clipboard
+                       if (!PasswordBox && Operator && MarkBegin != MarkEnd)
+                       {
+                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+                               // copy
+                               core::stringc sc;
+                               sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
+                               Operator->copyToClipboard(sc.c_str());
+
+                               if (IsEnabled)
+                               {
+                                       // delete
+                                       core::stringw s;
+                                       s = Text.subString(0, realmbgn);
+                                       s.append( Text.subString(realmend, Text.size()-realmend) );
+                                       Text = s;
+
+                                       CursorPos = realmbgn;
+                                       newMarkBegin = 0;
+                                       newMarkEnd = 0;
+                                       textChanged = true;
+                               }
+                       }
+                       break;
+               case KEY_KEY_V:
+                       if ( !IsEnabled )
+                               break;
+
+                       // paste from the clipboard
+                       if (Operator)
+                       {
+                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+                               // add new character
+                               const c8* p = Operator->getTextFromClipboard();
+                               if (p)
+                               {
+                                       if (MarkBegin == MarkEnd)
+                                       {
+                                               // insert text
+                                               core::stringw s = Text.subString(0, CursorPos);
+                                               s.append(p);
+                                               s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+
+                                               if (!Max || s.size()<=Max) // thx to Fish FH for fix
+                                               {
+                                                       Text = s;
+                                                       s = p;
+                                                       CursorPos += s.size();
+                                               }
+                                       }
+                                       else
+                                       {
+                                               // replace text
+
+                                               core::stringw s = Text.subString(0, realmbgn);
+                                               s.append(p);
+                                               s.append( Text.subString(realmend, Text.size()-realmend) );
+
+                                               if (!Max || s.size()<=Max)  // thx to Fish FH for fix
+                                               {
+                                                       Text = s;
+                                                       s = p;
+                                                       CursorPos = realmbgn + s.size();
+                                               }
+                                       }
+                               }
+
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                               textChanged = true;
+                       }
+                       break;
+               case KEY_HOME:
+                       // move/highlight to start of text
+                       if (event.KeyInput.Shift)
+                       {
+                               newMarkEnd = CursorPos;
+                               newMarkBegin = 0;
+                               CursorPos = 0;
+                       }
+                       else
+                       {
+                               CursorPos = 0;
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                       }
+                       break;
+               case KEY_END:
+                       // move/highlight to end of text
+                       if (event.KeyInput.Shift)
+                       {
+                               newMarkBegin = CursorPos;
+                               newMarkEnd = Text.size();
+                               CursorPos = 0;
+                       }
+                       else
+                       {
+                               CursorPos = Text.size();
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                       }
+                       break;
+               default:
+                       return false;
+               }
+       }
+       // default keyboard handling
+       else
+       switch(event.KeyInput.Key)
+       {
+       case KEY_END:
+               {
+                       s32 p = Text.size();
+                       if (WordWrap || MultiLine)
+                       {
+                               p = getLineFromPos(CursorPos);
+                               p = BrokenTextPositions[p] + (s32)BrokenText[p].size();
+                               if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' ))
+                                       p-=1;
+                       }
+
+                       if (event.KeyInput.Shift)
+                       {
+                               if (MarkBegin == MarkEnd)
+                                       newMarkBegin = CursorPos;
+
+                               newMarkEnd = p;
+                       }
+                       else
+                       {
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                       }
+                       CursorPos = p;
+                       BlinkStartTime = porting::getTimeMs();
+               }
+               break;
+       case KEY_HOME:
+               {
+
+                       s32 p = 0;
+                       if (WordWrap || MultiLine)
+                       {
+                               p = getLineFromPos(CursorPos);
+                               p = BrokenTextPositions[p];
+                       }
+
+                       if (event.KeyInput.Shift)
+                       {
+                               if (MarkBegin == MarkEnd)
+                                       newMarkBegin = CursorPos;
+                               newMarkEnd = p;
+                       }
+                       else
+                       {
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                       }
+                       CursorPos = p;
+                       BlinkStartTime = porting::getTimeMs();
+               }
+               break;
+       case KEY_RETURN:
+               if (MultiLine)
+               {
+                       inputChar(L'\n');
+                       return true;
+               }
+               else
+               {
+                   sendGuiEvent( EGET_EDITBOX_ENTER );
+               }
+               break;
+       case KEY_LEFT:
+
+               if (event.KeyInput.Shift)
+               {
+                       if (CursorPos > 0)
+                       {
+                               if (MarkBegin == MarkEnd)
+                                       newMarkBegin = CursorPos;
+
+                               newMarkEnd = CursorPos-1;
+                       }
+               }
+               else
+               {
+                       newMarkBegin = 0;
+                       newMarkEnd = 0;
+               }
+
+               if (CursorPos > 0) CursorPos--;
+               BlinkStartTime = porting::getTimeMs();
+               break;
+
+       case KEY_RIGHT:
+               if (event.KeyInput.Shift)
+               {
+                       if (Text.size() > (u32)CursorPos)
+                       {
+                               if (MarkBegin == MarkEnd)
+                                       newMarkBegin = CursorPos;
+
+                               newMarkEnd = CursorPos+1;
+                       }
+               }
+               else
+               {
+                       newMarkBegin = 0;
+                       newMarkEnd = 0;
+               }
+
+               if (Text.size() > (u32)CursorPos) CursorPos++;
+               BlinkStartTime = porting::getTimeMs();
+               break;
+       case KEY_UP:
+               if (MultiLine || (WordWrap && BrokenText.size() > 1) )
+               {
+                       s32 lineNo = getLineFromPos(CursorPos);
+                       s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd);
+                       if (lineNo > 0)
+                       {
+                               s32 cp = CursorPos - BrokenTextPositions[lineNo];
+                               if ((s32)BrokenText[lineNo-1].size() < cp)
+                                       CursorPos = BrokenTextPositions[lineNo-1] + (s32)BrokenText[lineNo-1].size()-1;
+                               else
+                                       CursorPos = BrokenTextPositions[lineNo-1] + cp;
+                       }
+
+                       if (event.KeyInput.Shift)
+                       {
+                               newMarkBegin = mb;
+                               newMarkEnd = CursorPos;
+                       }
+                       else
+                       {
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                       }
+
+               }
+               else
+               {
+                       return false;
+               }
+               break;
+       case KEY_DOWN:
+               if (MultiLine || (WordWrap && BrokenText.size() > 1) )
+               {
+                       s32 lineNo = getLineFromPos(CursorPos);
+                       s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd);
+                       if (lineNo < (s32)BrokenText.size()-1)
+                       {
+                               s32 cp = CursorPos - BrokenTextPositions[lineNo];
+                               if ((s32)BrokenText[lineNo+1].size() < cp)
+                                       CursorPos = BrokenTextPositions[lineNo+1] + BrokenText[lineNo+1].size()-1;
+                               else
+                                       CursorPos = BrokenTextPositions[lineNo+1] + cp;
+                       }
+
+                       if (event.KeyInput.Shift)
+                       {
+                               newMarkBegin = mb;
+                               newMarkEnd = CursorPos;
+                       }
+                       else
+                       {
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                       }
+
+               }
+               else
+               {
+                       return false;
+               }
+               break;
+
+       case KEY_BACK:
+               if ( !this->IsEnabled )
+                       break;
+
+               if (Text.size())
+               {
+                       core::stringw s;
+
+                       if (MarkBegin != MarkEnd)
+                       {
+                               // delete marked text
+                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+                               s = Text.subString(0, realmbgn);
+                               s.append( Text.subString(realmend, Text.size()-realmend) );
+                               Text = s;
+
+                               CursorPos = realmbgn;
+                       }
+                       else
+                       {
+                               // delete text behind cursor
+                               if (CursorPos>0)
+                                       s = Text.subString(0, CursorPos-1);
+                               else
+                                       s = L"";
+                               s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+                               Text = s;
+                               --CursorPos;
+                       }
+
+                       if (CursorPos < 0)
+                               CursorPos = 0;
+                       BlinkStartTime = porting::getTimeMs();
+                       newMarkBegin = 0;
+                       newMarkEnd = 0;
+                       textChanged = true;
+               }
+               break;
+       case KEY_DELETE:
+               if ( !this->IsEnabled )
+                       break;
+
+               if (Text.size() != 0)
+               {
+                       core::stringw s;
+
+                       if (MarkBegin != MarkEnd)
+                       {
+                               // delete marked text
+                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+                               s = Text.subString(0, realmbgn);
+                               s.append( Text.subString(realmend, Text.size()-realmend) );
+                               Text = s;
+
+                               CursorPos = realmbgn;
+                       }
+                       else
+                       {
+                               // delete text before cursor
+                               s = Text.subString(0, CursorPos);
+                               s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) );
+                               Text = s;
+                       }
+
+                       if (CursorPos > (s32)Text.size())
+                               CursorPos = (s32)Text.size();
+
+                       BlinkStartTime = porting::getTimeMs();
+                       newMarkBegin = 0;
+                       newMarkEnd = 0;
+                       textChanged = true;
+               }
+               break;
+
+       case KEY_ESCAPE:
+       case KEY_TAB:
+       case KEY_SHIFT:
+       case KEY_F1:
+       case KEY_F2:
+       case KEY_F3:
+       case KEY_F4:
+       case KEY_F5:
+       case KEY_F6:
+       case KEY_F7:
+       case KEY_F8:
+       case KEY_F9:
+       case KEY_F10:
+       case KEY_F11:
+       case KEY_F12:
+       case KEY_F13:
+       case KEY_F14:
+       case KEY_F15:
+       case KEY_F16:
+       case KEY_F17:
+       case KEY_F18:
+       case KEY_F19:
+       case KEY_F20:
+       case KEY_F21:
+       case KEY_F22:
+       case KEY_F23:
+       case KEY_F24:
+               // ignore these keys
+               return false;
+
+       default:
+               inputChar(event.KeyInput.Char);
+               return true;
+       }
+
+    // Set new text markers
+    setTextMarkers( newMarkBegin, newMarkEnd );
+
+       // break the text if it has changed
+       if (textChanged)
+       {
+               breakText();
+               sendGuiEvent(EGET_EDITBOX_CHANGED);
+       }
+
+       calculateScrollPos();
+
+       return true;
+}
+
+
+//! draws the element and its children
+void intlGUIEditBox::draw()
+{
+       if (!IsVisible)
+               return;
+
+       const bool focus = Environment->hasFocus(this);
+
+       IGUISkin* skin = Environment->getSkin();
+       if (!skin)
+               return;
+
+       FrameRect = AbsoluteRect;
+
+       // draw the border
+
+       if (Border)
+       {
+               skin->draw3DSunkenPane(this, skin->getColor(EGDC_WINDOW),
+                       false, true, FrameRect, &AbsoluteClippingRect);
+
+               FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+               FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+               FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+               FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+       }
+       core::rect<s32> localClipRect = FrameRect;
+       localClipRect.clipAgainst(AbsoluteClippingRect);
+
+       // draw the text
+
+       IGUIFont* font = OverrideFont;
+       if (!OverrideFont)
+               font = skin->getFont();
+
+       s32 cursorLine = 0;
+       s32 charcursorpos = 0;
+
+       if (font)
+       {
+               if (LastBreakFont != font)
+               {
+                       breakText();
+               }
+
+               // calculate cursor pos
+
+               core::stringw *txtLine = &Text;
+               s32 startPos = 0;
+
+               core::stringw s, s2;
+
+               // get mark position
+               const bool ml = (!PasswordBox && (WordWrap || MultiLine));
+               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+               const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0;
+               const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1;
+               const s32 lineCount = ml ? BrokenText.size() : 1;
+
+               // Save the override color information.
+               // Then, alter it if the edit box is disabled.
+               const bool prevOver = OverrideColorEnabled;
+               const video::SColor prevColor = OverrideColor;
+
+               if (Text.size())
+               {
+                       if (!IsEnabled && !OverrideColorEnabled)
+                       {
+                               OverrideColorEnabled = true;
+                               OverrideColor = skin->getColor(EGDC_GRAY_TEXT);
+                       }
+
+                       for (s32 i=0; i < lineCount; ++i)
+                       {
+                               setTextRect(i);
+
+                               // clipping test - don't draw anything outside the visible area
+                               core::rect<s32> c = localClipRect;
+                               c.clipAgainst(CurrentTextRect);
+                               if (!c.isValid())
+                                       continue;
+
+                               // get current line
+                               if (PasswordBox)
+                               {
+                                       if (BrokenText.size() != 1)
+                                       {
+                                               BrokenText.clear();
+                                               BrokenText.push_back(core::stringw());
+                                       }
+                                       if (BrokenText[0].size() != Text.size())
+                                       {
+                                               BrokenText[0] = Text;
+                                               for (u32 q = 0; q < Text.size(); ++q)
+                                               {
+                                                       BrokenText[0] [q] = PasswordChar;
+                                               }
+                                       }
+                                       txtLine = &BrokenText[0];
+                                       startPos = 0;
+                               }
+                               else
+                               {
+                                       txtLine = ml ? &BrokenText[i] : &Text;
+                                       startPos = ml ? BrokenTextPositions[i] : 0;
+                               }
+
+
+                               // draw normal text
+                               font->draw(txtLine->c_str(), CurrentTextRect,
+                                       OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
+                                       false, true, &localClipRect);
+
+                               // draw mark and marked text
+                               if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)
+                               {
+
+                                       s32 mbegin = 0, mend = 0;
+                                       s32 lineStartPos = 0, lineEndPos = txtLine->size();
+
+                                       if (i == hlineStart)
+                                       {
+                                               // highlight start is on this line
+                                               s = txtLine->subString(0, realmbgn - startPos);
+                                               mbegin = font->getDimension(s.c_str()).Width;
+
+                                               // deal with kerning
+                                               mbegin += font->getKerningWidth(
+                                                       &((*txtLine)[realmbgn - startPos]),
+                                                       realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0);
+
+                                               lineStartPos = realmbgn - startPos;
+                                       }
+                                       if (i == hlineStart + hlineCount - 1)
+                                       {
+                                               // highlight end is on this line
+                                               s2 = txtLine->subString(0, realmend - startPos);
+                                               mend = font->getDimension(s2.c_str()).Width;
+                                               lineEndPos = (s32)s2.size();
+                                       }
+                                       else
+                                               mend = font->getDimension(txtLine->c_str()).Width;
+
+                                       CurrentTextRect.UpperLeftCorner.X += mbegin;
+                                       CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin;
+
+                                       // draw mark
+                                       skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect);
+
+                                       // draw marked text
+                                       s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos);
+
+                                       if (s.size())
+                                               font->draw(s.c_str(), CurrentTextRect,
+                                                       OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
+                                                       false, true, &localClipRect);
+
+                               }
+                       }
+
+                       // Return the override color information to its previous settings.
+                       OverrideColorEnabled = prevOver;
+                       OverrideColor = prevColor;
+               }
+
+               // draw cursor
+
+               if (WordWrap || MultiLine)
+               {
+                       cursorLine = getLineFromPos(CursorPos);
+                       txtLine = &BrokenText[cursorLine];
+                       startPos = BrokenTextPositions[cursorLine];
+               }
+               s = txtLine->subString(0,CursorPos-startPos);
+               charcursorpos = font->getDimension(s.c_str()).Width +
+                       font->getKerningWidth(L"_", CursorPos-startPos > 0 ? &((*txtLine)[CursorPos-startPos-1]) : 0);
+
+               if (focus && (porting::getTimeMs() - BlinkStartTime) % 700 < 350)
+               {
+                       setTextRect(cursorLine);
+                       CurrentTextRect.UpperLeftCorner.X += charcursorpos;
+
+                       font->draw(L"_", CurrentTextRect,
+                               OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
+                               false, true, &localClipRect);
+               }
+       }
+
+       // draw children
+       IGUIElement::draw();
+}
+
+
+//! Sets the new caption of this element.
+void intlGUIEditBox::setText(const wchar_t* text)
+{
+       Text = text;
+       if (u32(CursorPos) > Text.size())
+               CursorPos = Text.size();
+       HScrollPos = 0;
+       breakText();
+}
+
+
+//! Enables or disables automatic scrolling with cursor position
+//! \param enable: If set to true, the text will move around with the cursor position
+void intlGUIEditBox::setAutoScroll(bool enable)
+{
+       AutoScroll = enable;
+}
+
+
+//! Checks to see if automatic scrolling is enabled
+//! \return true if automatic scrolling is enabled, false if not
+bool intlGUIEditBox::isAutoScrollEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return AutoScroll;
+}
+
+
+//! Gets the area of the text in the edit box
+//! \return Returns the size in pixels of the text
+core::dimension2du intlGUIEditBox::getTextDimension()
+{
+       core::rect<s32> ret;
+
+       setTextRect(0);
+       ret = CurrentTextRect;
+
+       for (u32 i=1; i < BrokenText.size(); ++i)
+       {
+               setTextRect(i);
+               ret.addInternalPoint(CurrentTextRect.UpperLeftCorner);
+               ret.addInternalPoint(CurrentTextRect.LowerRightCorner);
+       }
+
+       return core::dimension2du(ret.getSize());
+}
+
+
+//! Sets the maximum amount of characters which may be entered in the box.
+//! \param max: Maximum amount of characters. If 0, the character amount is
+//! infinity.
+void intlGUIEditBox::setMax(u32 max)
+{
+       Max = max;
+
+       if (Text.size() > Max && Max != 0)
+               Text = Text.subString(0, Max);
+}
+
+
+//! Returns maximum amount of characters, previously set by setMax();
+u32 intlGUIEditBox::getMax() const
+{
+       return Max;
+}
+
+
+bool intlGUIEditBox::processMouse(const SEvent& event)
+{
+       switch(event.MouseInput.Event)
+       {
+       case irr::EMIE_LMOUSE_LEFT_UP:
+               if (Environment->hasFocus(this))
+               {
+                       CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+                       if (MouseMarking)
+                       {
+                           setTextMarkers( MarkBegin, CursorPos );
+                       }
+                       MouseMarking = false;
+                       calculateScrollPos();
+                       return true;
+               }
+               break;
+       case irr::EMIE_MOUSE_MOVED:
+               {
+                       if (MouseMarking)
+                       {
+                               CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+                               setTextMarkers( MarkBegin, CursorPos );
+                               calculateScrollPos();
+                               return true;
+                       }
+               }
+               break;
+       case EMIE_LMOUSE_PRESSED_DOWN:
+               if (!Environment->hasFocus(this))
+               {
+                       BlinkStartTime = porting::getTimeMs();
+                       MouseMarking = true;
+                       CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+                       setTextMarkers(CursorPos, CursorPos );
+                       calculateScrollPos();
+                       return true;
+               }
+               else
+               {
+                       if (!AbsoluteClippingRect.isPointInside(
+                               core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y)))
+                       {
+                               return false;
+                       }
+                       else
+                       {
+                               // move cursor
+                               CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+
+                s32 newMarkBegin = MarkBegin;
+                               if (!MouseMarking)
+                                       newMarkBegin = CursorPos;
+
+                               MouseMarking = true;
+                               setTextMarkers( newMarkBegin, CursorPos);
+                               calculateScrollPos();
+                               return true;
+                       }
+               }
+       default:
+               break;
+       }
+
+       return false;
+}
+
+
+s32 intlGUIEditBox::getCursorPos(s32 x, s32 y)
+{
+       IGUIFont* font = OverrideFont;
+       IGUISkin* skin = Environment->getSkin();
+       if (!OverrideFont)
+               font = skin->getFont();
+
+       const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
+
+       core::stringw *txtLine=0;
+       s32 startPos=0;
+       x+=3;
+
+       for (u32 i=0; i < lineCount; ++i)
+       {
+               setTextRect(i);
+               if (i == 0 && y < CurrentTextRect.UpperLeftCorner.Y)
+                       y = CurrentTextRect.UpperLeftCorner.Y;
+               if (i == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y )
+                       y = CurrentTextRect.LowerRightCorner.Y;
+
+               // is it inside this region?
+               if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y)
+               {
+                       // we've found the clicked line
+                       txtLine = (WordWrap || MultiLine) ? &BrokenText[i] : &Text;
+                       startPos = (WordWrap || MultiLine) ? BrokenTextPositions[i] : 0;
+                       break;
+               }
+       }
+
+       if (x < CurrentTextRect.UpperLeftCorner.X)
+               x = CurrentTextRect.UpperLeftCorner.X;
+
+       s32 idx = font->getCharacterFromPos(Text.c_str(), x - CurrentTextRect.UpperLeftCorner.X);
+
+       // click was on or left of the line
+       if (idx != -1)
+               return idx + startPos;
+
+       // click was off the right edge of the line, go to end.
+       return txtLine->size() + startPos;
+}
+
+
+//! Breaks the single text line.
+void intlGUIEditBox::breakText()
+{
+       IGUISkin* skin = Environment->getSkin();
+
+       if ((!WordWrap && !MultiLine) || !skin)
+               return;
+
+       BrokenText.clear(); // need to reallocate :/
+       BrokenTextPositions.set_used(0);
+
+       IGUIFont* font = OverrideFont;
+       if (!OverrideFont)
+               font = skin->getFont();
+
+       if (!font)
+               return;
+
+       LastBreakFont = font;
+
+       core::stringw line;
+       core::stringw word;
+       core::stringw whitespace;
+       s32 lastLineStart = 0;
+       s32 size = Text.size();
+       s32 length = 0;
+       s32 elWidth = RelativeRect.getWidth() - 6;
+       wchar_t c;
+
+       for (s32 i=0; i<size; ++i)
+       {
+               c = Text[i];
+               bool lineBreak = false;
+
+               if (c == L'\r') // Mac or Windows breaks
+               {
+                       lineBreak = true;
+                       c = ' ';
+                       if (Text[i+1] == L'\n') // Windows breaks
+                       {
+                               Text.erase(i+1);
+                               --size;
+                       }
+               }
+               else if (c == L'\n') // Unix breaks
+               {
+                       lineBreak = true;
+                       c = ' ';
+               }
+
+               // don't break if we're not a multi-line edit box
+               if (!MultiLine)
+                       lineBreak = false;
+
+               if (c == L' ' || c == 0 || i == (size-1))
+               {
+                       if (word.size())
+                       {
+                               // here comes the next whitespace, look if
+                               // we can break the last word to the next line.
+                               s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
+                               s32 worldlgth = font->getDimension(word.c_str()).Width;
+
+                               if (WordWrap && length + worldlgth + whitelgth > elWidth)
+                               {
+                                       // break to next line
+                                       length = worldlgth;
+                                       BrokenText.push_back(line);
+                                       BrokenTextPositions.push_back(lastLineStart);
+                                       lastLineStart = i - (s32)word.size();
+                                       line = word;
+                               }
+                               else
+                               {
+                                       // add word to line
+                                       line += whitespace;
+                                       line += word;
+                                       length += whitelgth + worldlgth;
+                               }
+
+                               word = L"";
+                               whitespace = L"";
+                       }
+
+                       whitespace += c;
+
+                       // compute line break
+                       if (lineBreak)
+                       {
+                               line += whitespace;
+                               line += word;
+                               BrokenText.push_back(line);
+                               BrokenTextPositions.push_back(lastLineStart);
+                               lastLineStart = i+1;
+                               line = L"";
+                               word = L"";
+                               whitespace = L"";
+                               length = 0;
+                       }
+               }
+               else
+               {
+                       // yippee this is a word..
+                       word += c;
+               }
+       }
+
+       line += whitespace;
+       line += word;
+       BrokenText.push_back(line);
+       BrokenTextPositions.push_back(lastLineStart);
+}
+
+
+void intlGUIEditBox::setTextRect(s32 line)
+{
+       core::dimension2du d;
+
+       IGUISkin* skin = Environment->getSkin();
+       if (!skin)
+               return;
+
+       IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
+
+       if (!font)
+               return;
+
+       // get text dimension
+       const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
+       if (WordWrap || MultiLine)
+       {
+               d = font->getDimension(BrokenText[line].c_str());
+       }
+       else
+       {
+               d = font->getDimension(Text.c_str());
+               d.Height = AbsoluteRect.getHeight();
+       }
+       d.Height += font->getKerningHeight();
+
+       // justification
+       switch (HAlign)
+       {
+       case EGUIA_CENTER:
+               // align to h centre
+               CurrentTextRect.UpperLeftCorner.X = (FrameRect.getWidth()/2) - (d.Width/2);
+               CurrentTextRect.LowerRightCorner.X = (FrameRect.getWidth()/2) + (d.Width/2);
+               break;
+       case EGUIA_LOWERRIGHT:
+               // align to right edge
+               CurrentTextRect.UpperLeftCorner.X = FrameRect.getWidth() - d.Width;
+               CurrentTextRect.LowerRightCorner.X = FrameRect.getWidth();
+               break;
+       default:
+               // align to left edge
+               CurrentTextRect.UpperLeftCorner.X = 0;
+               CurrentTextRect.LowerRightCorner.X = d.Width;
+
+       }
+
+       switch (VAlign)
+       {
+       case EGUIA_CENTER:
+               // align to v centre
+               CurrentTextRect.UpperLeftCorner.Y =
+                       (FrameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line;
+               break;
+       case EGUIA_LOWERRIGHT:
+               // align to bottom edge
+               CurrentTextRect.UpperLeftCorner.Y =
+                       FrameRect.getHeight() - lineCount*d.Height + d.Height*line;
+               break;
+       default:
+               // align to top edge
+               CurrentTextRect.UpperLeftCorner.Y = d.Height*line;
+               break;
+       }
+
+       CurrentTextRect.UpperLeftCorner.X  -= HScrollPos;
+       CurrentTextRect.LowerRightCorner.X -= HScrollPos;
+       CurrentTextRect.UpperLeftCorner.Y  -= VScrollPos;
+       CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height;
+
+       CurrentTextRect += FrameRect.UpperLeftCorner;
+
+}
+
+
+s32 intlGUIEditBox::getLineFromPos(s32 pos)
+{
+       if (!WordWrap && !MultiLine)
+               return 0;
+
+       s32 i=0;
+       while (i < (s32)BrokenTextPositions.size())
+       {
+               if (BrokenTextPositions[i] > pos)
+                       return i-1;
+               ++i;
+       }
+       return (s32)BrokenTextPositions.size() - 1;
+}
+
+
+void intlGUIEditBox::inputChar(wchar_t c)
+{
+       if (!IsEnabled)
+               return;
+
+       if (c != 0)
+       {
+               if (Text.size() < Max || Max == 0)
+               {
+                       core::stringw s;
+
+                       if (MarkBegin != MarkEnd)
+                       {
+                               // replace marked text
+                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+                               s = Text.subString(0, realmbgn);
+                               s.append(c);
+                               s.append( Text.subString(realmend, Text.size()-realmend) );
+                               Text = s;
+                               CursorPos = realmbgn+1;
+                       }
+                       else
+                       {
+                               // add new character
+                               s = Text.subString(0, CursorPos);
+                               s.append(c);
+                               s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+                               Text = s;
+                               ++CursorPos;
+                       }
+
+                       BlinkStartTime = porting::getTimeMs();
+                       setTextMarkers(0, 0);
+               }
+       }
+       breakText();
+       sendGuiEvent(EGET_EDITBOX_CHANGED);
+       calculateScrollPos();
+}
+
+
+void intlGUIEditBox::calculateScrollPos()
+{
+       if (!AutoScroll)
+               return;
+
+       // calculate horizontal scroll position
+       s32 cursLine = getLineFromPos(CursorPos);
+       setTextRect(cursLine);
+
+       // don't do horizontal scrolling when wordwrap is enabled.
+       if (!WordWrap)
+       {
+               // get cursor position
+               IGUISkin* skin = Environment->getSkin();
+               if (!skin)
+                       return;
+               IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
+               if (!font)
+                       return;
+
+               core::stringw *txtLine = MultiLine ? &BrokenText[cursLine] : &Text;
+               s32 cPos = MultiLine ? CursorPos - BrokenTextPositions[cursLine] : CursorPos;
+
+               s32 cStart = CurrentTextRect.UpperLeftCorner.X + HScrollPos +
+                       font->getDimension(txtLine->subString(0, cPos).c_str()).Width;
+
+               s32 cEnd = cStart + font->getDimension(L"_ ").Width;
+
+               if (FrameRect.LowerRightCorner.X < cEnd)
+                       HScrollPos = cEnd - FrameRect.LowerRightCorner.X;
+               else if (FrameRect.UpperLeftCorner.X > cStart)
+                       HScrollPos = cStart - FrameRect.UpperLeftCorner.X;
+               else
+                       HScrollPos = 0;
+
+               // todo: adjust scrollbar
+       }
+
+       // vertical scroll position
+       if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos)
+               VScrollPos = CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y + VScrollPos;
+
+       else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos)
+               VScrollPos = CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y + VScrollPos;
+       else
+               VScrollPos = 0;
+
+       // todo: adjust scrollbar
+}
+
+//! set text markers
+void intlGUIEditBox::setTextMarkers(s32 begin, s32 end)
+{
+    if ( begin != MarkBegin || end != MarkEnd )
+    {
+        MarkBegin = begin;
+        MarkEnd = end;
+        sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
+    }
+}
+
+//! send some gui event to parent
+void intlGUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type)
+{
+       if ( Parent )
+       {
+        SEvent e;
+        e.EventType = EET_GUI_EVENT;
+        e.GUIEvent.Caller = this;
+        e.GUIEvent.Element = 0;
+        e.GUIEvent.EventType = type;
+
+        Parent->OnEvent(e);
+       }
+}
+
+//! Writes attributes of the element.
+void intlGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
+{
+       // IGUIEditBox::serializeAttributes(out,options);
+
+       out->addBool  ("OverrideColorEnabled",OverrideColorEnabled );
+       out->addColor ("OverrideColor",       OverrideColor);
+       // out->addFont("OverrideFont",OverrideFont);
+       out->addInt   ("MaxChars",            Max);
+       out->addBool  ("WordWrap",            WordWrap);
+       out->addBool  ("MultiLine",           MultiLine);
+       out->addBool  ("AutoScroll",          AutoScroll);
+       out->addBool  ("PasswordBox",         PasswordBox);
+       core::stringw ch = L" ";
+       ch[0] = PasswordChar;
+       out->addString("PasswordChar",        ch.c_str());
+       out->addEnum  ("HTextAlign",          HAlign, GUIAlignmentNames);
+       out->addEnum  ("VTextAlign",          VAlign, GUIAlignmentNames);
+
+       IGUIEditBox::serializeAttributes(out,options);
+}
+
+
+//! Reads attributes of the element
+void intlGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
+{
+       IGUIEditBox::deserializeAttributes(in,options);
+
+       setOverrideColor(in->getAttributeAsColor("OverrideColor"));
+       enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
+       setMax(in->getAttributeAsInt("MaxChars"));
+       setWordWrap(in->getAttributeAsBool("WordWrap"));
+       setMultiLine(in->getAttributeAsBool("MultiLine"));
+       setAutoScroll(in->getAttributeAsBool("AutoScroll"));
+       core::stringw ch = in->getAttributeAsStringW("PasswordChar");
+
+       if (!ch.size())
+               setPasswordBox(in->getAttributeAsBool("PasswordBox"));
+       else
+               setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
+
+       setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
+                       (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
+
+       // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
+}
+
+
+} // end namespace gui
+} // end namespace irr
+
+#endif // _IRR_COMPILE_WITH_GUI_
diff --git a/src/intlGUIEditBox.h b/src/intlGUIEditBox.h
new file mode 100644 (file)
index 0000000..e3ee15a
--- /dev/null
@@ -0,0 +1,178 @@
+// Copyright (C) 2002-2013 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#ifndef __C_INTL_GUI_EDIT_BOX_H_INCLUDED__
+#define __C_INTL_GUI_EDIT_BOX_H_INCLUDED__
+
+#include "IrrCompileConfig.h"
+//#ifdef _IRR_COMPILE_WITH_GUI_
+
+#include "IGUIEditBox.h"
+#include "irrArray.h"
+#include "IOSOperator.h"
+
+namespace irr
+{
+namespace gui
+{
+       class intlGUIEditBox : public IGUIEditBox
+       {
+       public:
+
+               //! constructor
+               intlGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment,
+                       IGUIElement* parent, s32 id, const core::rect<s32>& rectangle);
+
+               //! destructor
+               virtual ~intlGUIEditBox();
+
+               //! Sets another skin independent font.
+               virtual void setOverrideFont(IGUIFont* font=0);
+
+               //! Gets the override font (if any)
+               /** \return The override font (may be 0) */
+               virtual IGUIFont* getOverrideFont() const;
+
+               //! Get the font which is used right now for drawing
+               /** Currently this is the override font when one is set and the
+               font of the active skin otherwise */
+               virtual IGUIFont* getActiveFont() const;
+
+               //! Sets another color for the text.
+               virtual void setOverrideColor(video::SColor color);
+
+               //! Gets the override color
+               virtual video::SColor getOverrideColor() const;
+
+               //! Sets if the text should use the overide color or the
+               //! color in the gui skin.
+               virtual void enableOverrideColor(bool enable);
+
+               //! Checks if an override color is enabled
+               /** \return true if the override color is enabled, false otherwise */
+               virtual bool isOverrideColorEnabled(void) const;
+
+               //! Sets whether to draw the background
+               virtual void setDrawBackground(bool draw);
+
+               //! Turns the border on or off
+               virtual void setDrawBorder(bool border);
+
+               //! Enables or disables word wrap for using the edit box as multiline text editor.
+               virtual void setWordWrap(bool enable);
+
+               //! Checks if word wrap is enabled
+               //! \return true if word wrap is enabled, false otherwise
+               virtual bool isWordWrapEnabled() const;
+
+               //! Enables or disables newlines.
+               /** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
+               instead a newline character will be inserted. */
+               virtual void setMultiLine(bool enable);
+
+               //! Checks if multi line editing is enabled
+               //! \return true if mult-line is enabled, false otherwise
+               virtual bool isMultiLineEnabled() const;
+
+               //! Enables or disables automatic scrolling with cursor position
+               //! \param enable: If set to true, the text will move around with the cursor position
+               virtual void setAutoScroll(bool enable);
+
+               //! Checks to see if automatic scrolling is enabled
+               //! \return true if automatic scrolling is enabled, false if not
+               virtual bool isAutoScrollEnabled() const;
+
+               //! Gets the size area of the text in the edit box
+               //! \return Returns the size in pixels of the text
+               virtual core::dimension2du getTextDimension();
+
+               //! Sets text justification
+               virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
+
+               //! called if an event happened.
+               virtual bool OnEvent(const SEvent& event);
+
+               //! draws the element and its children
+               virtual void draw();
+
+               //! Sets the new caption of this element.
+               virtual void setText(const wchar_t* text);
+
+               //! Sets the maximum amount of characters which may be entered in the box.
+               //! \param max: Maximum amount of characters. If 0, the character amount is
+               //! infinity.
+               virtual void setMax(u32 max);
+
+               //! Returns maximum amount of characters, previously set by setMax();
+               virtual u32 getMax() const;
+
+               //! Sets whether the edit box is a password box. Setting this to true will
+               /** disable MultiLine, WordWrap and the ability to copy with ctrl+c or ctrl+x
+               \param passwordBox: true to enable password, false to disable
+               \param passwordChar: the character that is displayed instead of letters */
+               virtual void setPasswordBox(bool passwordBox, wchar_t passwordChar = L'*');
+
+               //! Returns true if the edit box is currently a password box.
+               virtual bool isPasswordBox() const;
+
+               //! Updates the absolute position, splits text if required
+               virtual void updateAbsolutePosition();
+
+               //! Writes attributes of the element.
+               virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const;
+
+               //! Reads attributes of the element
+               virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options);
+
+       protected:
+               //! Breaks the single text line.
+               void breakText();
+               //! sets the area of the given line
+               void setTextRect(s32 line);
+               //! returns the line number that the cursor is on
+               s32 getLineFromPos(s32 pos);
+               //! adds a letter to the edit box
+               void inputChar(wchar_t c);
+               //! calculates the current scroll position
+               void calculateScrollPos();
+               //! send some gui event to parent
+               void sendGuiEvent(EGUI_EVENT_TYPE type);
+               //! set text markers
+               void setTextMarkers(s32 begin, s32 end);
+
+               bool processKey(const SEvent& event);
+               bool processMouse(const SEvent& event);
+               s32 getCursorPos(s32 x, s32 y);
+
+               bool MouseMarking;
+               bool Border;
+               bool OverrideColorEnabled;
+               s32 MarkBegin;
+               s32 MarkEnd;
+
+               video::SColor OverrideColor;
+               gui::IGUIFont *OverrideFont, *LastBreakFont;
+               IOSOperator* Operator;
+
+               u32 BlinkStartTime;
+               s32 CursorPos;
+               s32 HScrollPos, VScrollPos; // scroll position in characters
+               u32 Max;
+
+               bool WordWrap, MultiLine, AutoScroll, PasswordBox;
+               wchar_t PasswordChar;
+               EGUI_ALIGNMENT HAlign, VAlign;
+
+               core::array< core::stringw > BrokenText;
+               core::array< s32 > BrokenTextPositions;
+
+               core::rect<s32> CurrentTextRect, FrameRect; // temporary values
+       };
+
+
+} // end namespace gui
+} // end namespace irr
+
+//#endif // _IRR_COMPILE_WITH_GUI_
+#endif // __C_GUI_EDIT_BOX_H_INCLUDED__
index 231eaf6be02e553f420220f1043b8ea7af0ccbe3..49aff4a1f91776b3997d05222af4b9575149dc59 100644 (file)
@@ -29,9 +29,103 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <iomanip>
 #include <map>
 
+#ifndef _WIN32
+#include <iconv.h>
+#else
+#define _WIN32_WINNT 0x0501
+#include <windows.h>
+#endif
+
 static bool parseHexColorString(const std::string &value, video::SColor &color);
 static bool parseNamedColorString(const std::string &value, video::SColor &color);
 
+#ifndef _WIN32
+size_t convert(const char *to, const char *from, char *outbuf,
+               size_t outbuf_size, char *inbuf, size_t inbuf_size)
+{
+       iconv_t cd = iconv_open(to, from);
+
+#if defined(__FreeBSD__) || defined(__FreeBSD)
+       const char *inbuf_ptr = inbuf;
+#else
+       char *inbuf_ptr = inbuf;
+#endif
+
+       char *outbuf_ptr = outbuf;
+
+       size_t *inbuf_left_ptr = &inbuf_size;
+       size_t *outbuf_left_ptr = &outbuf_size;
+
+       while (inbuf_size > 0)
+               iconv(cd, &inbuf_ptr, inbuf_left_ptr, &outbuf_ptr, outbuf_left_ptr);
+
+       iconv_close(cd);
+       return 0;
+}
+
+std::wstring utf8_to_wide(const std::string &input)
+{
+       size_t inbuf_size = input.length() + 1;
+       // maximum possible size, every character is sizeof(wchar_t) bytes
+       size_t outbuf_size = (input.length() + 1) * sizeof(wchar_t);
+
+       char *inbuf = new char[inbuf_size];
+       memcpy(inbuf, input.c_str(), inbuf_size);
+       char *outbuf = new char[outbuf_size];
+       memset(outbuf, 0, outbuf_size);
+
+       convert("WCHAR_T", "UTF-8", outbuf, outbuf_size, inbuf, inbuf_size);
+       std::wstring out((wchar_t*)outbuf);
+
+       delete[] inbuf;
+       delete[] outbuf;
+
+       return out;
+}
+
+std::string wide_to_utf8(const std::wstring &input)
+{
+       size_t inbuf_size = (input.length() + 1) * sizeof(wchar_t);
+       // maximum possible size: utf-8 encodes codepoints using 1 up to 6 bytes
+       size_t outbuf_size = (input.length() + 1) * 6;
+
+       char *inbuf = new char[inbuf_size];
+       memcpy(inbuf, input.c_str(), inbuf_size);
+       char *outbuf = new char[outbuf_size];
+       memset(outbuf, 0, outbuf_size);
+
+       convert("UTF-8", "WCHAR_T", outbuf, outbuf_size, inbuf, inbuf_size);
+       std::string out(outbuf);
+
+       delete[] inbuf;
+       delete[] outbuf;
+
+       return out;
+}
+#else
+std::wstring utf8_to_wide(const std::string &input)
+{
+       size_t outbuf_size = input.size() + 1;
+       wchar_t *outbuf = new wchar_t[outbuf_size];
+       memset(outbuf, 0, outbuf_size * sizeof(wchar_t));
+       MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.size(), outbuf, outbuf_size);
+       std::wstring out(outbuf);
+       delete[] outbuf;
+       return out;
+}
+
+std::string wide_to_utf8(const std::wstring &input)
+{
+       size_t outbuf_size = (input.size() + 1) * 6;
+       char *outbuf = new char[outbuf_size];
+       memset(outbuf, 0, outbuf_size);
+       WideCharToMultiByte(CP_UTF8, 0, input.c_str(), input.size(), outbuf, outbuf_size, NULL, NULL);
+       std::string out(outbuf);
+       delete[] outbuf;
+       return out;
+}
+#endif
+
 
 // You must free the returned string!
 // The returned string is allocated using new
index b80e3c9a82b01ca179f7c6fa405dd2fdda2918fc..5bf2b5b7c4c20214d1f064258a6bb352f818ed57 100644 (file)
@@ -39,10 +39,18 @@ struct FlagDesc {
        u32 flag;
 };
 
+// try not to convert between wide/utf8 encodings; this can result in data loss
+// try to only convert between them when you need to input/output stuff via Irrlicht
+std::wstring utf8_to_wide(const std::string &input);
+std::string wide_to_utf8(const std::wstring &input);
+
+// NEVER use those two functions unless you have a VERY GOOD reason to
+// they just convert between wide and multibyte encoding
+// multibyte encoding depends on current locale, this is no good, especially on Windows
+
 // You must free the returned string!
 // The returned string is allocated using new
 wchar_t *narrow_to_wide_c(const char *str);
-
 std::wstring narrow_to_wide(const std::string &mbs);
 std::string wide_to_narrow(const std::wstring &wcs);