description = "Get help for commands or list privileges",
func = function(name, param)
local function format_help_line(cmd, def)
- local msg = "/"..cmd
+ local msg = core.colorize("00ffff", "/"..cmd)
if def.params and def.params ~= "" then
msg = msg .. " " .. def.params
end
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
+
+function core.colorize(color, message)
+ return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("ffffff")
+end
#include "chat.h"
#include "debug.h"
+#include "config.h"
#include "util/strfnd.h"
#include <cctype>
#include <sstream>
u32 hanging_indentation = 0;
// Format the sender name and produce fragments
- if (!line.name.empty())
- {
+ if (!line.name.empty()) {
temp_frag.text = L"<";
temp_frag.column = 0;
//temp_frag.bold = 0;
next_frags.push_back(temp_frag);
}
+ std::wstring name_sanitized = removeEscapes(line.name);
+
// Choose an indentation level
- if (line.name.empty())
- {
+ if (line.name.empty()) {
// Server messages
hanging_indentation = 0;
}
- else if (line.name.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);
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())
}
// 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
while (frag_length < remaining_in_input &&
frag_length < remaining_in_output)
{
- if (isspace(line.text[in_pos + frag_length]))
+ if (isspace(line_text[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);
void ChatBackend::addMessage(std::wstring name, std::wstring text)
{
- name = unescape_enriched(name);
- text = unescape_enriched(text);
-
// Note: A message may consist of multiple lines, for example the MOTD.
WStrfnd fnd(text);
while (!fnd.at_end())
#ifndef CHAT_HEADER
#define CHAT_HEADER
-#include "irrlichttypes.h"
#include <string>
#include <vector>
#include <list>
+#include "irrlichttypes.h"
+#include "util/coloredstring.h"
+
// Chat console related classes
struct ChatLine
// name of sending player, or empty if sent by server
std::wstring name;
// message text
- std::wstring text;
+ ColoredString text;
ChatLine(std::wstring a_name, std::wstring a_text):
age(0.0),
set(client_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp
PARENT_SCOPE
)
--- /dev/null
+/*
+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();
+ }
+}
+
--- /dev/null
+/*
+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
+
#include "log.h"
#include "filesys.h"
#include "gettext.h"
-#include "guiChatConsole.h"
+#include "client/guiChatConsole.h"
#include "guiFormSpecMenu.h"
#include "guiKeyChangeMenu.h"
#include "guiPasswordChange.h"
#include "minimap.h"
#include "mapblock_mesh.h"
+#if USE_FREETYPE
+ #include "util/statictext.h"
+#endif
+
#include "sound.h"
#if USE_SOUND
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(
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();
+++ /dev/null
-/*
-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);
- m_font->draw(
- fragment.text.c_str(),
- destrect,
- video::SColor(255, 255, 255, 255),
- false,
- false,
- &AbsoluteClippingRect);
- }
- }
-}
-
-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();
- }
-}
-
+++ /dev/null
-/*
-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
-
+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}/numeric.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
${CMAKE_CURRENT_SOURCE_DIR}/string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp
${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp
+ ${UTIL_FREETYPEDEP_SRCS}
PARENT_SCOPE)
--- /dev/null
+/*
+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;
+}
--- /dev/null
+/*
+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
--- /dev/null
+// 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_
--- /dev/null
+// 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