diff --git a/src/guiengine/screen_loader.cpp b/src/guiengine/screen_loader.cpp index eab6163eb..373cbed45 100644 --- a/src/guiengine/screen_loader.cpp +++ b/src/guiengine/screen_loader.cpp @@ -229,7 +229,6 @@ if(prop_name != NULL) widget.m_properties[prop_flag] = core::stringc(prop_name). if (text != NULL) { widget.m_text = _(text); - widget.m_is_text_rtl = (translations->isRTLLanguage() && widget.m_text != text); } const wchar_t* raw_text = xml->getAttributeValue(L"raw_text"); diff --git a/src/guiengine/widget.cpp b/src/guiengine/widget.cpp index 932a3f5f7..58b7cb324 100644 --- a/src/guiengine/widget.cpp +++ b/src/guiengine/widget.cpp @@ -76,7 +76,6 @@ Widget::Widget(WidgetType type, bool reserve_id) m_supports_multiplayer = false; m_is_bounding_box_round = false; m_has_tooltip = false; - m_is_text_rtl = false; m_absolute_x = m_absolute_y = m_absolute_w = m_absolute_h = -1; m_relative_x = m_relative_y = m_relative_w = m_relative_h = -1; diff --git a/src/guiengine/widget.hpp b/src/guiengine/widget.hpp index ca3ca8a2c..e7fb8aa50 100644 --- a/src/guiengine/widget.hpp +++ b/src/guiengine/widget.hpp @@ -156,9 +156,6 @@ namespace GUIEngine * go in the map above, which uses narrow strings */ irr::core::stringw m_text; - /** Whether the text in m_text is right-to-left */ - bool m_is_text_rtl; - /** When true, this widget shall use a bigger and more colourful font */ bool m_title_font; diff --git a/src/guiengine/widgets/bubble_widget.cpp b/src/guiengine/widgets/bubble_widget.cpp index 3536fbecd..65fec72a1 100644 --- a/src/guiengine/widgets/bubble_widget.cpp +++ b/src/guiengine/widgets/bubble_widget.cpp @@ -53,11 +53,7 @@ void BubbleWidget::add() false, true /* word wrap */, m_parent, (m_focusable ? getNewID() : getNewNoFocusID())); irrwidget->setTextRestrainedInside(false); - -#if IRRLICHT_VERSION_MAJOR > 1 || (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8) - irrwidget->setRightToLeft( translations->isRTLLanguage() ); -#endif - + irrwidget->setRightToLeft(translations->isRTLText(message)); m_element = irrwidget; replaceText(); @@ -120,9 +116,11 @@ void BubbleWidget::replaceText() void BubbleWidget::setText(const irr::core::stringw &s) { Widget::setText(s); - //If add() has already been called (and thus m_element is set) we need to replace the text. - if(m_element != NULL){ + if (m_element != NULL) + { + //If add() has already been called (and thus m_element is set) we need to replace the text. replaceText(); + getIrrlichtElement()->setRightToLeft(translations->isRTLText(getText())); } } diff --git a/src/guiengine/widgets/icon_button_widget.cpp b/src/guiengine/widgets/icon_button_widget.cpp index d15110f06..a951996ed 100644 --- a/src/guiengine/widgets/icon_button_widget.cpp +++ b/src/guiengine/widgets/icon_button_widget.cpp @@ -196,10 +196,8 @@ void IconButtonWidget::add() setLabelFont(); -#if IRRLICHT_VERSION_MAJOR > 1 || (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8) - m_label->setRightToLeft( translations->isRTLLanguage() ); + m_label->setRightToLeft(translations->isRTLText(message)); m_label->setTextRestrainedInside(false); -#endif } // ---- IDs diff --git a/src/guiengine/widgets/label_widget.cpp b/src/guiengine/widgets/label_widget.cpp index f27c8e39b..18522ab08 100644 --- a/src/guiengine/widgets/label_widget.cpp +++ b/src/guiengine/widgets/label_widget.cpp @@ -80,13 +80,9 @@ void LabelWidget::add() { irrwidget = GUIEngine::getGUIEnv()->addStaticText(message.c_str(), widget_size, false, word_wrap, m_parent, -1); -#if IRRLICHT_VERSION_MAJOR > 1 || (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8) irrwidget->setTextRestrainedInside(false); -#endif } -#if IRRLICHT_VERSION_MAJOR > 1 || (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8) - irrwidget->setRightToLeft( m_is_text_rtl ); -#endif + irrwidget->setRightToLeft(translations->isRTLText(message)); m_element = irrwidget; irrwidget->setTextAlignment( align, valign ); @@ -155,6 +151,8 @@ void LabelWidget::setText(const wchar_t *text, bool expandIfNeeded) } Widget::setText(text); + if (m_element) + getIrrlichtElement()->setRightToLeft(translations->isRTLText(getText())); } // setText // ---------------------------------------------------------------------------- diff --git a/src/guiengine/widgets/ribbon_widget.cpp b/src/guiengine/widgets/ribbon_widget.cpp index 8177826df..6b04de7dd 100644 --- a/src/guiengine/widgets/ribbon_widget.cpp +++ b/src/guiengine/widgets/ribbon_widget.cpp @@ -262,6 +262,7 @@ void RibbonWidget::add() label->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER); label->setTabStop(false); label->setNotClipped(true); + label->setRightToLeft(translations->isRTLText(message)); m_labels.push_back(label); subbtn->setTabStop(false); diff --git a/src/guiengine/widgets/spinner_widget.cpp b/src/guiengine/widgets/spinner_widget.cpp index f49d9f581..034bc6c29 100644 --- a/src/guiengine/widgets/spinner_widget.cpp +++ b/src/guiengine/widgets/spinner_widget.cpp @@ -145,7 +145,8 @@ void SpinnerWidget::add() else { rect subsize_label = rect(m_h, 0, m_w - m_h, m_h); - IGUIStaticText* label = GUIEngine::getGUIEnv()->addStaticText(stringw(m_value).c_str(), subsize_label, + const wchar_t *text = stringw(m_value).c_str(); + IGUIStaticText* label = GUIEngine::getGUIEnv()->addStaticText(text, subsize_label, false /* border */, true /* word wrap */, btn, getNewNoFocusID()); m_children[1].m_element = label; @@ -155,6 +156,7 @@ void SpinnerWidget::add() label->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER); label->setTabStop(false); label->setNotClipped(true); + label->setRightToLeft(translations->isRTLText(text)); if (m_labels.size() > 0) diff --git a/src/states_screens/dialogs/add_device_dialog.cpp b/src/states_screens/dialogs/add_device_dialog.cpp index 96a21121f..958455601 100644 --- a/src/states_screens/dialogs/add_device_dialog.cpp +++ b/src/states_screens/dialogs/add_device_dialog.cpp @@ -76,7 +76,7 @@ AddDeviceDialog::AddDeviceDialog() : ModalDialog(0.90f, 0.80f) /*word wrap*/true, m_irrlicht_window); b->setTabStop(false); - b->setRightToLeft(translations->isRTLLanguage()); + b->setRightToLeft(translations->isRTLText(msg)); // because it looks like 'setRightToLeft' applies next time // setText is called only b->setText(msg.c_str()); diff --git a/src/states_screens/dialogs/gp_info_dialog.cpp b/src/states_screens/dialogs/gp_info_dialog.cpp index 712126929..b70a775a0 100644 --- a/src/states_screens/dialogs/gp_info_dialog.cpp +++ b/src/states_screens/dialogs/gp_info_dialog.cpp @@ -79,12 +79,14 @@ GPInfoDialog::~GPInfoDialog() void GPInfoDialog::addTitle() { core::rect< s32 > area_top(0, 0, m_area.getWidth(), m_under_title); + const wchar_t *text = translations->fribidize(m_gp.getName()); IGUIStaticText* title = GUIEngine::getGUIEnv()->addStaticText( - translations->fribidize(m_gp.getName()), + text, area_top, false, true, // border, word wrap m_irrlicht_window); title->setTabStop(false); title->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER); + title->setRightToLeft(translations->isRTLText(text)); } // ---------------------------------------------------------------------------- diff --git a/src/utils/translation.cpp b/src/utils/translation.cpp index 677a8b9cf..da9139bbb 100644 --- a/src/utils/translation.cpp +++ b/src/utils/translation.cpp @@ -94,6 +94,81 @@ wchar_t* utf8_to_wide(const char* input) return &utf16line[0]; } +// ---------------------------------------------------------------------------- +/** Frees the memory allocated for the result of toFribidiChar(). */ +void freeFribidiChar(FriBidiChar *str) +{ +#ifdef TEST_BIDI + delete[] str; +#else + if (sizeof(wchar_t) != sizeof(FriBidiChar)) + delete[] str; +#endif +} + +/** Frees the memory allocated for the result of fromFribidiChar(). */ +void freeFribidiChar(wchar_t *str) +{ + if (sizeof(wchar_t) != sizeof(FriBidiChar)) + delete[] str; +} + +// ---------------------------------------------------------------------------- +/** Converts a wstring to a FriBidi-string. + The caller must take care to free (or not to free) the result after use. + Freeing should be done with freeFribidiChar(). + + On linux, the string doesn't need to be converted because wchar_t is + already UTF-32. On windows the string is converted from UTF-16 by this + function. */ +FriBidiChar* toFribidiChar(const wchar_t* str) +{ + std::size_t length = wcslen(str); + FriBidiChar *result; + if (sizeof(wchar_t) == sizeof(FriBidiChar)) + result = (FriBidiChar*) str; + else + { + // On windows FriBidiChar is 4 bytes, but wchar_t is 2 bytes. + // So we simply copy the characters over here (note that this + // is technically incorrect, all characters we use/support fit + // in 16 bits, which is what irrlicht supports atm). + result = new FriBidiChar[length + 1]; + for (std::size_t i = 0; i <= length; i++) + result[i] = str[i]; + } + +#ifdef TEST_BIDI + // Prepend a character that forces RTL style + FriBidiChar *tmp = result; + result = new FriBidiChar[++length + 1]; + std::memcpy(result + 1, tmp, length * sizeof(FriBidiChar)); + result[0] = L'\u202E'; + freeFribidiChar(tmp); +#endif + + return result; +} + +wchar_t* fromFribidiChar(const FriBidiChar* str) +{ + wchar_t *result; + if (sizeof(wchar_t) == sizeof(FriBidiChar)) + result = (wchar_t*) str; + else + { + std::size_t length = 0; + while (str[length]) + length++; + + // Copy back to wchar_t array + result = new wchar_t[length + 1]; + for (std::size_t i = 0; i <= length; i++) + result[i] = str[i]; + } + return result; +} + // ---------------------------------------------------------------------------- Translations::Translations() //: m_dictionary_manager("UTF-16") { @@ -288,27 +363,10 @@ const wchar_t* Translations::fribidize(const wchar_t* in_ptr) #if ENABLE_BIDI if(this->isRTLLanguage()) { - std::size_t length = wcslen(in_ptr); - FriBidiChar *fribidiInput; - - if (sizeof(wchar_t) == sizeof(FriBidiChar)) - fribidiInput = (FriBidiChar*) in_ptr; - else - { - // On windows FriBidiChar is 4 bytes, but wchar_t is 2 bytes. - // So we simply copy the characters over here (note that this - // is technically incorrect, all characters we use/support fit - // in 16 bits, which is what irrlicht supports atm). - fribidiInput = new FriBidiChar[length + 1]; - for (std::size_t i = 0; i <= length; i++) - fribidiInput[i] = in_ptr[i]; - } -#ifdef TEST_BIDI - FriBidiChar *tmp = fribidiInput; - fribidiInput = new FriBidiChar[++length + 1]; - std::memcpy(fribidiInput + 1, tmp, length * sizeof(FriBidiChar)); - fribidiInput[0] = L'\u202E'; -#endif + FriBidiChar *fribidiInput = toFribidiChar(in_ptr); + std::size_t length = 0; + while (fribidiInput[length]) + length++; // Assume right to left as start direction. #if FRIBIDI_MINOR_VERSION==10 @@ -330,13 +388,8 @@ const wchar_t* Translations::fribidize(const wchar_t* in_ptr) /* gint *position_V_to_L_list */ NULL, /* gint8 *embedding_level_list */ NULL ); -#ifdef TEST_BIDI - delete[] fribidiInput; - fribidiInput = tmp; -#endif - if (sizeof(wchar_t) != sizeof(FriBidiChar)) - delete[] fribidiInput; + freeFribidiChar(fribidiInput); if (!result) { @@ -346,17 +399,9 @@ const wchar_t* Translations::fribidize(const wchar_t* in_ptr) return m_converted_string.c_str(); } - if (sizeof(wchar_t) == sizeof(FriBidiChar)) - m_converted_string = core::stringw((wchar_t*) fribidiOutput); - else - { - // Copy back to wchar_t array - wchar_t *out = new wchar_t[length + 1]; - for (std::size_t i = 0; i <= length; i++) - out[i] = fribidiOutput[i]; - m_converted_string = core::stringw(out); - delete[] out; - } + wchar_t *convertedString = fromFribidiChar(fribidiOutput); + m_converted_string = core::stringw(convertedString); + freeFribidiChar(convertedString); delete[] fribidiOutput; return m_converted_string.c_str(); } @@ -365,6 +410,28 @@ const wchar_t* Translations::fribidize(const wchar_t* in_ptr) return in_ptr; } +bool Translations::isRTLText(const wchar_t *in_ptr) +{ +#if ENABLE_BIDI + if (this->isRTLLanguage()) + { + std::size_t length = wcslen(in_ptr); + FriBidiChar *fribidiInput = toFribidiChar(in_ptr); + + FriBidiCharType *types = new FriBidiCharType[length]; + fribidi_get_bidi_types(fribidiInput, length, types); + freeFribidiChar(fribidiInput); + + FriBidiParType type = fribidi_get_par_direction(types, length); + delete[] types; + return type == FRIBIDI_PAR_RTL; + } + return false; +#else + return false; +#endif +} + /** * \param original Message to translate * \param context Optional, can be set to differentiate 2 strings that are identical diff --git a/src/utils/translation.hpp b/src/utils/translation.hpp index 3782dc2b0..eeeec161b 100644 --- a/src/utils/translation.hpp +++ b/src/utils/translation.hpp @@ -64,6 +64,9 @@ public: const wchar_t* fribidize(const wchar_t* in_ptr); const wchar_t* fribidize(const irr::core::stringw &str) { return fribidize(str.c_str()); } + bool isRTLText(const wchar_t* in_ptr); + bool isRTLText(const irr::core::stringw &str) { return isRTLText(str.c_str()); } + const std::vector* getLanguageList() const; std::string getCurrentLanguageName();