#include "bookpage.hpp" #include "MyGUI_FontManager.h" #include "MyGUI_RenderItem.h" #include "MyGUI_RenderManager.h" #include "MyGUI_TextureUtility.h" #include "MyGUI_FactoryManager.h" #include #include #include #include #include namespace MWGui { struct TypesetBookImpl; struct PageDisplay; struct BookPageImpl; } using namespace MyGUI; using namespace MWGui; static bool ucs_space (int code_point); static bool ucs_line_break (int code_point); static bool ucs_breaking_space (int code_point); struct BookTypesetter::Style { virtual ~Style () {} }; struct MWGui::TypesetBookImpl : TypesetBook { typedef std::vector content; typedef std::list contents; typedef utf8_stream::point utf8_point; typedef std::pair range; struct StyleImpl : BookTypesetter::Style { IFont* mFont; Colour mHotColour; Colour mActiveColour; Colour mNormalColour; interactive_id mInteractiveId; bool match (IFont* tstFont, Colour tstHotColour, Colour tstActiveColour, Colour tstNormalColour, intptr_t tstInteractiveId) { return (mFont == tstFont) && partal_match (tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId); } bool match (char const * tstFont, Colour tstHotColour, Colour tstActiveColour, Colour tstNormalColour, intptr_t tstInteractiveId) { return (mFont->getResourceName () == tstFont) && partal_match (tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId); } bool partal_match (Colour tstHotColour, Colour tstActiveColour, Colour tstNormalColour, intptr_t tstInteractiveId) { return (mHotColour == tstHotColour ) && (mActiveColour == tstActiveColour ) && (mNormalColour == tstNormalColour ) && (mInteractiveId == tstInteractiveId ) ; } }; typedef std::list styles; struct Run { StyleImpl* mStyle; range mRange; int mLeft, mRight; int mPrintableChars; }; typedef std::vector runs; struct Line { runs mRuns; IntRect mRect; }; typedef std::vector lines; struct Section { lines mLines; IntRect mRect; }; typedef std::vector
sections; typedef std::pair page; typedef std::vector pages; pages mPages; sections mSections; contents mContents; styles mStyles; IntRect mRect; virtual ~TypesetBookImpl () {} range addContent (BookTypesetter::utf8_span Text) { contents::iterator i = mContents.insert (mContents.end (), content (Text.first, Text.second)); if (i->size () == 0) return range (utf8_point (NULL), utf8_point (NULL)); utf8_point begin = &i->front (); utf8_point end = &i->front () + i->size (); return range (begin, end); } size_t pageCount () const { return mPages.size (); } std::pair getSize () const { return std::make_pair (mRect.width (), mRect.height ()); } template void visitRuns (int top, int bottom, IFont* Font, visitor const & Visitor) const { for (sections::const_iterator i = mSections.begin (); i != mSections.end (); ++i) { if (top >= mRect.bottom || bottom <= i->mRect.top) continue; for (lines::const_iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) { if (top >= j->mRect.bottom || bottom <= j->mRect.top) continue; for (runs::const_iterator k = j->mRuns.begin (); k != j->mRuns.end (); ++k) if (!Font || k->mStyle->mFont == Font) Visitor (*i, *j, *k); } } } template void visitRuns (int top, int bottom, visitor const & Visitor) const { visitRuns (top, bottom, NULL, Visitor); } StyleImpl * hitTest (int left, int top) const { for (sections::const_iterator i = mSections.begin (); i != mSections.end (); ++i) { if (top < i->mRect.top || top >= i->mRect.bottom) continue; int left1 = left - i->mRect.left; for (lines::const_iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) { if (top < j->mRect.top || top >= j->mRect.bottom) continue; int left2 = left1 - j->mRect.left; for (runs::const_iterator k = j->mRuns.begin (); k != j->mRuns.end (); ++k) { if (left2 < k->mLeft || left2 >= k->mRight) continue; return k->mStyle; } } } return nullptr; } IFont* affectedFont (StyleImpl* Style) { for (styles::iterator i = mStyles.begin (); i != mStyles.end (); ++i) if (&*i == Style) return i->mFont; return NULL; } struct Typesetter; }; struct TypesetBookImpl::Typesetter : BookTypesetter { typedef TypesetBookImpl book; typedef boost::shared_ptr book_ptr; int mPageWidth; int mPageHeight; book_ptr mBook; Section * mSection; Line * mLine; Run * mRun; std::vector mSectionAlignment; book::content const * mCurrentContent; alignment mCurrentAlignment; Typesetter (size_t Width, size_t Height) : mPageWidth (Width), mPageHeight(Height), mSection (NULL), mLine (NULL), mRun (NULL), mCurrentAlignment (alignLeft), mCurrentContent (NULL) { mBook = boost::make_shared (); } virtual ~Typesetter () { } Style * createStyle (char const * FontName, Colour FontColour) { for (styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) if (i->match (FontName, FontColour, FontColour, FontColour, 0)) return &*i; StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); style.mFont = FontManager::getInstance().getByName(FontName); style.mHotColour = FontColour; style.mActiveColour = FontColour; style.mNormalColour = FontColour; style.mInteractiveId = 0; return &style; } Style* createHotStyle (Style* _BaseStyle, coulour NormalColour, coulour HoverColour, coulour ActiveColour, interactive_id Id, bool Unique) { StyleImpl* BaseStyle = dynamic_cast (_BaseStyle); if (!Unique) for (styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) if (i->match (BaseStyle->mFont, HoverColour, ActiveColour, NormalColour, Id)) return &*i; StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); style.mFont = BaseStyle->mFont; style.mHotColour = HoverColour; style.mActiveColour = ActiveColour; style.mNormalColour = NormalColour; style.mInteractiveId = Id; return &style; } void write (Style * _Style, utf8_span Text) { range text = mBook->addContent (Text); write_impl (dynamic_cast (_Style), text.first, text.second); } intptr_t add_content (utf8_span Text, bool Select) { contents::iterator i = mBook->mContents.insert (mBook->mContents.end (), content (Text.first, Text.second)); if (Select) mCurrentContent = &(*i); return reinterpret_cast (&(*i)); } void select_content (intptr_t contentHandle) { mCurrentContent = reinterpret_cast (contentHandle); } void write (Style * style, size_t begin, size_t end) { assert (mCurrentContent != NULL); assert (end <= mCurrentContent->size ()); assert (begin <= mCurrentContent->size ()); utf8_point begin_ = &mCurrentContent->front () + begin; utf8_point end_ = &mCurrentContent->front () + end ; write_impl (dynamic_cast (style), begin_, end_); } void lineBreak (float margin) { assert (margin == 0); //TODO: figure out proper behavior here... mRun = NULL; mLine = NULL; } void sectionBreak (float margin) { if (mBook->mSections.size () > 0) { mRun = NULL; mLine = NULL; mSection = NULL; if (mBook->mRect.bottom < (mBook->mSections.back ().mRect.bottom + margin)) mBook->mRect.bottom = (mBook->mSections.back ().mRect.bottom + margin); } } void setSectionAlignment (alignment sectionAlignment) { if (mSection != NULL) mSectionAlignment.back () = sectionAlignment; mCurrentAlignment = sectionAlignment; } TypesetBook::ptr complete () { int curPageStart = 0; int curPageStop = 0; std::vector ::iterator sa = mSectionAlignment.begin (); for (sections::iterator i = mBook->mSections.begin (); i != mBook->mSections.end (); ++i, ++sa) { // apply alignment to individual lines... for (lines::iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) { int width = j->mRect.width (); int excess = mPageWidth - width; switch (*sa) { default: case alignLeft: j->mRect.left = 0; break; case alignCenter: j->mRect.left = excess/2; break; case alignRight: j->mRect.left = excess; break; } j->mRect.right = j->mRect.left + width; } if (curPageStop == curPageStart) { curPageStart = i->mRect.top; curPageStop = i->mRect.top; } int spaceLeft = mPageHeight - (curPageStop - curPageStart); int sectionHeight = i->mRect.height (); if (sectionHeight <= mPageHeight) { if (sectionHeight > spaceLeft) { assert (curPageStart != curPageStop); mBook->mPages.push_back (page (curPageStart, curPageStop)); curPageStart = i->mRect.top; curPageStop = i->mRect.bottom; } else curPageStop = i->mRect.bottom; } else { //split section } } if (curPageStart != curPageStop) mBook->mPages.push_back (page (curPageStart, curPageStop)); return mBook; } void write_impl (StyleImpl * Style, utf8_stream::point _begin, utf8_stream::point _end) { int line_height = Style->mFont->getDefaultHeight (); utf8_stream stream (_begin, _end); while (!stream.eof ()) { if (ucs_line_break (stream.peek ())) { stream.consume (); mLine = NULL, mRun = NULL; continue; } int word_width = 0; int word_height = 0; int space_width = 0; int character_count = 0; utf8_stream::point lead = stream.current (); while (!stream.eof () && !ucs_line_break (stream.peek ()) && ucs_breaking_space (stream.peek ())) { GlyphInfo* gi = Style->mFont->getGlyphInfo (stream.peek ()); space_width += gi->advance; stream.consume (); } utf8_stream::point origin = stream.current (); while (!stream.eof () && !ucs_line_break (stream.peek ()) && !ucs_breaking_space (stream.peek ())) { GlyphInfo* gi = Style->mFont->getGlyphInfo (stream.peek ()); word_width += gi->advance + gi->bearingX; word_height = line_height; ++character_count; stream.consume (); } utf8_stream::point extent = stream.current (); if (lead == extent) break; int left = mLine ? mLine->mRect.right : 0; if (left + space_width + word_width > mPageWidth) { mLine = NULL, mRun = NULL; append_run (Style, origin, extent, extent - origin, word_width, mBook->mRect.bottom + word_height); } else { int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; append_run (Style, lead, extent, extent - origin, left + space_width + word_width, top + word_height); } } } void append_run (StyleImpl * style, utf8_stream::point begin, utf8_stream::point end, int pc, int right, int bottom) { if (mSection == NULL) { mBook->mSections.push_back (Section ()); mSection = &mBook->mSections.back (); mSection->mRect = IntRect (0, mBook->mRect.bottom, 0, mBook->mRect.bottom); mSectionAlignment.push_back (mCurrentAlignment); } if (mLine == NULL) { mSection->mLines.push_back (Line ()); mLine = &mSection->mLines.back (); mLine->mRect = IntRect (0, mSection->mRect.bottom, 0, mBook->mRect.bottom); } if (mBook->mRect.right < right) mBook->mRect.right = right; if (mBook->mRect.bottom < bottom) mBook->mRect.bottom = bottom; if (mSection->mRect.right < right) mSection->mRect.right = right; if (mSection->mRect.bottom < bottom) mSection->mRect.bottom = bottom; if (mLine->mRect.right < right) mLine->mRect.right = right; if (mLine->mRect.bottom < bottom) mLine->mRect.bottom = bottom; if (mRun == NULL || mRun->mStyle != style || mRun->mRange.second != begin) { int left = mRun ? mRun->mRight : mLine->mRect.left; mLine->mRuns.push_back (Run ()); mRun = &mLine->mRuns.back (); mRun->mStyle = style; mRun->mLeft = left; mRun->mRight = right; mRun->mRange.first = begin; mRun->mRange.second = end; mRun->mPrintableChars = pc; //Run->Locale = Locale; } else { mRun->mRight = right; mRun->mRange.second = end; mRun->mPrintableChars += pc; } } }; BookTypesetter::ptr BookTypesetter::create (int pageWidth, int pageHeight) { return boost::make_shared (pageWidth, pageHeight); } namespace { struct RenderXform { public: float clipTop; float clipLeft; float clipRight; float clipBottom; float absoluteLeft; float absoluteTop; float leftOffset; float topOffset; float pixScaleX; float pixScaleY; float hOffset; float vOffset; RenderXform (ICroppedRectangle* croppedParent, RenderTargetInfo const & renderTargetInfo) { clipTop = croppedParent->_getMarginTop (); clipLeft = croppedParent->_getMarginLeft (); clipRight = croppedParent->getWidth () - croppedParent->_getMarginRight (); clipBottom = croppedParent->getHeight () - croppedParent->_getMarginBottom (); absoluteLeft = croppedParent->getAbsoluteLeft(); absoluteTop = croppedParent->getAbsoluteTop(); leftOffset = renderTargetInfo.leftOffset; topOffset = renderTargetInfo.topOffset; pixScaleX = renderTargetInfo.pixScaleX; pixScaleY = renderTargetInfo.pixScaleY; hOffset = renderTargetInfo.hOffset; vOffset = renderTargetInfo.vOffset; } bool clip (FloatRect & vr, FloatRect & tr) { if (vr.bottom <= clipTop || vr.right <= clipLeft || vr.left >= clipRight || vr.top >= clipBottom ) return false; if (vr.top < clipTop) { tr.top += tr.height () * (clipTop - vr.top) / vr.height (); vr.top = clipTop; } if (vr.left < clipLeft) { tr.left += tr.width () * (clipLeft - vr.left) / vr.width (); vr.left = clipLeft; } if (vr.right > clipRight) { tr.right -= tr.width () * (vr.right - clipRight) / vr.width (); vr.right = clipRight; } if (vr.bottom > clipBottom) { tr.bottom -= tr.height () * (vr.bottom - clipBottom) / vr.height (); vr.bottom = clipBottom; } return true; } FloatPoint operator () (FloatPoint pt) { pt.left = absoluteLeft - leftOffset + pt.left; pt.top = absoluteTop - topOffset + pt.top; pt.left = +(((pixScaleX * pt.left + hOffset) * 2.0f) - 1.0f); pt.top = -(((pixScaleY * pt.top + vOffset) * 2.0f) - 1.0f); return pt; } }; struct GlyphStream { float mZ; uint32_t mC; IFont* mFont; FloatPoint mOrigin; FloatPoint mCursor; Vertex* mVertices; RenderXform mRenderXform; MyGUI::VertexColourType mVertexColourType; GlyphStream (IFont* font, float left, float top, float Z, Vertex* vertices, RenderXform const & renderXform) : mZ(Z), mOrigin (left, top), mFont (font), mVertices (vertices), mRenderXform (renderXform) { mVertexColourType = RenderManager::getInstance().getVertexFormat(); } ~GlyphStream () { } Vertex* end () const { return mVertices; } void reset (float left, float top, Colour colour) { mC = texture_utility::toColourARGB(colour) | 0xFF000000; texture_utility::convertColour(mC, mVertexColourType); mCursor.left = mOrigin.left + left; mCursor.top = mOrigin.top + top; } void emitGlyph (wchar_t ch) { GlyphInfo* gi = mFont->getGlyphInfo (ch); FloatRect vr; vr.left = mCursor.left + gi->bearingX; vr.top = mCursor.top + gi->bearingY; vr.right = vr.left + gi->width; vr.bottom = vr.top + gi->height; FloatRect tr = gi->uvRect; if (mRenderXform.clip (vr, tr)) quad (vr, tr); mCursor.left += gi->bearingX + gi->advance; } void emitSpace (wchar_t ch) { GlyphInfo* gi = mFont->getGlyphInfo (ch); mCursor.left += gi->bearingX + gi->advance; } private: void quad (const FloatRect& vr, const FloatRect& tr) { vertex (vr.left, vr.top, tr.left, tr.top); vertex (vr.right, vr.top, tr.right, tr.top); vertex (vr.left, vr.bottom, tr.left, tr.bottom); vertex (vr.right, vr.top, tr.right, tr.top); vertex (vr.left, vr.bottom, tr.left, tr.bottom); vertex (vr.right, vr.bottom, tr.right, tr.bottom); } void vertex (float X, float Y, float U, float V) { FloatPoint pt = mRenderXform (FloatPoint (X, Y)); mVertices->x = pt.left; mVertices->y = pt.top ; mVertices->z = mZ; mVertices->u = U; mVertices->v = V; mVertices->colour = mC; ++mVertices; } }; } class MWGui::PageDisplay : public ISubWidgetText { MYGUI_RTTI_DERIVED(PageDisplay) protected: typedef TypesetBookImpl::Section Section; typedef TypesetBookImpl::Line Line; typedef TypesetBookImpl::Run Run; struct TextFormat : ISubWidget { typedef IFont* id; id mFont; int mCountVertex; ITexture* mTexture; RenderItem* mRenderItem; PageDisplay * mDisplay; TextFormat (IFont* Id, PageDisplay * Display) : mFont (Id), mTexture (NULL), mRenderItem (NULL), mDisplay (Display), mCountVertex (0) { } void createDrawItem (ILayerNode* node) { assert (mRenderItem == NULL); if (mTexture != NULL) { mRenderItem = node->addToRenderItem(mTexture, false, false); mRenderItem->addDrawItem(this, mCountVertex); } } void destroyDrawItem (ILayerNode* node) { assert (mTexture != NULL ? mRenderItem != NULL : mRenderItem == NULL); if (mTexture != NULL) { mRenderItem->removeDrawItem (this); mRenderItem = NULL; } } void doRender() { mDisplay->doRender (*this); } // this isn't really a sub-widget, its just a "drawitem" which // should have its own interface void createDrawItem(ITexture* _texture, ILayerNode* _node) {} void destroyDrawItem() {}; }; public: typedef TypesetBookImpl::StyleImpl style; typedef std::map active_text_formats; int mViewTop; int mViewBottom; style* mFocusItem; bool mItemActive; MouseButton mLastDown; boost::function mLinkClicked; boost::shared_ptr mBook; size_t mPage; ILayerNode* mNode; active_text_formats mActiveTextFormats; PageDisplay () { mPage = -1; } void dirtyFocusItem () { if (mFocusItem != 0) { IFont* Font = mBook->affectedFont (mFocusItem); active_text_formats::iterator i = mActiveTextFormats.find (Font); mNode->outOfDate (i->second->mRenderItem); } } void onMouseLostFocus () { if (!mBook) return; dirtyFocusItem (); mFocusItem = 0; mItemActive = false; } void onMouseMove (int _left, int _top) { if (!mBook) return; _left -= mCroppedParent->getAbsoluteLeft (); _top -= mCroppedParent->getAbsoluteTop (); style * Hit = mBook->hitTest (_left, mViewTop + _top); if (mLastDown == MouseButton::None) { if (Hit != mFocusItem) { dirtyFocusItem (); mFocusItem = Hit; mItemActive = false; dirtyFocusItem (); } } else if (mFocusItem != 0) { bool newItemActive = Hit == mFocusItem; if (newItemActive != mItemActive) { mItemActive = newItemActive; dirtyFocusItem (); } } } void onMouseButtonPressed (int _left, int _top, MouseButton _id) { if (!mBook) return; _left -= mCroppedParent->getAbsoluteLeft (); _top -= mCroppedParent->getAbsoluteTop (); if (mLastDown == MouseButton::None) { mFocusItem = mBook->hitTest (_left, mViewTop + _top); mItemActive = true; dirtyFocusItem (); mLastDown = _id; } } void onMouseButtonReleased(int _left, int _top, MouseButton _id) { if (!mBook) return; _left -= mCroppedParent->getAbsoluteLeft (); _top -= mCroppedParent->getAbsoluteTop (); if (mLastDown == _id) { style * mItem = mBook->hitTest (_left, mViewTop + _top); bool clicked = mFocusItem == mItem; mItemActive = false; dirtyFocusItem (); mLastDown = MouseButton::None; if (clicked && mLinkClicked && mItem && mItem->mInteractiveId != 0) mLinkClicked (mItem->mInteractiveId); } } void showPage (TypesetBook::ptr _Book, size_t newPage) { boost::shared_ptr newBook = boost::dynamic_pointer_cast (_Book); if (mBook != newBook) { mFocusItem = nullptr; mItemActive = 0; for (active_text_formats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) { if (mNode != NULL) i->second->destroyDrawItem (mNode); delete i->second; } mActiveTextFormats.clear (); if (newBook != NULL) { createActiveFormats (newBook); mBook = newBook; mPage = newPage; if (newPage < mBook->mPages.size ()) { mViewTop = mBook->mPages [newPage].first; mViewBottom = mBook->mPages [newPage].second; } else { mViewTop = 0; mViewBottom = 0; } } else { mBook.reset (); mPage = -1; mViewTop = 0; mViewBottom = 0; } } else if (mBook && mPage != newPage) { if (mNode != NULL) for (active_text_formats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate(i->second->mRenderItem); mPage = newPage; if (newPage < mBook->mPages.size ()) { mViewTop = mBook->mPages [newPage].first; mViewBottom = mBook->mPages [newPage].second; } else { mViewTop = 0; mViewBottom = 0; } } } struct createActiveFormat { PageDisplay * this_; createActiveFormat (PageDisplay * this_) : this_ (this_) {} void operator () (Section const & Section, Line const & Line, Run const & Run) const { IFont* Font = Run.mStyle->mFont; active_text_formats::iterator j = this_->mActiveTextFormats.find (Font); if (j == this_->mActiveTextFormats.end ()) { TextFormat * textFormat = new TextFormat (Font, this_); textFormat->mTexture = Font->getTextureFont (); j = this_->mActiveTextFormats.insert (std::make_pair (Font, textFormat)).first; } j->second->mCountVertex += Run.mPrintableChars * 6; } }; void createActiveFormats (boost::shared_ptr newBook) { newBook->visitRuns (0, 0x7FFFFFFF, createActiveFormat (this)); if (mNode != NULL) for (active_text_formats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->createDrawItem (mNode); } void setVisible (bool newVisible) { if (mVisible == newVisible) return; mVisible = newVisible; if (mVisible) { // reset input state mLastDown = MouseButton::None; mFocusItem = nullptr; mItemActive = 0; } if (nullptr != mNode) { for (active_text_formats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate(i->second->mRenderItem); } } void createDrawItem(ITexture* _texture, ILayerNode* _node) { //test (); mNode = _node; for (active_text_formats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->createDrawItem (_node); } struct renderRun { PageDisplay * this_; GlyphStream &glyphStream; renderRun (PageDisplay * this_, GlyphStream &glyphStream) : this_(this_), glyphStream (glyphStream) { } void operator () (Section const & Section, Line const & Line, Run const & Run) const { bool isActive = Run.mStyle->mInteractiveId && (Run.mStyle == this_->mFocusItem); Colour colour = isActive ? (this_->mItemActive ? Run.mStyle->mActiveColour: Run.mStyle->mHotColour) : Run.mStyle->mNormalColour; glyphStream.reset (Section.mRect.left + Line.mRect.left + Run.mLeft, Line.mRect.top, colour); utf8_stream stream (Run.mRange); while (!stream.eof ()) { utf8_stream::unicode_char code_point = stream.consume (); if (!ucs_space (code_point)) glyphStream.emitGlyph (code_point); else glyphStream.emitSpace (code_point); } } }; /* queue up rendering operations for this text format */ void doRender(TextFormat & textFormat) { if (!mVisible) return; Vertex* vertices = textFormat.mRenderItem->getCurrentVertexBuffer(); RenderXform renderXform (mCroppedParent, textFormat.mRenderItem->getRenderTarget()->getInfo()); GlyphStream glyphStream (textFormat.mFont, mCoord.left, mCoord.top-mViewTop, -1 /*mNode->getNodeDepth()*/, vertices, renderXform); int visit_top = (std::max) (mViewTop, mViewTop + int (renderXform.clipTop )); int visit_bottom = (std::min) (mViewBottom, mViewTop + int (renderXform.clipBottom)); mBook->visitRuns (visit_top, visit_bottom, textFormat.mFont, renderRun (this, glyphStream)); textFormat.mRenderItem->setLastVertexCount(glyphStream.end () - vertices); } // ISubWidget should not necessarily be a drawitem // in this case, it is not... void doRender() { } void _updateView () { } void _correctView() { _checkMargin (); if (mNode != NULL) for (active_text_formats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate (i->second->mRenderItem); } void destroyDrawItem() { for (active_text_formats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->destroyDrawItem (mNode); mNode = NULL; } }; class MWGui::BookPageImpl : public BookPage { MYGUI_RTTI_DERIVED(BookPage) public: void showPage (TypesetBook::ptr Book, size_t Page) { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) pd->showPage (Book, Page); else throw std::runtime_error ("The main sub-widget for a BookPage must be a PageDisplay."); } void adviseLinkClicked (boost::function linkClicked) { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) { pd->mLinkClicked = linkClicked; } } void unadviseLinkClicked () { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) { pd->mLinkClicked = boost::function (); } } protected: void onMouseLostFocus(Widget* _new) { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) { pd->onMouseLostFocus (); } else Widget::onMouseLostFocus (_new); } void onMouseMove(int _left, int _top) { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) { pd->onMouseMove (_left, _top); } else Widget::onMouseMove (_left, _top); } void onMouseButtonPressed (int _left, int _top, MouseButton _id) { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) { pd->onMouseButtonPressed (_left, _top, _id); } else Widget::onMouseButtonPressed (_left, _top, _id); } void onMouseButtonReleased(int _left, int _top, MouseButton _id) { if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) { pd->onMouseButtonReleased (_left, _top, _id); } else Widget::onMouseButtonReleased (_left, _top, _id); } }; void BookPage::registerMyGUIComponents () { FactoryManager & factory = FactoryManager::getInstance(); factory.registerFactory("Widget"); factory.registerFactory("BasisSkin"); } static bool ucs_line_break (int code_point) { return code_point == '\n'; } static bool ucs_space (int code_point) { switch (code_point) { case 0x0020: // SPACE case 0x00A0: // NO-BREAK SPACE case 0x1680: // OGHAM SPACE MARK case 0x180E: // MONGOLIAN VOWEL SEPARATOR case 0x2000: // EN QUAD case 0x2001: // EM QUAD case 0x2002: // EN SPACE case 0x2003: // EM SPACE case 0x2004: // THREE-PER-EM SPACE case 0x2005: // FOUR-PER-EM SPACE case 0x2006: // SIX-PER-EM SPACE case 0x2007: // FIGURE SPACE case 0x2008: // PUNCTUATION SPACE case 0x2009: // THIN SPACE case 0x200A: // HAIR SPACE case 0x200B: // ZERO WIDTH SPACE case 0x202F: // NARROW NO-BREAK SPACE case 0x205F: // MEDIUM MATHEMATICAL SPACE case 0x3000: // IDEOGRAPHIC SPACE case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE return true; default: return false; } } static bool ucs_breaking_space (int code_point) { switch (code_point) { case 0x0020: // SPACE //case 0x00A0: // NO-BREAK SPACE case 0x1680: // OGHAM SPACE MARK case 0x180E: // MONGOLIAN VOWEL SEPARATOR case 0x2000: // EN QUAD case 0x2001: // EM QUAD case 0x2002: // EN SPACE case 0x2003: // EM SPACE case 0x2004: // THREE-PER-EM SPACE case 0x2005: // FOUR-PER-EM SPACE case 0x2006: // SIX-PER-EM SPACE case 0x2007: // FIGURE SPACE case 0x2008: // PUNCTUATION SPACE case 0x2009: // THIN SPACE case 0x200A: // HAIR SPACE case 0x200B: // ZERO WIDTH SPACE case 0x202F: // NARROW NO-BREAK SPACE case 0x205F: // MEDIUM MATHEMATICAL SPACE case 0x3000: // IDEOGRAPHIC SPACE //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE return true; default: return false; } }