]> git.lizzy.rs Git - dragonfireclient.git/commitdiff
Add colored text (not only colored chat).
authorEkdohibs <nathanael.courant@laposte.net>
Tue, 31 May 2016 15:30:11 +0000 (17:30 +0200)
committerEkdohibs <nathanael.courant@laposte.net>
Tue, 31 May 2016 15:34:29 +0000 (17:34 +0200)
Add documentation, move files to a proper place and avoid memory leaks.
Make it work with most kind of texts, and allow backgrounds too.

32 files changed:
builtin/game/chatcommands.lua
builtin/game/misc.lua
builtin/settingtypes.txt
doc/lua_api.txt
src/CMakeLists.txt
src/cguittfont/CGUITTFont.cpp
src/cguittfont/CGUITTFont.h
src/chat.cpp
src/chat.h
src/client/CMakeLists.txt
src/client/guiChatConsole.cpp [deleted file]
src/client/guiChatConsole.h [deleted file]
src/defaultsettings.cpp
src/game.cpp
src/guiChatConsole.cpp [new file with mode: 0644]
src/guiChatConsole.h [new file with mode: 0644]
src/guiEngine.cpp
src/guiEngine.h
src/guiFormSpecMenu.cpp
src/guiFormSpecMenu.h
src/irrlicht_changes/CMakeLists.txt [new file with mode: 0644]
src/irrlicht_changes/static_text.cpp [new file with mode: 0644]
src/irrlicht_changes/static_text.h [new file with mode: 0644]
src/terminal_chat_console.cpp
src/util/CMakeLists.txt
src/util/coloredstring.cpp [deleted file]
src/util/coloredstring.h [deleted file]
src/util/enriched_string.cpp [new file with mode: 0644]
src/util/enriched_string.h [new file with mode: 0644]
src/util/statictext.cpp [deleted file]
src/util/statictext.h [deleted file]
src/util/string.h

index 2627559a5f0a8289c7472ca86a531bf875c2505b..22755386b2dea85ef964830f328464ea8b221ce1 100644 (file)
@@ -102,7 +102,7 @@ core.register_chatcommand("help", {
        description = "Get help for commands or list privileges",
        func = function(name, param)
                local function format_help_line(cmd, def)
-                       local msg = core.colorize("00ffff", "/"..cmd)
+                       local msg = core.colorize("#00ffff", "/"..cmd)
                        if def.params and def.params ~= "" then
                                msg = msg .. " " .. def.params
                        end
index 8d5c80216ce173975b6c87c525b515d4ee1abc14..918315656e5f6cb6e3368c4560daea5a814ad3a5 100644 (file)
@@ -198,19 +198,34 @@ function core.http_add_fetch(httpenv)
        return httpenv
 end
 
-function core.get_color_escape_sequence(color)
-       --if string.len(color) == 3 then
-       --      local r = string.sub(color, 1, 1)
-       --      local g = string.sub(color, 2, 2)
-       --      local b = string.sub(color, 3, 3)
-       --      color = r ..  r .. g .. g .. b .. b
-       --end
-
-       --assert(#color == 6, "Color must be six characters in length.")
-       --return "\v" .. color
-       return "\v(color;" .. color .. ")"
-end
+if minetest.setting_getbool("disable_escape_sequences") then
+
+       function core.get_color_escape_sequence(color)
+               return ""
+       end
+
+       function core.get_background_escape_sequence(color)
+               return ""
+       end
+
+       function core.colorize(color, message)
+               return message
+       end
+
+else
+
+       local ESCAPE_CHAR = string.char(0x1b)
+       function core.get_color_escape_sequence(color)
+               return ESCAPE_CHAR .. "(c@" .. color .. ")"
+       end
+
+       function core.get_background_escape_sequence(color)
+               return ESCAPE_CHAR .. "(b@" .. color .. ")"
+       end
+
+       function core.colorize(color, message)
+               return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("#ffffff")
+       end
 
-function core.colorize(color, message)
-       return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("ffffff")
 end
+
index 93fb8e95273abfabc93c3bc9e0715d026ae861c0..538a04f33f7c659046a01b1af76ab62f845fb255 100644 (file)
@@ -615,6 +615,11 @@ server_announce (Announce server) bool false
 #    If you want to announce your ipv6 address, use  serverlist_url = v6.servers.minetest.net.
 serverlist_url (Serverlist URL) string servers.minetest.net
 
+#    Disable escape sequences, e.g. chat coloring.
+#    Use this if you want to run a server with pre-0.4.14 clients and you want to disable
+#    the escape sequences generated by mods.
+disable_escape_sequences (Disable escape sequences) bool false
+
 [*Network]
 
 #    Network port to listen (UDP).
index 82a0acbeec06da8e65f6660600eb76e1608e43c9..aa0d7e45d609eaff65f43ff82620efb89725fde7 100644 (file)
@@ -1701,6 +1701,24 @@ numerical form, the raw integer value of an ARGB8 quad:
 or string form, a ColorString (defined above):
     `colorspec = "green"`
 
+Escape sequences
+----------------
+Most text can contain escape sequences, that can for example color the text.
+There are a few exceptions: tab headers, dropdowns and vertical labels can't.
+The following functions provide escape sequences:
+* `core.get_color_escape_sequence(color)`:
+    * `color` is a ColorString
+    * The escape sequence sets the text color to `color`
+* `core.colorize(color, message)`:
+    * Equivalent to:
+      `core.get_color_escape_sequence(color) ..
+       message ..
+       core.get_color_escape_sequence("#ffffff")`
+* `color.get_background_escape_sequence(color)`
+    * `color` is a ColorString
+    * The escape sequence sets the background of the whole text element to
+      `color`. Only defined for item descriptions and tooltips.
+
 Spatial Vectors
 ---------------
 * `vector.new(a[, b, c])`: returns a vector:
index ea1564eebc2c39d4fcaba7f02a74b6fdd732a5ed..f02812415351df2372e4206c62f29fb12a853b5f 100644 (file)
@@ -376,6 +376,7 @@ add_subdirectory(network)
 add_subdirectory(script)
 add_subdirectory(unittest)
 add_subdirectory(util)
+add_subdirectory(irrlicht_changes)
 
 set(common_SRCS
        ban.cpp
@@ -493,6 +494,7 @@ set(client_SRCS
        ${common_SRCS}
        ${sound_SRCS}
        ${client_network_SRCS}
+       ${client_irrlicht_changes_SRCS}
        camera.cpp
        client.cpp
        clientmap.cpp
index 2342eb7485f21b93d40514fc7b5ad14a13f5a29f..c2d37c6c0dda0b3f61666d65810f52326e1bdfab 100644 (file)
@@ -1,6 +1,7 @@
 /*
    CGUITTFont FreeType class for Irrlicht
    Copyright (c) 2009-2010 John Norman
+   Copyright (c) 2016 NathanaĆ«l Courant
 
    This software is provided 'as-is', without any express or implied
    warranty. In no event will the authors be held liable for any
@@ -545,6 +546,13 @@ void CGUITTFont::setFontHinting(const bool enable, const bool enable_auto_hintin
 
 void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
 {
+       draw(EnrichedString(std::wstring(text.c_str()), color), position, color, hcenter, vcenter, clip);
+}
+
+void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
+{
+       std::vector<video::SColor> colors = text.getColors();
+
        if (!Driver)
                return;
 
@@ -572,7 +580,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
        }
 
        // Convert to a unicode string.
-       core::ustring utext(text);
+       core::ustring utext = text.getString();
 
        // Set up our render map.
        core::map<u32, CGUITTGlyphPage*> Render_Map;
@@ -581,6 +589,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
        u32 n;
        uchar32_t previousChar = 0;
        core::ustring::const_iterator iter(utext);
+       std::vector<video::SColor> applied_colors;
        while (!iter.atEnd())
        {
                uchar32_t currentChar = *iter;
@@ -590,7 +599,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
                if (currentChar == L'\r') // Mac or Windows breaks
                {
                        lineBreak = true;
-                       if (*(iter + 1) == (uchar32_t)'\n')     // Windows line breaks.
+                       if (*(iter + 1) == (uchar32_t)'\n')     // Windows line breaks.
                                currentChar = *(++iter);
                }
                else if (currentChar == (uchar32_t)'\n') // Unix breaks
@@ -627,6 +636,9 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
                        page->render_positions.push_back(core::position2di(offset.X + offx, offset.Y + offy));
                        page->render_source_rects.push_back(glyph.source_rect);
                        Render_Map.set(glyph.glyph_page, page);
+                       u32 current_color = iter.getPos();
+                       if (current_color < colors.size())
+                               applied_colors.push_back(colors[current_color]);
                }
                offset.X += getWidthFromCharacter(currentChar);
 
@@ -645,8 +657,6 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
 
                CGUITTGlyphPage* page = n->getValue();
 
-               if (!use_transparency) color.color |= 0xff000000;
-
                if (shadow_offset) {
                        for (size_t i = 0; i < page->render_positions.size(); ++i)
                                page->render_positions[i] += core::vector2di(shadow_offset, shadow_offset);
@@ -654,7 +664,17 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
                        for (size_t i = 0; i < page->render_positions.size(); ++i)
                                page->render_positions[i] -= core::vector2di(shadow_offset, shadow_offset);
                }
-               Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, color, true);
+               for (size_t i = 0; i < page->render_positions.size(); ++i) {
+                       irr::video::SColor col;
+                       if (!applied_colors.empty()) {
+                               col = applied_colors[i < applied_colors.size() ? i : 0];
+                       } else {
+                               col = irr::video::SColor(255, 255, 255, 255);
+                       }
+                       if (!use_transparency)
+                               col.color |= 0xff000000;
+                       Driver->draw2DImage(page->texture, page->render_positions[i], page->render_source_rects[i], clip, col, true);
+               }
        }
 }
 
index e24d8f18b27499f17c95a323120781bc9252748f..0aa540c5cfce7bf8b1ec5f69965fdccae7ef5cde 100644 (file)
@@ -1,6 +1,7 @@
 /*
    CGUITTFont FreeType class for Irrlicht
    Copyright (c) 2009-2010 John Norman
+   Copyright (c) 2016 NathanaĆ«l Courant
 
    This software is provided 'as-is', without any express or implied
    warranty. In no event will the authors be held liable for any
@@ -33,6 +34,8 @@
 
 #include <irrlicht.h>
 #include <ft2build.h>
+#include <vector>
+#include "util/enriched_string.h"
 #include FT_FREETYPE_H
 
 namespace irr
@@ -258,6 +261,10 @@ namespace gui
                        virtual void draw(const core::stringw& text, const core::rect<s32>& position,
                                video::SColor color, bool hcenter=false, bool vcenter=false,
                                const core::rect<s32>* clip=0);
+                       
+                       virtual void draw(const EnrichedString& text, const core::rect<s32>& position,
+                               video::SColor color, bool hcenter=false, bool vcenter=false,
+                               const core::rect<s32>* clip=0);
 
                        //! Returns the dimension of a character produced by this font.
                        virtual core::dimension2d<u32> getCharDimension(const wchar_t ch) const;
index 958389df517eebb20625f8fc611bfe5a107be6ad..46555b3dcc8f9da920efa2c44cade7e7ca971d6a 100644 (file)
@@ -267,28 +267,26 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
                next_frags.push_back(temp_frag);
        }
 
-       std::wstring name_sanitized = removeEscapes(line.name);
+       std::wstring name_sanitized = line.name.c_str();
 
        // Choose an indentation level
        if (line.name.empty()) {
                // Server messages
                hanging_indentation = 0;
-       }
-       else if (name_sanitized.size() + 3 <= cols/2) {
+       } else if (name_sanitized.size() + 3 <= cols/2) {
                // Names shorter than about half the console width
                hanging_indentation = line.name.size() + 3;
-       }
-       else {
+       } else {
                // Very long names
                hanging_indentation = 2;
        }
-       ColoredString line_text(line.text);
+       //EnrichedString line_text(line.text);
 
        next_line.first = true;
        bool text_processing = false;
 
        // Produce fragments and layout them into lines
-       while (!next_frags.empty() || in_pos < line_text.size())
+       while (!next_frags.empty() || in_pos < line.text.size())
        {
                // Layout fragments into lines
                while (!next_frags.empty())
@@ -326,9 +324,9 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
                }
 
                // Produce fragment
-               if (in_pos < line_text.size())
+               if (in_pos < line.text.size())
                {
-                       u32 remaining_in_input = line_text.size() - in_pos;
+                       u32 remaining_in_input = line.text.size() - in_pos;
                        u32 remaining_in_output = cols - out_column;
 
                        // Determine a fragment length <= the minimum of
@@ -338,14 +336,14 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
                        while (frag_length < remaining_in_input &&
                                        frag_length < remaining_in_output)
                        {
-                               if (isspace(line_text[in_pos + frag_length]))
+                               if (isspace(line.text.getString()[in_pos + frag_length]))
                                        space_pos = frag_length;
                                ++frag_length;
                        }
                        if (space_pos != 0 && frag_length < remaining_in_input)
                                frag_length = space_pos + 1;
 
-                       temp_frag.text = line_text.substr(in_pos, frag_length);
+                       temp_frag.text = line.text.substr(in_pos, frag_length);
                        temp_frag.column = 0;
                        //temp_frag.bold = 0;
                        next_frags.push_back(temp_frag);
@@ -729,19 +727,22 @@ ChatBuffer& ChatBackend::getRecentBuffer()
        return m_recent_buffer;
 }
 
-std::wstring ChatBackend::getRecentChat()
+EnrichedString ChatBackend::getRecentChat()
 {
-       std::wostringstream stream;
+       EnrichedString result;
        for (u32 i = 0; i < m_recent_buffer.getLineCount(); ++i)
        {
                const ChatLine& line = m_recent_buffer.getLine(i);
                if (i != 0)
-                       stream << L"\n";
-               if (!line.name.empty())
-                       stream << L"<" << line.name << L"> ";
-               stream << line.text;
+                       result += L"\n";
+               if (!line.name.empty()) {
+                       result += L"<";
+                       result += line.name;
+                       result += L"> ";
+               }
+               result += line.text;
        }
-       return stream.str();
+       return result;
 }
 
 ChatPrompt& ChatBackend::getPrompt()
index 661cafc82e09c777db1d53003d1fea2a416c4f4a..11061fd39e1c0e5619ec9d4b59c2961a7fe1260a 100644 (file)
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <list>
 
 #include "irrlichttypes.h"
-#include "util/coloredstring.h"
+#include "util/enriched_string.h"
 
 // Chat console related classes
 
@@ -34,9 +34,9 @@ struct ChatLine
        // age in seconds
        f32 age;
        // name of sending player, or empty if sent by server
-       std::wstring name;
+       EnrichedString name;
        // message text
-       ColoredString text;
+       EnrichedString text;
 
        ChatLine(std::wstring a_name, std::wstring a_text):
                age(0.0),
@@ -44,12 +44,19 @@ struct ChatLine
                text(a_text)
        {
        }
+
+       ChatLine(EnrichedString a_name, EnrichedString a_text):
+               age(0.0),
+               name(a_name),
+               text(a_text)
+       {
+       }
 };
 
 struct ChatFormattedFragment
 {
        // text string
-       std::wstring text;
+       EnrichedString text;
        // starting column
        u32 column;
        // formatting
@@ -262,7 +269,7 @@ class ChatBackend
        // Get the recent messages buffer
        ChatBuffer& getRecentBuffer();
        // Concatenate all recent messages
-       std::wstring getRecentChat();
+       EnrichedString getRecentChat();
        // Get the console prompt
        ChatPrompt& getPrompt();
 
index bcf114760e3be842579e2cef63017bcb8a37e005..a1ec37fe39cee399c1b8bc6da41591376fbe6375 100644 (file)
@@ -1,6 +1,5 @@
 set(client_SRCS
        ${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp
-       ${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp
        PARENT_SCOPE
 )
diff --git a/src/client/guiChatConsole.cpp b/src/client/guiChatConsole.cpp
deleted file mode 100644 (file)
index d883755..0000000
+++ /dev/null
@@ -1,664 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "guiChatConsole.h"
-#include "chat.h"
-#include "client.h"
-#include "debug.h"
-#include "gettime.h"
-#include "keycode.h"
-#include "settings.h"
-#include "porting.h"
-#include "client/tile.h"
-#include "fontengine.h"
-#include "log.h"
-#include "gettext.h"
-#include <string>
-
-#if USE_FREETYPE
-       #include "xCGUITTFont.h"
-#endif
-
-inline u32 clamp_u8(s32 value)
-{
-       return (u32) MYMIN(MYMAX(value, 0), 255);
-}
-
-
-GUIChatConsole::GUIChatConsole(
-               gui::IGUIEnvironment* env,
-               gui::IGUIElement* parent,
-               s32 id,
-               ChatBackend* backend,
-               Client* client,
-               IMenuManager* menumgr
-):
-       IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
-                       core::rect<s32>(0,0,100,100)),
-       m_chat_backend(backend),
-       m_client(client),
-       m_menumgr(menumgr),
-       m_screensize(v2u32(0,0)),
-       m_animate_time_old(0),
-       m_open(false),
-       m_close_on_enter(false),
-       m_height(0),
-       m_desired_height(0),
-       m_desired_height_fraction(0.0),
-       m_height_speed(5.0),
-       m_open_inhibited(0),
-       m_cursor_blink(0.0),
-       m_cursor_blink_speed(0.0),
-       m_cursor_height(0.0),
-       m_background(NULL),
-       m_background_color(255, 0, 0, 0),
-       m_font(NULL),
-       m_fontsize(0, 0)
-{
-       m_animate_time_old = getTimeMs();
-
-       // load background settings
-       s32 console_alpha = g_settings->getS32("console_alpha");
-       m_background_color.setAlpha(clamp_u8(console_alpha));
-
-       // load the background texture depending on settings
-       ITextureSource *tsrc = client->getTextureSource();
-       if (tsrc->isKnownSourceImage("background_chat.jpg")) {
-               m_background = tsrc->getTexture("background_chat.jpg");
-               m_background_color.setRed(255);
-               m_background_color.setGreen(255);
-               m_background_color.setBlue(255);
-       } else {
-               v3f console_color = g_settings->getV3F("console_color");
-               m_background_color.setRed(clamp_u8(myround(console_color.X)));
-               m_background_color.setGreen(clamp_u8(myround(console_color.Y)));
-               m_background_color.setBlue(clamp_u8(myround(console_color.Z)));
-       }
-
-       m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono);
-
-       if (m_font == NULL)
-       {
-               errorstream << "GUIChatConsole: Unable to load mono font ";
-       }
-       else
-       {
-               core::dimension2d<u32> dim = m_font->getDimension(L"M");
-               m_fontsize = v2u32(dim.Width, dim.Height);
-               m_font->grab();
-       }
-       m_fontsize.X = MYMAX(m_fontsize.X, 1);
-       m_fontsize.Y = MYMAX(m_fontsize.Y, 1);
-
-       // set default cursor options
-       setCursor(true, true, 2.0, 0.1);
-}
-
-GUIChatConsole::~GUIChatConsole()
-{
-       if (m_font)
-               m_font->drop();
-}
-
-void GUIChatConsole::openConsole(f32 height)
-{
-       m_open = true;
-       m_desired_height_fraction = height;
-       m_desired_height = height * m_screensize.Y;
-       reformatConsole();
-       m_animate_time_old = getTimeMs();
-       IGUIElement::setVisible(true);
-       Environment->setFocus(this);
-       m_menumgr->createdMenu(this);
-}
-
-bool GUIChatConsole::isOpen() const
-{
-       return m_open;
-}
-
-bool GUIChatConsole::isOpenInhibited() const
-{
-       return m_open_inhibited > 0;
-}
-
-void GUIChatConsole::closeConsole()
-{
-       m_open = false;
-       Environment->removeFocus(this);
-       m_menumgr->deletingMenu(this);
-}
-
-void GUIChatConsole::closeConsoleAtOnce()
-{
-       closeConsole();
-       m_height = 0;
-       recalculateConsolePosition();
-}
-
-f32 GUIChatConsole::getDesiredHeight() const
-{
-       return m_desired_height_fraction;
-}
-
-void GUIChatConsole::replaceAndAddToHistory(std::wstring line)
-{
-       ChatPrompt& prompt = m_chat_backend->getPrompt();
-       prompt.addToHistory(prompt.getLine());
-       prompt.replace(line);
-}
-
-
-void GUIChatConsole::setCursor(
-       bool visible, bool blinking, f32 blink_speed, f32 relative_height)
-{
-       if (visible)
-       {
-               if (blinking)
-               {
-                       // leave m_cursor_blink unchanged
-                       m_cursor_blink_speed = blink_speed;
-               }
-               else
-               {
-                       m_cursor_blink = 0x8000;  // on
-                       m_cursor_blink_speed = 0.0;
-               }
-       }
-       else
-       {
-               m_cursor_blink = 0;  // off
-               m_cursor_blink_speed = 0.0;
-       }
-       m_cursor_height = relative_height;
-}
-
-void GUIChatConsole::draw()
-{
-       if(!IsVisible)
-               return;
-
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-
-       // Check screen size
-       v2u32 screensize = driver->getScreenSize();
-       if (screensize != m_screensize)
-       {
-               // screen size has changed
-               // scale current console height to new window size
-               if (m_screensize.Y != 0)
-                       m_height = m_height * screensize.Y / m_screensize.Y;
-               m_desired_height = m_desired_height_fraction * m_screensize.Y;
-               m_screensize = screensize;
-               reformatConsole();
-       }
-
-       // Animation
-       u32 now = getTimeMs();
-       animate(now - m_animate_time_old);
-       m_animate_time_old = now;
-
-       // Draw console elements if visible
-       if (m_height > 0)
-       {
-               drawBackground();
-               drawText();
-               drawPrompt();
-       }
-
-       gui::IGUIElement::draw();
-}
-
-void GUIChatConsole::reformatConsole()
-{
-       s32 cols = m_screensize.X / m_fontsize.X - 2; // make room for a margin (looks better)
-       s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt
-       if (cols <= 0 || rows <= 0)
-               cols = rows = 0;
-       m_chat_backend->reformat(cols, rows);
-}
-
-void GUIChatConsole::recalculateConsolePosition()
-{
-       core::rect<s32> rect(0, 0, m_screensize.X, m_height);
-       DesiredRect = rect;
-       recalculateAbsolutePosition(false);
-}
-
-void GUIChatConsole::animate(u32 msec)
-{
-       // animate the console height
-       s32 goal = m_open ? m_desired_height : 0;
-
-       // Set invisible if close animation finished (reset by openConsole)
-       // This function (animate()) is never called once its visibility becomes false so do not
-       //              actually set visible to false before the inhibited period is over
-       if (!m_open && m_height == 0 && m_open_inhibited == 0)
-               IGUIElement::setVisible(false);
-
-       if (m_height != goal)
-       {
-               s32 max_change = msec * m_screensize.Y * (m_height_speed / 1000.0);
-               if (max_change == 0)
-                       max_change = 1;
-
-               if (m_height < goal)
-               {
-                       // increase height
-                       if (m_height + max_change < goal)
-                               m_height += max_change;
-                       else
-                               m_height = goal;
-               }
-               else
-               {
-                       // decrease height
-                       if (m_height > goal + max_change)
-                               m_height -= max_change;
-                       else
-                               m_height = goal;
-               }
-
-               recalculateConsolePosition();
-       }
-
-       // blink the cursor
-       if (m_cursor_blink_speed != 0.0)
-       {
-               u32 blink_increase = 0x10000 * msec * (m_cursor_blink_speed / 1000.0);
-               if (blink_increase == 0)
-                       blink_increase = 1;
-               m_cursor_blink = ((m_cursor_blink + blink_increase) & 0xffff);
-       }
-
-       // decrease open inhibit counter
-       if (m_open_inhibited > msec)
-               m_open_inhibited -= msec;
-       else
-               m_open_inhibited = 0;
-}
-
-void GUIChatConsole::drawBackground()
-{
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-       if (m_background != NULL)
-       {
-               core::rect<s32> sourcerect(0, -m_height, m_screensize.X, 0);
-               driver->draw2DImage(
-                       m_background,
-                       v2s32(0, 0),
-                       sourcerect,
-                       &AbsoluteClippingRect,
-                       m_background_color,
-                       false);
-       }
-       else
-       {
-               driver->draw2DRectangle(
-                       m_background_color,
-                       core::rect<s32>(0, 0, m_screensize.X, m_height),
-                       &AbsoluteClippingRect);
-       }
-}
-
-void GUIChatConsole::drawText()
-{
-       if (m_font == NULL)
-               return;
-
-       ChatBuffer& buf = m_chat_backend->getConsoleBuffer();
-       for (u32 row = 0; row < buf.getRows(); ++row)
-       {
-               const ChatFormattedLine& line = buf.getFormattedLine(row);
-               if (line.fragments.empty())
-                       continue;
-
-               s32 line_height = m_fontsize.Y;
-               s32 y = row * line_height + m_height - m_desired_height;
-               if (y + line_height < 0)
-                       continue;
-
-               for (u32 i = 0; i < line.fragments.size(); ++i)
-               {
-                       const ChatFormattedFragment& fragment = line.fragments[i];
-                       s32 x = (fragment.column + 1) * m_fontsize.X;
-                       core::rect<s32> destrect(
-                               x, y, x + m_fontsize.X * fragment.text.size(), y + m_fontsize.Y);
-
-
-                       #if USE_FREETYPE
-                       // Draw colored text if FreeType is enabled
-                               irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(m_font);
-                               tmp->draw(
-                                       fragment.text.c_str(),
-                                       destrect,
-                                       fragment.text.getColors(),
-                                       false,
-                                       false,
-                                       &AbsoluteClippingRect);
-                       #else
-                       // Otherwise use standard text
-                               m_font->draw(
-                                       fragment.text.c_str(),
-                                       destrect,
-                                       video::SColor(255, 255, 255, 255),
-                                       false,
-                                       false,
-                                       &AbsoluteClippingRect);
-                       #endif
-               }
-       }
-}
-
-void GUIChatConsole::drawPrompt()
-{
-       if (m_font == NULL)
-               return;
-
-       u32 row = m_chat_backend->getConsoleBuffer().getRows();
-       s32 line_height = m_fontsize.Y;
-       s32 y = row * line_height + m_height - m_desired_height;
-
-       ChatPrompt& prompt = m_chat_backend->getPrompt();
-       std::wstring prompt_text = prompt.getVisiblePortion();
-
-       // FIXME Draw string at once, not character by character
-       // That will only work with the cursor once we have a monospace font
-       for (u32 i = 0; i < prompt_text.size(); ++i)
-       {
-               wchar_t ws[2] = {prompt_text[i], 0};
-               s32 x = (1 + i) * m_fontsize.X;
-               core::rect<s32> destrect(
-                       x, y, x + m_fontsize.X, y + m_fontsize.Y);
-               m_font->draw(
-                       ws,
-                       destrect,
-                       video::SColor(255, 255, 255, 255),
-                       false,
-                       false,
-                       &AbsoluteClippingRect);
-       }
-
-       // Draw the cursor during on periods
-       if ((m_cursor_blink & 0x8000) != 0)
-       {
-               s32 cursor_pos = prompt.getVisibleCursorPosition();
-               if (cursor_pos >= 0)
-               {
-                       s32 cursor_len = prompt.getCursorLength();
-                       video::IVideoDriver* driver = Environment->getVideoDriver();
-                       s32 x = (1 + cursor_pos) * m_fontsize.X;
-                       core::rect<s32> destrect(
-                               x,
-                               y + m_fontsize.Y * (1.0 - m_cursor_height),
-                               x + m_fontsize.X * MYMAX(cursor_len, 1),
-                               y + m_fontsize.Y * (cursor_len ? m_cursor_height+1 : 1)
-                       );
-                       video::SColor cursor_color(255,255,255,255);
-                       driver->draw2DRectangle(
-                               cursor_color,
-                               destrect,
-                               &AbsoluteClippingRect);
-               }
-       }
-
-}
-
-bool GUIChatConsole::OnEvent(const SEvent& event)
-{
-
-       ChatPrompt &prompt = m_chat_backend->getPrompt();
-
-       if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
-       {
-               // Key input
-               if(KeyPress(event.KeyInput) == getKeySetting("keymap_console"))
-               {
-                       closeConsole();
-
-                       // inhibit open so the_game doesn't reopen immediately
-                       m_open_inhibited = 50;
-                       m_close_on_enter = false;
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_ESCAPE)
-               {
-                       closeConsoleAtOnce();
-                       m_close_on_enter = false;
-                       // inhibit open so the_game doesn't reopen immediately
-                       m_open_inhibited = 1; // so the ESCAPE button doesn't open the "pause menu"
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_PRIOR)
-               {
-                       m_chat_backend->scrollPageUp();
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_NEXT)
-               {
-                       m_chat_backend->scrollPageDown();
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_RETURN)
-               {
-                       prompt.addToHistory(prompt.getLine());
-                       std::wstring text = prompt.replace(L"");
-                       m_client->typeChatMessage(text);
-                       if (m_close_on_enter) {
-                               closeConsoleAtOnce();
-                               m_close_on_enter = false;
-                       }
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_UP)
-               {
-                       // Up pressed
-                       // Move back in history
-                       prompt.historyPrev();
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_DOWN)
-               {
-                       // Down pressed
-                       // Move forward in history
-                       prompt.historyNext();
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT)
-               {
-                       // Left/right pressed
-                       // Move/select character/word to the left depending on control and shift keys
-                       ChatPrompt::CursorOp op = event.KeyInput.Shift ?
-                               ChatPrompt::CURSOROP_SELECT :
-                               ChatPrompt::CURSOROP_MOVE;
-                       ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ?
-                               ChatPrompt::CURSOROP_DIR_LEFT :
-                               ChatPrompt::CURSOROP_DIR_RIGHT;
-                       ChatPrompt::CursorOpScope scope = event.KeyInput.Control ?
-                               ChatPrompt::CURSOROP_SCOPE_WORD :
-                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
-                       prompt.cursorOperation(op, dir, scope);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_HOME)
-               {
-                       // Home pressed
-                       // move to beginning of line
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_MOVE,
-                               ChatPrompt::CURSOROP_DIR_LEFT,
-                               ChatPrompt::CURSOROP_SCOPE_LINE);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_END)
-               {
-                       // End pressed
-                       // move to end of line
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_MOVE,
-                               ChatPrompt::CURSOROP_DIR_RIGHT,
-                               ChatPrompt::CURSOROP_SCOPE_LINE);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_BACK)
-               {
-                       // Backspace or Ctrl-Backspace pressed
-                       // delete character / word to the left
-                       ChatPrompt::CursorOpScope scope =
-                               event.KeyInput.Control ?
-                               ChatPrompt::CURSOROP_SCOPE_WORD :
-                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_DELETE,
-                               ChatPrompt::CURSOROP_DIR_LEFT,
-                               scope);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_DELETE)
-               {
-                       // Delete or Ctrl-Delete pressed
-                       // delete character / word to the right
-                       ChatPrompt::CursorOpScope scope =
-                               event.KeyInput.Control ?
-                               ChatPrompt::CURSOROP_SCOPE_WORD :
-                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_DELETE,
-                               ChatPrompt::CURSOROP_DIR_RIGHT,
-                               scope);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control)
-               {
-                       // Ctrl-A pressed
-                       // Select all text
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_SELECT,
-                               ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
-                               ChatPrompt::CURSOROP_SCOPE_LINE);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control)
-               {
-                       // Ctrl-C pressed
-                       // Copy text to clipboard
-                       if (prompt.getCursorLength() <= 0)
-                               return true;
-                       std::wstring wselected = prompt.getSelection();
-                       std::string selected(wselected.begin(), wselected.end());
-                       Environment->getOSOperator()->copyToClipboard(selected.c_str());
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control)
-               {
-                       // Ctrl-V pressed
-                       // paste text from clipboard
-                       if (prompt.getCursorLength() > 0) {
-                               // Delete selected section of text
-                               prompt.cursorOperation(
-                                       ChatPrompt::CURSOROP_DELETE,
-                                       ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
-                                       ChatPrompt::CURSOROP_SCOPE_SELECTION);
-                       }
-                       IOSOperator *os_operator = Environment->getOSOperator();
-                       const c8 *text = os_operator->getTextFromClipboard();
-                       if (!text)
-                               return true;
-                       std::basic_string<unsigned char> str((const unsigned char*)text);
-                       prompt.input(std::wstring(str.begin(), str.end()));
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control)
-               {
-                       // Ctrl-X pressed
-                       // Cut text to clipboard
-                       if (prompt.getCursorLength() <= 0)
-                               return true;
-                       std::wstring wselected = prompt.getSelection();
-                       std::string selected(wselected.begin(), wselected.end());
-                       Environment->getOSOperator()->copyToClipboard(selected.c_str());
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_DELETE,
-                               ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
-                               ChatPrompt::CURSOROP_SCOPE_SELECTION);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_KEY_U && event.KeyInput.Control)
-               {
-                       // Ctrl-U pressed
-                       // kill line to left end
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_DELETE,
-                               ChatPrompt::CURSOROP_DIR_LEFT,
-                               ChatPrompt::CURSOROP_SCOPE_LINE);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_KEY_K && event.KeyInput.Control)
-               {
-                       // Ctrl-K pressed
-                       // kill line to right end
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_DELETE,
-                               ChatPrompt::CURSOROP_DIR_RIGHT,
-                               ChatPrompt::CURSOROP_SCOPE_LINE);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_TAB)
-               {
-                       // Tab or Shift-Tab pressed
-                       // Nick completion
-                       std::list<std::string> names = m_client->getConnectedPlayerNames();
-                       bool backwards = event.KeyInput.Shift;
-                       prompt.nickCompletion(names, backwards);
-                       return true;
-               }
-               else if(event.KeyInput.Char != 0 && !event.KeyInput.Control)
-               {
-                       #if (defined(linux) || defined(__linux))
-                               wchar_t wc = L'_';
-                               mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
-                               prompt.input(wc);
-                       #else
-                               prompt.input(event.KeyInput.Char);
-                       #endif
-                       return true;
-               }
-       }
-       else if(event.EventType == EET_MOUSE_INPUT_EVENT)
-       {
-               if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
-               {
-                       s32 rows = myround(-3.0 * event.MouseInput.Wheel);
-                       m_chat_backend->scroll(rows);
-               }
-       }
-
-       return Parent ? Parent->OnEvent(event) : false;
-}
-
-void GUIChatConsole::setVisible(bool visible)
-{
-       m_open = visible;
-       IGUIElement::setVisible(visible);
-       if (!visible) {
-               m_height = 0;
-               recalculateConsolePosition();
-       }
-}
-
diff --git a/src/client/guiChatConsole.h b/src/client/guiChatConsole.h
deleted file mode 100644 (file)
index 3013a1d..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef GUICHATCONSOLE_HEADER
-#define GUICHATCONSOLE_HEADER
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include "chat.h"
-#include "config.h"
-
-class Client;
-
-class GUIChatConsole : public gui::IGUIElement
-{
-public:
-       GUIChatConsole(gui::IGUIEnvironment* env,
-                       gui::IGUIElement* parent,
-                       s32 id,
-                       ChatBackend* backend,
-                       Client* client,
-                       IMenuManager* menumgr);
-       virtual ~GUIChatConsole();
-
-       // Open the console (height = desired fraction of screen size)
-       // This doesn't open immediately but initiates an animation.
-       // You should call isOpenInhibited() before this.
-       void openConsole(f32 height);
-
-       bool isOpen() const;
-
-       // Check if the console should not be opened at the moment
-       // This is to avoid reopening the console immediately after closing
-       bool isOpenInhibited() const;
-       // Close the console, equivalent to openConsole(0).
-       // This doesn't close immediately but initiates an animation.
-       void closeConsole();
-       // Close the console immediately, without animation.
-       void closeConsoleAtOnce();
-       // Set whether to close the console after the user presses enter.
-       void setCloseOnEnter(bool close) { m_close_on_enter = close; }
-
-       // Return the desired height (fraction of screen size)
-       // Zero if the console is closed or getting closed
-       f32 getDesiredHeight() const;
-
-       // Replace actual line when adding the actual to the history (if there is any)
-       void replaceAndAddToHistory(std::wstring line);
-
-       // Change how the cursor looks
-       void setCursor(
-               bool visible,
-               bool blinking = false,
-               f32 blink_speed = 1.0,
-               f32 relative_height = 1.0);
-
-       // Irrlicht draw method
-       virtual void draw();
-
-       bool canTakeFocus(gui::IGUIElement* element) { return false; }
-
-       virtual bool OnEvent(const SEvent& event);
-
-       virtual void setVisible(bool visible);
-
-private:
-       void reformatConsole();
-       void recalculateConsolePosition();
-
-       // These methods are called by draw
-       void animate(u32 msec);
-       void drawBackground();
-       void drawText();
-       void drawPrompt();
-
-private:
-       ChatBackend* m_chat_backend;
-       Client* m_client;
-       IMenuManager* m_menumgr;
-
-       // current screen size
-       v2u32 m_screensize;
-
-       // used to compute how much time passed since last animate()
-       u32 m_animate_time_old;
-
-       // should the console be opened or closed?
-       bool m_open;
-       // should it close after you press enter?
-       bool m_close_on_enter;
-       // current console height [pixels]
-       s32 m_height;
-       // desired height [pixels]
-       f32 m_desired_height;
-       // desired height [screen height fraction]
-       f32 m_desired_height_fraction;
-       // console open/close animation speed [screen height fraction / second]
-       f32 m_height_speed;
-       // if nonzero, opening the console is inhibited [milliseconds]
-       u32 m_open_inhibited;
-
-       // cursor blink frame (16-bit value)
-       // cursor is off during [0,32767] and on during [32768,65535]
-       u32 m_cursor_blink;
-       // cursor blink speed [on/off toggles / second]
-       f32 m_cursor_blink_speed;
-       // cursor height [line height]
-       f32 m_cursor_height;
-
-       // background texture
-       video::ITexture* m_background;
-       // background color (including alpha)
-       video::SColor m_background_color;
-
-       // font
-       gui::IGUIFont* m_font;
-       v2u32 m_fontsize;
-};
-
-
-#endif
-
index e8b652c17ad52f7b945fabf73cd15c41e29fa7fc..632ec0df93fbf51cbdf59ae73459cefbdb966d2b 100644 (file)
@@ -202,6 +202,8 @@ void set_default_settings(Settings *settings)
        settings->setDefault("server_name", "");
        settings->setDefault("server_description", "");
 
+       settings->setDefault("disable_escape_sequences", "false");
+
 #if USE_FREETYPE
        settings->setDefault("freetype", "true");
        settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "liberationsans.ttf"));
index 71a04aef57c1fa5bf2c3b66108ae3e9ed1eff5ea..def202fe558711cb6c19fa273dca64ca4038ad20 100644 (file)
@@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "filesys.h"
 #include "gettext.h"
-#include "client/guiChatConsole.h"
+#include "guiChatConsole.h"
 #include "guiFormSpecMenu.h"
 #include "guiKeyChangeMenu.h"
 #include "guiPasswordChange.h"
@@ -55,14 +55,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "tool.h"
 #include "util/directiontables.h"
 #include "util/pointedthing.h"
+#include "irrlicht_changes/static_text.h"
 #include "version.h"
 #include "minimap.h"
 #include "mapblock_mesh.h"
 
-#if USE_FREETYPE
-       #include "util/statictext.h"
-#endif
-
 #include "sound.h"
 
 #if USE_SOUND
@@ -541,7 +538,7 @@ void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, FontEngine *fe,
                std::ostringstream os(std::ios_base::binary);
                g_profiler->printPage(os, show_profiler, show_profiler_max);
                std::wstring text = utf8_to_wide(os.str());
-               guitext_profiler->setText(text.c_str());
+               setStaticText(guitext_profiler, text.c_str());
                guitext_profiler->setVisible(true);
 
                s32 w = fe->getTextWidth(text.c_str());
@@ -1244,7 +1241,11 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
 
        // Get new messages from error log buffer
        while (!chat_log_error_buf.empty()) {
-               chat_backend.addMessage(L"", utf8_to_wide(chat_log_error_buf.get()));
+               std::wstring error_message = utf8_to_wide(chat_log_error_buf.get());
+               if (!g_settings->getBool("disable_escape_sequences")) {
+                       error_message = L"\x1b(c@red)" + error_message + L"\x1b(c@white)";
+               }
+               chat_backend.addMessage(L"", error_message);
        }
 
        // Get new messages from client
@@ -1259,10 +1260,10 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
 
        // Display all messages in a static text element
        unsigned int recent_chat_count = chat_backend.getRecentBuffer().getLineCount();
-       std::wstring recent_chat       = chat_backend.getRecentChat();
+       EnrichedString recent_chat     = chat_backend.getRecentChat();
        unsigned int line_height       = g_fontengine->getLineHeight();
 
-       guitext_chat->setText(recent_chat.c_str());
+       setStaticText(guitext_chat, recent_chat);
 
        // Update gui element size and position
        s32 chat_y = 5 + line_height;
@@ -1271,7 +1272,7 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
                chat_y += line_height;
 
        // first pass to calculate height of text to be set
-       s32 width = std::min(g_fontengine->getTextWidth(recent_chat) + 10,
+       s32 width = std::min(g_fontengine->getTextWidth(recent_chat.c_str()) + 10,
                             porting::getWindowSize().X - 20);
        core::rect<s32> rect(10, chat_y, width, chat_y + porting::getWindowSize().Y);
        guitext_chat->setRelativePosition(rect);
@@ -2218,45 +2219,39 @@ bool Game::createClient(const std::string &playername,
 bool Game::initGui()
 {
        // First line of debug text
-       guitext = guienv->addStaticText(
+       guitext = addStaticText(guienv,
                        utf8_to_wide(PROJECT_NAME_C).c_str(),
                        core::rect<s32>(0, 0, 0, 0),
                        false, false, guiroot);
 
        // Second line of debug text
-       guitext2 = guienv->addStaticText(
+       guitext2 = addStaticText(guienv,
                        L"",
                        core::rect<s32>(0, 0, 0, 0),
                        false, false, guiroot);
 
        // At the middle of the screen
        // Object infos are shown in this
-       guitext_info = guienv->addStaticText(
+       guitext_info = addStaticText(guienv,
                        L"",
                        core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + v2s32(100, 200),
                        false, true, guiroot);
 
        // Status text (displays info when showing and hiding GUI stuff, etc.)
-       guitext_status = guienv->addStaticText(
+       guitext_status = addStaticText(guienv,
                        L"<Status>",
                        core::rect<s32>(0, 0, 0, 0),
                        false, false, guiroot);
        guitext_status->setVisible(false);
 
-#if USE_FREETYPE
-       // Colored chat support when using FreeType
-       guitext_chat = new gui::StaticText(L"", false, guienv, guiroot, -1, core::rect<s32>(0, 0, 0, 0), false);
-       guitext_chat->setWordWrap(true);
-       guitext_chat->drop();
-#else
-       // Standard chat when FreeType is disabled
        // Chat text
-       guitext_chat = guienv->addStaticText(
+       guitext_chat = addStaticText(
+                       guienv,
                        L"",
                        core::rect<s32>(0, 0, 0, 0),
                        //false, false); // Disable word wrap as of now
                        false, true, guiroot);
-#endif
+
        // Remove stale "recent" chat messages from previous connections
        chat_backend->clearRecentChat();
 
@@ -2270,7 +2265,7 @@ bool Game::initGui()
        }
 
        // Profiler text (size is updated when text is updated)
-       guitext_profiler = guienv->addStaticText(
+       guitext_profiler = addStaticText(guienv,
                        L"<Profiler>",
                        core::rect<s32>(0, 0, 0, 0),
                        false, false, guiroot);
@@ -4308,12 +4303,12 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
                   << ", v_range = " << draw_control->wanted_range
                   << std::setprecision(3)
                   << ", RTT = " << client->getRTT();
-               guitext->setText(utf8_to_wide(os.str()).c_str());
+               setStaticText(guitext, utf8_to_wide(os.str()).c_str());
                guitext->setVisible(true);
        } else if (flags.show_hud || flags.show_chat) {
                std::ostringstream os(std::ios_base::binary);
                os << PROJECT_NAME_C " " << g_version_hash;
-               guitext->setText(utf8_to_wide(os.str()).c_str());
+               setStaticText(guitext, utf8_to_wide(os.str()).c_str());
                guitext->setVisible(true);
        } else {
                guitext->setVisible(false);
@@ -4350,7 +4345,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
                        }
                }
 
-               guitext2->setText(utf8_to_wide(os.str()).c_str());
+               setStaticText(guitext2, utf8_to_wide(os.str()).c_str());
                guitext2->setVisible(true);
 
                core::rect<s32> rect(
@@ -4362,7 +4357,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
                guitext2->setVisible(false);
        }
 
-       guitext_info->setText(infotext.c_str());
+       setStaticText(guitext_info, infotext.c_str());
        guitext_info->setVisible(flags.show_hud && g_menumgr.menuCount() == 0);
 
        float statustext_time_max = 1.5;
@@ -4376,7 +4371,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
                }
        }
 
-       guitext_status->setText(statustext.c_str());
+       setStaticText(guitext_status, statustext.c_str());
        guitext_status->setVisible(!statustext.empty());
 
        if (!statustext.empty()) {
diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp
new file mode 100644 (file)
index 0000000..bb58d13
--- /dev/null
@@ -0,0 +1,664 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "guiChatConsole.h"
+#include "chat.h"
+#include "client.h"
+#include "debug.h"
+#include "gettime.h"
+#include "keycode.h"
+#include "settings.h"
+#include "porting.h"
+#include "client/tile.h"
+#include "fontengine.h"
+#include "log.h"
+#include "gettext.h"
+#include <string>
+
+#if USE_FREETYPE
+       #include "xCGUITTFont.h"
+#endif
+
+inline u32 clamp_u8(s32 value)
+{
+       return (u32) MYMIN(MYMAX(value, 0), 255);
+}
+
+
+GUIChatConsole::GUIChatConsole(
+               gui::IGUIEnvironment* env,
+               gui::IGUIElement* parent,
+               s32 id,
+               ChatBackend* backend,
+               Client* client,
+               IMenuManager* menumgr
+):
+       IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
+                       core::rect<s32>(0,0,100,100)),
+       m_chat_backend(backend),
+       m_client(client),
+       m_menumgr(menumgr),
+       m_screensize(v2u32(0,0)),
+       m_animate_time_old(0),
+       m_open(false),
+       m_close_on_enter(false),
+       m_height(0),
+       m_desired_height(0),
+       m_desired_height_fraction(0.0),
+       m_height_speed(5.0),
+       m_open_inhibited(0),
+       m_cursor_blink(0.0),
+       m_cursor_blink_speed(0.0),
+       m_cursor_height(0.0),
+       m_background(NULL),
+       m_background_color(255, 0, 0, 0),
+       m_font(NULL),
+       m_fontsize(0, 0)
+{
+       m_animate_time_old = getTimeMs();
+
+       // load background settings
+       s32 console_alpha = g_settings->getS32("console_alpha");
+       m_background_color.setAlpha(clamp_u8(console_alpha));
+
+       // load the background texture depending on settings
+       ITextureSource *tsrc = client->getTextureSource();
+       if (tsrc->isKnownSourceImage("background_chat.jpg")) {
+               m_background = tsrc->getTexture("background_chat.jpg");
+               m_background_color.setRed(255);
+               m_background_color.setGreen(255);
+               m_background_color.setBlue(255);
+       } else {
+               v3f console_color = g_settings->getV3F("console_color");
+               m_background_color.setRed(clamp_u8(myround(console_color.X)));
+               m_background_color.setGreen(clamp_u8(myround(console_color.Y)));
+               m_background_color.setBlue(clamp_u8(myround(console_color.Z)));
+       }
+
+       m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono);
+
+       if (m_font == NULL)
+       {
+               errorstream << "GUIChatConsole: Unable to load mono font ";
+       }
+       else
+       {
+               core::dimension2d<u32> dim = m_font->getDimension(L"M");
+               m_fontsize = v2u32(dim.Width, dim.Height);
+               m_font->grab();
+       }
+       m_fontsize.X = MYMAX(m_fontsize.X, 1);
+       m_fontsize.Y = MYMAX(m_fontsize.Y, 1);
+
+       // set default cursor options
+       setCursor(true, true, 2.0, 0.1);
+}
+
+GUIChatConsole::~GUIChatConsole()
+{
+       if (m_font)
+               m_font->drop();
+}
+
+void GUIChatConsole::openConsole(f32 height)
+{
+       m_open = true;
+       m_desired_height_fraction = height;
+       m_desired_height = height * m_screensize.Y;
+       reformatConsole();
+       m_animate_time_old = getTimeMs();
+       IGUIElement::setVisible(true);
+       Environment->setFocus(this);
+       m_menumgr->createdMenu(this);
+}
+
+bool GUIChatConsole::isOpen() const
+{
+       return m_open;
+}
+
+bool GUIChatConsole::isOpenInhibited() const
+{
+       return m_open_inhibited > 0;
+}
+
+void GUIChatConsole::closeConsole()
+{
+       m_open = false;
+       Environment->removeFocus(this);
+       m_menumgr->deletingMenu(this);
+}
+
+void GUIChatConsole::closeConsoleAtOnce()
+{
+       closeConsole();
+       m_height = 0;
+       recalculateConsolePosition();
+}
+
+f32 GUIChatConsole::getDesiredHeight() const
+{
+       return m_desired_height_fraction;
+}
+
+void GUIChatConsole::replaceAndAddToHistory(std::wstring line)
+{
+       ChatPrompt& prompt = m_chat_backend->getPrompt();
+       prompt.addToHistory(prompt.getLine());
+       prompt.replace(line);
+}
+
+
+void GUIChatConsole::setCursor(
+       bool visible, bool blinking, f32 blink_speed, f32 relative_height)
+{
+       if (visible)
+       {
+               if (blinking)
+               {
+                       // leave m_cursor_blink unchanged
+                       m_cursor_blink_speed = blink_speed;
+               }
+               else
+               {
+                       m_cursor_blink = 0x8000;  // on
+                       m_cursor_blink_speed = 0.0;
+               }
+       }
+       else
+       {
+               m_cursor_blink = 0;  // off
+               m_cursor_blink_speed = 0.0;
+       }
+       m_cursor_height = relative_height;
+}
+
+void GUIChatConsole::draw()
+{
+       if(!IsVisible)
+               return;
+
+       video::IVideoDriver* driver = Environment->getVideoDriver();
+
+       // Check screen size
+       v2u32 screensize = driver->getScreenSize();
+       if (screensize != m_screensize)
+       {
+               // screen size has changed
+               // scale current console height to new window size
+               if (m_screensize.Y != 0)
+                       m_height = m_height * screensize.Y / m_screensize.Y;
+               m_desired_height = m_desired_height_fraction * m_screensize.Y;
+               m_screensize = screensize;
+               reformatConsole();
+       }
+
+       // Animation
+       u32 now = getTimeMs();
+       animate(now - m_animate_time_old);
+       m_animate_time_old = now;
+
+       // Draw console elements if visible
+       if (m_height > 0)
+       {
+               drawBackground();
+               drawText();
+               drawPrompt();
+       }
+
+       gui::IGUIElement::draw();
+}
+
+void GUIChatConsole::reformatConsole()
+{
+       s32 cols = m_screensize.X / m_fontsize.X - 2; // make room for a margin (looks better)
+       s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt
+       if (cols <= 0 || rows <= 0)
+               cols = rows = 0;
+       m_chat_backend->reformat(cols, rows);
+}
+
+void GUIChatConsole::recalculateConsolePosition()
+{
+       core::rect<s32> rect(0, 0, m_screensize.X, m_height);
+       DesiredRect = rect;
+       recalculateAbsolutePosition(false);
+}
+
+void GUIChatConsole::animate(u32 msec)
+{
+       // animate the console height
+       s32 goal = m_open ? m_desired_height : 0;
+
+       // Set invisible if close animation finished (reset by openConsole)
+       // This function (animate()) is never called once its visibility becomes false so do not
+       //              actually set visible to false before the inhibited period is over
+       if (!m_open && m_height == 0 && m_open_inhibited == 0)
+               IGUIElement::setVisible(false);
+
+       if (m_height != goal)
+       {
+               s32 max_change = msec * m_screensize.Y * (m_height_speed / 1000.0);
+               if (max_change == 0)
+                       max_change = 1;
+
+               if (m_height < goal)
+               {
+                       // increase height
+                       if (m_height + max_change < goal)
+                               m_height += max_change;
+                       else
+                               m_height = goal;
+               }
+               else
+               {
+                       // decrease height
+                       if (m_height > goal + max_change)
+                               m_height -= max_change;
+                       else
+                               m_height = goal;
+               }
+
+               recalculateConsolePosition();
+       }
+
+       // blink the cursor
+       if (m_cursor_blink_speed != 0.0)
+       {
+               u32 blink_increase = 0x10000 * msec * (m_cursor_blink_speed / 1000.0);
+               if (blink_increase == 0)
+                       blink_increase = 1;
+               m_cursor_blink = ((m_cursor_blink + blink_increase) & 0xffff);
+       }
+
+       // decrease open inhibit counter
+       if (m_open_inhibited > msec)
+               m_open_inhibited -= msec;
+       else
+               m_open_inhibited = 0;
+}
+
+void GUIChatConsole::drawBackground()
+{
+       video::IVideoDriver* driver = Environment->getVideoDriver();
+       if (m_background != NULL)
+       {
+               core::rect<s32> sourcerect(0, -m_height, m_screensize.X, 0);
+               driver->draw2DImage(
+                       m_background,
+                       v2s32(0, 0),
+                       sourcerect,
+                       &AbsoluteClippingRect,
+                       m_background_color,
+                       false);
+       }
+       else
+       {
+               driver->draw2DRectangle(
+                       m_background_color,
+                       core::rect<s32>(0, 0, m_screensize.X, m_height),
+                       &AbsoluteClippingRect);
+       }
+}
+
+void GUIChatConsole::drawText()
+{
+       if (m_font == NULL)
+               return;
+
+       ChatBuffer& buf = m_chat_backend->getConsoleBuffer();
+       for (u32 row = 0; row < buf.getRows(); ++row)
+       {
+               const ChatFormattedLine& line = buf.getFormattedLine(row);
+               if (line.fragments.empty())
+                       continue;
+
+               s32 line_height = m_fontsize.Y;
+               s32 y = row * line_height + m_height - m_desired_height;
+               if (y + line_height < 0)
+                       continue;
+
+               for (u32 i = 0; i < line.fragments.size(); ++i)
+               {
+                       const ChatFormattedFragment& fragment = line.fragments[i];
+                       s32 x = (fragment.column + 1) * m_fontsize.X;
+                       core::rect<s32> destrect(
+                               x, y, x + m_fontsize.X * fragment.text.size(), y + m_fontsize.Y);
+
+
+                       #if USE_FREETYPE
+                       // Draw colored text if FreeType is enabled
+                               irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(m_font);
+                               tmp->draw(
+                                       fragment.text,
+                                       destrect,
+                                       video::SColor(255, 255, 255, 255),
+                                       false,
+                                       false,
+                                       &AbsoluteClippingRect);
+                       #else
+                       // Otherwise use standard text
+                               m_font->draw(
+                                       fragment.text.c_str(),
+                                       destrect,
+                                       video::SColor(255, 255, 255, 255),
+                                       false,
+                                       false,
+                                       &AbsoluteClippingRect);
+                       #endif
+               }
+       }
+}
+
+void GUIChatConsole::drawPrompt()
+{
+       if (m_font == NULL)
+               return;
+
+       u32 row = m_chat_backend->getConsoleBuffer().getRows();
+       s32 line_height = m_fontsize.Y;
+       s32 y = row * line_height + m_height - m_desired_height;
+
+       ChatPrompt& prompt = m_chat_backend->getPrompt();
+       std::wstring prompt_text = prompt.getVisiblePortion();
+
+       // FIXME Draw string at once, not character by character
+       // That will only work with the cursor once we have a monospace font
+       for (u32 i = 0; i < prompt_text.size(); ++i)
+       {
+               wchar_t ws[2] = {prompt_text[i], 0};
+               s32 x = (1 + i) * m_fontsize.X;
+               core::rect<s32> destrect(
+                       x, y, x + m_fontsize.X, y + m_fontsize.Y);
+               m_font->draw(
+                       ws,
+                       destrect,
+                       video::SColor(255, 255, 255, 255),
+                       false,
+                       false,
+                       &AbsoluteClippingRect);
+       }
+
+       // Draw the cursor during on periods
+       if ((m_cursor_blink & 0x8000) != 0)
+       {
+               s32 cursor_pos = prompt.getVisibleCursorPosition();
+               if (cursor_pos >= 0)
+               {
+                       s32 cursor_len = prompt.getCursorLength();
+                       video::IVideoDriver* driver = Environment->getVideoDriver();
+                       s32 x = (1 + cursor_pos) * m_fontsize.X;
+                       core::rect<s32> destrect(
+                               x,
+                               y + m_fontsize.Y * (1.0 - m_cursor_height),
+                               x + m_fontsize.X * MYMAX(cursor_len, 1),
+                               y + m_fontsize.Y * (cursor_len ? m_cursor_height+1 : 1)
+                       );
+                       video::SColor cursor_color(255,255,255,255);
+                       driver->draw2DRectangle(
+                               cursor_color,
+                               destrect,
+                               &AbsoluteClippingRect);
+               }
+       }
+
+}
+
+bool GUIChatConsole::OnEvent(const SEvent& event)
+{
+
+       ChatPrompt &prompt = m_chat_backend->getPrompt();
+
+       if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
+       {
+               // Key input
+               if(KeyPress(event.KeyInput) == getKeySetting("keymap_console"))
+               {
+                       closeConsole();
+
+                       // inhibit open so the_game doesn't reopen immediately
+                       m_open_inhibited = 50;
+                       m_close_on_enter = false;
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_ESCAPE)
+               {
+                       closeConsoleAtOnce();
+                       m_close_on_enter = false;
+                       // inhibit open so the_game doesn't reopen immediately
+                       m_open_inhibited = 1; // so the ESCAPE button doesn't open the "pause menu"
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_PRIOR)
+               {
+                       m_chat_backend->scrollPageUp();
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_NEXT)
+               {
+                       m_chat_backend->scrollPageDown();
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_RETURN)
+               {
+                       prompt.addToHistory(prompt.getLine());
+                       std::wstring text = prompt.replace(L"");
+                       m_client->typeChatMessage(text);
+                       if (m_close_on_enter) {
+                               closeConsoleAtOnce();
+                               m_close_on_enter = false;
+                       }
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_UP)
+               {
+                       // Up pressed
+                       // Move back in history
+                       prompt.historyPrev();
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_DOWN)
+               {
+                       // Down pressed
+                       // Move forward in history
+                       prompt.historyNext();
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT)
+               {
+                       // Left/right pressed
+                       // Move/select character/word to the left depending on control and shift keys
+                       ChatPrompt::CursorOp op = event.KeyInput.Shift ?
+                               ChatPrompt::CURSOROP_SELECT :
+                               ChatPrompt::CURSOROP_MOVE;
+                       ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ?
+                               ChatPrompt::CURSOROP_DIR_LEFT :
+                               ChatPrompt::CURSOROP_DIR_RIGHT;
+                       ChatPrompt::CursorOpScope scope = event.KeyInput.Control ?
+                               ChatPrompt::CURSOROP_SCOPE_WORD :
+                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
+                       prompt.cursorOperation(op, dir, scope);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_HOME)
+               {
+                       // Home pressed
+                       // move to beginning of line
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_MOVE,
+                               ChatPrompt::CURSOROP_DIR_LEFT,
+                               ChatPrompt::CURSOROP_SCOPE_LINE);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_END)
+               {
+                       // End pressed
+                       // move to end of line
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_MOVE,
+                               ChatPrompt::CURSOROP_DIR_RIGHT,
+                               ChatPrompt::CURSOROP_SCOPE_LINE);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_BACK)
+               {
+                       // Backspace or Ctrl-Backspace pressed
+                       // delete character / word to the left
+                       ChatPrompt::CursorOpScope scope =
+                               event.KeyInput.Control ?
+                               ChatPrompt::CURSOROP_SCOPE_WORD :
+                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_DELETE,
+                               ChatPrompt::CURSOROP_DIR_LEFT,
+                               scope);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_DELETE)
+               {
+                       // Delete or Ctrl-Delete pressed
+                       // delete character / word to the right
+                       ChatPrompt::CursorOpScope scope =
+                               event.KeyInput.Control ?
+                               ChatPrompt::CURSOROP_SCOPE_WORD :
+                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_DELETE,
+                               ChatPrompt::CURSOROP_DIR_RIGHT,
+                               scope);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control)
+               {
+                       // Ctrl-A pressed
+                       // Select all text
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_SELECT,
+                               ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
+                               ChatPrompt::CURSOROP_SCOPE_LINE);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control)
+               {
+                       // Ctrl-C pressed
+                       // Copy text to clipboard
+                       if (prompt.getCursorLength() <= 0)
+                               return true;
+                       std::wstring wselected = prompt.getSelection();
+                       std::string selected(wselected.begin(), wselected.end());
+                       Environment->getOSOperator()->copyToClipboard(selected.c_str());
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control)
+               {
+                       // Ctrl-V pressed
+                       // paste text from clipboard
+                       if (prompt.getCursorLength() > 0) {
+                               // Delete selected section of text
+                               prompt.cursorOperation(
+                                       ChatPrompt::CURSOROP_DELETE,
+                                       ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
+                                       ChatPrompt::CURSOROP_SCOPE_SELECTION);
+                       }
+                       IOSOperator *os_operator = Environment->getOSOperator();
+                       const c8 *text = os_operator->getTextFromClipboard();
+                       if (!text)
+                               return true;
+                       std::basic_string<unsigned char> str((const unsigned char*)text);
+                       prompt.input(std::wstring(str.begin(), str.end()));
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control)
+               {
+                       // Ctrl-X pressed
+                       // Cut text to clipboard
+                       if (prompt.getCursorLength() <= 0)
+                               return true;
+                       std::wstring wselected = prompt.getSelection();
+                       std::string selected(wselected.begin(), wselected.end());
+                       Environment->getOSOperator()->copyToClipboard(selected.c_str());
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_DELETE,
+                               ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
+                               ChatPrompt::CURSOROP_SCOPE_SELECTION);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_U && event.KeyInput.Control)
+               {
+                       // Ctrl-U pressed
+                       // kill line to left end
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_DELETE,
+                               ChatPrompt::CURSOROP_DIR_LEFT,
+                               ChatPrompt::CURSOROP_SCOPE_LINE);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_K && event.KeyInput.Control)
+               {
+                       // Ctrl-K pressed
+                       // kill line to right end
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_DELETE,
+                               ChatPrompt::CURSOROP_DIR_RIGHT,
+                               ChatPrompt::CURSOROP_SCOPE_LINE);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_TAB)
+               {
+                       // Tab or Shift-Tab pressed
+                       // Nick completion
+                       std::list<std::string> names = m_client->getConnectedPlayerNames();
+                       bool backwards = event.KeyInput.Shift;
+                       prompt.nickCompletion(names, backwards);
+                       return true;
+               }
+               else if(event.KeyInput.Char != 0 && !event.KeyInput.Control)
+               {
+                       #if (defined(linux) || defined(__linux))
+                               wchar_t wc = L'_';
+                               mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
+                               prompt.input(wc);
+                       #else
+                               prompt.input(event.KeyInput.Char);
+                       #endif
+                       return true;
+               }
+       }
+       else if(event.EventType == EET_MOUSE_INPUT_EVENT)
+       {
+               if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
+               {
+                       s32 rows = myround(-3.0 * event.MouseInput.Wheel);
+                       m_chat_backend->scroll(rows);
+               }
+       }
+
+       return Parent ? Parent->OnEvent(event) : false;
+}
+
+void GUIChatConsole::setVisible(bool visible)
+{
+       m_open = visible;
+       IGUIElement::setVisible(visible);
+       if (!visible) {
+               m_height = 0;
+               recalculateConsolePosition();
+       }
+}
+
diff --git a/src/guiChatConsole.h b/src/guiChatConsole.h
new file mode 100644 (file)
index 0000000..3013a1d
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef GUICHATCONSOLE_HEADER
+#define GUICHATCONSOLE_HEADER
+
+#include "irrlichttypes_extrabloated.h"
+#include "modalMenu.h"
+#include "chat.h"
+#include "config.h"
+
+class Client;
+
+class GUIChatConsole : public gui::IGUIElement
+{
+public:
+       GUIChatConsole(gui::IGUIEnvironment* env,
+                       gui::IGUIElement* parent,
+                       s32 id,
+                       ChatBackend* backend,
+                       Client* client,
+                       IMenuManager* menumgr);
+       virtual ~GUIChatConsole();
+
+       // Open the console (height = desired fraction of screen size)
+       // This doesn't open immediately but initiates an animation.
+       // You should call isOpenInhibited() before this.
+       void openConsole(f32 height);
+
+       bool isOpen() const;
+
+       // Check if the console should not be opened at the moment
+       // This is to avoid reopening the console immediately after closing
+       bool isOpenInhibited() const;
+       // Close the console, equivalent to openConsole(0).
+       // This doesn't close immediately but initiates an animation.
+       void closeConsole();
+       // Close the console immediately, without animation.
+       void closeConsoleAtOnce();
+       // Set whether to close the console after the user presses enter.
+       void setCloseOnEnter(bool close) { m_close_on_enter = close; }
+
+       // Return the desired height (fraction of screen size)
+       // Zero if the console is closed or getting closed
+       f32 getDesiredHeight() const;
+
+       // Replace actual line when adding the actual to the history (if there is any)
+       void replaceAndAddToHistory(std::wstring line);
+
+       // Change how the cursor looks
+       void setCursor(
+               bool visible,
+               bool blinking = false,
+               f32 blink_speed = 1.0,
+               f32 relative_height = 1.0);
+
+       // Irrlicht draw method
+       virtual void draw();
+
+       bool canTakeFocus(gui::IGUIElement* element) { return false; }
+
+       virtual bool OnEvent(const SEvent& event);
+
+       virtual void setVisible(bool visible);
+
+private:
+       void reformatConsole();
+       void recalculateConsolePosition();
+
+       // These methods are called by draw
+       void animate(u32 msec);
+       void drawBackground();
+       void drawText();
+       void drawPrompt();
+
+private:
+       ChatBackend* m_chat_backend;
+       Client* m_client;
+       IMenuManager* m_menumgr;
+
+       // current screen size
+       v2u32 m_screensize;
+
+       // used to compute how much time passed since last animate()
+       u32 m_animate_time_old;
+
+       // should the console be opened or closed?
+       bool m_open;
+       // should it close after you press enter?
+       bool m_close_on_enter;
+       // current console height [pixels]
+       s32 m_height;
+       // desired height [pixels]
+       f32 m_desired_height;
+       // desired height [screen height fraction]
+       f32 m_desired_height_fraction;
+       // console open/close animation speed [screen height fraction / second]
+       f32 m_height_speed;
+       // if nonzero, opening the console is inhibited [milliseconds]
+       u32 m_open_inhibited;
+
+       // cursor blink frame (16-bit value)
+       // cursor is off during [0,32767] and on during [32768,65535]
+       u32 m_cursor_blink;
+       // cursor blink speed [on/off toggles / second]
+       f32 m_cursor_blink_speed;
+       // cursor height [line height]
+       f32 m_cursor_height;
+
+       // background texture
+       video::ITexture* m_background;
+       // background color (including alpha)
+       video::SColor m_background_color;
+
+       // font
+       gui::IGUIFont* m_font;
+       v2u32 m_fontsize;
+};
+
+
+#endif
+
index ba286a91c56926fafc22e6a44cefa01b0b5f6367..96de7a4f7f6cce8b73d97624704cc42c3f5a79a1 100644 (file)
@@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "fontengine.h"
 #include "guiscalingfilter.h"
+#include "irrlicht_changes/static_text.h"
 
 #ifdef __ANDROID__
 #include "client/tile.h"
@@ -172,15 +173,16 @@ GUIEngine::GUIEngine(     irr::IrrlichtDevice* dev,
                m_sound_manager = &dummySoundManager;
 
        //create topleft header
-       std::wstring t = utf8_to_wide(std::string(PROJECT_NAME_C " ") +
+       m_toplefttext = utf8_to_wide(std::string(PROJECT_NAME_C " ") +
                        g_version_hash);
 
-       core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(t), g_fontengine->getTextHeight());
+       core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()),
+               g_fontengine->getTextHeight());
        rect += v2s32(4, 0);
 
        m_irr_toplefttext =
-               m_device->getGUIEnvironment()->addStaticText(t.c_str(),
-               rect,false,true,0,-1);
+               addStaticText(m_device->getGUIEnvironment(), m_toplefttext,
+                       rect, false, true, 0, -1);
 
        //create formspecsource
        m_formspecgui = new FormspecFormSource("");
@@ -578,7 +580,7 @@ void GUIEngine::setTopleftText(std::string append)
                toset += utf8_to_wide(append);
        }
 
-       m_irr_toplefttext->setText(toset.c_str());
+       m_toplefttext = toset;
 
        updateTopLeftTextSize();
 }
@@ -586,15 +588,14 @@ void GUIEngine::setTopleftText(std::string append)
 /******************************************************************************/
 void GUIEngine::updateTopLeftTextSize()
 {
-       std::wstring text = m_irr_toplefttext->getText();
-
-       core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(text), g_fontengine->getTextHeight());
-               rect += v2s32(4, 0);
+       core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()),
+               g_fontengine->getTextHeight());
+       rect += v2s32(4, 0);
 
        m_irr_toplefttext->remove();
        m_irr_toplefttext =
-               m_device->getGUIEnvironment()->addStaticText(text.c_str(),
-               rect,false,true,0,-1);
+               addStaticText(m_device->getGUIEnvironment(), m_toplefttext,
+                       rect, false, true, 0, -1);
 }
 
 /******************************************************************************/
index d527f722243dc735832a682bf1f3e0f71fc31950..fa98a21e4fa7cfc89b516ee9eb648db54da6624f 100644 (file)
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "guiFormSpecMenu.h"
 #include "sound.h"
 #include "client/tile.h"
+#include "util/enriched_string.h"
 
 /******************************************************************************/
 /* Typedefs and macros                                                        */
@@ -275,6 +276,8 @@ class GUIEngine {
 
        /** pointer to gui element shown at topleft corner */
        irr::gui::IGUIStaticText*       m_irr_toplefttext;
+       /** and text that is in it */
+       EnrichedString m_toplefttext;
 
        /** initialize cloud subsystem */
        void cloudInit();
index 99b1153c121593a6f3a159d6e0045788d64d54d5..cf01dc38c8f8d45b25948aa4156cf10bd7bfedcd 100644 (file)
@@ -50,6 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/hex.h"
 #include "util/numeric.h"
 #include "util/string.h" // for parseColorString()
+#include "irrlicht_changes/static_text.h"
 #include "guiscalingfilter.h"
 
 #if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
@@ -249,37 +250,6 @@ std::vector<std::string>* GUIFormSpecMenu::getDropDownValues(const std::string &
        return NULL;
 }
 
-static std::vector<std::string> split(const std::string &s, char delim)
-{
-       std::vector<std::string> tokens;
-
-       std::string current = "";
-       bool last_was_escape = false;
-       for (unsigned int i = 0; i < s.size(); i++) {
-               char si = s.c_str()[i];
-               if (last_was_escape) {
-                       current += '\\';
-                       current += si;
-                       last_was_escape = false;
-               } else {
-                       if (si == delim) {
-                               tokens.push_back(current);
-                               current = "";
-                               last_was_escape = false;
-                       } else if (si == '\\') {
-                               last_was_escape = true;
-                       } else {
-                               current += si;
-                               last_was_escape = false;
-                       }
-               }
-       }
-       //push last element
-       tokens.push_back(current);
-
-       return tokens;
-}
-
 void GUIFormSpecMenu::parseSize(parserData* data,std::string element)
 {
        std::vector<std::string> parts = split(element,',');
@@ -966,7 +936,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
                        int font_height = g_fontengine->getTextHeight();
                        rect.UpperLeftCorner.Y -= font_height;
                        rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
-                       Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
+                       addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
                }
 
                e->setPasswordBox(true,L'*');
@@ -1021,7 +991,7 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
        if (name == "")
        {
                // spec field id to 0, this stops submit searching for a value that isn't there
-               Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
+               addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid);
        }
        else
        {
@@ -1056,7 +1026,7 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
                        int font_height = g_fontengine->getTextHeight();
                        rect.UpperLeftCorner.Y -= font_height;
                        rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
-                       Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
+                       addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
                }
        }
 
@@ -1117,7 +1087,7 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
        if (name == "")
        {
                // spec field id to 0, this stops submit searching for a value that isn't there
-               Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
+               addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid);
        }
        else
        {
@@ -1161,7 +1131,7 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
                        int font_height = g_fontengine->getTextHeight();
                        rect.UpperLeftCorner.Y -= font_height;
                        rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
-                       Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
+                       addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
                }
        }
        m_fields.push_back(spec);
@@ -1230,7 +1200,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
                                258+m_fields.size()
                        );
                        gui::IGUIStaticText *e =
-                               Environment->addStaticText(spec.flabel.c_str(),
+                               addStaticText(Environment, spec.flabel.c_str(),
                                        rect, false, false, this, spec.fid);
                        e->setTextAlignment(gui::EGUIA_UPPERLEFT,
                                                gui::EGUIA_CENTER);
@@ -1284,7 +1254,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
                        258+m_fields.size()
                );
                gui::IGUIStaticText *t =
-                               Environment->addStaticText(spec.flabel.c_str(), rect, false, false, this, spec.fid);
+                               addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid);
                t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
                m_fields.push_back(spec);
                return;
@@ -1910,7 +1880,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
        {
                assert(m_tooltip_element == NULL);
                // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
-               m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
+               m_tooltip_element = addStaticText(Environment, L"",core::rect<s32>(0,0,110,18));
                m_tooltip_element->enableOverrideColor(true);
                m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
                m_tooltip_element->setDrawBackground(true);
@@ -2255,7 +2225,6 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
                        std::wstring tooltip_text = L"";
                        if (hovering && !m_selected_item) {
                                tooltip_text = utf8_to_wide(item.getDefinition(m_gamedef->idef()).description);
-                               tooltip_text = unescape_enriched(tooltip_text);
                        }
                        if (tooltip_text != L"") {
                                std::vector<std::wstring> tt_rows = str_split(tooltip_text, L'\n');
@@ -2263,7 +2232,7 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
                                m_tooltip_element->setOverrideColor(m_default_tooltip_color);
                                m_tooltip_element->setVisible(true);
                                this->bringToFront(m_tooltip_element);
-                               m_tooltip_element->setText(tooltip_text.c_str());
+                               setStaticText(m_tooltip_element, tooltip_text.c_str());
                                s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
 #if IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2
                                s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
@@ -2535,8 +2504,10 @@ void GUIFormSpecMenu::drawMenu()
                                        iter != m_fields.end(); ++iter) {
                                if (iter->fid == id && m_tooltips[iter->fname].tooltip != L"") {
                                        if (m_old_tooltip != m_tooltips[iter->fname].tooltip) {
+                                               m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor);
+                                               m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color);
                                                m_old_tooltip = m_tooltips[iter->fname].tooltip;
-                                               m_tooltip_element->setText(m_tooltips[iter->fname].tooltip.c_str());
+                                               setStaticText(m_tooltip_element, m_tooltips[iter->fname].tooltip.c_str());
                                                std::vector<std::wstring> tt_rows = str_split(m_tooltips[iter->fname].tooltip, L'\n');
                                                s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
                                                s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
@@ -2558,8 +2529,6 @@ void GUIFormSpecMenu::drawMenu()
                                                core::position2d<s32>(tooltip_x, tooltip_y),
                                                core::dimension2d<s32>(tooltip_width, tooltip_height)));
                                        }
-                                       m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor);
-                                       m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color);
                                        m_tooltip_element->setVisible(true);
                                        this->bringToFront(m_tooltip_element);
                                        break;
@@ -2568,6 +2537,8 @@ void GUIFormSpecMenu::drawMenu()
                }
        }
 
+       m_tooltip_element->draw();
+
        /*
                Draw dragged item stack
        */
index ef230c81c4153d94ec0d51a4a304815e5877b6b3..4122b1f565fbac9801ae4f5acdf0526ade9aa7d1 100644 (file)
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "guiTable.h"
 #include "network/networkprotocol.h"
 #include "util/string.h"
+#include "util/enriched_string.h"
 
 class IGameDef;
 class InventoryManager;
@@ -202,7 +203,8 @@ class GUIFormSpecMenu : public GUIModalMenu
                        fname(name),
                        fid(id)
                {
-                       flabel = unescape_enriched(label);
+                       //flabel = unescape_enriched(label);
+                       flabel = label;
                        fdefault = unescape_enriched(default_text);
                        send = false;
                        ftype = f_Unknown;
@@ -239,7 +241,8 @@ class GUIFormSpecMenu : public GUIModalMenu
                        bgcolor(a_bgcolor),
                        color(a_color)
                {
-                       tooltip = unescape_enriched(utf8_to_wide(a_tooltip));
+                       //tooltip = unescape_enriched(utf8_to_wide(a_tooltip));
+                       tooltip = utf8_to_wide(a_tooltip);
                }
                std::wstring tooltip;
                irr::video::SColor bgcolor;
@@ -256,7 +259,8 @@ class GUIFormSpecMenu : public GUIModalMenu
                        rect(a_rect),
                        parent_button(NULL)
                {
-                       text = unescape_enriched(a_text);
+                       //text = unescape_enriched(a_text);
+                       text = a_text;
                }
                StaticTextSpec(const std::wstring &a_text,
                                const core::rect<s32> &a_rect,
@@ -264,7 +268,8 @@ class GUIFormSpecMenu : public GUIModalMenu
                        rect(a_rect),
                        parent_button(a_parent_button)
                {
-                       text = unescape_enriched(a_text);
+                       //text = unescape_enriched(a_text);
+                       text = a_text;
                }
                std::wstring text;
                core::rect<s32> rect;
diff --git a/src/irrlicht_changes/CMakeLists.txt b/src/irrlicht_changes/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3a265c9
--- /dev/null
@@ -0,0 +1,7 @@
+if (BUILD_CLIENT)
+       set(client_irrlicht_changes_SRCS
+               ${CMAKE_CURRENT_SOURCE_DIR}/static_text.cpp
+               PARENT_SCOPE
+       )
+endif()
+
diff --git a/src/irrlicht_changes/static_text.cpp b/src/irrlicht_changes/static_text.cpp
new file mode 100644 (file)
index 0000000..703287e
--- /dev/null
@@ -0,0 +1,679 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt
+// Copyright (C) 2016 NathanaĆ«l Courant:
+//   Modified the functions to use EnrichedText instead of string.
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#include "static_text.h"
+#ifdef _IRR_COMPILE_WITH_GUI_
+
+#include <vector>
+#include <string>
+#include <iostream>
+#include <IGUISkin.h>
+#include <IGUIEnvironment.h>
+#include <IGUIFont.h>
+#include <IVideoDriver.h>
+#include <rect.h>
+#include <SColor.h>
+
+#if USE_FREETYPE
+       #include "cguittfont/xCGUITTFont.h"
+#endif
+
+#include "util/string.h"
+
+namespace irr
+{
+
+#if USE_FREETYPE
+
+namespace gui
+{
+//! constructor
+StaticText::StaticText(const EnrichedString &text, bool border,
+                       IGUIEnvironment* environment, IGUIElement* parent,
+                       s32 id, const core::rect<s32>& rectangle,
+                       bool background)
+: IGUIStaticText(environment, parent, id, rectangle),
+       HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_UPPERLEFT),
+       Border(border), OverrideColorEnabled(false), OverrideBGColorEnabled(false), WordWrap(false), Background(background),
+       RestrainTextInside(true), RightToLeft(false),
+       OverrideColor(video::SColor(101,255,255,255)), BGColor(video::SColor(101,210,210,210)),
+       OverrideFont(0), LastBreakFont(0)
+{
+       #ifdef _DEBUG
+       setDebugName("StaticText");
+       #endif
+
+       Text = text.c_str();
+       cText = text;
+       if (environment && environment->getSkin())
+       {
+               BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE);
+       }
+}
+
+
+//! destructor
+StaticText::~StaticText()
+{
+       if (OverrideFont)
+               OverrideFont->drop();
+}
+
+//! draws the element and its children
+void StaticText::draw()
+{
+       if (!IsVisible)
+               return;
+
+       IGUISkin* skin = Environment->getSkin();
+       if (!skin)
+               return;
+       video::IVideoDriver* driver = Environment->getVideoDriver();
+
+       core::rect<s32> frameRect(AbsoluteRect);
+
+       // draw background
+
+       if (Background)
+       {
+               if ( !OverrideBGColorEnabled )  // skin-colors can change
+                       BGColor = skin->getColor(gui::EGDC_3D_FACE);
+
+               driver->draw2DRectangle(BGColor, frameRect, &AbsoluteClippingRect);
+       }
+
+       // draw the border
+
+       if (Border)
+       {
+               skin->draw3DSunkenPane(this, 0, true, false, frameRect, &AbsoluteClippingRect);
+               frameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X);
+       }
+
+       // draw the text
+       if (cText.size())
+       {
+               IGUIFont* font = getActiveFont();
+
+               if (font)
+               {
+                       if (!WordWrap)
+                       {
+                               // TODO: add colors here
+                               if (VAlign == EGUIA_LOWERRIGHT)
+                               {
+                                       frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y -
+                                               font->getDimension(L"A").Height - font->getKerningHeight();
+                               }
+                               if (HAlign == EGUIA_LOWERRIGHT)
+                               {
+                                       frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
+                                               font->getDimension(cText.c_str()).Width;
+                               }
+
+                               irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(font);
+                               tmp->draw(cText, frameRect,
+                                       OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),
+                                       HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
+                       }
+                       else
+                       {
+                               if (font != LastBreakFont)
+                                       breakText();
+
+                               core::rect<s32> r = frameRect;
+                               s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
+                               s32 totalHeight = height * BrokenText.size();
+                               if (VAlign == EGUIA_CENTER)
+                               {
+                                       r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2);
+                               }
+                               else if (VAlign == EGUIA_LOWERRIGHT)
+                               {
+                                       r.UpperLeftCorner.Y = r.LowerRightCorner.Y - totalHeight;
+                               }
+
+                               irr::video::SColor previous_color(255, 255, 255, 255);
+                               for (u32 i=0; i<BrokenText.size(); ++i)
+                               {
+                                       if (HAlign == EGUIA_LOWERRIGHT)
+                                       {
+                                               r.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
+                                                       font->getDimension(BrokenText[i].c_str()).Width;
+                                       }
+
+                                       //std::vector<irr::video::SColor> colors;
+                                       //std::wstring str;
+                                       EnrichedString str = BrokenText[i];
+
+                                       //str = colorizeText(BrokenText[i].c_str(), colors, previous_color);
+                                       //if (!colors.empty())
+                                       //      previous_color = colors[colors.size() - 1];
+
+                                       irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(font);
+                                       tmp->draw(str, r,
+                                               previous_color, // FIXME
+                                               HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
+
+                                       r.LowerRightCorner.Y += height;
+                                       r.UpperLeftCorner.Y += height;
+                               }
+                       }
+               }
+       }
+
+       IGUIElement::draw();
+}
+
+
+//! Sets another skin independent font.
+void StaticText::setOverrideFont(IGUIFont* font)
+{
+       if (OverrideFont == font)
+               return;
+
+       if (OverrideFont)
+               OverrideFont->drop();
+
+       OverrideFont = font;
+
+       if (OverrideFont)
+               OverrideFont->grab();
+
+       breakText();
+}
+
+//! Gets the override font (if any)
+IGUIFont * StaticText::getOverrideFont() const
+{
+       return OverrideFont;
+}
+
+//! Get the font which is used right now for drawing
+IGUIFont* StaticText::getActiveFont() const
+{
+       if ( OverrideFont )
+               return OverrideFont;
+       IGUISkin* skin = Environment->getSkin();
+       if (skin)
+               return skin->getFont();
+       return 0;
+}
+
+//! Sets another color for the text.
+void StaticText::setOverrideColor(video::SColor color)
+{
+       OverrideColor = color;
+       OverrideColorEnabled = true;
+}
+
+
+//! Sets another color for the text.
+void StaticText::setBackgroundColor(video::SColor color)
+{
+       BGColor = color;
+       OverrideBGColorEnabled = true;
+       Background = true;
+}
+
+
+//! Sets whether to draw the background
+void StaticText::setDrawBackground(bool draw)
+{
+       Background = draw;
+}
+
+
+//! Gets the background color
+video::SColor StaticText::getBackgroundColor() const
+{
+       return BGColor;
+}
+
+
+//! Checks if background drawing is enabled
+bool StaticText::isDrawBackgroundEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return Background;
+}
+
+
+//! Sets whether to draw the border
+void StaticText::setDrawBorder(bool draw)
+{
+       Border = draw;
+}
+
+
+//! Checks if border drawing is enabled
+bool StaticText::isDrawBorderEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return Border;
+}
+
+
+void StaticText::setTextRestrainedInside(bool restrainTextInside)
+{
+       RestrainTextInside = restrainTextInside;
+}
+
+
+bool StaticText::isTextRestrainedInside() const
+{
+       return RestrainTextInside;
+}
+
+
+void StaticText::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
+{
+       HAlign = horizontal;
+       VAlign = vertical;
+}
+
+
+#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7
+const video::SColor& StaticText::getOverrideColor() const
+#else
+video::SColor StaticText::getOverrideColor() const
+#endif
+{
+       return OverrideColor;
+}
+
+
+//! Sets if the static text should use the overide color or the
+//! color in the gui skin.
+void StaticText::enableOverrideColor(bool enable)
+{
+       OverrideColorEnabled = enable;
+}
+
+
+bool StaticText::isOverrideColorEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return OverrideColorEnabled;
+}
+
+
+//! Enables or disables word wrap for using the static text as
+//! multiline text control.
+void StaticText::setWordWrap(bool enable)
+{
+       WordWrap = enable;
+       breakText();
+}
+
+
+bool StaticText::isWordWrapEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return WordWrap;
+}
+
+
+void StaticText::setRightToLeft(bool rtl)
+{
+       if (RightToLeft != rtl)
+       {
+               RightToLeft = rtl;
+               breakText();
+       }
+}
+
+
+bool StaticText::isRightToLeft() const
+{
+       return RightToLeft;
+}
+
+
+//! Breaks the single text line.
+void StaticText::breakText()
+{
+       if (!WordWrap)
+               return;
+
+       BrokenText.clear();
+
+       IGUISkin* skin = Environment->getSkin();
+       IGUIFont* font = getActiveFont();
+       if (!font)
+               return;
+
+       LastBreakFont = font;
+
+       EnrichedString line;
+       EnrichedString word;
+       EnrichedString whitespace;
+       s32 size = cText.size();
+       s32 length = 0;
+       s32 elWidth = RelativeRect.getWidth();
+       if (Border)
+               elWidth -= 2*skin->getSize(EGDS_TEXT_DISTANCE_X);
+       wchar_t c;
+
+       //std::vector<irr::video::SColor> colors;
+
+       // We have to deal with right-to-left and left-to-right differently
+       // However, most parts of the following code is the same, it's just
+       // some order and boundaries which change.
+       if (!RightToLeft)
+       {
+               // regular (left-to-right)
+               for (s32 i=0; i<size; ++i)
+               {
+                       c = cText.getString()[i];
+                       bool lineBreak = false;
+
+                       if (c == L'\r') // Mac or Windows breaks
+                       {
+                               lineBreak = true;
+                               //if (Text[i+1] == L'\n') // Windows breaks
+                               //{
+                               //      Text.erase(i+1);
+                               //      --size;
+                               //}
+                               c = '\0';
+                       }
+                       else if (c == L'\n') // Unix breaks
+                       {
+                               lineBreak = true;
+                               c = '\0';
+                       }
+
+                       bool isWhitespace = (c == L' ' || c == 0);
+                       if ( !isWhitespace )
+                       {
+                               // part of a word
+                               //word += c;
+                               word.addChar(cText, i);
+                       }
+
+                       if ( isWhitespace || i == (size-1))
+                       {
+                               if (word.size())
+                               {
+                                       // here comes the next whitespace, look if
+                                       // we must break the last word to the next line.
+                                       const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
+                                       //const std::wstring sanitized = removeEscapes(word.c_str());
+                                       const s32 wordlgth = font->getDimension(word.c_str()).Width;
+
+                                       if (wordlgth > elWidth)
+                                       {
+                                               // This word is too long to fit in the available space, look for
+                                               // the Unicode Soft HYphen (SHY / 00AD) character for a place to
+                                               // break the word at
+                                               int where = core::stringw(word.c_str()).findFirst( wchar_t(0x00AD) );
+                                               if (where != -1)
+                                               {
+                                                       EnrichedString first = word.substr(0, where);
+                                                       EnrichedString second = word.substr(where, word.size() - where);
+                                                       first.addCharNoColor(L'-');
+                                                       BrokenText.push_back(line + first);
+                                                       const s32 secondLength = font->getDimension(second.c_str()).Width;
+
+                                                       length = secondLength;
+                                                       line = second;
+                                               }
+                                               else
+                                               {
+                                                       // No soft hyphen found, so there's nothing more we can do
+                                                       // break to next line
+                                                       if (length)
+                                                               BrokenText.push_back(line);
+                                                       length = wordlgth;
+                                                       line = word;
+                                               }
+                                       }
+                                       else if (length && (length + wordlgth + whitelgth > elWidth))
+                                       {
+                                               // break to next line
+                                               BrokenText.push_back(line);
+                                               length = wordlgth;
+                                               line = word;
+                                       }
+                                       else
+                                       {
+                                               // add word to line
+                                               line += whitespace;
+                                               line += word;
+                                               length += whitelgth + wordlgth;
+                                       }
+
+                                       word.clear();
+                                       whitespace.clear();
+                               }
+
+                               if ( isWhitespace && c != 0)
+                               {
+                                       whitespace.addChar(cText, i);
+                               }
+
+                               // compute line break
+                               if (lineBreak)
+                               {
+                                       line += whitespace;
+                                       line += word;
+                                       BrokenText.push_back(line);
+                                       line.clear();
+                                       word.clear();
+                                       whitespace.clear();
+                                       length = 0;
+                               }
+                       }
+               }
+
+               line += whitespace;
+               line += word;
+               BrokenText.push_back(line);
+       }
+       else
+       {
+               // right-to-left
+               for (s32 i=size; i>=0; --i)
+               {
+                       c = cText.getString()[i];
+                       bool lineBreak = false;
+
+                       if (c == L'\r') // Mac or Windows breaks
+                       {
+                               lineBreak = true;
+                               //if ((i>0) && Text[i-1] == L'\n') // Windows breaks
+                               //{
+                               //      Text.erase(i-1);
+                               //      --size;
+                               //}
+                               c = '\0';
+                       }
+                       else if (c == L'\n') // Unix breaks
+                       {
+                               lineBreak = true;
+                               c = '\0';
+                       }
+
+                       if (c==L' ' || c==0 || i==0)
+                       {
+                               if (word.size())
+                               {
+                                       // here comes the next whitespace, look if
+                                       // we must break the last word to the next line.
+                                       const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
+                                       const s32 wordlgth = font->getDimension(word.c_str()).Width;
+
+                                       if (length && (length + wordlgth + whitelgth > elWidth))
+                                       {
+                                               // break to next line
+                                               BrokenText.push_back(line);
+                                               length = wordlgth;
+                                               line = word;
+                                       }
+                                       else
+                                       {
+                                               // add word to line
+                                               line = whitespace + line;
+                                               line = word + line;
+                                               length += whitelgth + wordlgth;
+                                       }
+
+                                       word.clear();
+                                       whitespace.clear();
+                               }
+
+                               if (c != 0)
+                               //      whitespace = core::stringw(&c, 1) + whitespace;
+                               whitespace = cText.substr(i, 1) + whitespace;
+
+                               // compute line break
+                               if (lineBreak)
+                               {
+                                       line = whitespace + line;
+                                       line = word + line;
+                                       BrokenText.push_back(line);
+                                       line.clear();
+                                       word.clear();
+                                       whitespace.clear();
+                                       length = 0;
+                               }
+                       }
+                       else
+                       {
+                               // yippee this is a word..
+                               //word = core::stringw(&c, 1) + word;
+                               word = cText.substr(i, 1) + word;
+                       }
+               }
+
+               line = whitespace + line;
+               line = word + line;
+               BrokenText.push_back(line);
+       }
+}
+
+
+//! Sets the new caption of this element.
+void StaticText::setText(const wchar_t* text)
+{
+       setText(EnrichedString(text));
+}
+
+//! Sets the new caption of this element.
+void StaticText::setText(const EnrichedString &text)
+{
+       IGUIElement::setText(text.c_str());
+       cText = text;
+       if (text.hasBackground()) {
+               setBackgroundColor(text.getBackground());
+       }
+       breakText();
+}
+
+
+void StaticText::updateAbsolutePosition()
+{
+       IGUIElement::updateAbsolutePosition();
+       breakText();
+}
+
+
+//! Returns the height of the text in pixels when it is drawn.
+s32 StaticText::getTextHeight() const
+{
+       IGUIFont* font = getActiveFont();
+       if (!font)
+               return 0;
+
+       s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
+
+       if (WordWrap)
+               height *= BrokenText.size();
+
+       return height;
+}
+
+
+s32 StaticText::getTextWidth() const
+{
+       IGUIFont * font = getActiveFont();
+       if(!font)
+               return 0;
+
+       if(WordWrap)
+       {
+               s32 widest = 0;
+
+               for(u32 line = 0; line < BrokenText.size(); ++line)
+               {
+                       s32 width = font->getDimension(BrokenText[line].c_str()).Width;
+
+                       if(width > widest)
+                               widest = width;
+               }
+
+               return widest;
+       }
+       else
+       {
+               return font->getDimension(cText.c_str()).Width;
+       }
+}
+
+
+//! Writes attributes of the element.
+//! Implement this to expose the attributes of your element for
+//! scripting languages, editors, debuggers or xml serialization purposes.
+void StaticText::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
+{
+       IGUIStaticText::serializeAttributes(out,options);
+
+       out->addBool    ("Border",              Border);
+       out->addBool    ("OverrideColorEnabled",OverrideColorEnabled);
+       out->addBool    ("OverrideBGColorEnabled",OverrideBGColorEnabled);
+       out->addBool    ("WordWrap",            WordWrap);
+       out->addBool    ("Background",          Background);
+       out->addBool    ("RightToLeft",         RightToLeft);
+       out->addBool    ("RestrainTextInside",  RestrainTextInside);
+       out->addColor   ("OverrideColor",       OverrideColor);
+       out->addColor   ("BGColor",             BGColor);
+       out->addEnum    ("HTextAlign",          HAlign, GUIAlignmentNames);
+       out->addEnum    ("VTextAlign",          VAlign, GUIAlignmentNames);
+
+       // out->addFont ("OverrideFont",        OverrideFont);
+}
+
+
+//! Reads attributes of the element
+void StaticText::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
+{
+       IGUIStaticText::deserializeAttributes(in,options);
+
+       Border = in->getAttributeAsBool("Border");
+       enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
+       OverrideBGColorEnabled = in->getAttributeAsBool("OverrideBGColorEnabled");
+       setWordWrap(in->getAttributeAsBool("WordWrap"));
+       Background = in->getAttributeAsBool("Background");
+       RightToLeft = in->getAttributeAsBool("RightToLeft");
+       RestrainTextInside = in->getAttributeAsBool("RestrainTextInside");
+       OverrideColor = in->getAttributeAsColor("OverrideColor");
+       BGColor = in->getAttributeAsColor("BGColor");
+
+       setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
+                      (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
+
+       // OverrideFont = in->getAttributeAsFont("OverrideFont");
+}
+
+} // end namespace gui
+
+#endif // USE_FREETYPE
+
+} // end namespace irr
+
+
+#endif // _IRR_COMPILE_WITH_GUI_
diff --git a/src/irrlicht_changes/static_text.h b/src/irrlicht_changes/static_text.h
new file mode 100644 (file)
index 0000000..408a127
--- /dev/null
@@ -0,0 +1,268 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt
+// Copyright (C) 2016 NathanaĆ«l Courant
+//   Modified this class to work with EnrichedStrings too
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#ifndef __C_GUI_STATIC_TEXT_H_INCLUDED__
+#define __C_GUI_STATIC_TEXT_H_INCLUDED__
+
+#include "IrrCompileConfig.h"
+#ifdef _IRR_COMPILE_WITH_GUI_
+
+#include "IGUIStaticText.h"
+#include "irrArray.h"
+
+#include "log.h"
+
+#include <vector>
+
+#include "util/enriched_string.h"
+#include "config.h"
+#include <IGUIEnvironment.h>
+
+#if USE_FREETYPE
+
+namespace irr
+{
+
+namespace gui
+{
+
+       const EGUI_ELEMENT_TYPE EGUIET_ENRICHED_STATIC_TEXT = (EGUI_ELEMENT_TYPE)(0x1000);
+
+       class StaticText : public IGUIStaticText
+       {
+       public:
+
+               //! constructor
+               StaticText(const EnrichedString &text, bool border, IGUIEnvironment* environment,
+                       IGUIElement* parent, s32 id, const core::rect<s32>& rectangle,
+                       bool background = false);
+
+               //! destructor
+               virtual ~StaticText();
+
+               //! draws the element and its children
+               virtual void draw();
+
+               //! Sets another skin independent font.
+               virtual void setOverrideFont(IGUIFont* font=0);
+
+               //! Gets the override font (if any)
+               virtual IGUIFont* getOverrideFont() const;
+
+               //! Get the font which is used right now for drawing
+               virtual IGUIFont* getActiveFont() const;
+
+               //! Sets another color for the text.
+               virtual void setOverrideColor(video::SColor color);
+
+               //! Sets another color for the background.
+               virtual void setBackgroundColor(video::SColor color);
+
+               //! Sets whether to draw the background
+               virtual void setDrawBackground(bool draw);
+
+               //! Gets the background color
+               virtual video::SColor getBackgroundColor() const;
+
+               //! Checks if background drawing is enabled
+               virtual bool isDrawBackgroundEnabled() const;
+
+               //! Sets whether to draw the border
+               virtual void setDrawBorder(bool draw);
+
+               //! Checks if border drawing is enabled
+               virtual bool isDrawBorderEnabled() const;
+
+               //! Sets alignment mode for text
+               virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
+
+               //! Gets the override color
+               #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7
+               virtual const video::SColor& getOverrideColor() const;
+               #else
+               virtual video::SColor getOverrideColor() const;
+               #endif
+
+               //! Sets if the static 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
+               virtual bool isOverrideColorEnabled() const;
+
+               //! Set whether the text in this label should be clipped if it goes outside bounds
+               virtual void setTextRestrainedInside(bool restrainedInside);
+
+               //! Checks if the text in this label should be clipped if it goes outside bounds
+               virtual bool isTextRestrainedInside() const;
+
+               //! Enables or disables word wrap for using the static text as
+               //! multiline text control.
+               virtual void setWordWrap(bool enable);
+
+               //! Checks if word wrap is enabled
+               virtual bool isWordWrapEnabled() const;
+
+               //! Sets the new caption of this element.
+               virtual void setText(const wchar_t* text);
+
+               //! Returns the height of the text in pixels when it is drawn.
+               virtual s32 getTextHeight() const;
+
+               //! Returns the width of the current text, in the current font
+               virtual s32 getTextWidth() const;
+
+               //! Updates the absolute position, splits text if word wrap is enabled
+               virtual void updateAbsolutePosition();
+
+               //! Set whether the string should be interpreted as right-to-left (RTL) text
+               /** \note This component does not implement the Unicode bidi standard, the
+               text of the component should be already RTL if you call this. The
+               main difference when RTL is enabled is that the linebreaks for multiline
+               elements are performed starting from the end.
+               */
+               virtual void setRightToLeft(bool rtl);
+
+               //! Checks if the text should be interpreted as right-to-left text
+               virtual bool isRightToLeft() const;
+
+               //! 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);
+
+               virtual bool hasType(EGUI_ELEMENT_TYPE t) const {
+                       return (t == EGUIET_ENRICHED_STATIC_TEXT) || (t == EGUIET_STATIC_TEXT);
+               };
+
+               virtual bool hasType(EGUI_ELEMENT_TYPE t) {
+                       return (t == EGUIET_ENRICHED_STATIC_TEXT) || (t == EGUIET_STATIC_TEXT);
+               };
+
+               void setText(const EnrichedString &text);
+
+       private:
+
+               //! Breaks the single text line.
+               void breakText();
+
+               EGUI_ALIGNMENT HAlign, VAlign;
+               bool Border;
+               bool OverrideColorEnabled;
+               bool OverrideBGColorEnabled;
+               bool WordWrap;
+               bool Background;
+               bool RestrainTextInside;
+               bool RightToLeft;
+
+               video::SColor OverrideColor, BGColor;
+               gui::IGUIFont* OverrideFont;
+               gui::IGUIFont* LastBreakFont; // stored because: if skin changes, line break must be recalculated.
+
+               EnrichedString cText;
+               core::array< EnrichedString > BrokenText;
+       };
+
+
+} // end namespace gui
+
+} // end namespace irr
+
+inline irr::gui::IGUIStaticText *addStaticText(
+               irr::gui::IGUIEnvironment *guienv,
+               const EnrichedString &text,
+               const core::rect< s32 > &rectangle,
+               bool border = false,
+               bool wordWrap = true,
+               irr::gui::IGUIElement *parent = NULL,
+               s32 id = -1,
+               bool fillBackground = false)
+{
+       if (parent == NULL) {
+               // parent is NULL, so we must find one, or we need not to drop
+               // result, but then there will be a memory leak.
+               //
+               // What Irrlicht does is to use guienv as a parent, but the problem
+               // is that guienv is here only an IGUIEnvironment, while it is a
+               // CGUIEnvironment in Irrlicht, which inherits from both IGUIElement
+               // and IGUIEnvironment.
+               //
+               // A solution would be to dynamic_cast guienv to a
+               // IGUIElement*, but Irrlicht is shipped without rtti support
+               // in some distributions, causing the dymanic_cast to segfault.
+               //
+               // Thus, to find the parent, we create a dummy StaticText and ask
+               // for its parent, and then remove it.
+               irr::gui::IGUIStaticText *dummy_text =
+                       guienv->addStaticText(L"", rectangle, border, wordWrap,
+                       parent, id, fillBackground);
+               parent = dummy_text->getParent();
+               dummy_text->remove();
+       }
+       irr::gui::IGUIStaticText *result = new irr::gui::StaticText(
+               text, border, guienv, parent,
+               id, rectangle, fillBackground);
+
+       result->setWordWrap(wordWrap);
+       result->drop();
+       return result;
+}
+
+inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text)
+{
+       // dynamic_cast not possible due to some distributions shipped
+       // without rtti support in irrlicht
+       if (static_text->hasType(irr::gui::EGUIET_ENRICHED_STATIC_TEXT)) {
+               irr::gui::StaticText* stext = static_cast<irr::gui::StaticText*>(static_text);
+               stext->setText(text);
+       } else {
+               static_text->setText(text.c_str());
+       }
+}
+
+#else // USE_FREETYPE
+
+inline irr::gui::IGUIStaticText *addStaticText(
+               irr::gui::IGUIEnvironment *guienv,
+               const EnrichedString &text,
+               const core::rect< s32 > &rectangle,
+               bool border = false,
+               bool wordWrap = true,
+               irr::gui::IGUIElement *parent = NULL,
+               s32 id = -1,
+               bool fillBackground = false)
+{
+       return guienv->addStaticText(text.c_str(), rectangle, border, wordWrap, parent, id, fillBackground);
+}
+
+inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text)
+{
+       static_text->setText(text.c_str());
+}
+
+#endif
+
+inline irr::gui::IGUIStaticText *addStaticText(
+               irr::gui::IGUIEnvironment *guienv,
+               const wchar_t *text,
+               const core::rect< s32 > &rectangle,
+               bool border = false,
+               bool wordWrap = true,
+               irr::gui::IGUIElement *parent = NULL,
+               s32 id = -1,
+               bool fillBackground = false) {
+       return addStaticText(guienv, EnrichedString(text), rectangle, border, wordWrap, parent, id, fillBackground);
+}
+
+inline void setStaticText(irr::gui::IGUIStaticText *static_text, const wchar_t *text)
+{
+       setStaticText(static_text, EnrichedString(text));
+}
+
+#endif // _IRR_COMPILE_WITH_GUI_
+
+#endif // C_GUI_STATIC_TEXT_H_INCLUDED
index c86a960fa49d0dacc964f4569609d8c0d101f04e..a8c4ebaef1a8926e559f06ae534567b46fe8c3f1 100644 (file)
@@ -345,9 +345,11 @@ void TerminalChatConsole::step(int ch)
                if (p.first > m_log_level)
                        continue;
 
-               m_chat_backend.addMessage(
-                       utf8_to_wide(Logger::getLevelLabel(p.first)),
-                       utf8_to_wide(p.second));
+               std::wstring error_message = utf8_to_wide(Logger::getLevelLabel(p.first));
+               if (!g_settings->getBool("disable_escape_sequences")) {
+                       error_message = L"\x1b(c@red)" + error_message + L"\x1b(c@white)";
+               }
+               m_chat_backend.addMessage(error_message, utf8_to_wide(p.second));
        }
 
        // handle input
@@ -438,7 +440,7 @@ void TerminalChatConsole::draw_text()
                        continue;
                for (u32 i = 0; i < line.fragments.size(); ++i) {
                        const ChatFormattedFragment& fragment = line.fragments[i];
-                       addstr(wide_to_utf8(fragment.text).c_str());
+                       addstr(wide_to_utf8(fragment.text.getString()).c_str());
                }
        }
 }
index e028a0435b0df54547c7256dcfb177c05abf9669..f571ab22ca75a2746ad8aea95bbdc36bf68dbe4d 100644 (file)
@@ -1,17 +1,9 @@
-if(USE_FREETYPE)
-       set(UTIL_FREETYPEDEP_SRCS
-               ${CMAKE_CURRENT_SOURCE_DIR}/statictext.cpp
-       )
-else()
-       set(UTIL_FREETYPEDEP_SRCS )
-endif(USE_FREETYPE)
-
 set(UTIL_SRCS
        ${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
-       ${CMAKE_CURRENT_SOURCE_DIR}/coloredstring.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp
@@ -20,6 +12,5 @@ set(UTIL_SRCS
        ${CMAKE_CURRENT_SOURCE_DIR}/string.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp
-       ${UTIL_FREETYPEDEP_SRCS}
        PARENT_SCOPE)
 
diff --git a/src/util/coloredstring.cpp b/src/util/coloredstring.cpp
deleted file mode 100644 (file)
index 7db5865..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-Copyright (C) 2013 xyz, Ilya Zhuravlev <whatever@xyz.is>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "coloredstring.h"
-#include "util/string.h"
-
-ColoredString::ColoredString()
-{}
-
-ColoredString::ColoredString(const std::wstring &string, const std::vector<SColor> &colors):
-       m_string(string),
-       m_colors(colors)
-{}
-
-ColoredString::ColoredString(const std::wstring &s) {
-       m_string = colorizeText(s, m_colors, SColor(255, 255, 255, 255));
-}
-
-void ColoredString::operator=(const wchar_t *str) {
-       m_string = colorizeText(str, m_colors, SColor(255, 255, 255, 255));
-}
-
-size_t ColoredString::size() const {
-       return m_string.size();
-}
-
-ColoredString ColoredString::substr(size_t pos, size_t len) const {
-       if (pos == m_string.length())
-               return ColoredString();
-       if (len == std::string::npos || pos + len > m_string.length()) {
-               return ColoredString(
-                          m_string.substr(pos, std::string::npos),
-                          std::vector<SColor>(m_colors.begin() + pos, m_colors.end())
-                      );
-       } else {
-               return ColoredString(
-                          m_string.substr(pos, len),
-                          std::vector<SColor>(m_colors.begin() + pos, m_colors.begin() + pos + len)
-                      );
-       }
-}
-
-const wchar_t *ColoredString::c_str() const {
-       return m_string.c_str();
-}
-
-const std::vector<SColor> &ColoredString::getColors() const {
-       return m_colors;
-}
-
-const std::wstring &ColoredString::getString() const {
-       return m_string;
-}
diff --git a/src/util/coloredstring.h b/src/util/coloredstring.h
deleted file mode 100644 (file)
index a6d98db..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
-Copyright (C) 2013 xyz, Ilya Zhuravlev <whatever@xyz.is>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef COLOREDSTRING_HEADER
-#define COLOREDSTRING_HEADER
-
-#include <string>
-#include <vector>
-#include <SColor.h>
-
-using namespace irr::video;
-
-class ColoredString {
-public:
-       ColoredString();
-       ColoredString(const std::wstring &s);
-       ColoredString(const std::wstring &string, const std::vector<SColor> &colors);
-       void operator=(const wchar_t *str);
-       size_t size() const;
-       ColoredString substr(size_t pos = 0, size_t len = std::string::npos) const;
-       const wchar_t *c_str() const;
-       const std::vector<SColor> &getColors() const;
-       const std::wstring &getString() const;
-private:
-       std::wstring m_string;
-       std::vector<SColor> m_colors;
-};
-
-#endif
diff --git a/src/util/enriched_string.cpp b/src/util/enriched_string.cpp
new file mode 100644 (file)
index 0000000..a7fc3a8
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+Copyright (C) 2013 xyz, Ilya Zhuravlev <whatever@xyz.is>
+Copyright (C) 2016 Nore, NathanaĆ«l Courant <nore@mesecons.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "enriched_string.h"
+#include "util/string.h"
+#include "log.h"
+using namespace irr::video;
+
+EnrichedString::EnrichedString()
+{
+       clear();
+}
+
+EnrichedString::EnrichedString(const std::wstring &string,
+               const std::vector<SColor> &colors):
+       m_string(string),
+       m_colors(colors),
+       m_has_background(false)
+{}
+
+EnrichedString::EnrichedString(const std::wstring &s, const SColor &color)
+{
+       clear();
+       addAtEnd(s, color);
+}
+
+EnrichedString::EnrichedString(const wchar_t *str, const SColor &color)
+{
+       clear();
+       addAtEnd(std::wstring(str), color);
+}
+
+void EnrichedString::operator=(const wchar_t *str)
+{
+       clear();
+       addAtEnd(std::wstring(str), SColor(255, 255, 255, 255));
+}
+
+void EnrichedString::addAtEnd(const std::wstring &s, const SColor &initial_color)
+{
+       SColor color(initial_color);
+       size_t i = 0;
+       while (i < s.length()) {
+               if (s[i] != L'\x1b') {
+                       m_string += s[i];
+                       m_colors.push_back(color);
+                       ++i;
+                       continue;
+               }
+               ++i;
+               size_t start_index = i;
+               size_t length;
+               if (i == s.length()) {
+                       break;
+               }
+               if (s[i] == L'(') {
+                       ++i;
+                       ++start_index;
+                       while (i < s.length() && s[i] != L')') {
+                               if (s[i] == L'\\') {
+                                       ++i;
+                               }
+                               ++i;
+                       }
+                       length = i - start_index;
+                       ++i;
+               } else {
+                       ++i;
+                       length = 1;
+               }
+               std::wstring escape_sequence(s, start_index, length);
+               std::vector<std::wstring> parts = split(escape_sequence, L'@');
+               if (parts[0] == L"c") {
+                       if (parts.size() < 2) {
+                               continue;
+                       }
+                       parseColorString(wide_to_utf8(parts[1]), color, true);
+               } else if (parts[0] == L"b") {
+                       if (parts.size() < 2) {
+                               continue;
+                       }
+                       parseColorString(wide_to_utf8(parts[1]), m_background, true);
+                       m_has_background = true;
+               }
+               continue;
+       }
+}
+
+void EnrichedString::addChar(const EnrichedString &source, size_t i)
+{
+       m_string += source.m_string[i];
+       m_colors.push_back(source.m_colors[i]);
+}
+
+void EnrichedString::addCharNoColor(wchar_t c)
+{
+       m_string += c;
+       if (m_colors.empty()) {
+               m_colors.push_back(SColor(255, 255, 255, 255));
+       } else {
+               m_colors.push_back(m_colors[m_colors.size() - 1]);
+       }
+}
+
+EnrichedString EnrichedString::operator+(const EnrichedString &other) const
+{
+       std::vector<SColor> result;
+       result.insert(result.end(), m_colors.begin(), m_colors.end());
+       result.insert(result.end(), other.m_colors.begin(), other.m_colors.end());
+       return EnrichedString(m_string + other.m_string, result);
+}
+
+void EnrichedString::operator+=(const EnrichedString &other)
+{
+       m_string += other.m_string;
+       m_colors.insert(m_colors.end(), other.m_colors.begin(), other.m_colors.end());
+}
+
+EnrichedString EnrichedString::substr(size_t pos, size_t len) const
+{
+       if (pos == m_string.length()) {
+               return EnrichedString();
+       }
+       if (len == std::string::npos || pos + len > m_string.length()) {
+               return EnrichedString(
+                          m_string.substr(pos, std::string::npos),
+                          std::vector<SColor>(m_colors.begin() + pos, m_colors.end())
+                      );
+       } else {
+               return EnrichedString(
+                          m_string.substr(pos, len),
+                          std::vector<SColor>(m_colors.begin() + pos, m_colors.begin() + pos + len)
+                      );
+       }
+}
+
+const wchar_t *EnrichedString::c_str() const
+{
+       return m_string.c_str();
+}
+
+const std::vector<SColor> &EnrichedString::getColors() const
+{
+       return m_colors;
+}
+
+const std::wstring &EnrichedString::getString() const
+{
+       return m_string;
+}
diff --git a/src/util/enriched_string.h b/src/util/enriched_string.h
new file mode 100644 (file)
index 0000000..1aca894
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+Copyright (C) 2013 xyz, Ilya Zhuravlev <whatever@xyz.is>
+Copyright (C) 2016 Nore, NathanaĆ«l Courant <nore@mesecons.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef ENRICHEDSTRING_HEADER
+#define ENRICHEDSTRING_HEADER
+
+#include <string>
+#include <vector>
+#include <SColor.h>
+
+class EnrichedString {
+public:
+       EnrichedString();
+       EnrichedString(const std::wstring &s,
+               const irr::video::SColor &color = irr::video::SColor(255, 255, 255, 255));
+       EnrichedString(const wchar_t *str,
+               const irr::video::SColor &color = irr::video::SColor(255, 255, 255, 255));
+       EnrichedString(const std::wstring &string,
+               const std::vector<irr::video::SColor> &colors);
+       void operator=(const wchar_t *str);
+       void addAtEnd(const std::wstring &s, const irr::video::SColor &color);
+
+       // Adds the character source[i] at the end.
+       // An EnrichedString should always be able to be copied
+       // to the end of an existing EnrichedString that way.
+       void addChar(const EnrichedString &source, size_t i);
+
+       // Adds a single character at the end, without specifying its
+       // color. The color used will be the one from the last character.
+       void addCharNoColor(wchar_t c);
+
+       EnrichedString substr(size_t pos = 0, size_t len = std::string::npos) const;
+       EnrichedString operator+(const EnrichedString &other) const;
+       void operator+=(const EnrichedString &other);
+       const wchar_t *c_str() const;
+       const std::vector<irr::video::SColor> &getColors() const;
+       const std::wstring &getString() const;
+       inline bool operator==(const EnrichedString &other) const
+       {
+               return (m_string == other.m_string && m_colors == other.m_colors);
+       }
+       inline bool operator!=(const EnrichedString &other) const
+       {
+               return !(*this == other);
+       }
+       inline void clear()
+       {
+               m_string.clear();
+               m_colors.clear();
+               m_has_background = false;
+       }
+       inline bool empty() const
+       {
+               return m_string.empty();
+       }
+       inline size_t size() const
+       {
+               return m_string.size();
+       }
+       inline bool hasBackground() const
+       {
+               return m_has_background;
+       }
+       inline irr::video::SColor getBackground() const
+       {
+               return m_background;
+       }
+private:
+       std::wstring m_string;
+       std::vector<irr::video::SColor> m_colors;
+       bool m_has_background;
+       irr::video::SColor m_background;
+};
+
+#endif
diff --git a/src/util/statictext.cpp b/src/util/statictext.cpp
deleted file mode 100644 (file)
index b534b56..0000000
+++ /dev/null
@@ -1,654 +0,0 @@
-// Copyright (C) 2002-2012 Nikolaus Gebhardt
-// This file is part of the "Irrlicht Engine".
-// For conditions of distribution and use, see copyright notice in irrlicht.h
-
-#include "statictext.h"
-#ifdef _IRR_COMPILE_WITH_GUI_
-
-//Only compile this if freetype is enabled.
-
-#include <vector>
-#include <string>
-#include <iostream>
-#include <IGUISkin.h>
-#include <IGUIEnvironment.h>
-#include <IGUIFont.h>
-#include <IVideoDriver.h>
-#include <rect.h>
-#include <SColor.h>
-
-#include "cguittfont/xCGUITTFont.h"
-#include "util/string.h"
-
-namespace irr
-{
-namespace gui
-{
-//! constructor
-StaticText::StaticText(const wchar_t* text, bool border,
-                       IGUIEnvironment* environment, IGUIElement* parent,
-                       s32 id, const core::rect<s32>& rectangle,
-                       bool background)
-: IGUIStaticText(environment, parent, id, rectangle),
-       HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_UPPERLEFT),
-       Border(border), OverrideColorEnabled(false), OverrideBGColorEnabled(false), WordWrap(false), Background(background),
-       RestrainTextInside(true), RightToLeft(false),
-       OverrideColor(video::SColor(101,255,255,255)), BGColor(video::SColor(101,210,210,210)),
-       OverrideFont(0), LastBreakFont(0)
-{
-       #ifdef _DEBUG
-       setDebugName("StaticText");
-       #endif
-
-       Text = text;
-       if (environment && environment->getSkin())
-       {
-               BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE);
-       }
-}
-
-
-//! destructor
-StaticText::~StaticText()
-{
-       if (OverrideFont)
-               OverrideFont->drop();
-}
-
-
-//! draws the element and its children
-void StaticText::draw()
-{
-       if (!IsVisible)
-               return;
-
-       IGUISkin* skin = Environment->getSkin();
-       if (!skin)
-               return;
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-
-       core::rect<s32> frameRect(AbsoluteRect);
-
-       // draw background
-
-       if (Background)
-       {
-               if ( !OverrideBGColorEnabled )  // skin-colors can change
-                       BGColor = skin->getColor(gui::EGDC_3D_FACE);
-
-               driver->draw2DRectangle(BGColor, frameRect, &AbsoluteClippingRect);
-       }
-
-       // draw the border
-
-       if (Border)
-       {
-               skin->draw3DSunkenPane(this, 0, true, false, frameRect, &AbsoluteClippingRect);
-               frameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X);
-       }
-
-       // draw the text
-       if (Text.size())
-       {
-               IGUIFont* font = getActiveFont();
-
-               if (font)
-               {
-                       if (!WordWrap)
-                       {
-                               // TODO: add colors here
-                               if (VAlign == EGUIA_LOWERRIGHT)
-                               {
-                                       frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y -
-                                               font->getDimension(L"A").Height - font->getKerningHeight();
-                               }
-                               if (HAlign == EGUIA_LOWERRIGHT)
-                               {
-                                       frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
-                                               font->getDimension(Text.c_str()).Width;
-                               }
-
-                               font->draw(Text.c_str(), frameRect,
-                                       OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),
-                                       HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
-                       }
-                       else
-                       {
-                               if (font != LastBreakFont)
-                                       breakText();
-
-                               core::rect<s32> r = frameRect;
-                               s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
-                               s32 totalHeight = height * BrokenText.size();
-                               if (VAlign == EGUIA_CENTER)
-                               {
-                                       r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2);
-                               }
-                               else if (VAlign == EGUIA_LOWERRIGHT)
-                               {
-                                       r.UpperLeftCorner.Y = r.LowerRightCorner.Y - totalHeight;
-                               }
-
-                               irr::video::SColor previous_color(255, 255, 255, 255);
-                               for (u32 i=0; i<BrokenText.size(); ++i)
-                               {
-                                       if (HAlign == EGUIA_LOWERRIGHT)
-                                       {
-                                               r.UpperLeftCorner.X = frameRect.LowerRightCorner.X -
-                                                       font->getDimension(BrokenText[i].c_str()).Width;
-                                       }
-
-                                       std::vector<irr::video::SColor> colors;
-                                       std::wstring str;
-
-                                       str = colorizeText(BrokenText[i].c_str(), colors, previous_color);
-                                       if (!colors.empty())
-                                               previous_color = colors[colors.size() - 1];
-
-                                       irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(font);
-                                       tmp->draw(str.c_str(), r,
-                                               colors,
-                                               HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL));
-
-                                       r.LowerRightCorner.Y += height;
-                                       r.UpperLeftCorner.Y += height;
-                               }
-                       }
-               }
-       }
-
-       IGUIElement::draw();
-}
-
-
-//! Sets another skin independent font.
-void StaticText::setOverrideFont(IGUIFont* font)
-{
-       if (OverrideFont == font)
-               return;
-
-       if (OverrideFont)
-               OverrideFont->drop();
-
-       OverrideFont = font;
-
-       if (OverrideFont)
-               OverrideFont->grab();
-
-       breakText();
-}
-
-//! Gets the override font (if any)
-IGUIFont * StaticText::getOverrideFont() const
-{
-       return OverrideFont;
-}
-
-//! Get the font which is used right now for drawing
-IGUIFont* StaticText::getActiveFont() const
-{
-       if ( OverrideFont )
-               return OverrideFont;
-       IGUISkin* skin = Environment->getSkin();
-       if (skin)
-               return skin->getFont();
-       return 0;
-}
-
-//! Sets another color for the text.
-void StaticText::setOverrideColor(video::SColor color)
-{
-       OverrideColor = color;
-       OverrideColorEnabled = true;
-}
-
-
-//! Sets another color for the text.
-void StaticText::setBackgroundColor(video::SColor color)
-{
-       BGColor = color;
-       OverrideBGColorEnabled = true;
-       Background = true;
-}
-
-
-//! Sets whether to draw the background
-void StaticText::setDrawBackground(bool draw)
-{
-       Background = draw;
-}
-
-
-//! Gets the background color
-video::SColor StaticText::getBackgroundColor() const
-{
-       return BGColor;
-}
-
-
-//! Checks if background drawing is enabled
-bool StaticText::isDrawBackgroundEnabled() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return Background;
-}
-
-
-//! Sets whether to draw the border
-void StaticText::setDrawBorder(bool draw)
-{
-       Border = draw;
-}
-
-
-//! Checks if border drawing is enabled
-bool StaticText::isDrawBorderEnabled() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return Border;
-}
-
-
-void StaticText::setTextRestrainedInside(bool restrainTextInside)
-{
-       RestrainTextInside = restrainTextInside;
-}
-
-
-bool StaticText::isTextRestrainedInside() const
-{
-       return RestrainTextInside;
-}
-
-
-void StaticText::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
-{
-       HAlign = horizontal;
-       VAlign = vertical;
-}
-
-
-#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7
-const video::SColor& StaticText::getOverrideColor() const
-#else
-video::SColor StaticText::getOverrideColor() const
-#endif
-{
-       return OverrideColor;
-}
-
-
-//! Sets if the static text should use the overide color or the
-//! color in the gui skin.
-void StaticText::enableOverrideColor(bool enable)
-{
-       OverrideColorEnabled = enable;
-}
-
-
-bool StaticText::isOverrideColorEnabled() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return OverrideColorEnabled;
-}
-
-
-//! Enables or disables word wrap for using the static text as
-//! multiline text control.
-void StaticText::setWordWrap(bool enable)
-{
-       WordWrap = enable;
-       breakText();
-}
-
-
-bool StaticText::isWordWrapEnabled() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return WordWrap;
-}
-
-
-void StaticText::setRightToLeft(bool rtl)
-{
-       if (RightToLeft != rtl)
-       {
-               RightToLeft = rtl;
-               breakText();
-       }
-}
-
-
-bool StaticText::isRightToLeft() const
-{
-       return RightToLeft;
-}
-
-
-//! Breaks the single text line.
-void StaticText::breakText()
-{
-       if (!WordWrap)
-               return;
-
-       BrokenText.clear();
-
-       IGUISkin* skin = Environment->getSkin();
-       IGUIFont* font = getActiveFont();
-       if (!font)
-               return;
-
-       LastBreakFont = font;
-
-       core::stringw line;
-       core::stringw word;
-       core::stringw whitespace;
-       s32 size = Text.size();
-       s32 length = 0;
-       s32 elWidth = RelativeRect.getWidth();
-       if (Border)
-               elWidth -= 2*skin->getSize(EGDS_TEXT_DISTANCE_X);
-       wchar_t c;
-
-       std::vector<irr::video::SColor> colors;
-
-       // We have to deal with right-to-left and left-to-right differently
-       // However, most parts of the following code is the same, it's just
-       // some order and boundaries which change.
-       if (!RightToLeft)
-       {
-               // regular (left-to-right)
-               for (s32 i=0; i<size; ++i)
-               {
-                       c = Text[i];
-                       bool lineBreak = false;
-
-                       if (c == L'\r') // Mac or Windows breaks
-                       {
-                               lineBreak = true;
-                               if (Text[i+1] == L'\n') // Windows breaks
-                               {
-                                       Text.erase(i+1);
-                                       --size;
-                               }
-                               c = '\0';
-                       }
-                       else if (c == L'\n') // Unix breaks
-                       {
-                               lineBreak = true;
-                               c = '\0';
-                       }
-
-                       bool isWhitespace = (c == L' ' || c == 0);
-                       if ( !isWhitespace )
-                       {
-                               // part of a word
-                               word += c;
-                       }
-
-                       if ( isWhitespace || i == (size-1))
-                       {
-                               if (word.size())
-                               {
-                                       // here comes the next whitespace, look if
-                                       // we must break the last word to the next line.
-                                       const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
-                                       const std::wstring sanitized = removeEscapes(word.c_str());
-                                       const s32 wordlgth = font->getDimension(sanitized.c_str()).Width;
-
-                                       if (wordlgth > elWidth)
-                                       {
-                                               // This word is too long to fit in the available space, look for
-                                               // the Unicode Soft HYphen (SHY / 00AD) character for a place to
-                                               // break the word at
-                                               int where = word.findFirst( wchar_t(0x00AD) );
-                                               if (where != -1)
-                                               {
-                                                       core::stringw first  = word.subString(0, where);
-                                                       core::stringw second = word.subString(where, word.size() - where);
-                                                       BrokenText.push_back(line + first + L"-");
-                                                       const s32 secondLength = font->getDimension(second.c_str()).Width;
-
-                                                       length = secondLength;
-                                                       line = second;
-                                               }
-                                               else
-                                               {
-                                                       // No soft hyphen found, so there's nothing more we can do
-                                                       // break to next line
-                                                       if (length)
-                                                               BrokenText.push_back(line);
-                                                       length = wordlgth;
-                                                       line = word;
-                                               }
-                                       }
-                                       else if (length && (length + wordlgth + whitelgth > elWidth))
-                                       {
-                                               // break to next line
-                                               BrokenText.push_back(line);
-                                               length = wordlgth;
-                                               line = word;
-                                       }
-                                       else
-                                       {
-                                               // add word to line
-                                               line += whitespace;
-                                               line += word;
-                                               length += whitelgth + wordlgth;
-                                       }
-
-                                       word = L"";
-                                       whitespace = L"";
-                               }
-
-                               if ( isWhitespace )
-                               {
-                                       whitespace += c;
-                               }
-
-                               // compute line break
-                               if (lineBreak)
-                               {
-                                       line += whitespace;
-                                       line += word;
-                                       BrokenText.push_back(line);
-                                       line = L"";
-                                       word = L"";
-                                       whitespace = L"";
-                                       length = 0;
-                               }
-                       }
-               }
-
-               line += whitespace;
-               line += word;
-               BrokenText.push_back(line);
-       }
-       else
-       {
-               // right-to-left
-               for (s32 i=size; i>=0; --i)
-               {
-                       c = Text[i];
-                       bool lineBreak = false;
-
-                       if (c == L'\r') // Mac or Windows breaks
-                       {
-                               lineBreak = true;
-                               if ((i>0) && Text[i-1] == L'\n') // Windows breaks
-                               {
-                                       Text.erase(i-1);
-                                       --size;
-                               }
-                               c = '\0';
-                       }
-                       else if (c == L'\n') // Unix breaks
-                       {
-                               lineBreak = true;
-                               c = '\0';
-                       }
-
-                       if (c==L' ' || c==0 || i==0)
-                       {
-                               if (word.size())
-                               {
-                                       // here comes the next whitespace, look if
-                                       // we must break the last word to the next line.
-                                       const s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
-                                       const s32 wordlgth = font->getDimension(word.c_str()).Width;
-
-                                       if (length && (length + wordlgth + whitelgth > elWidth))
-                                       {
-                                               // break to next line
-                                               BrokenText.push_back(line);
-                                               length = wordlgth;
-                                               line = word;
-                                       }
-                                       else
-                                       {
-                                               // add word to line
-                                               line = whitespace + line;
-                                               line = word + line;
-                                               length += whitelgth + wordlgth;
-                                       }
-
-                                       word = L"";
-                                       whitespace = L"";
-                               }
-
-                               if (c != 0)
-                                       whitespace = core::stringw(&c, 1) + whitespace;
-
-                               // compute line break
-                               if (lineBreak)
-                               {
-                                       line = whitespace + line;
-                                       line = word + line;
-                                       BrokenText.push_back(line);
-                                       line = L"";
-                                       word = L"";
-                                       whitespace = L"";
-                                       length = 0;
-                               }
-                       }
-                       else
-                       {
-                               // yippee this is a word..
-                               word = core::stringw(&c, 1) + word;
-                       }
-               }
-
-               line = whitespace + line;
-               line = word + line;
-               BrokenText.push_back(line);
-       }
-}
-
-
-//! Sets the new caption of this element.
-void StaticText::setText(const wchar_t* text)
-{
-       IGUIElement::setText(text);
-       breakText();
-}
-
-
-void StaticText::updateAbsolutePosition()
-{
-       IGUIElement::updateAbsolutePosition();
-       breakText();
-}
-
-
-//! Returns the height of the text in pixels when it is drawn.
-s32 StaticText::getTextHeight() const
-{
-       IGUIFont* font = getActiveFont();
-       if (!font)
-               return 0;
-
-       s32 height = font->getDimension(L"A").Height + font->getKerningHeight();
-
-       if (WordWrap)
-               height *= BrokenText.size();
-
-       return height;
-}
-
-
-s32 StaticText::getTextWidth() const
-{
-       IGUIFont * font = getActiveFont();
-       if(!font)
-               return 0;
-
-       if(WordWrap)
-       {
-               s32 widest = 0;
-
-               for(u32 line = 0; line < BrokenText.size(); ++line)
-               {
-                       s32 width = font->getDimension(BrokenText[line].c_str()).Width;
-
-                       if(width > widest)
-                               widest = width;
-               }
-
-               return widest;
-       }
-       else
-       {
-               return font->getDimension(Text.c_str()).Width;
-       }
-}
-
-
-//! Writes attributes of the element.
-//! Implement this to expose the attributes of your element for
-//! scripting languages, editors, debuggers or xml serialization purposes.
-void StaticText::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
-{
-       IGUIStaticText::serializeAttributes(out,options);
-
-       out->addBool    ("Border",              Border);
-       out->addBool    ("OverrideColorEnabled",OverrideColorEnabled);
-       out->addBool    ("OverrideBGColorEnabled",OverrideBGColorEnabled);
-       out->addBool    ("WordWrap",            WordWrap);
-       out->addBool    ("Background",          Background);
-       out->addBool    ("RightToLeft",         RightToLeft);
-       out->addBool    ("RestrainTextInside",  RestrainTextInside);
-       out->addColor   ("OverrideColor",       OverrideColor);
-       out->addColor   ("BGColor",             BGColor);
-       out->addEnum    ("HTextAlign",          HAlign, GUIAlignmentNames);
-       out->addEnum    ("VTextAlign",          VAlign, GUIAlignmentNames);
-
-       // out->addFont ("OverrideFont",        OverrideFont);
-}
-
-
-//! Reads attributes of the element
-void StaticText::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
-{
-       IGUIStaticText::deserializeAttributes(in,options);
-
-       Border = in->getAttributeAsBool("Border");
-       enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
-       OverrideBGColorEnabled = in->getAttributeAsBool("OverrideBGColorEnabled");
-       setWordWrap(in->getAttributeAsBool("WordWrap"));
-       Background = in->getAttributeAsBool("Background");
-       RightToLeft = in->getAttributeAsBool("RightToLeft");
-       RestrainTextInside = in->getAttributeAsBool("RestrainTextInside");
-       OverrideColor = in->getAttributeAsColor("OverrideColor");
-       BGColor = in->getAttributeAsColor("BGColor");
-
-       setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
-                      (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
-
-       // OverrideFont = in->getAttributeAsFont("OverrideFont");
-}
-
-} // end namespace gui
-} // end namespace irr
-
-
-#endif // _IRR_COMPILE_WITH_GUI_
diff --git a/src/util/statictext.h b/src/util/statictext.h
deleted file mode 100644 (file)
index 8d2f879..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (C) 2002-2012 Nikolaus Gebhardt
-// This file is part of the "Irrlicht Engine".
-// For conditions of distribution and use, see copyright notice in irrlicht.h
-
-#ifndef __C_GUI_STATIC_TEXT_H_INCLUDED__
-#define __C_GUI_STATIC_TEXT_H_INCLUDED__
-
-#include "IrrCompileConfig.h"
-#ifdef _IRR_COMPILE_WITH_GUI_
-
-#include "IGUIStaticText.h"
-#include "irrArray.h"
-
-#include <vector>
-
-namespace irr
-{
-namespace gui
-{
-       class StaticText : public IGUIStaticText
-       {
-       public:
-
-               //! constructor
-               StaticText(const wchar_t* text, bool border, IGUIEnvironment* environment,
-                       IGUIElement* parent, s32 id, const core::rect<s32>& rectangle,
-                       bool background = false);
-
-               //! destructor
-               virtual ~StaticText();
-
-               //! draws the element and its children
-               virtual void draw();
-
-               //! Sets another skin independent font.
-               virtual void setOverrideFont(IGUIFont* font=0);
-
-               //! Gets the override font (if any)
-               virtual IGUIFont* getOverrideFont() const;
-
-               //! Get the font which is used right now for drawing
-               virtual IGUIFont* getActiveFont() const;
-
-               //! Sets another color for the text.
-               virtual void setOverrideColor(video::SColor color);
-
-               //! Sets another color for the background.
-               virtual void setBackgroundColor(video::SColor color);
-
-               //! Sets whether to draw the background
-               virtual void setDrawBackground(bool draw);
-
-               //! Gets the background color
-               virtual video::SColor getBackgroundColor() const;
-
-               //! Checks if background drawing is enabled
-               virtual bool isDrawBackgroundEnabled() const;
-
-               //! Sets whether to draw the border
-               virtual void setDrawBorder(bool draw);
-
-               //! Checks if border drawing is enabled
-               virtual bool isDrawBorderEnabled() const;
-
-               //! Sets alignment mode for text
-               virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
-
-               //! Gets the override color
-               #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7
-               virtual const video::SColor& getOverrideColor() const;
-               #else
-               virtual video::SColor getOverrideColor() const;
-               #endif
-
-               //! Sets if the static 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
-               virtual bool isOverrideColorEnabled() const;
-
-               //! Set whether the text in this label should be clipped if it goes outside bounds
-               virtual void setTextRestrainedInside(bool restrainedInside);
-
-               //! Checks if the text in this label should be clipped if it goes outside bounds
-               virtual bool isTextRestrainedInside() const;
-
-               //! Enables or disables word wrap for using the static text as
-               //! multiline text control.
-               virtual void setWordWrap(bool enable);
-
-               //! Checks if word wrap is enabled
-               virtual bool isWordWrapEnabled() const;
-
-               //! Sets the new caption of this element.
-               virtual void setText(const wchar_t* text);
-
-               //! Returns the height of the text in pixels when it is drawn.
-               virtual s32 getTextHeight() const;
-
-               //! Returns the width of the current text, in the current font
-               virtual s32 getTextWidth() const;
-
-               //! Updates the absolute position, splits text if word wrap is enabled
-               virtual void updateAbsolutePosition();
-
-               //! Set whether the string should be interpreted as right-to-left (RTL) text
-               /** \note This component does not implement the Unicode bidi standard, the
-               text of the component should be already RTL if you call this. The
-               main difference when RTL is enabled is that the linebreaks for multiline
-               elements are performed starting from the end.
-               */
-               virtual void setRightToLeft(bool rtl);
-
-               //! Checks if the text should be interpreted as right-to-left text
-               virtual bool isRightToLeft() const;
-
-               //! 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);
-
-       private:
-
-               //! Breaks the single text line.
-               void breakText();
-
-               EGUI_ALIGNMENT HAlign, VAlign;
-               bool Border;
-               bool OverrideColorEnabled;
-               bool OverrideBGColorEnabled;
-               bool WordWrap;
-               bool Background;
-               bool RestrainTextInside;
-               bool RightToLeft;
-
-               video::SColor OverrideColor, BGColor;
-               gui::IGUIFont* OverrideFont;
-               gui::IGUIFont* LastBreakFont; // stored because: if skin changes, line break must be recalculated.
-
-               core::array< core::stringw > BrokenText;
-       };
-
-} // end namespace gui
-} // end namespace irr
-
-#endif // _IRR_COMPILE_WITH_GUI_
-
-#endif // C_GUI_STATIC_TEXT_H_INCLUDED
index 40ef3e4d3700877df0f3d6bd9d98e0febe1e81f4..c77c5a6f97bbb25ce814e9a64a90d74e249c8514 100644 (file)
@@ -519,6 +519,38 @@ std::basic_string<T> unescape_enriched(const std::basic_string<T> &s)
        return output;
 }
 
+template <typename T>
+std::vector<std::basic_string<T> > split(const std::basic_string<T> &s, T delim)
+{
+       std::vector<std::basic_string<T> > tokens;
+
+       std::basic_string<T> current;
+       bool last_was_escape = false;
+       for (size_t i = 0; i < s.length(); i++) {
+               T si = s[i];
+               if (last_was_escape) {
+                       current += '\\';
+                       current += si;
+                       last_was_escape = false;
+               } else {
+                       if (si == delim) {
+                               tokens.push_back(current);
+                               current = std::basic_string<T>();
+                               last_was_escape = false;
+                       } else if (si == '\\') {
+                               last_was_escape = true;
+                       } else {
+                               current += si;
+                               last_was_escape = false;
+                       }
+               }
+       }
+       //push last element
+       tokens.push_back(current);
+
+       return tokens;
+}
+
 /**
  * Checks that all characters in \p to_check are a decimal digits.
  *