From 4f89c3e77ae78e8334cca850cfa3e256235bdc5d Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Sat, 20 Sep 2014 00:11:04 +0200 Subject: [PATCH 1/8] Rework book formatter (Fixes #1148) --- apps/openmw/mwgui/bookwindow.cpp | 61 ++-- apps/openmw/mwgui/bookwindow.hpp | 6 +- apps/openmw/mwgui/formatting.cpp | 552 ++++++++++++----------------- apps/openmw/mwgui/formatting.hpp | 149 +++++--- apps/openmw/mwgui/scrollwindow.cpp | 5 +- files/mygui/openmw_book.layout | 4 +- 6 files changed, 352 insertions(+), 425 deletions(-) diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 372e81844..92e876708 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -70,11 +70,6 @@ namespace MWGui void BookWindow::clearPages() { - for (std::vector::iterator it=mPages.begin(); - it!=mPages.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } mPages.clear(); } @@ -89,25 +84,9 @@ namespace MWGui MWWorld::LiveCellRef *ref = mBook.get(); - BookTextParser parser; - std::vector results = parser.split(ref->mBase->mText, mLeftPage->getSize().width, mLeftPage->getSize().height); - - int i=0; - for (std::vector::iterator it=results.begin(); - it!=results.end(); ++it) - { - MyGUI::Widget* parent; - if (i%2 == 0) - parent = mLeftPage; - else - parent = mRightPage; - - MyGUI::Widget* pageWidget = parent->createWidgetReal("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast(i)); - pageWidget->setNeedMouseFocus(false); - parser.parsePage(*it, pageWidget, mLeftPage->getSize().width); - mPages.push_back(pageWidget); - ++i; - } + Formatting::BookFormatter formatter; + mPages = formatter.markupToWidget(mLeftPage, ref->mBase->mText); + formatter.markupToWidget(mRightPage, ref->mBase->mText); updatePages(); @@ -164,19 +143,6 @@ namespace MWGui mLeftPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 1) ); mRightPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 2) ); - unsigned int i=0; - for (std::vector::iterator it = mPages.begin(); - it != mPages.end(); ++it) - { - if (mCurrentPage*2 == i || mCurrentPage*2+1 == i) - (*it)->setVisible(true); - else - { - (*it)->setVisible(false); - } - ++i; - } - //If it is the last page, hide the button "Next Page" if ( (mCurrentPage+1)*2 == mPages.size() || (mCurrentPage+1)*2 == mPages.size() + 1) @@ -191,6 +157,27 @@ namespace MWGui } else { mPrevPageButton->setVisible(true); } + + if (mPages.empty()) + return; + + MyGUI::Widget * paper; + + paper = mLeftPage->getChildAt(0); + paper->setCoord(paper->getPosition().left, -mPages[mCurrentPage*2].first, + paper->getWidth(), mPages[mCurrentPage*2].second); + + paper = mRightPage->getChildAt(0); + if ((mCurrentPage+1)*2 <= mPages.size()) + { + paper->setCoord(paper->getPosition().left, -mPages[mCurrentPage*2+1].first, + paper->getWidth(), mPages[mCurrentPage*2+1].second); + paper->setVisible(true); + } + else + { + paper->setVisible(false); + } } void BookWindow::adjustButton (Gui::ImageButton* button) diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index e0f921dc1..ea3057a6f 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -34,17 +34,21 @@ namespace MWGui void adjustButton(Gui::ImageButton* button); private: + typedef std::pair Page; + typedef std::vector Pages; + Gui::ImageButton* mCloseButton; Gui::ImageButton* mTakeButton; Gui::ImageButton* mNextPageButton; Gui::ImageButton* mPrevPageButton; + MyGUI::TextBox* mLeftPageNumber; MyGUI::TextBox* mRightPageNumber; MyGUI::Widget* mLeftPage; MyGUI::Widget* mRightPage; unsigned int mCurrentPage; // 0 is first page - std::vector mPages; + Pages mPages; MWWorld::Ptr mBook; diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 42dfb1310..25091def9 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -11,68 +11,14 @@ #include #include #include -#include +#include #include +#include + namespace { - int convertFromHex(std::string hex) - { - int value = 0; - - int a = 0; - int b = hex.length() - 1; - for (; b >= 0; a++, b--) - { - if (hex[b] >= '0' && hex[b] <= '9') - { - value += (hex[b] - '0') * (1 << (a * 4)); - } - else - { - switch (hex[b]) - { - case 'A': - case 'a': - value += 10 * (1 << (a * 4)); - break; - - case 'B': - case 'b': - value += 11 * (1 << (a * 4)); - break; - - case 'C': - case 'c': - value += 12 * (1 << (a * 4)); - break; - - case 'D': - case 'd': - value += 13 * (1 << (a * 4)); - break; - - case 'E': - case 'e': - value += 14 * (1 << (a * 4)); - break; - - case 'F': - case 'f': - value += 15 * (1 << (a * 4)); - break; - - default: - throw std::runtime_error("invalid character in hex number"); - break; - } - } - } - - return value; - } - Ogre::UTFString::unicode_char unicodeCharFromChar(char ch) { std::string s; @@ -80,56 +26,56 @@ namespace Ogre::UTFString string(s); return string.getChar(0); } - - bool is_not_empty(const std::string& s) { - std::string temp = s; - boost::algorithm::trim(temp); - return !temp.empty(); - } } namespace MWGui { - - std::vector BookTextParser::split(std::string utf8Text, const int width, const int height) + namespace Formatting { - using Ogre::UTFString; - std::vector result; - - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); - - boost::algorithm::replace_all(utf8Text, "\n", ""); - boost::algorithm::replace_all(utf8Text, "\r", ""); - boost::algorithm::replace_all(utf8Text, "
", "\n"); - boost::algorithm::replace_all(utf8Text, "

", "\n\n"); - - UTFString text(utf8Text); - const int spacing = 48; - - const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); - const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); - const UTFString::unicode_char SPACE = unicodeCharFromChar(' '); - - while (!text.empty()) + Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, std::string utf8Text, const int pageWidth, const int pageHeight) { - // read in characters until we have exceeded the size, or run out of text - int currentWidth = 0; - int currentHeight = 0; + using Ogre::UTFString; - size_t currentWordStart = 0; - size_t index = 0; - + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); + + boost::algorithm::replace_all(utf8Text, "\n", ""); + boost::algorithm::replace_all(utf8Text, "\r", ""); + boost::algorithm::replace_all(utf8Text, "
", "\n"); + boost::algorithm::replace_all(utf8Text, "

", "\n\n"); + + UTFString text(utf8Text); + UTFString plainText; + + const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); + const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); + + Paginator pag(pageWidth, pageHeight); + + while (parent->getChildCount()) { - std::string texToTrim = text.asUTF8(); - boost::algorithm::trim( texToTrim ); - text = UTFString(texToTrim); + MyGUI::Gui::getInstance().destroyWidget(parent->getChildAt(0)); } - - - while (currentHeight <= height - spacing && index < text.size()) + + MyGUI::Widget * paper = parent->createWidget("Widget", MyGUI::IntCoord(0, 0, pag.getPageWidth(), pag.getPageHeight()), MyGUI::Align::Left | MyGUI::Align::Top); + paper->setNeedMouseFocus(false); + + bool ignoreNewlines = true; + for (size_t index = 0; index < text.size(); ++index) { const UTFString::unicode_char ch = text.getChar(index); + if (!plainText.empty() && (ch == LEFT_ANGLE || index == text.size() - 1)) + { + // if there's a newline at the end of the box caption, remove it + if (plainText[plainText.size()-1] == NEWLINE) + plainText.erase(plainText.end()-1); + + TextElement elem(paper, pag, mTextStyle, plainText.asUTF8()); + elem.paginate(); + + plainText.clear(); + } + if (ch == LEFT_ANGLE) { const size_t tagStart = index + 1; @@ -140,263 +86,203 @@ namespace MWGui if (boost::algorithm::starts_with(tag, "IMG")) { - const int h = mHeight; - parseImage(tag, false); - currentHeight += (mHeight - h); - currentWidth = 0; + ImageElement elem(paper, pag, mTextStyle, tag); + elem.paginate(); + + ignoreNewlines = false; } else if (boost::algorithm::starts_with(tag, "FONT")) { parseFont(tag); - if (currentWidth != 0) { - currentHeight += currentFontHeight(); - } - currentWidth = 0; } else if (boost::algorithm::starts_with(tag, "DIV")) { parseDiv(tag); - if (currentWidth != 0) { - currentHeight += currentFontHeight(); - currentWidth = 0; - } } + index = tagEnd; } - else if (ch == NEWLINE) - { - currentHeight += currentFontHeight(); - currentWidth = 0; - currentWordStart = index; - } - else if (ch == SPACE) - { - currentWidth += 3; // keep this in sync with the font's SpaceWidth property - currentWordStart = index; - } else { - currentWidth += widthForCharGlyph(ch); - } - - if (currentWidth > width) - { - currentHeight += currentFontHeight(); - currentWidth = 0; - // add size of the current word - UTFString word = text.substr(currentWordStart, index - currentWordStart); - for (UTFString::const_iterator it = word.begin(), end = word.end(); it != end; ++it) - currentWidth += widthForCharGlyph(it.getCharacter()); - } - index += UTFString::_utf16_char_length(ch); - } - const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0) - ? currentWordStart : index; - - result.push_back(text.substr(0, pageEnd).asUTF8()); - text.erase(0, pageEnd); - } - - std::vector nonEmptyPages; - boost::copy(result | boost::adaptors::filtered(is_not_empty), std::back_inserter(nonEmptyPages)); - return nonEmptyPages; - } - - float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const - { - std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); - return MyGUI::FontManager::getInstance().getByName(fontName) - ->getGlyphInfo(unicodeChar)->width; - } - - float BookTextParser::currentFontHeight() const - { - std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); - return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); - } - - MyGUI::IntSize BookTextParser::parsePage(std::string text, MyGUI::Widget* parent, const int width) - { - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - text = Interpreter::fixDefinesBook(text, interpreterContext); - - mParent = parent; - mWidth = width; - mHeight = 0; - - assert(mParent); - while (mParent->getChildCount()) - { - MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); - } - - // remove trailing " - if (text[text.size()-1] == '\"') - text.erase(text.size()-1); - - parseSubText(text); - return MyGUI::IntSize(mWidth, mHeight); - } - - MyGUI::IntSize BookTextParser::parseScroll(std::string text, MyGUI::Widget* parent, const int width) - { - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - text = Interpreter::fixDefinesBook(text, interpreterContext); - - mParent = parent; - mWidth = width; - mHeight = 0; - - assert(mParent); - while (mParent->getChildCount()) - { - MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); - } - - boost::algorithm::replace_all(text, "
", "\n"); - boost::algorithm::replace_all(text, "

", "\n\n"); - boost::algorithm::trim_left(text); - - // remove trailing " - if (text[text.size()-1] == '\"') - text.erase(text.size()-1); - - parseSubText(text); - return MyGUI::IntSize(mWidth, mHeight); - } - - - void BookTextParser::parseImage(std::string tag, bool createWidget) - { - int src_start = tag.find("SRC=")+5; - - int width_start = tag.find("WIDTH=")+7; - int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start)); - - int height_start = tag.find("HEIGHT=")+8; - int height = boost::lexical_cast(tag.substr(height_start, tag.find('"', height_start)-height_start)); - - if (createWidget) - { - MyGUI::ImageBox* box = mParent->createWidget ("ImageBox", - MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top, - mParent->getName() + boost::lexical_cast(mParent->getChildCount())); - - std::string image = Misc::ResourceHelpers::correctBookartPath(tag.substr(src_start, tag.find('"', src_start)-src_start), width, height); - box->setImageTexture(image); - box->setProperty("NeedMouse", "false"); - } - - mWidth = std::max(mWidth, width); - mHeight += height; - } - - void BookTextParser::parseDiv(std::string tag) - { - if (tag.find("ALIGN=") == std::string::npos) - return; - - int align_start = tag.find("ALIGN=")+7; - std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start); - if (align == "CENTER") - mTextStyle.mTextAlign = MyGUI::Align::HCenter; - else if (align == "LEFT") - mTextStyle.mTextAlign = MyGUI::Align::Left; - } - - void BookTextParser::parseFont(std::string tag) - { - if (tag.find("COLOR=") != std::string::npos) - { - int color_start = tag.find("COLOR=")+7; - std::string color = tag.substr(color_start, tag.find('"', color_start)-color_start); - - mTextStyle.mColour = MyGUI::Colour( - convertFromHex(color.substr(0, 2))/255.0, - convertFromHex(color.substr(2, 2))/255.0, - convertFromHex(color.substr(4, 2))/255.0); - } - if (tag.find("FACE=") != std::string::npos) - { - int face_start = tag.find("FACE=")+6; - std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); - - if (face != "Magic Cards") - mTextStyle.mFont = face; - } - if (tag.find("SIZE=") != std::string::npos) - { - /// \todo - } - } - - void BookTextParser::parseSubText(std::string text) - { - if (text[0] == '<') - { - const size_t tagStart = 1; - const size_t tagEnd = text.find('>', tagStart); - if (tagEnd == std::string::npos) - throw std::runtime_error("BookTextParser Error: Tag is not terminated"); - const std::string tag = text.substr(tagStart, tagEnd - tagStart); - - if (boost::algorithm::starts_with(tag, "IMG")) - parseImage(tag); - if (boost::algorithm::starts_with(tag, "FONT")) - parseFont(tag); - if (boost::algorithm::starts_with(tag, "DIV")) - parseDiv(tag); - - text.erase(0, tagEnd + 1); - } - - size_t tagStart = std::string::npos; - std::string realText; // real text, without tags - for (size_t i = 0; i= text.size()) - throw std::runtime_error("BookTextParser Error: Tag is not terminated"); - ++i; - c = text[i]; + plainText.push_back(ch); + ignoreNewlines = false; } - continue; - } - else - { - tagStart = i; - break; } } - else - realText += c; + + // insert last page + pag << Paginator::Page(pag.getStartTop(), pag.getStartTop() + pag.getPageHeight()); + + paper->setSize(paper->getWidth(), pag.getCurrentTop()); + + return pag.getPages(); } - MyGUI::EditBox* box = mParent->createWidget("NormalText", - MyGUI::IntCoord(0, mHeight, mWidth, 24), MyGUI::Align::Left | MyGUI::Align::Top, - mParent->getName() + boost::lexical_cast(mParent->getChildCount())); - box->setProperty("Static", "true"); - box->setProperty("MultiLine", "true"); - box->setProperty("WordWrap", "true"); - box->setProperty("NeedMouse", "false"); - box->setMaxTextLength(realText.size()); - box->setTextAlign(mTextStyle.mTextAlign); - box->setTextColour(mTextStyle.mColour); - box->setFontName(mTextStyle.mFont); - box->setCaption(MyGUI::TextIterator::toTagsString(realText)); - box->setSize(box->getSize().width, box->getTextSize().height); - mHeight += box->getTextSize().height; - - if (tagStart != std::string::npos) + Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, std::string utf8Text) { - parseSubText(text.substr(tagStart, text.size())); + return markupToWidget(parent, utf8Text, parent->getWidth(), parent->getHeight()); + } + + void BookFormatter::parseDiv(std::string tag) + { + if (tag.find("ALIGN=") == std::string::npos) + return; + + int align_start = tag.find("ALIGN=")+7; + std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start); + if (align == "CENTER") + mTextStyle.mTextAlign = MyGUI::Align::HCenter; + else if (align == "LEFT") + mTextStyle.mTextAlign = MyGUI::Align::Left; + } + + void BookFormatter::parseFont(std::string tag) + { + if (tag.find("COLOR=") != std::string::npos) + { + int color_start = tag.find("COLOR=")+7; + + int color; + std::stringstream ss; + ss << tag.substr(color_start, tag.find('"', color_start)-color_start); + ss >> std::hex >> color; + + mTextStyle.mColour = MyGUI::Colour( + (color>>16 & 0xFF) / 255.f, + (color>>8 & 0xFF) / 255.f, + (color & 0xFF) / 255.f); + } + if (tag.find("FACE=") != std::string::npos) + { + int face_start = tag.find("FACE=")+6; + std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); + + if (face != "Magic Cards") + mTextStyle.mFont = face; + } + if (tag.find("SIZE=") != std::string::npos) + { + /// \todo + } + } + + /* GraphicElement */ + GraphicElement::GraphicElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style) + : mParent(parent), mPaginator(pag), mStyle(style) + { + } + + void GraphicElement::paginate() + { + int newTop = mPaginator.getCurrentTop() + getHeight(); + while (newTop-mPaginator.getStartTop() > mPaginator.getPageHeight()) + { + int newStartTop = pageSplit(); + mPaginator << Paginator::Page(mPaginator.getStartTop(), newStartTop); + mPaginator.setStartTop(newStartTop); + } + + mPaginator.modifyCurrentTop(getHeight()); + } + + int GraphicElement::pageSplit() + { + return mPaginator.getStartTop() + mPaginator.getPageHeight(); + } + + int GraphicElement::currentFontHeight() const + { + std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); + } + + float GraphicElement::widthForCharGlyph(MyGUI::Char unicodeChar) const + { + std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName) + ->getGlyphInfo(unicodeChar)->width; + } + + /* TextElement */ + TextElement::TextElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & text) + : GraphicElement(parent, pag, style) + { + MyGUI::EditBox* box = parent->createWidget("NormalText", + MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top, + parent->getName() + boost::lexical_cast(parent->getChildCount())); + box->setProperty("Static", "true"); + box->setProperty("MultiLine", "true"); + box->setProperty("WordWrap", "true"); + box->setProperty("NeedMouse", "false"); + box->setMaxTextLength(text.size()); + box->setTextAlign(mStyle.mTextAlign); + box->setTextColour(mStyle.mColour); + box->setFontName(mStyle.mFont); + box->setCaption(MyGUI::TextIterator::toTagsString(text)); + box->setSize(box->getSize().width, box->getTextSize().height); + mEditBox = box; + } + + int TextElement::getHeight() + { + return mEditBox->getTextSize().height; + } + + int TextElement::pageSplit() + { + // split lines + const int lineHeight = currentFontHeight(); + unsigned int lastLine = (mPaginator.getStartTop() + mPaginator.getPageHeight() - mPaginator.getCurrentTop()) / lineHeight; + int ret = mPaginator.getCurrentTop() + lastLine * lineHeight; + + // first empty lines that would go to the next page should be ignored + // unfortunately, getLineInfo method won't be available until 3.2.2 +#if (MYGUI_VERSION >= MYGUI_DEFINE_VERSION(3, 2, 2)) + const MyGUI::VectorLineInfo & lines = mEditBox->getSubWidgetText()->castType()->getLineInfo(); + for (unsigned int i = lastLine; i < lines.size(); ++i) + { + if (lines[i].width == 0) + ret += lineHeight; + else + break; + } +#endif + return ret; + } + + /* ImageElement */ + ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & tag) + : GraphicElement(parent, pag, style), + mImageHeight(0) + { + int src_start = tag.find("SRC=")+5; + std::string src = tag.substr(src_start, tag.find('"', src_start)-src_start); + + int width_start = tag.find("WIDTH=")+7; + int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start)); + + int height_start = tag.find("HEIGHT=")+8; + mImageHeight = boost::lexical_cast(tag.substr(height_start, tag.find('"', height_start)-height_start)); + + mImageBox = parent->createWidget ("ImageBox", + MyGUI::IntCoord(0, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top, + parent->getName() + boost::lexical_cast(parent->getChildCount())); + + std::string image = Misc::ResourceHelpers::correctBookartPath(src, width, mImageHeight); + mImageBox->setImageTexture(image); + mImageBox->setProperty("NeedMouse", "false"); + } + + int ImageElement::getHeight() + { + return mImageHeight; + } + + int ImageElement::pageSplit() + { + return mPaginator.getCurrentTop(); } } - } diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index a32d98fe5..b0a8bce0f 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -5,63 +5,112 @@ namespace MWGui { - struct TextStyle + namespace Formatting { - TextStyle() : - mColour(0,0,0) - , mFont("Default") - , mTextSize(16) - , mTextAlign(MyGUI::Align::Left | MyGUI::Align::Top) + struct TextStyle { - } + TextStyle() : + mColour(0,0,0) + , mFont("Default") + , mTextSize(16) + , mTextAlign(MyGUI::Align::Left | MyGUI::Align::Top) + { + } - MyGUI::Colour mColour; - std::string mFont; - int mTextSize; - MyGUI::Align mTextAlign; - }; + MyGUI::Colour mColour; + std::string mFont; + int mTextSize; + MyGUI::Align mTextAlign; + }; - /// \brief utilities for parsing book/scroll text as mygui widgets - class BookTextParser - { - public: - /** - * Parse markup as MyGUI widgets - * @param markup to parse - * @param parent for the created widgets - * @param maximum width - * @return size of the created widgets - */ - MyGUI::IntSize parsePage(std::string text, MyGUI::Widget* parent, const int width); - - /** - * Parse markup as MyGUI widgets - * @param markup to parse - * @param parent for the created widgets - * @param maximum width - * @return size of the created widgets - */ - MyGUI::IntSize parseScroll(std::string text, MyGUI::Widget* parent, const int width); + class Paginator + { + public: + typedef std::pair Page; + typedef std::vector Pages; - /** - * Split the specified text into pieces that fit in the area specified by width and height parameters - */ - std::vector split(std::string text, const int width, const int height); + Paginator(int pageWidth, int pageHeight) + : mStartTop(0), mCurrentTop(0), + mPageWidth(pageWidth), mPageHeight(pageHeight) + { + } - protected: - float widthForCharGlyph(unsigned unicodeChar) const; - float currentFontHeight() const; - void parseSubText(std::string text); + int getStartTop() const { return mStartTop; } + int getCurrentTop() const { return mCurrentTop; } + int getPageWidth() const { return mPageWidth; } + int getPageHeight() const { return mPageHeight; } + Pages getPages() const { return mPages; } - void parseImage(std::string tag, bool createWidget=true); - void parseDiv(std::string tag); - void parseFont(std::string tag); - private: - MyGUI::Widget* mParent; - int mWidth; // maximum width - int mHeight; // current height - TextStyle mTextStyle; - }; + void setStartTop(int top) { mStartTop = top; } + void setCurrentTop(int top) { mCurrentTop = top; } + void modifyStartTop(int mod) { mStartTop += mod; } + void modifyCurrentTop(int mod) { mCurrentTop += mod; } + + Paginator & operator<<(const Page & page) + { + mPages.push_back(page); + return *this; + } + + private: + int mStartTop, mCurrentTop; + int mPageWidth, mPageHeight; + Pages mPages; + }; + + /// \brief utilities for parsing book/scroll text as mygui widgets + class BookFormatter + { + public: + Paginator::Pages markupToWidget(MyGUI::Widget * parent, std::string utf8Text, const int pageWidth, const int pageHeight); + Paginator::Pages markupToWidget(MyGUI::Widget * parent, std::string utf8Text); + + protected: + void parseDiv(std::string tag); + void parseFont(std::string tag); + private: + TextStyle mTextStyle; + }; + + class GraphicElement + { + public: + GraphicElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style); + virtual int getHeight() = 0; + virtual void paginate(); + virtual int pageSplit(); + + protected: + int currentFontHeight() const; + float widthForCharGlyph(MyGUI::Char unicodeChar) const; + + MyGUI::Widget * mParent; + Paginator & mPaginator; + TextStyle mStyle; + }; + + class TextElement : public GraphicElement + { + public: + TextElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & text); + virtual int getHeight(); + virtual int pageSplit(); + private: + MyGUI::EditBox * mEditBox; + }; + + class ImageElement : public GraphicElement + { + public: + ImageElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & tag); + virtual int getHeight(); + virtual int pageSplit(); + + private: + int mImageHeight; + MyGUI::ImageBox * mImageBox; + }; + } } #endif diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 7530d7a9e..038d91ca9 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -54,8 +54,9 @@ namespace MWGui MWWorld::LiveCellRef *ref = mScroll.get(); - BookTextParser parser; - MyGUI::IntSize size = parser.parseScroll(ref->mBase->mText, mTextView, 390); + Formatting::BookFormatter formatter; + formatter.markupToWidget(mTextView, ref->mBase->mText, 390, mTextView->getHeight()); + MyGUI::IntSize size = mTextView->getChildAt(0)->getSize(); // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden mTextView->setVisibleVScroll(false); diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index 9f30bf253..f91cd696e 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -41,8 +41,8 @@ - - + + From 84d27d55e22fa2df43c70b9393e0f4ab0cca53f7 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Sat, 20 Sep 2014 00:51:46 +0200 Subject: [PATCH 2/8] Remove redundant includes --- apps/openmw/mwgui/formatting.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 25091def9..67ef59236 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -7,10 +7,7 @@ #include #include -#include #include -#include -#include #include #include From c362ec0f95ebacd7160789b3adb9630724261b20 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Sun, 21 Sep 2014 19:47:52 +0200 Subject: [PATCH 3/8] Book formatting: Handle line endings as per vanilla, fix tall images causing infinite loop, cleanup --- apps/openmw/mwgui/formatting.cpp | 322 +++++++++++++++++++++---------- apps/openmw/mwgui/formatting.hpp | 58 +++++- files/mygui/openmw_book.layout | 4 +- 3 files changed, 266 insertions(+), 118 deletions(-) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 67ef59236..a7aee1bc9 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "../mwscript/interpretercontext.hpp" @@ -14,39 +15,167 @@ #include -namespace -{ - Ogre::UTFString::unicode_char unicodeCharFromChar(char ch) - { - std::string s; - s += ch; - Ogre::UTFString string(s); - return string.getChar(0); - } -} - namespace MWGui { namespace Formatting { - Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, std::string utf8Text, const int pageWidth, const int pageHeight) + /* BookTextParser */ + BookTextParser::BookTextParser(const std::string & text) + : mIndex(-1), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true) { - using Ogre::UTFString; - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); + mText = Interpreter::fixDefinesBook(mText, interpreterContext); - boost::algorithm::replace_all(utf8Text, "\n", ""); - boost::algorithm::replace_all(utf8Text, "\r", ""); - boost::algorithm::replace_all(utf8Text, "
", "\n"); - boost::algorithm::replace_all(utf8Text, "

", "\n\n"); + boost::algorithm::replace_all(mText, "\r", ""); - UTFString text(utf8Text); - UTFString plainText; + registerTag("br", Event_BrTag); + registerTag("p", Event_PTag); + registerTag("img", Event_ImgTag); + registerTag("div", Event_DivTag); + registerTag("font", Event_FontTag); + } - const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); - const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); + void BookTextParser::registerTag(const std::string & tag, BookTextParser::Events type) + { + mTagTypes[tag] = type; + } + std::string BookTextParser::getReadyText() + { + return mReadyText; + } + + BookTextParser::Events BookTextParser::next() + { + while (1) + { + ++mIndex; + if (mIndex >= mText.size()) + return Event_EOF; + + char ch = mText[mIndex]; + if (ch == '<') + { + const size_t tagStart = mIndex + 1; + const size_t tagEnd = mText.find('>', tagStart); + if (tagEnd == std::string::npos) + throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + parseTag(mText.substr(tagStart, tagEnd - tagStart)); + mIndex = tagEnd; + + if (mTagTypes.find(mTag) != mTagTypes.end()) + { + Events type = mTagTypes.at(mTag); + + if (type == Event_BrTag || type == Event_PTag) + { + if (!mIgnoreNewlineTags) + { + if (type == Event_BrTag) + mBuffer.push_back('\n'); + else + { + mBuffer.append("\n\n"); + } + } + mIgnoreLineEndings = true; + } + else + flushBuffer(); + + if (type == Event_ImgTag) + { + mIgnoreLineEndings = false; + mIgnoreNewlineTags = false; + } + + return type; + } + } + else + { + if (!mIgnoreLineEndings || ch != '\n') + { + mBuffer.push_back(ch); + mIgnoreLineEndings = false; + mIgnoreNewlineTags = false; + } + } + + if (mIndex == mText.size() - 1) + { + flushBuffer(); + return Event_LastText; + } + } + } + + void BookTextParser::flushBuffer() + { + mReadyText = mBuffer; + mBuffer.clear(); + } + + const BookTextParser::Attributes & BookTextParser::getAttributes() const + { + return mAttributes; + } + + void BookTextParser::parseTag(std::string tag) + { + size_t tagNameEndPos = tag.find(' '); + mTag = tag.substr(0, tagNameEndPos); + Misc::StringUtils::toLower(mTag); + mAttributes.clear(); + if (tagNameEndPos == std::string::npos) + return; + tag.erase(0, tagNameEndPos+1); + + while (!tag.empty()) + { + int sepPos = tag.find('='); + if (sepPos == std::string::npos) + return; + + std::string key = tag.substr(0, sepPos); + Misc::StringUtils::toLower(key); + tag.erase(0, sepPos+1); + + std::string value; + + if (tag.empty()) + return; + + if (tag[0] == '"') + { + size_t quoteEndPos = tag.find('"', 1); + if (quoteEndPos == std::string::npos) + throw std::runtime_error("BookTextParser Error: Missing end quote in tag"); + value = tag.substr(1, quoteEndPos-1); + tag.erase(0, quoteEndPos+2); + } + else + { + size_t valEndPos = tag.find(' '); + if (valEndPos == std::string::npos) + { + value = tag; + tag.erase(); + } + else + { + value = tag.substr(0, valEndPos); + tag.erase(0, valEndPos+1); + } + } + + mAttributes[key] = value; + } + } + + /* BookFormatter */ + Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, const std::string & markup, const int pageWidth, const int pageHeight) + { Paginator pag(pageWidth, pageHeight); while (parent->getChildCount()) @@ -57,55 +186,49 @@ namespace MWGui MyGUI::Widget * paper = parent->createWidget("Widget", MyGUI::IntCoord(0, 0, pag.getPageWidth(), pag.getPageHeight()), MyGUI::Align::Left | MyGUI::Align::Top); paper->setNeedMouseFocus(false); - bool ignoreNewlines = true; - for (size_t index = 0; index < text.size(); ++index) + BookTextParser parser(markup); + BookTextParser::Events event; + while ((event = parser.next()) != BookTextParser::Event_EOF) { - const UTFString::unicode_char ch = text.getChar(index); - if (!plainText.empty() && (ch == LEFT_ANGLE || index == text.size() - 1)) + if (event == BookTextParser::Event_BrTag || event == BookTextParser::Event_PTag) + continue; + + std::string plainText = parser.getReadyText(); + if (!plainText.empty()) { // if there's a newline at the end of the box caption, remove it - if (plainText[plainText.size()-1] == NEWLINE) + if (plainText[plainText.size()-1] == '\n') plainText.erase(plainText.end()-1); - TextElement elem(paper, pag, mTextStyle, plainText.asUTF8()); + TextElement elem(paper, pag, mTextStyle, plainText); elem.paginate(); - - plainText.clear(); } - if (ch == LEFT_ANGLE) + switch (event) { - const size_t tagStart = index + 1; - const size_t tagEnd = text.find('>', tagStart); - if (tagEnd == UTFString::npos) - throw std::runtime_error("BookTextParser Error: Tag is not terminated"); - const std::string tag = text.substr(tagStart, tagEnd - tagStart).asUTF8(); - - if (boost::algorithm::starts_with(tag, "IMG")) + case BookTextParser::Event_ImgTag: { - ImageElement elem(paper, pag, mTextStyle, tag); + const BookTextParser::Attributes & attr = parser.getAttributes(); + + if (attr.find("src") == attr.end() || attr.find("width") == attr.end() || attr.find("height") == attr.end()) + continue; + + std::string src = attr.at("src"); + int width = boost::lexical_cast(attr.at("width")); + int height = boost::lexical_cast(attr.at("height")); + + ImageElement elem(paper, pag, src, width, height); elem.paginate(); - - ignoreNewlines = false; - } - else if (boost::algorithm::starts_with(tag, "FONT")) - { - parseFont(tag); - } - else if (boost::algorithm::starts_with(tag, "DIV")) - { - parseDiv(tag); - } - - index = tagEnd; - } - else - { - if (!ignoreNewlines || ch != NEWLINE) - { - plainText.push_back(ch); - ignoreNewlines = false; + break; } + case BookTextParser::Event_FontTag: + handleFont(parser.getAttributes()); + break; + case BookTextParser::Event_DivTag: + handleDiv(parser.getAttributes()); + break; + default: + break; } } @@ -117,33 +240,31 @@ namespace MWGui return pag.getPages(); } - Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, std::string utf8Text) + Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget * parent, const std::string & markup) { - return markupToWidget(parent, utf8Text, parent->getWidth(), parent->getHeight()); + return markupToWidget(parent, markup, parent->getWidth(), parent->getHeight()); } - void BookFormatter::parseDiv(std::string tag) + void BookFormatter::handleDiv(const BookTextParser::Attributes & attr) { - if (tag.find("ALIGN=") == std::string::npos) + if (attr.find("align") == attr.end()) return; - int align_start = tag.find("ALIGN=")+7; - std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start); - if (align == "CENTER") + std::string align = attr.at("align"); + + if (Misc::StringUtils::ciEqual(align, "center")) mTextStyle.mTextAlign = MyGUI::Align::HCenter; - else if (align == "LEFT") + else if (Misc::StringUtils::ciEqual(align, "left")) mTextStyle.mTextAlign = MyGUI::Align::Left; } - void BookFormatter::parseFont(std::string tag) + void BookFormatter::handleFont(const BookTextParser::Attributes & attr) { - if (tag.find("COLOR=") != std::string::npos) + if (attr.find("color") != attr.end()) { - int color_start = tag.find("COLOR=")+7; - int color; std::stringstream ss; - ss << tag.substr(color_start, tag.find('"', color_start)-color_start); + ss << attr.at("color"); ss >> std::hex >> color; mTextStyle.mColour = MyGUI::Colour( @@ -151,23 +272,22 @@ namespace MWGui (color>>8 & 0xFF) / 255.f, (color & 0xFF) / 255.f); } - if (tag.find("FACE=") != std::string::npos) + if (attr.find("face") != attr.end()) { - int face_start = tag.find("FACE=")+6; - std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); + std::string face = attr.at("face"); if (face != "Magic Cards") mTextStyle.mFont = face; } - if (tag.find("SIZE=") != std::string::npos) + if (attr.find("size") != attr.end()) { /// \todo } } /* GraphicElement */ - GraphicElement::GraphicElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style) - : mParent(parent), mPaginator(pag), mStyle(style) + GraphicElement::GraphicElement(MyGUI::Widget * parent, Paginator & pag) + : mParent(parent), mPaginator(pag) { } @@ -189,22 +309,11 @@ namespace MWGui return mPaginator.getStartTop() + mPaginator.getPageHeight(); } - int GraphicElement::currentFontHeight() const - { - std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont); - return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); - } - - float GraphicElement::widthForCharGlyph(MyGUI::Char unicodeChar) const - { - std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont); - return MyGUI::FontManager::getInstance().getByName(fontName) - ->getGlyphInfo(unicodeChar)->width; - } - /* TextElement */ - TextElement::TextElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & text) - : GraphicElement(parent, pag, style) + TextElement::TextElement(MyGUI::Widget * parent, Paginator & pag, + const TextStyle & style, const std::string & text) + : GraphicElement(parent, pag), + mStyle(style) { MyGUI::EditBox* box = parent->createWidget("NormalText", MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top, @@ -222,6 +331,12 @@ namespace MWGui mEditBox = box; } + int TextElement::currentFontHeight() const + { + std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); + } + int TextElement::getHeight() { return mEditBox->getTextSize().height; @@ -250,19 +365,11 @@ namespace MWGui } /* ImageElement */ - ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & tag) - : GraphicElement(parent, pag, style), - mImageHeight(0) + ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag, + const std::string & src, int width, int height) + : GraphicElement(parent, pag), + mImageHeight(height) { - int src_start = tag.find("SRC=")+5; - std::string src = tag.substr(src_start, tag.find('"', src_start)-src_start); - - int width_start = tag.find("WIDTH=")+7; - int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start)); - - int height_start = tag.find("HEIGHT=")+8; - mImageHeight = boost::lexical_cast(tag.substr(height_start, tag.find('"', height_start)-height_start)); - mImageBox = parent->createWidget ("ImageBox", MyGUI::IntCoord(0, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top, parent->getName() + boost::lexical_cast(parent->getChildCount())); @@ -279,6 +386,9 @@ namespace MWGui int ImageElement::pageSplit() { + // if the image is larger than the page, fall back to the default pageSplit implementation + if (mImageHeight > mPaginator.getPageHeight()) + return GraphicElement::pageSplit(); return mPaginator.getCurrentTop(); } } diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index b0a8bce0f..545c1f31d 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -2,6 +2,7 @@ #define MWGUI_FORMATTING_H #include +#include namespace MWGui { @@ -23,6 +24,44 @@ namespace MWGui MyGUI::Align mTextAlign; }; + class BookTextParser + { + public: + typedef std::map Attributes; + enum Events + { + Event_None = -2, + Event_EOF = -1, + Event_LastText = 0, + Event_BrTag, + Event_PTag, + Event_ImgTag, + Event_DivTag, + Event_FontTag + }; + + BookTextParser(const std::string & text); + void registerTag(const std::string & tag, Events type); + std::string getReadyText(); + + Events next(); + void flushBuffer(); + const Attributes & getAttributes() const; + void parseTag(std::string tag); + + private: + int mIndex; + std::string mText; + std::string mReadyText; + + bool mIgnoreNewlineTags; + bool mIgnoreLineEndings; + Attributes mAttributes; + std::string mTag; + std::map mTagTypes; + std::string mBuffer; + }; + class Paginator { public: @@ -62,12 +101,13 @@ namespace MWGui class BookFormatter { public: - Paginator::Pages markupToWidget(MyGUI::Widget * parent, std::string utf8Text, const int pageWidth, const int pageHeight); - Paginator::Pages markupToWidget(MyGUI::Widget * parent, std::string utf8Text); + Paginator::Pages markupToWidget(MyGUI::Widget * parent, const std::string & markup, const int pageWidth, const int pageHeight); + Paginator::Pages markupToWidget(MyGUI::Widget * parent, const std::string & markup); protected: - void parseDiv(std::string tag); - void parseFont(std::string tag); + void handleImg(const BookTextParser::Attributes & attr); + void handleDiv(const BookTextParser::Attributes & attr); + void handleFont(const BookTextParser::Attributes & attr); private: TextStyle mTextStyle; }; @@ -75,18 +115,14 @@ namespace MWGui class GraphicElement { public: - GraphicElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style); + GraphicElement(MyGUI::Widget * parent, Paginator & pag); virtual int getHeight() = 0; virtual void paginate(); virtual int pageSplit(); protected: - int currentFontHeight() const; - float widthForCharGlyph(MyGUI::Char unicodeChar) const; - MyGUI::Widget * mParent; Paginator & mPaginator; - TextStyle mStyle; }; class TextElement : public GraphicElement @@ -96,13 +132,15 @@ namespace MWGui virtual int getHeight(); virtual int pageSplit(); private: + int currentFontHeight() const; + TextStyle mStyle; MyGUI::EditBox * mEditBox; }; class ImageElement : public GraphicElement { public: - ImageElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & tag); + ImageElement(MyGUI::Widget * parent, Paginator & pag, const std::string & src, int width, int height); virtual int getHeight(); virtual int pageSplit(); diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index f91cd696e..3b1cfea64 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -41,8 +41,8 @@ - - + + From 51cd2678aeb094b1b50d417b8d189b0331b4ab56 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Sun, 21 Sep 2014 22:25:54 +0200 Subject: [PATCH 4/8] Book formatter: Fix last block of text not displaying --- apps/openmw/mwgui/formatting.cpp | 18 +++++++++++------- apps/openmw/mwgui/formatting.hpp | 1 - 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index a7aee1bc9..c8b6f3983 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -50,8 +50,12 @@ namespace MWGui while (1) { ++mIndex; + if (mIndex >= mText.size()) + { + flushBuffer(); return Event_EOF; + } char ch = mText[mIndex]; if (ch == '<') @@ -102,11 +106,7 @@ namespace MWGui } } - if (mIndex == mText.size() - 1) - { - flushBuffer(); - return Event_LastText; - } + } } @@ -133,7 +133,7 @@ namespace MWGui while (!tag.empty()) { - int sepPos = tag.find('='); + size_t sepPos = tag.find('='); if (sepPos == std::string::npos) return; @@ -188,8 +188,9 @@ namespace MWGui BookTextParser parser(markup); BookTextParser::Events event; - while ((event = parser.next()) != BookTextParser::Event_EOF) + for (;;) { + event = parser.next(); if (event == BookTextParser::Event_BrTag || event == BookTextParser::Event_PTag) continue; @@ -204,6 +205,9 @@ namespace MWGui elem.paginate(); } + if (event == BookTextParser::Event_EOF) + break; + switch (event) { case BookTextParser::Event_ImgTag: diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index 545c1f31d..71e398f7f 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -32,7 +32,6 @@ namespace MWGui { Event_None = -2, Event_EOF = -1, - Event_LastText = 0, Event_BrTag, Event_PTag, Event_ImgTag, From 4b1df64fba0ec3a79c2773b231f014035f2f3c82 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Mon, 22 Sep 2014 16:28:17 +0200 Subject: [PATCH 5/8] Book parser: Fix comparison between signed and unsigned integer expressions --- apps/openmw/mwgui/formatting.cpp | 18 +++++++----------- apps/openmw/mwgui/formatting.hpp | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index c8b6f3983..f7188c8b6 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -21,7 +21,7 @@ namespace MWGui { /* BookTextParser */ BookTextParser::BookTextParser(const std::string & text) - : mIndex(-1), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true) + : mIndex(0), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true) { MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor mText = Interpreter::fixDefinesBook(mText, interpreterContext); @@ -47,16 +47,8 @@ namespace MWGui BookTextParser::Events BookTextParser::next() { - while (1) + while (mIndex < mText.size()) { - ++mIndex; - - if (mIndex >= mText.size()) - { - flushBuffer(); - return Event_EOF; - } - char ch = mText[mIndex]; if (ch == '<') { @@ -93,6 +85,7 @@ namespace MWGui mIgnoreNewlineTags = false; } + ++mIndex; return type; } } @@ -106,8 +99,11 @@ namespace MWGui } } - + ++mIndex; } + + flushBuffer(); + return Event_EOF; } void BookTextParser::flushBuffer() diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index 71e398f7f..f119422c9 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -49,7 +49,7 @@ namespace MWGui void parseTag(std::string tag); private: - int mIndex; + size_t mIndex; std::string mText; std::string mReadyText; From f87d7c3fce95b33dbfd0cfad155053fe76fc4694 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Mon, 22 Sep 2014 21:25:41 +0200 Subject: [PATCH 6/8] Book formatter: Do not insert last page if it's empty, code cleanup --- apps/openmw/mwgui/formatting.cpp | 5 +++-- apps/openmw/mwgui/formatting.hpp | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index f7188c8b6..9c918e626 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -233,7 +233,8 @@ namespace MWGui } // insert last page - pag << Paginator::Page(pag.getStartTop(), pag.getStartTop() + pag.getPageHeight()); + if (pag.getStartTop() != pag.getCurrentTop()) + pag << Paginator::Page(pag.getStartTop(), pag.getStartTop() + pag.getPageHeight()); paper->setSize(paper->getWidth(), pag.getCurrentTop()); @@ -301,7 +302,7 @@ namespace MWGui mPaginator.setStartTop(newStartTop); } - mPaginator.modifyCurrentTop(getHeight()); + mPaginator.setCurrentTop(newTop); } int GraphicElement::pageSplit() diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index f119422c9..cf55b36fb 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -81,8 +81,6 @@ namespace MWGui void setStartTop(int top) { mStartTop = top; } void setCurrentTop(int top) { mCurrentTop = top; } - void modifyStartTop(int mod) { mStartTop += mod; } - void modifyCurrentTop(int mod) { mCurrentTop += mod; } Paginator & operator<<(const Page & page) { From 608e1518a5d078a08e6137b8348a527c5d61330e Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Mon, 22 Sep 2014 21:47:21 +0200 Subject: [PATCH 7/8] Hack to prevent newlines at the end of books possibly creating unnecessary pages for mygui versions lower than 3.2.2 --- apps/openmw/mwgui/formatting.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 9c918e626..7de4950ee 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -197,6 +197,18 @@ namespace MWGui if (plainText[plainText.size()-1] == '\n') plainText.erase(plainText.end()-1); +#if (MYGUI_VERSION < MYGUI_DEFINE_VERSION(3, 2, 2)) + // splitting won't be fully functional until 3.2.2 (see TextElement::pageSplit()) + // hack: prevent newlines at the end of the book possibly creating unnecessary pages + if (event == BookTextParser::Event_EOF) + { + while (plainText[plainText.size()-1] == '\n') + { + plainText.erase(plainText.end()-1); + } + } +#endif + TextElement elem(paper, pag, mTextStyle, plainText); elem.paginate(); } From 22c71cec18c5e73e3cd90afaec6bd625fd81cd18 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Mon, 22 Sep 2014 23:17:44 +0200 Subject: [PATCH 8/8] Check for text size --- apps/openmw/mwgui/formatting.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 7de4950ee..d838ae8af 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -202,10 +202,8 @@ namespace MWGui // hack: prevent newlines at the end of the book possibly creating unnecessary pages if (event == BookTextParser::Event_EOF) { - while (plainText[plainText.size()-1] == '\n') - { + while (plainText.size() && plainText[plainText.size()-1] == '\n') plainText.erase(plainText.end()-1); - } } #endif