NXWidgets  1.19
ctext.cxx
Go to the documentation of this file.
1 /****************************************************************************
2  * NxWidgets/libnxwidgets/src/ctext.cxx
3  *
4  * Copyright (C) 2012, 2016 Gregory Nutt. All rights reserved.
5  * Author: Gregory Nutt <gnutt@nuttx.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  * 3. Neither the name NuttX, NxWidgets, nor the names of its contributors
18  * me be used to endorse or promote products derived from this software
19  * without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  ****************************************************************************
35  *
36  * Portions of this package derive from Woopsi (http://woopsi.org/) and
37  * portions are original efforts. It is difficult to determine at this
38  * point what parts are original efforts and which parts derive from Woopsi.
39  * However, in any event, the work of Antony Dzeryn will be acknowledged
40  * in most NxWidget files. Thanks Antony!
41  *
42  * Copyright (c) 2007-2011, Antony Dzeryn
43  * All rights reserved.
44  *
45  * Redistribution and use in source and binary forms, with or without
46  * modification, are permitted provided that the following conditions are met:
47  *
48  * * Redistributions of source code must retain the above copyright
49  * notice, this list of conditions and the following disclaimer.
50  * * Redistributions in binary form must reproduce the above copyright
51  * notice, this list of conditions and the following disclaimer in the
52  * documentation and/or other materials provided with the distribution.
53  * * Neither the names "Woopsi", "Simian Zombie" nor the
54  * names of its contributors may be used to endorse or promote products
55  * derived from this software without specific prior written permission.
56  *
57  * THIS SOFTWARE IS PROVIDED BY Antony Dzeryn ``AS IS'' AND ANY
58  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
59  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
60  * DISCLAIMED. IN NO EVENT SHALL Antony Dzeryn BE LIABLE FOR ANY
61  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
62  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
63  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
64  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
65  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
66  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
67  *
68  ****************************************************************************/
69 
70 /****************************************************************************
71  * Included Files
72  ****************************************************************************/
73 
74 #include <nuttx/config.h>
75 
76 #include <stdint.h>
77 #include <stdbool.h>
78 
79 #include "ctext.hxx"
80 #include "cstringiterator.hxx"
81 
82 /****************************************************************************
83  * Pre-Processor Definitions
84  ****************************************************************************/
85 
86 /****************************************************************************
87  * Method Implementations
88  ****************************************************************************/
89 
90 using namespace NXWidgets;
91 
92 /**
93  * Constructor.
94  *
95  * @param font The font to use for this text object.
96  * @param text A string that this text object should wrap around.
97  * @param width The pixel width at which the text should wrap.
98  */
99 
100 CText::CText(CNxFont *font, const CNxString &text, nxgl_coord_t width)
101 : CNxString(text)
102 {
103  m_font = font;
104  m_width = width;
105  m_lineSpacing = 1;
106  wrap();
107 }
108 
109 /**
110  * Set the text in the string.
111  *
112  * @param text Char array to use as the new data for this string.
113  */
114 
115 void CText::setText(const CNxString &text)
116 {
117  CNxString::setText(text);
118  wrap();
119 }
120 
121 /**
122  * Set the text in the string.
123  *
124  * @param text Char array to use as the new data for this string.
125  */
126 
127 void CText::setText(FAR const char *text)
128 {
129  CNxString::setText(text);
130  wrap();
131 }
132 
133 /**
134  * Set the text in the string.
135  *
136  * @param text Character to to use as the new data for this string.
137  */
138 
140 {
141  CNxString::setText(text);
142  wrap();
143 }
144 
145 /**
146  * Append text to the end of the string.
147  *
148  * @param text String to append.
149  */
150 
151 void CText::append(const CNxString &text)
152 {
153  CNxString::append(text);
154  wrap(getLength() - 1);
155 }
156 
157 /**
158  * Insert text at the specified character index.
159  *
160  * @param text The text to insert.
161  * @param index The char index to insert at.
162  */
163 
164 void CText::insert(const CNxString &text, const int index)
165 {
166  CNxString::insert(text, index);
167  wrap(index);
168 }
169 
170 /**
171  * Remove all characters from the string from the start index onwards.
172  *
173  * @param startIndex The char index to start removing from.
174  */
175 
176 void CText::remove(const int startIndex)
177 {
178  CNxString::remove(startIndex);
179  wrap(startIndex);
180 }
181 
182 /**
183  * Remove all characters from the string from the start index onwards.
184  *
185  * @param startIndex The char index to start removing from.
186  * @param count The number of chars to remove.
187  */
188 
189 void CText::remove(const int startIndex, const int count)
190 {
191  CNxString::remove(startIndex, count);
192  wrap(startIndex);
193 }
194 
195 
196 /**
197  * Set the vertical spacing between rows of text.
198  *
199  * @param lineSpacing The line spacing.
200  */
201 
202 void CText::setLineSpacing(nxgl_coord_t lineSpacing)
203 {
204  m_lineSpacing = lineSpacing;
205  wrap();
206 }
207 
208 /**
209  * Sets the pixel width of the text; text wider than
210  * this will automatically wrap.
211  *
212  * @param width Maximum pixel width of the text.
213  */
214 
215 void CText::setWidth(nxgl_coord_t width)
216 {
217  m_width = width;
218  wrap();
219 }
220 
221 /**
222  * Set the font to use.
223  *
224  * @param font Pointer to the new font.
225  */
226 
228 {
229  m_font = font;
230  wrap();
231 }
232 
233 /**
234  * Get the number of characters in the specified line number.
235  *
236  * @param lineNumber The line number to check.
237  * @return The number of characters in the line.
238  */
239 
240 const int CText::getLineLength(const int lineNumber) const
241 {
242  if (lineNumber < getLineCount() - 1)
243  {
244  return m_linePositions[lineNumber + 1] - m_linePositions[lineNumber];
245  }
246 
247  return getLength() - m_linePositions[lineNumber];
248 }
249 
250 /**
251  * Get the number of characters in the specified line number,
252  * ignoring any trailing blank characters.
253  *
254  * @param lineNumber The line number to check.
255  * @return The number of characters in the line.
256  */
257 
258 const int CText::getLineTrimmedLength(const int lineNumber) const
259 {
260  nxgl_coord_t length = getLineLength(lineNumber);
261 
262  // Loop through string until the end
263 
264  CStringIterator *iterator = newStringIterator();
265 
266  // Get char at the end of the line
267 
268  if (iterator->moveTo(m_linePositions[lineNumber] + length - 1))
269  {
270  do
271  {
272  if (!m_font->isCharBlank(iterator->getChar()))
273  {
274  break;
275  }
276  length--;
277  }
278  while (iterator->moveToPrevious() && (length > 0));
279 
280  delete iterator;
281  return length;
282  }
283 
284  // May occur if data has been horribly corrupted somewhere
285 
286  delete iterator;
287  return 0;
288 }
289 
290 /**
291  * Get the width in pixels of the specified line number.
292  *
293  * @param lineNumber The line number to check.
294  * @return The pixel width of the line.
295  */
296 
297 const nxgl_coord_t CText::getLinePixelLength(const int lineNumber) const
298 {
299  return m_font->getStringWidth(*this, getLineStartIndex(lineNumber),
300  getLineLength(lineNumber));
301 }
302 
303 /**
304  * Get the width in pixels of the specified line number,
305  * ignoring any trailing blank characters.
306  *
307  * @param lineNumber The line number to check.
308  * @return The pixel width of the line.
309  */
310 
311 const nxgl_coord_t CText::getLineTrimmedPixelLength(const int lineNumber) const
312 {
313  return m_font->getStringWidth(*this, getLineStartIndex(lineNumber),
314  getLineTrimmedLength(lineNumber));
315 }
316 
317 /**
318  * Get a pointer to the CText object's font.
319  *
320  * @return Pointer to the font.
321  */
322 
324 {
325  return m_font;
326 }
327 
328 /**
329  * Removes lines of text from the start of the text buffer.
330  *
331  * @param lines Number of lines to remove
332  */
333 
334 void CText::stripTopLines(const int lines)
335 {
336  // Get the start point of the text we want to keep
337 
338  nxgl_coord_t textStart = 0;
339 
340  for (int i = 0; i < lines; i++)
341  {
342  textStart += getLineLength(i);
343  }
344 
345  // Remove the characters from the start of the string to the found location
346 
347  remove(0, textStart);
348 
349  // Rewrap the text
350 
351  wrap();
352 }
353 
354 /**
355  * Wrap all of the text.
356  */
357 
358 void CText::wrap(void)
359 {
360  wrap(0);
361 }
362 
363 /**
364  * Wrap the text from the line containing the specified char index onwards.
365  *
366  * @param charIndex The index of the char to start wrapping from; note
367  * that the wrapping function will re-wrap that entire line of text.
368  */
369 
370 void CText::wrap(int charIndex)
371 {
372  // Declare vars in advance of loop
373 
374  int pos = 0;
375  int lineWidth;
376  int breakIndex;
377  bool endReached = false;
378 
379  if (m_linePositions.size() == 0)
380  {
381  charIndex = 0;
382  }
383 
384  // If we're wrapping from an offset in the text, ensure that any existing data
385  // after the offset gets removed
386 
387  if (charIndex > 0)
388  {
389  // Remove wrapping data past this point
390 
391  // Get the index of the line in which the char index appears
392 
393  int lineIndex = getLineContainingCharIndex(charIndex);
394 
395  // Remove any longest line records that occur from the line index onwards
396 
397  while ((m_longestLines.size() > 0) &&
398  (m_longestLines[m_longestLines.size() - 1].index >= lineIndex))
399  {
400  m_longestLines.pop_back();
401  }
402 
403  // If there are any longest line records remaining, update the text pixel width
404  // The last longest line record will always be the last valid longest line as
405  // the vector is sorted by length
406 
407  if (m_longestLines.size() > 0)
408  {
409  m_textPixelWidth = m_longestLines[m_longestLines.size() - 1].width;
410  }
411  else
412  {
413  m_textPixelWidth = 0;
414  }
415 
416  // Remove any wrapping data from after this line index onwards
417 
418  while ((m_linePositions.size() > 0) &&
419  (m_linePositions.size() - 1 > (int)lineIndex))
420  {
422  }
423 
424  // Adjust start position of wrapping loop so that it starts with
425  // the current line index
426 
427  if (m_linePositions.size() > 0)
428  {
429  pos = m_linePositions[m_linePositions.size() - 1];
430  }
431  }
432  else
433  {
434  // Remove all wrapping data
435 
436  // Wipe the width variable
437 
438  m_textPixelWidth = 0;
439 
440  // Empty existing longest lines
441 
442  m_longestLines.clear();
443 
444  // Empty existing line positions
445 
447 
448  // Push first line start into vector
449 
451  }
452 
453  // Loop through string until the end
454 
455  CStringIterator *iterator = newStringIterator();
456 
457  while (!endReached)
458  {
459  breakIndex = 0;
460  lineWidth = 0;
461 
462  if (iterator->moveTo(pos))
463  {
464  // Search for line breaks and valid breakpoints until we
465  // exceed the width of the text field or we run out of
466  // string to process
467 
468  while (lineWidth + m_font->getCharWidth(iterator->getChar()) <= m_width)
469  {
470  lineWidth += m_font->getCharWidth(iterator->getChar());
471 
472  // Check for line return
473 
474  if (iterator->getChar() == '\n')
475  {
476  // Remember this breakpoint
477 
478  breakIndex = iterator->getIndex();
479  break;
480  }
481  else if ((iterator->getChar() == ' ') ||
482  (iterator->getChar() == ',') ||
483  (iterator->getChar() == '.') ||
484  (iterator->getChar() == '-') ||
485  (iterator->getChar() == ':') ||
486  (iterator->getChar() == ';') ||
487  (iterator->getChar() == '?') ||
488  (iterator->getChar() == '!') ||
489  (iterator->getChar() == '+') ||
490  (iterator->getChar() == '=') ||
491  (iterator->getChar() == '/') ||
492  (iterator->getChar() == '\0'))
493  {
494  // Remember the most recent breakpoint
495 
496  breakIndex = iterator->getIndex();
497  }
498 
499  // Move to the next character
500 
501  if (!iterator->moveToNext())
502  {
503  // No more text; abort loop
504 
505  endReached = true;
506  break;
507  }
508  }
509  }
510  else
511  {
512  endReached = true;
513  }
514 
515  if ((!endReached) && (iterator->getIndex() > pos))
516  {
517  // Process any found data
518 
519  // If we didn't find a breakpoint split at the current position
520 
521  if (breakIndex == 0)
522  {
523  breakIndex = iterator->getIndex() - 1;
524  }
525 
526  // Trim blank space from the start of the next line
527 
528  CStringIterator *breakIterator = newStringIterator();
529 
530  if (breakIterator->moveTo(breakIndex + 1))
531  {
532  while (breakIterator->getChar() == ' ')
533  {
534  if (breakIterator->moveToNext())
535  {
536  breakIndex++;
537  }
538  else
539  {
540  break;
541  }
542  }
543  }
544 
545  delete breakIterator;
546 
547  // Add the start of the next line to the vector
548 
549  pos = breakIndex + 1;
551 
552  // Is this the longest line observed so far?
553 
554  if (lineWidth > m_textPixelWidth)
555  {
556  m_textPixelWidth = lineWidth;
557 
558  // Push the description of the line into the longest lines
559  // vector (note that we store the index in m_linePositions that
560  // refers to the start of the line, *not* the position of the
561  // line in the char array)
562 
563  LongestLine line;
564  line.index = m_linePositions.size() - 2;
565  line.width = lineWidth;
566  m_longestLines.push_back(line);
567  }
568  }
569  else if (!endReached)
570  {
571  // Add a blank row if we're not at the end of the string
572 
573  pos++;
575  }
576  }
577 
578  // Add marker indicating end of text
579  // If we reached the end of the text, append the stopping point
580 
581  if (m_linePositions[m_linePositions.size() - 1] != getLength() + 1)
582  {
584  }
585 
586  delete iterator;
587 
588  // Calculate the total height of the text
589 
591 
592  // Ensure height is always at least one row
593 
594  if (m_textPixelHeight == 0)
595  {
597  }
598 }
599 
600 /**
601  * Get the index of the line of text that contains the specified index
602  * within the raw char array.
603  *
604  * @param index The index to locate within the wrapped lines of text.
605  * @return The number of the line of wrapped text that contains the
606  * specified index.
607  */
608 
609 const int CText::getLineContainingCharIndex(const int index) const
610 {
611  // Early exit if there is no existing line data
612 
613  if (m_linePositions.size() == 0)
614  {
615  return 0;
616  }
617 
618  // Early exit if the character is in the last row
619 
620  if (index >= m_linePositions[m_linePositions.size() - 2])
621  {
622  return m_linePositions.size() - 2;
623  }
624 
625  // Binary search the line vector for the line containing the supplied index
626 
627  int bottom = 0;
628  int top = m_linePositions.size() - 1;
629  int mid;
630 
631  while (bottom <= top)
632  {
633  // Standard binary search
634 
635  mid = (bottom + top) >> 1;
636 
637  if (index < m_linePositions[mid])
638  {
639  // Index is somewhere in the lower search space
640 
641  top = mid - 1;
642  }
643  else if (index > m_linePositions[mid])
644  {
645  // Index is somewhere in the upper search space
646 
647  bottom = mid + 1;
648  }
649  else if (index == m_linePositions[mid])
650  {
651  // Located the index
652 
653  return mid;
654  }
655 
656  // Check to see if we've moved past the line that contains the index
657  // We have to do this because the index we're looking for can be within
658  // a line; it isn't necessarily the start of a line (which is what is
659  // stored in the m_linePositions vector)
660 
661  if (index > m_linePositions[top])
662  {
663  // Search index falls within the line represented by the top position
664 
665  return top;
666  }
667  else if (index < m_linePositions[bottom])
668  {
669  // Search index falls within the line represented by the bottom position
670 
671  return bottom - 1;
672  }
673  }
674 
675  // Line cannot be found
676 
677  return 0;
678 }
const int getLineContainingCharIndex(const int index) const
Definition: ctext.cxx:609
nxgl_coord_t getCharWidth(nxwidget_char_t letter) const
Definition: cnxfont.cxx:254
const int getLineLength(const int lineNumber) const
Definition: ctext.cxx:240
nxgl_coord_t m_lineSpacing
Definition: ctext.hxx:129
void stripTopLines(const int lines)
Definition: ctext.cxx:334
TNxArray< int > m_linePositions
Definition: ctext.hxx:124
const int getLineTrimmedLength(const int lineNumber) const
Definition: ctext.cxx:258
CNxFont * m_font
Definition: ctext.hxx:123
virtual void setText(const CNxString &text)
Definition: ctext.cxx:115
void append(const CNxString &text)
Definition: cnxstring.cxx:267
void pop_back(void)
Definition: tnxarray.hxx:283
const nxgl_coord_t getLineTrimmedPixelLength(const int lineNumber) const
Definition: ctext.cxx:311
nxgl_coord_t getStringWidth(const CNxString &text) const
Definition: cnxfont.cxx:142
virtual void append(const CNxString &text)
Definition: ctext.cxx:151
void remove(const int startIndex)
Definition: cnxstring.cxx:398
int32_t m_textPixelHeight
Definition: ctext.hxx:130
const nxgl_coord_t getLinePixelLength(const int lineNumber) const
Definition: ctext.cxx:297
CText(CNxFont *font, const CNxString &text, nxgl_coord_t width)
Definition: ctext.cxx:100
uint16_t nxwidget_char_t
Definition: nxconfig.hxx:454
const int getLineStartIndex(const int line) const
Definition: ctext.hxx:382
nxwidget_char_t getChar(void) const
nxgl_coord_t m_width
Definition: ctext.hxx:134
void clear()
Definition: tnxarray.hxx:428
void setText(const CNxString &text)
Definition: cnxstring.cxx:185
virtual void insert(const CNxString &text, const int index)
Definition: ctext.cxx:164
uint8_t m_textPixelWidth
Definition: ctext.hxx:132
CNxFont * getFont(void) const
Definition: ctext.cxx:323
void wrap(void)
Definition: ctext.cxx:358
void push_back(const T &value)
Definition: tnxarray.hxx:267
void setFont(CNxFont *font)
Definition: ctext.cxx:227
const uint8_t getHeight(void) const
Definition: cnxfont.hxx:229
const unsigned int getLength(void) const
Definition: cnxstring.hxx:325
const bool isCharBlank(const nxwidget_char_t letter) const
Definition: cnxfont.cxx:93
void setWidth(nxgl_coord_t width)
Definition: ctext.cxx:215
CStringIterator * newStringIterator(void) const
Definition: cnxstring.cxx:156
TNxArray< LongestLine > m_longestLines
Definition: ctext.hxx:126
const int getLineCount(void) const
Definition: ctext.hxx:327
void insert(const CNxString &text, const int index)
Definition: cnxstring.cxx:295
const int size(void) const
Definition: tnxarray.hxx:261
virtual void remove(const int startIndex)
Definition: ctext.cxx:176
void setLineSpacing(nxgl_coord_t lineSpacing)
Definition: ctext.cxx:202