From 4dedf494c242b2c4b52707441eefe2a36fbacf0f Mon Sep 17 00:00:00 2001 From: patacongo Date: Thu, 22 Mar 2012 21:22:59 +0000 Subject: First check-in of NxWidgets git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4505 42af7a65-404d-4744-a932-0658087f49c3 --- NxWidgets/libnxwidgets/src/ctext.cxx | 675 +++++++++++++++++++++++++++++++++++ 1 file changed, 675 insertions(+) create mode 100644 NxWidgets/libnxwidgets/src/ctext.cxx (limited to 'NxWidgets/libnxwidgets/src/ctext.cxx') diff --git a/NxWidgets/libnxwidgets/src/ctext.cxx b/NxWidgets/libnxwidgets/src/ctext.cxx new file mode 100644 index 000000000..688031d88 --- /dev/null +++ b/NxWidgets/libnxwidgets/src/ctext.cxx @@ -0,0 +1,675 @@ +/**************************************************************************** + * NxWidgets/libnxwidgets/src/ctext.cxx + * + * Copyright (C) 2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX, NxWidgets, nor the names of its contributors + * me be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + **************************************************************************** + * + * Portions of this package derive from Woopsi (http://woopsi.org/) and + * portions are original efforts. It is difficult to determine at this + * point what parts are original efforts and which parts derive from Woopsi. + * However, in any event, the work of Antony Dzeryn will be acknowledged + * in most NxWidget files. Thanks Antony! + * + * Copyright (c) 2007-2011, Antony Dzeryn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names "Woopsi", "Simian Zombie" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Antony Dzeryn ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Antony Dzeryn BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "ctext.hxx" +#include "cstringiterator.hxx" + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Method Implementations + ****************************************************************************/ + +using namespace NXWidgets; + +/** + * Constructor. + * + * @param font The font to use for this text object. + * @param text A string that this text object should wrap around. + * @param width The pixel width at which the text should wrap. + */ + +CText::CText(CNxFont *font, const CNxString &text, nxgl_coord_t width) +: CNxString(text) +{ + m_font = font; + m_width = width; + m_lineSpacing = 1; + wrap(); +} + +/** + * Set the text in the string. + * + * @param text Char array to use as the new data for this string. + */ + +void CText::setText(const CNxString &text) +{ + CNxString::setText(text); + wrap(); +} + +/** + * Set the text in the string. + * + * @param text Char array to use as the new data for this string. + */ + +void CText::setText(FAR const char *text) +{ + CNxString::setText(text); + wrap(); +} + +/** + * Set the text in the string. + * + * @param text Character to to use as the new data for this string. + */ + +void CText::setText(const nxwidget_char_t text) +{ + CNxString::setText(text); + wrap(); +} + +/** + * Append text to the end of the string. + * + * @param text String to append. + */ + +void CText::append(const CNxString &text) +{ + CNxString::append(text); + wrap(getLength() - 1); +} + +/** + * Insert text at the specified character index. + * + * @param text The text to insert. + * @param index The char index to insert at. + */ + +void CText::insert(const CNxString &text, const int index) +{ + CNxString::insert(text, index); + wrap(index); +} + +/** + * Remove all characters from the string from the start index onwards. + * + * @param startIndex The char index to start removing from. + */ + +void CText::remove(const int startIndex) +{ + CNxString::remove(startIndex); + wrap(startIndex); +} + +/** + * Remove all characters from the string from the start index onwards. + * + * @param startIndex The char index to start removing from. + * @param count The number of chars to remove. + */ + +void CText::remove(const int startIndex, const int count) +{ + CNxString::remove(startIndex, count); + wrap(startIndex); +} + + +/** + * Set the vertical spacing between rows of text. + * + * @param lineSpacing The line spacing. + */ + +void CText::setLineSpacing(uint8_t lineSpacing) +{ + m_lineSpacing = lineSpacing; + wrap(); +} + +/** + * Sets the pixel width of the text; text wider than + * this will automatically wrap. + * + * @param width Maximum pixel width of the text. + */ + +void CText::setWidth(nxgl_coord_t width) +{ + m_width = width; + wrap(); +} + +/** + * Set the font to use. + * + * @param font Pointer to the new font. + */ + +void CText::setFont(CNxFont* font) +{ + m_font = font; + wrap(); +} + +/** + * Get the number of characters in the specified line number. + * + * @param lineNumber The line number to check. + * @return The number of characters in the line. + */ + +const int CText::getLineLength(const int lineNumber) const +{ + if (lineNumber < getLineCount() - 1) + { + return m_linePositions[lineNumber + 1] - m_linePositions[lineNumber]; + } + + return getLength() - m_linePositions[lineNumber]; +} + +/** + * Get the number of characters in the specified line number, + * ignoring any trailing blank characters. + * + * @param lineNumber The line number to check. + * @return The number of characters in the line. + */ + +const int CText::getLineTrimmedLength(const int lineNumber) const +{ + nxgl_coord_t length = getLineLength(lineNumber); + + // Loop through string until the end + + CStringIterator *iterator = newStringIterator(); + + // Get char at the end of the line + + if (iterator->moveTo(m_linePositions[lineNumber] + length - 1)) + { + do + { + if (!m_font->isCharBlank(iterator->getChar())) + { + break; + } + length--; + } + while (iterator->moveToPrevious() && (length > 0)); + return length; + } + + // May occur if data has been horribly corrupted somewhere + + return 0; +} + +/** + * Get the width in pixels of the specified line number. + * + * @param lineNumber The line number to check. + * @return The pixel width of the line. + */ + +const nxgl_coord_t CText::getLinePixelLength(const int lineNumber) const +{ + return m_font->getStringWidth(*this, getLineStartIndex(lineNumber), + getLineLength(lineNumber)); +} + +/** + * Get the width in pixels of the specified line number, + * ignoring any trailing blank characters. + * + * @param lineNumber The line number to check. + * @return The pixel width of the line. + */ + +const nxgl_coord_t CText::getLineTrimmedPixelLength(const int lineNumber) const +{ + return m_font->getStringWidth(*this, getLineStartIndex(lineNumber), + getLineTrimmedLength(lineNumber)); +} + +/** + * Get a pointer to the CText object's font. + * + * @return Pointer to the font. + */ + +CNxFont *CText::getFont(void) const +{ + return m_font; +} + +/** + * Removes lines of text from the start of the text buffer. + * + * @param lines Number of lines to remove + */ + +void CText::stripTopLines(const int lines) +{ + // Get the start point of the text we want to keep + + nxgl_coord_t textStart = 0; + + for (int i = 0; i < lines; i++) + { + textStart += getLineLength(i); + } + + // Remove the characters from the start of the string to the found location + + remove(0, textStart); + + // Rewrap the text + + wrap(); +} + +/** + * Wrap all of the text. + */ + +void CText::wrap(void) +{ + wrap(0); +} + +/** + * Wrap the text from the line containing the specified char index onwards. + * + * @param charIndex The index of the char to start wrapping from; note + * that the wrapping function will re-wrap that entire line of text. + */ + +void CText::wrap(int charIndex) +{ + // Declare vars in advance of loop + + int pos = 0; + int lineWidth; + int breakIndex; + bool endReached = false; + + if (m_linePositions.size() == 0) + { + charIndex = 0; + } + + // If we're wrapping from an offset in the text, ensure that any existing data + // after the offset gets removed + + if (charIndex > 0) + { + // Remove wrapping data past this point + + // Get the index of the line in which the char index appears + + int lineIndex = getLineContainingCharIndex(charIndex); + + // Remove any longest line records that occur from the line index onwards + + while ((m_longestLines.size() > 0) && + (m_longestLines[m_longestLines.size() - 1].index >= lineIndex)) + { + m_longestLines.pop_back(); + } + + // If there are any longest line records remaining, update the text pixel width + // The last longest line record will always be the last valid longest line as + // the vector is sorted by length + + if (m_longestLines.size() > 0) + { + m_textPixelWidth = m_longestLines[m_longestLines.size() - 1].width; + } + else + { + m_textPixelWidth = 0; + } + + // Remove any wrapping data from after this line index onwards + + while ((m_linePositions.size() > 0) && + (m_linePositions.size() - 1 > (int)lineIndex)) + { + m_linePositions.pop_back(); + } + + // Adjust start position of wrapping loop so that it starts with + // the current line index + + if (m_linePositions.size() > 0) + { + pos = m_linePositions[m_linePositions.size() - 1]; + } + } + else + { + // Remove all wrapping data + + // Wipe the width variable + + m_textPixelWidth = 0; + + // Empty existing longest lines + + m_longestLines.clear(); + + // Empty existing line positions + + m_linePositions.clear(); + + // Push first line start into vector + + m_linePositions.push_back(0); + } + + // Loop through string until the end + + CStringIterator *iterator = newStringIterator(); + + while (!endReached) + { + breakIndex = 0; + lineWidth = 0; + + if (iterator->moveTo(pos)) + { + // Search for line breaks and valid breakpoints until we + // exceed the width of the text field or we run out of + // string to process + + while (lineWidth + m_font->getCharWidth(iterator->getChar()) <= m_width) + { + lineWidth += m_font->getCharWidth(iterator->getChar()); + + // Check for line return + + if (iterator->getChar() == '\n') + { + // Remember this breakpoint + + breakIndex = iterator->getIndex(); + break; + } + else if ((iterator->getChar() == ' ') || + (iterator->getChar() == ',') || + (iterator->getChar() == '.') || + (iterator->getChar() == '-') || + (iterator->getChar() == ':') || + (iterator->getChar() == ';') || + (iterator->getChar() == '?') || + (iterator->getChar() == '!') || + (iterator->getChar() == '+') || + (iterator->getChar() == '=') || + (iterator->getChar() == '/') || + (iterator->getChar() == '\0')) + { + // Remember the most recent breakpoint + + breakIndex = iterator->getIndex(); + } + + // Move to the next character + + if (!iterator->moveToNext()) + { + // No more text; abort loop + + endReached = true; + break; + } + } + } + else + { + endReached = true; + } + + if ((!endReached) && (iterator->getIndex() > pos)) + { + // Process any found data + + // If we didn't find a breakpoint split at the current position + + if (breakIndex == 0) + { + breakIndex = iterator->getIndex() - 1; + } + + // Trim blank space from the start of the next line + + CStringIterator *breakIterator = newStringIterator(); + + if (breakIterator->moveTo(breakIndex + 1)) + { + while (breakIterator->getChar() == ' ') + { + if (breakIterator->moveToNext()) + { + breakIndex++; + } + else + { + break; + } + } + } + + delete breakIterator; + + // Add the start of the next line to the vector + + pos = breakIndex + 1; + m_linePositions.push_back(pos); + + // Is this the longest line observed so far? + + if (lineWidth > m_textPixelWidth) + { + m_textPixelWidth = lineWidth; + + // Push the description of the line into the longest lines + // vector (note that we store the index in m_linePositions that + // refers to the start of the line, *not* the position of the + // line in the char array) + + LongestLine line; + line.index = m_linePositions.size() - 2; + line.width = lineWidth; + m_longestLines.push_back(line); + } + } + else if (!endReached) + { + // Add a blank row if we're not at the end of the string + + pos++; + m_linePositions.push_back(pos); + } + } + + // Add marker indicating end of text + // If we reached the end of the text, append the stopping point + + if (m_linePositions[m_linePositions.size() - 1] != getLength() + 1) + { + m_linePositions.push_back(getLength()); + } + + delete iterator; + + // Calculate the total height of the text + + m_textPixelHeight = getLineCount() * (m_font->getHeight() + m_lineSpacing); + + // Ensure height is always at least one row + + if (m_textPixelHeight == 0) + { + m_textPixelHeight = m_font->getHeight() + m_lineSpacing; + } +} + +/** + * Get the index of the line of text that contains the specified index + * within the raw char array. + * + * @param index The index to locate within the wrapped lines of text. + * @return The number of the line of wrapped text that contains the + * specified index. + */ + +const int CText::getLineContainingCharIndex(const int index) const +{ + // Early exit if there is no existing line data + + if (m_linePositions.size() == 0) + { + return 0; + } + + // Early exit if the character is in the last row + + if (index >= m_linePositions[m_linePositions.size() - 2]) + { + return m_linePositions.size() - 2; + } + + // Binary search the line vector for the line containing the supplied index + + int bottom = 0; + int top = m_linePositions.size() - 1; + int mid; + + while (bottom <= top) + { + // Standard binary search + + mid = (bottom + top) >> 1; + + if (index < m_linePositions[mid]) + { + // Index is somewhere in the lower search space + + top = mid - 1; + } + else if (index > m_linePositions[mid]) + { + // Index is somewhere in the upper search space + + bottom = mid + 1; + } + else if (index == m_linePositions[mid]) + { + // Located the index + + return mid; + } + + // Check to see if we've moved past the line that contains the index + // We have to do this because the index we're looking for can be within + // a line; it isn't necessarily the start of a line (which is what is + // stored in the m_linePositions vector) + + if (index > m_linePositions[top]) + { + // Search index falls within the line represented by the top position + + return top; + } + else if (index < m_linePositions[bottom]) + { + // Search index falls within the line represented by the bottom position + + return bottom - 1; + } + } + + // Line cannot be found + + return 0; +} -- cgit v1.2.3