* apps/graphics/nxwidgets/src/ctext.cxx
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************
*
* 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 <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include "graphics/nxwidgets/ctext.hxx"
#include "graphics/nxwidgets/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 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(nxgl_coord_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);
CStringIterator *iterator = newStringIterator();
if (iterator->moveTo(m_linePositions[lineNumber] + length - 1))
{
do
{
if (!m_font->isCharBlank(iterator->getChar()))
{
break;
}
length--;
}
while (iterator->moveToPrevious() && (length > 0));
delete iterator;
return length;
}
delete iterator;
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)
{
nxgl_coord_t textStart = 0;
for (int i = 0; i < lines; i++)
{
textStart += getLineLength(i);
}
remove(0, textStart);
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)
{
int pos = 0;
int lineWidth;
int breakIndex;
bool endReached = false;
if (m_linePositions.size() == 0)
{
charIndex = 0;
}
if (charIndex > 0)
{
int lineIndex = getLineContainingCharIndex(charIndex);
while ((m_longestLines.size() > 0) &&
(m_longestLines[m_longestLines.size() - 1].index >= lineIndex))
{
m_longestLines.pop_back();
}
if (m_longestLines.size() > 0)
{
m_textPixelWidth = m_longestLines[m_longestLines.size() - 1].width;
}
else
{
m_textPixelWidth = 0;
}
while ((m_linePositions.size() > 0) &&
(m_linePositions.size() - 1 > (int)lineIndex))
{
m_linePositions.pop_back();
}
if (m_linePositions.size() > 0)
{
pos = m_linePositions[m_linePositions.size() - 1];
}
}
else
{
m_textPixelWidth = 0;
m_longestLines.clear();
m_linePositions.clear();
m_linePositions.push_back(0);
}
CStringIterator *iterator = newStringIterator();
while (!endReached)
{
breakIndex = 0;
lineWidth = 0;
if (iterator->moveTo(pos))
{
while (lineWidth + m_font->getCharWidth(iterator->getChar()) <= m_width)
{
lineWidth += m_font->getCharWidth(iterator->getChar());
if (iterator->getChar() == '\n')
{
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'))
{
breakIndex = iterator->getIndex();
}
if (!iterator->moveToNext())
{
endReached = true;
break;
}
}
}
else
{
endReached = true;
}
if ((!endReached) && (iterator->getIndex() > pos))
{
if (breakIndex == 0)
{
breakIndex = iterator->getIndex() - 1;
}
CStringIterator *breakIterator = newStringIterator();
if (breakIterator->moveTo(breakIndex + 1))
{
while (breakIterator->getChar() == ' ')
{
if (breakIterator->moveToNext())
{
breakIndex++;
}
else
{
break;
}
}
}
delete breakIterator;
pos = breakIndex + 1;
m_linePositions.push_back(pos);
if (lineWidth > m_textPixelWidth)
{
m_textPixelWidth = lineWidth;
LongestLine line;
line.index = m_linePositions.size() - 2;
line.width = lineWidth;
m_longestLines.push_back(line);
}
}
else if (!endReached)
{
pos++;
m_linePositions.push_back(pos);
}
}
if ((unsigned int)m_linePositions[m_linePositions.size() - 1] != getLength() + 1)
{
m_linePositions.push_back(getLength());
}
delete iterator;
m_textPixelHeight = getLineCount() * (m_font->getHeight() + m_lineSpacing);
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
{
if (m_linePositions.size() == 0)
{
return 0;
}
if (index >= m_linePositions[m_linePositions.size() - 2])
{
return m_linePositions.size() - 2;
}
int bottom = 0;
int top = m_linePositions.size() - 1;
int mid;
while (bottom <= top)
{
mid = (bottom + top) >> 1;
if (index < m_linePositions[mid])
{
top = mid - 1;
}
else if (index > m_linePositions[mid])
{
bottom = mid + 1;
}
else if (index == m_linePositions[mid])
{
return mid;
}
if (index > m_linePositions[top])
{
return top;
}
else if (index < m_linePositions[bottom])
{
return bottom - 1;
}
}
return 0;
}