diff options
Diffstat (limited to 'gui/scrolllist.cpp')
-rw-r--r-- | gui/scrolllist.cpp | 657 |
1 files changed, 657 insertions, 0 deletions
diff --git a/gui/scrolllist.cpp b/gui/scrolllist.cpp new file mode 100644 index 000000000..ecfb5fe81 --- /dev/null +++ b/gui/scrolllist.cpp @@ -0,0 +1,657 @@ +/* + Copyright 2013 bigbiff/Dees_Troy TeamWin + This file is part of TWRP/TeamWin Recovery Project. + + TWRP is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TWRP 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TWRP. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> + +extern "C" { +#include "../twcommon.h" +} +#include "../minuitwrp/minui.h" + +#include "rapidxml.hpp" +#include "objects.hpp" +#include "../data.hpp" + +const float SCROLLING_SPEED_DECREMENT = 0.9; // friction +const int SCROLLING_FLOOR = 2; // minimum pixels for scrolling to stop + +GUIScrollList::GUIScrollList(xml_node<>* node) : GUIObject(node) +{ + xml_node<>* child; + + firstDisplayedItem = mItemSpacing = mFontHeight = mSeparatorH = y_offset = scrollingSpeed = 0; + maxIconWidth = maxIconHeight = mHeaderIconHeight = mHeaderIconWidth = 0; + mHeaderSeparatorH = mHeaderH = actualItemHeight = 0; + mHeaderIsStatic = false; + mBackground = mHeaderIcon = NULL; + mFont = NULL; + mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0; + mFastScrollRectCurrentY = mFastScrollRectCurrentH = mFastScrollRectTouchY = 0; + lastY = last2Y = fastScroll = 0; + mUpdate = 0; + touchDebounce = 6; + ConvertStrToColor("black", &mBackgroundColor); + ConvertStrToColor("black", &mHeaderBackgroundColor); + ConvertStrToColor("black", &mSeparatorColor); + ConvertStrToColor("black", &mHeaderSeparatorColor); + ConvertStrToColor("white", &mFontColor); + ConvertStrToColor("white", &mHeaderFontColor); + ConvertStrToColor("white", &mFastScrollLineColor); + ConvertStrToColor("white", &mFastScrollRectColor); + hasHighlightColor = false; + allowSelection = true; + selectedItem = NO_ITEM; + + // Load header text + // note: node can be NULL for the emergency console + child = node ? node->first_node("text") : NULL; + if (child) mHeaderText = child->value(); + // Simple way to check for static state + mLastHeaderValue = gui_parse_text(mHeaderText); + mHeaderIsStatic = (mLastHeaderValue == mHeaderText); + + mHighlightColor = LoadAttrColor(FindNode(node, "highlight"), "color", &hasHighlightColor); + + child = FindNode(node, "background"); + if (child) + { + mBackground = LoadAttrImage(child, "resource"); + mBackgroundColor = LoadAttrColor(child, "color"); + } + + // Load the placement + LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH); + SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH); + + // Load the font, and possibly override the color + child = FindNode(node, "font"); + if (child) + { + mFont = LoadAttrFont(child, "resource"); + mFontColor = LoadAttrColor(child, "color"); + mFontHighlightColor = LoadAttrColor(child, "highlightcolor", mFontColor); + mItemSpacing = LoadAttrIntScaleY(child, "spacing"); + } + + // Load the separator if it exists + child = FindNode(node, "separator"); + if (child) + { + mSeparatorColor = LoadAttrColor(child, "color"); + mSeparatorH = LoadAttrIntScaleY(child, "height"); + } + + // Fast scroll + child = FindNode(node, "fastscroll"); + if (child) + { + mFastScrollLineColor = LoadAttrColor(child, "linecolor"); + mFastScrollRectColor = LoadAttrColor(child, "rectcolor"); + + mFastScrollW = LoadAttrIntScaleX(child, "w"); + mFastScrollLineW = LoadAttrIntScaleX(child, "linew"); + mFastScrollRectW = LoadAttrIntScaleX(child, "rectw"); + mFastScrollRectH = LoadAttrIntScaleY(child, "recth"); + } + + // Retrieve the line height + mFontHeight = mFont->GetHeight(); + actualItemHeight = mFontHeight + mItemSpacing + mSeparatorH; + + // Load the header if it exists + child = FindNode(node, "header"); + if (child) + { + mHeaderH = mFontHeight; + mHeaderIcon = LoadAttrImage(child, "icon"); + mHeaderBackgroundColor = LoadAttrColor(child, "background", mBackgroundColor); + mHeaderFontColor = LoadAttrColor(child, "textcolor", mFontColor); + mHeaderSeparatorColor = LoadAttrColor(child, "separatorcolor", mSeparatorColor); + mHeaderSeparatorH = LoadAttrIntScaleY(child, "separatorheight", mSeparatorH); + + if (mHeaderIcon && mHeaderIcon->GetResource()) + { + mHeaderIconWidth = mHeaderIcon->GetWidth(); + mHeaderIconHeight = mHeaderIcon->GetHeight(); + if (mHeaderIconHeight > mHeaderH) + mHeaderH = mHeaderIconHeight; + if (mHeaderIconWidth > maxIconWidth) + maxIconWidth = mHeaderIconWidth; + } + + mHeaderH += mItemSpacing + mHeaderSeparatorH; + if (mHeaderH < actualItemHeight) + mHeaderH = actualItemHeight; + } + + if (actualItemHeight / 3 > 6) + touchDebounce = actualItemHeight / 3; +} + +GUIScrollList::~GUIScrollList() +{ +} + +void GUIScrollList::SetMaxIconSize(int w, int h) +{ + if (w > maxIconWidth) + maxIconWidth = w; + if (h > maxIconHeight) + maxIconHeight = h; + if (maxIconHeight > mFontHeight) { + actualItemHeight = maxIconHeight + mItemSpacing + mSeparatorH; + if (mHeaderH > 0 && actualItemHeight > mHeaderH) + mHeaderH = actualItemHeight; + } +} + +void GUIScrollList::SetVisibleListLocation(size_t list_index) +{ + // This will make sure that the item indicated by list_index is visible on the screen + size_t lines = GetDisplayItemCount(); + + if (list_index <= (unsigned)firstDisplayedItem) { + // list_index is above the currently displayed items, put the selected item at the very top + firstDisplayedItem = list_index; + y_offset = 0; + } else if (list_index >= firstDisplayedItem + lines) { + // list_index is below the currently displayed items, put the selected item at the very bottom + firstDisplayedItem = list_index - lines + 1; + if (GetDisplayRemainder() != 0) { + // There's a partial row displayed, set the scrolling offset so that the selected item really is at the very bottom + firstDisplayedItem--; + y_offset = GetDisplayRemainder() - actualItemHeight; + } else { + // There's no partial row so zero out the offset + y_offset = 0; + } + if (firstDisplayedItem < 0) + firstDisplayedItem = 0; + } + scrollingSpeed = 0; // stop kinetic scrolling on setting visible location + mUpdate = 1; +} + +int GUIScrollList::Render(void) +{ + if (!isConditionTrue()) + return 0; + + // First step, fill background + gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, mBackgroundColor.alpha); + gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH); + + // don't paint outside of the box + gr_clip(mRenderX, mRenderY, mRenderW, mRenderH); + + // Next, render the background resource (if it exists) + if (mBackground && mBackground->GetResource()) + { + int BackgroundW = mBackground->GetWidth(); + int BackgroundH = mBackground->GetHeight(); + int BackgroundX = mRenderX + ((mRenderW - BackgroundW) / 2); + int BackgroundY = mRenderY + ((mRenderH - BackgroundH) / 2); + gr_blit(mBackground->GetResource(), 0, 0, BackgroundW, BackgroundH, BackgroundX, BackgroundY); + } + + // This tells us how many full lines we can actually render + size_t lines = GetDisplayItemCount(); + + size_t listSize = GetItemCount(); + int listW = mRenderW; // this is only used for the separators - the list items are rendered in the full width of the list + + if (listSize <= lines) { + hasScroll = false; + scrollingSpeed = 0; + lines = listSize; + y_offset = 0; + } else { + hasScroll = true; + listW -= mFastScrollW; // space for fast scroll + lines++; + if (lines < listSize) + lines++; + } + + int yPos = mRenderY + mHeaderH + y_offset; + + // render all visible items + for (size_t line = 0; line < lines; line++) + { + size_t itemindex = line + firstDisplayedItem; + if (itemindex >= listSize) + break; + + RenderItem(itemindex, yPos, itemindex == selectedItem); + + // Add the separator + gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, mSeparatorColor.alpha); + gr_fill(mRenderX, yPos + actualItemHeight - mSeparatorH, listW, mSeparatorH); + + // Move the yPos + yPos += actualItemHeight; + } + + // Render the Header (last so that it overwrites the top most row for per pixel scrolling) + yPos = mRenderY; + if (mHeaderH > 0) { + // First step, fill background + gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, mHeaderBackgroundColor.alpha); + gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH); + + int IconOffsetX = 0; + + // render the icon if it exists + if (mHeaderIcon && mHeaderIcon->GetResource()) + { + gr_blit(mHeaderIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - maxIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2))); + IconOffsetX = maxIconWidth; + } + + // render the text + if (mFont && mFont->GetResource()) { + gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, mHeaderFontColor.alpha); + gr_textEx_scaleW(mRenderX + IconOffsetX + 5, yPos + (int)(mHeaderH / 2), mLastHeaderValue.c_str(), mFont->GetResource(), mRenderW, TEXT_ONLY_RIGHT, 0); + } + + // Add the separator + gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, mHeaderSeparatorColor.alpha); + gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH); + } + + // reset clipping + gr_noclip(); + + // render fast scroll + if (hasScroll) { + int fWidth = mRenderW - listW; + int fHeight = mRenderH - mHeaderH; + int centerX = listW + mRenderX + fWidth / 2; + + // first determine the total list height and where we are in the list + int totalHeight = GetItemCount() * actualItemHeight; // total height of the full list in pixels + int topPos = firstDisplayedItem * actualItemHeight - y_offset; + + // now scale it proportionally to the scrollbar height + int boxH = fHeight * fHeight / totalHeight; // proportional height of the displayed portion + boxH = std::max(boxH, mFastScrollRectH); // but keep a minimum height + int boxY = (fHeight - boxH) * topPos / (totalHeight - fHeight); // pixels relative to top of list + int boxW = mFastScrollRectW; + + int x = centerX - boxW / 2; + int y = mRenderY + mHeaderH + boxY; + + // line above and below box (needs to be split because box can be transparent) + gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, mFastScrollLineColor.alpha); + gr_fill(centerX - mFastScrollLineW / 2, mRenderY + mHeaderH, mFastScrollLineW, boxY); + gr_fill(centerX - mFastScrollLineW / 2, y + boxH, mFastScrollLineW, fHeight - boxY - boxH); + + // box + gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, mFastScrollRectColor.alpha); + gr_fill(x, y, boxW, boxH); + + mFastScrollRectCurrentY = boxY; + mFastScrollRectCurrentH = boxH; + } + mUpdate = 0; + return 0; +} + +void GUIScrollList::RenderItem(size_t itemindex __unused, int yPos, bool selected) +{ + RenderStdItem(yPos, selected, NULL, "implement RenderItem!"); +} + +void GUIScrollList::RenderStdItem(int yPos, bool selected, ImageResource* icon, const char* text, int iconAndTextH) +{ + if (hasHighlightColor && selected) { + // Highlight the item background of the selected item + gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha); + gr_fill(mRenderX, yPos, mRenderW, actualItemHeight); + } + + if (selected) { + // Use the highlight color for the font + gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, mFontHighlightColor.alpha); + } else { + // Set the color for the font + gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha); + } + + if (!iconAndTextH) + iconAndTextH = actualItemHeight; + + // render icon + if (icon && icon->GetResource()) { + int iconH = icon->GetHeight(); + int iconW = icon->GetWidth(); + int iconY = yPos + (iconAndTextH - iconH) / 2; + int iconX = mRenderX + (maxIconWidth - iconW) / 2; + gr_blit(icon->GetResource(), 0, 0, iconW, iconH, iconX, iconY); + } + + // render label text + if (mFont && mFont->GetResource()) { + int textX = mRenderX + maxIconWidth + 5; + int textY = yPos + (iconAndTextH / 2); + gr_textEx_scaleW(textX, textY, text, mFont->GetResource(), mRenderW, TEXT_ONLY_RIGHT, 0); + } +} + +int GUIScrollList::Update(void) +{ + if (!isConditionTrue()) + return 0; + + if (!mHeaderIsStatic) { + std::string newValue = gui_parse_text(mHeaderText); + if (mLastHeaderValue != newValue) { + mLastHeaderValue = newValue; + mUpdate = 1; + } + } + + // Handle kinetic scrolling + // maximum number of items to scroll per update + float maxItemsScrolledPerFrame = std::max(2.5, float(GetDisplayItemCount() / 4) + 0.5); + + int maxScrollDistance = actualItemHeight * maxItemsScrolledPerFrame; + int oldScrollingSpeed = scrollingSpeed; + if (scrollingSpeed == 0) { + // Do nothing + return 0; + } else if (scrollingSpeed > 0) { + if (scrollingSpeed < maxScrollDistance) + y_offset += scrollingSpeed; + else + y_offset += maxScrollDistance; + scrollingSpeed *= SCROLLING_SPEED_DECREMENT; + if (scrollingSpeed == oldScrollingSpeed) + --scrollingSpeed; + } else if (scrollingSpeed < 0) { + if (abs(scrollingSpeed) < maxScrollDistance) + y_offset += scrollingSpeed; + else + y_offset -= maxScrollDistance; + scrollingSpeed *= SCROLLING_SPEED_DECREMENT; + if (scrollingSpeed == oldScrollingSpeed) + ++scrollingSpeed; + } + if (abs(scrollingSpeed) < SCROLLING_FLOOR) + scrollingSpeed = 0; + HandleScrolling(); + mUpdate = 1; + + return 0; +} + +size_t GUIScrollList::HitTestItem(int x __unused, int y) +{ + // We only care about y position + if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH) + return NO_ITEM; + + int startSelection = (y - mRenderY - mHeaderH); + + // Locate the correct item + size_t actualSelection = firstDisplayedItem; + int selectY = y_offset; + while (selectY + actualItemHeight < startSelection) { + selectY += actualItemHeight; + actualSelection++; + } + + if (actualSelection < GetItemCount()) + return actualSelection; + + return NO_ITEM; +} + +int GUIScrollList::NotifyTouch(TOUCH_STATE state, int x, int y) +{ + if (!isConditionTrue()) + return -1; + + switch (state) + { + case TOUCH_START: + if (hasScroll && x >= mRenderX + mRenderW - mFastScrollW) { + fastScroll = 1; // Initial touch is in the fast scroll region + int fastScrollBoxTop = mFastScrollRectCurrentY + mRenderY + mHeaderH; + int fastScrollBoxBottom = fastScrollBoxTop + mFastScrollRectCurrentH; + if (y >= fastScrollBoxTop && y < fastScrollBoxBottom) + // user grabbed the fastscroll bar + // try to keep the initially touched part of the scrollbar under the finger + mFastScrollRectTouchY = y - fastScrollBoxTop; + else + // user tapped outside the fastscroll bar + // center fastscroll rect on the initial touch position + mFastScrollRectTouchY = mFastScrollRectCurrentH / 2; + } + + if (scrollingSpeed != 0) { + selectedItem = NO_ITEM; // this allows the user to tap the list to stop the scrolling without selecting the item they tap + scrollingSpeed = 0; // stop scrolling on a new touch + } else if (!fastScroll && allowSelection) { + // find out which item the user touched + selectedItem = HitTestItem(x, y); + } + if (selectedItem != NO_ITEM) + mUpdate = 1; + lastY = last2Y = y; + break; + + case TOUCH_DRAG: + if (fastScroll) + { + int relY = y - mRenderY - mHeaderH; // touch position relative to window + int windowH = mRenderH - mHeaderH; + int totalHeight = GetItemCount() * actualItemHeight; // total height of the full list in pixels + + // calculate new top position of the fastscroll bar relative to window + int newY = relY - mFastScrollRectTouchY; + // keep it fully inside the list + newY = std::min(std::max(newY, 0), windowH - mFastScrollRectCurrentH); + + // now compute the new scroll position for the list + int newTopPos = newY * (totalHeight - windowH) / (windowH - mFastScrollRectCurrentH); // new top pixel of list + newTopPos = std::min(newTopPos, totalHeight - windowH); // account for rounding errors + firstDisplayedItem = newTopPos / actualItemHeight; + y_offset = - newTopPos % actualItemHeight; + + selectedItem = NO_ITEM; + mUpdate = 1; + scrollingSpeed = 0; // prevent kinetic scrolling when using fast scroll + break; + } + + // Provide some debounce on initial touches + if (selectedItem != NO_ITEM && abs(y - lastY) < touchDebounce) { + mUpdate = 1; + break; + } + + selectedItem = NO_ITEM; // nothing is selected because we dragged too far + // Handle scrolling + if (hasScroll) { + y_offset += y - lastY; // adjust the scrolling offset based on the difference between the starting touch and the current touch + last2Y = lastY; // keep track of previous y locations so that we can tell how fast to scroll for kinetic scrolling + lastY = y; // update last touch to the current touch so we can tell how far and what direction we scroll for the next touch event + + HandleScrolling(); + } else + y_offset = 0; + mUpdate = 1; + break; + + case TOUCH_RELEASE: + if (fastScroll) + mUpdate = 1; // get rid of touch effects on the fastscroll bar + fastScroll = 0; + if (selectedItem != NO_ITEM) { + // We've selected an item! + NotifySelect(selectedItem); + mUpdate = 1; + + DataManager::Vibrate("tw_button_vibrate"); + selectedItem = NO_ITEM; + } else { + // Start kinetic scrolling + scrollingSpeed = lastY - last2Y; + if (abs(scrollingSpeed) < touchDebounce) + scrollingSpeed = 0; + } + case TOUCH_REPEAT: + case TOUCH_HOLD: + break; + } + return 0; +} + +void GUIScrollList::HandleScrolling() +{ + // handle dragging downward, scrolling upward + // the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed + while (firstDisplayedItem && y_offset > 0) { + firstDisplayedItem--; + y_offset -= actualItemHeight; + } + if (firstDisplayedItem == 0 && y_offset > 0) { + y_offset = 0; // user kept dragging downward past the top of the list, so always reset the offset to 0 since we can't scroll any further in this direction + scrollingSpeed = 0; // stop kinetic scrolling + } + + // handle dragging upward, scrolling downward + int totalSize = GetItemCount(); + int lines = GetDisplayItemCount(); // number of full lines our list can display at once + int bottom_offset = GetDisplayRemainder() - actualItemHeight; // extra display area that can display a partial line for per pixel scrolling + + // the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed + while (firstDisplayedItem + lines + (bottom_offset ? 1 : 0) < totalSize && abs(y_offset) > actualItemHeight) { + firstDisplayedItem++; + y_offset += actualItemHeight; + } + // Check if we dragged too far, set the list at the bottom and adjust offset as needed + if (bottom_offset != 0 && firstDisplayedItem + lines + 1 >= totalSize && y_offset <= bottom_offset) { + firstDisplayedItem = totalSize - lines - 1; + y_offset = bottom_offset; + scrollingSpeed = 0; // stop kinetic scrolling + } else if (firstDisplayedItem + lines >= totalSize && y_offset < 0) { + firstDisplayedItem = totalSize - lines; + y_offset = 0; + scrollingSpeed = 0; // stop kinetic scrolling + } +} + +int GUIScrollList::GetDisplayItemCount() +{ + return (mRenderH - mHeaderH) / (actualItemHeight); +} + +int GUIScrollList::GetDisplayRemainder() +{ + return (mRenderH - mHeaderH) % actualItemHeight; +} + +int GUIScrollList::NotifyVarChange(const std::string& varName, const std::string& value) +{ + GUIObject::NotifyVarChange(varName, value); + + if (!isConditionTrue()) + return 0; + + if (!mHeaderIsStatic) { + std::string newValue = gui_parse_text(mHeaderText); + if (mLastHeaderValue != newValue) { + mLastHeaderValue = newValue; + firstDisplayedItem = 0; + y_offset = 0; + scrollingSpeed = 0; // stop kinetic scrolling on variable changes + mUpdate = 1; + } + } + return 0; +} + +int GUIScrollList::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */) +{ + mRenderX = x; + mRenderY = y; + if (w || h) + { + mRenderW = w; + mRenderH = h; + } + SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH); + mUpdate = 1; + return 0; +} + +void GUIScrollList::SetPageFocus(int inFocus) +{ + if (inFocus) { + NotifyVarChange("", ""); // This forces a check for the header text + scrollingSpeed = 0; // stop kinetic scrolling on page changes + mUpdate = 1; + } +} + +bool GUIScrollList::AddLines(std::vector<std::string>* origText, std::vector<std::string>* origColor, size_t* lastCount, std::vector<std::string>* rText, std::vector<std::string>* rColor) +{ + if (!mFont || !mFont->GetResource()) + return false; + if (*lastCount == origText->size()) + return false; // nothing to add + + size_t prevCount = *lastCount; + *lastCount = origText->size(); + + // Due to word wrap, figure out what / how the newly added text needs to be added to the render vector that is word wrapped + // Note, that multiple consoles on different GUI pages may be different widths or use different fonts, so the word wrapping + // may different in different console windows + for (size_t i = prevCount; i < *lastCount; i++) { + string curr_line = origText->at(i); + string curr_color; + if (origColor) + curr_color = origColor->at(i); + for (;;) { + size_t line_char_width = gr_ttf_maxExW(curr_line.c_str(), mFont->GetResource(), mRenderW); + if (line_char_width < curr_line.size()) { + //string left = curr_line.substr(0, line_char_width); + size_t wrap_pos = curr_line.find_last_of(" ,./:-_;", line_char_width - 1); + if (wrap_pos == string::npos) + wrap_pos = line_char_width; + else if (wrap_pos < line_char_width - 1) + wrap_pos++; + rText->push_back(curr_line.substr(0, wrap_pos)); + if (origColor) + rColor->push_back(curr_color); + curr_line = curr_line.substr(wrap_pos); + /* After word wrapping, delete any leading spaces. Note that the word wrapping is not smart enough to know not + * to wrap in the middle of something like ... so some of the ... could appear on the following line. */ + curr_line.erase(0, curr_line.find_first_not_of(" ")); + } else { + rText->push_back(curr_line); + if (origColor) + rColor->push_back(curr_color); + break; + } + } + } + return true; +} |