NXWidgets  1.19
cmultilinetextbox.cxx
Go to the documentation of this file.
1 /****************************************************************************
2  * NxWidgets/libnxwidgets/src/cmultilinetextbox.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 "cwidgetcontrol.hxx"
80 #include "cnxfont.hxx"
81 #include "ctext.hxx"
82 #include "cgraphicsport.hxx"
83 #include "singletons.hxx"
84 #include "cstringiterator.hxx"
85 #include "cnxtimer.hxx"
86 #include "cmultilinetextbox.hxx"
87 
88 /****************************************************************************
89  * Pre-Processor Definitions
90  ****************************************************************************/
91 
92 /****************************************************************************
93  * Method Implementations
94  ****************************************************************************/
95 
96 using namespace NXWidgets;
97 
98 /**
99  * Constructor.
100  *
101  * @param pWidgetControl The widget control for the display.
102  * @param x The x coordinate of the text box, relative to its parent.
103  * @param y The y coordinate of the text box, relative to its parent.
104  * @param width The width of the textbox.
105  * @param height The height of the textbox.
106  * @param text Pointer to a string to display in the textbox.
107  * @param flags Standard widget flag options.
108  * @param maxRows The maximum number of rows the textbox can track. Adding
109  * text beyond this number will cause rows at the start of the text to be
110  * forgotten; text is essentially stored as a queue, and adding to the back
111  * of a full queue causes the front items to be popped off. Setting this to
112  * 0 will make the textbox track only the visible rows.
113  * @param style The style that the widget should use. If this is not
114  * specified, the widget will use the values stored in the global
115  * g_defaultWidgetStyle object. The widget will copy the properties of
116  * the style into its own internal style object.
117  */
118 
120  nxgl_coord_t x, nxgl_coord_t y,
121  nxgl_coord_t width, nxgl_coord_t height,
122  const CNxString &text, uint32_t flags,
123  nxgl_coord_t maxRows, CWidgetStyle *style)
124 : CScrollingPanel(pWidgetControl, x, y, width, height, flags, style)
125 {
128  m_topRow = 0;
129 
130  // The border is one line thick
131 
132  m_borderSize.top = 1;
133  m_borderSize.right = 1;
134  m_borderSize.bottom = 1;
135  m_borderSize.left = 1;
136 
137  CRect rect;
138  getClientRect(rect);
139  m_text = new CText(getFont(), "", rect.getWidth());
140  m_canvasWidth = rect.getWidth();
141 
142  m_flags.draggable = true;
143  m_flags.doubleClickable = true;
144  m_maxRows = maxRows;
145 
147 
148  // Set maximum rows if value not set
149 
150  if (m_maxRows == 0)
151  {
152  m_maxRows = m_visibleRows + 1;
153  }
154 
155  m_cursorPos = 0;
157  m_wrapCursor = false;
158 
159  setText(text);
160 }
161 
162 /**
163  * Set the horizontal alignment of text within the textbox.
164  *
165  * @param alignment The horizontal position of the text.
166  */
167 
169 {
170  m_hAlignment = alignment;
171  redraw();
172 }
173 
174 /**
175  * Set the vertical alignment of text within the textbox.
176  *
177  * @param alignment The vertical position of the text.
178  */
179 
181 {
182  m_vAlignment = alignment;
183  redraw();
184 }
185 
186 /**
187  * Returns the number of "pages" that the text spans. A page
188  * is defined as the amount of text that can be displayed within
189  * the textbox at one time.
190  *
191  * @return The page count.
192  */
193 
194 const int CMultiLineTextBox::getPageCount(void) const
195 {
196  if (m_visibleRows > 0)
197  {
198  return (m_text->getLineCount() / m_visibleRows) + 1;
199  }
200  else
201  {
202  return 1;
203  }
204 }
205 
206 /**
207  * Returns the current page.
208  *
209  * @return The current page.
210  * @see getPageCount().
211  */
212 
214 {
215  // Calculate the top line of text
216 
217  int topRow = -m_canvasY / m_text->getLineHeight();
218 
219  // Return the page on which the top row falls
220 
221  if (m_visibleRows > 0)
222  {
223  return topRow / m_visibleRows;
224  }
225  else
226  {
227  return 1;
228  }
229 }
230 
231 /**
232  * Set the text displayed in the textbox.
233  *
234  * @param text String to display.
235  */
236 
238 {
239  bool drawingEnabled = m_flags.drawingEnabled;
240  disableDrawing();
241 
242  m_text->setText(text);
243 
244  cullTopLines();
247 
248  if (drawingEnabled)
249  {
250  enableDrawing();
251  }
252  redraw();
253 
255 }
256 
257 /**
258  * Append new text to the end of the current text
259  * displayed in the textbox.
260  *
261  * @param text String to append.
262  */
263 
265 {
266  bool drawingEnabled = m_flags.drawingEnabled;
267  disableDrawing();
268 
269  m_text->append(text);
270 
271  cullTopLines();
274 
275  if (drawingEnabled)
276  {
277  enableDrawing();
278  }
279  redraw();
280 
282 }
283 
284 /**
285  * Remove all characters from the string from the start index onwards.
286  *
287  * @param startIndex Index to remove from.
288  */
289 
290 void CMultiLineTextBox::removeText(const unsigned int startIndex)
291 {
292  removeText(startIndex, m_text->getLength() - startIndex);
293 }
294 
295 /**
296  * Remove specified number of characters from the string from the
297  * start index onwards.
298  *
299  * @param startIndex Index to remove from.
300  * @param count Number of characters to remove.
301  */
302 
303 void CMultiLineTextBox::removeText(const unsigned int startIndex,
304  const unsigned int count)
305 {
306  bool drawingEnabled = m_flags.drawingEnabled;
307  disableDrawing();
308 
309  m_text->remove(startIndex, count);
310 
312  limitCanvasY();
313 
314  moveCursorToPosition(startIndex);
315 
316  if (drawingEnabled)
317  {
318  enableDrawing();
319  }
320  redraw();
321 
323 }
324 
325 /**
326  * Set the font used in the textbox.
327  *
328  * @param font Pointer to the new font.
329  */
330 
332 {
333  bool drawingEnabled = m_flags.drawingEnabled;
334  disableDrawing();
335 
336  m_style.font = font;
337  m_text->setFont(font);
338 
339  cullTopLines();
341  limitCanvasY();
342 
343  if (drawingEnabled)
344  {
345  enableDrawing();
346  }
347  redraw();
348 
350 }
351 
352 /**
353  * Get the length of the text string.
354  *
355  * @return The length of the text string.
356  */
357 
358 const int CMultiLineTextBox::getTextLength(void) const
359 {
360  return m_text->getLength();
361 }
362 
363 /**
364  * Sets the cursor display mode.
365  *
366  * @param cursorMode Determines cursor display mode
367  */
368 
370 {
371  if (m_showCursor != cursorMode)
372  {
373  m_showCursor = (uint8_t)cursorMode;
374  redraw();
375  }
376 }
377 
378 /**
379  * Move the cursor to the text position specified. 0 indicates the start
380  * of the string. If position is greater than the length of the string,
381  * the cursor is moved to the end of the string.
382  *
383  * @param position The new cursor position.
384  */
385 
387 {
389 
390  // Erase existing cursor
391 
392  drawCursor(port);
393 
394  // Force position to within confines of string
395 
396  if (position < 0)
397  {
398  m_cursorPos = 0;
399  }
400  else
401  {
402  int len = (int)m_text->getLength();
403  m_cursorPos = len > position ? position : len;
404  }
405 
406  // Draw cursor in new position
407 
408  drawCursor(port);
409 }
410 
411 /**
412  * Insert text at the specified index.
413  *
414  * @param text The text to insert.
415  * @param index Index at which to insert the text.
416  */
417 
419  const unsigned int index)
420 {
421  bool drawingEnabled = m_flags.drawingEnabled;
422  disableDrawing();
423 
424  m_text->insert(text, index);
425 
426  cullTopLines();
428 
429  moveCursorToPosition(index + text.getLength());
430 
431  if (drawingEnabled)
432  {
433  enableDrawing();
434  }
435  redraw();
436 
438 }
439 
440 /**
441  * Insert text at the current cursor position.
442  *
443  * @param text The text to insert.
444  */
445 
447 {
448  insertText(text, getCursorPosition());
449 }
450 
451 /**
452  * Handle a keyboard press event.
453  *
454  * @param e The event data.
455  */
456 
458 {
459  nxwidget_char_t key = e.getKey();
460 
461  if (key == KEY_CODE_BACKSPACE)
462  {
463  // Delete character in front of cursor
464 
465  if (m_cursorPos > 0)
466  {
467  removeText(m_cursorPos - 1, 1);
468  }
469  }
470  else if (key != KEY_CODE_NONE)
471  {
472  // Not modifier; append value
473 
474  insertTextAtCursor(key);
475  }
476 }
477 
478 /**
479  * Get the coordinates of the cursor relative to the text.
480  *
481  * @param x Will be populated with the x coordinate of the cursor.
482  * @param y Will be populated with the y coordinate of the cursor.
483  */
484 
485 void CMultiLineTextBox::getCursorCoordinates(nxgl_coord_t &x, nxgl_coord_t &y) const
486 {
487  unsigned int cursorRow = 0;
488 
489  x = 0;
490  y = 0;
491 
492  // Only calculate the cursor position if the cursor isn't at the start
493  // of the text
494 
495  if (m_cursorPos > 0)
496  {
497  // Calculate the row in which the cursor appears
498 
500 
501  // Cursor line offset gives us the distance of the cursor from the
502  // start of the line
503 
504  uint8_t cursorLineOffset = m_cursorPos - m_text->getLineStartIndex(cursorRow);
505 
507  iterator->moveTo(m_text->getLineStartIndex(cursorRow));
508 
509  // Sum the width of each char in the row to find the x coordinate
510 
511  for (int i = 0; i < cursorLineOffset; ++i)
512  {
513  x += getFont()->getCharWidth(iterator->getChar());
514  iterator->moveToNext();
515  }
516 
517  delete iterator;
518  }
519 
520  // Add offset of row to calculated value
521 
522  x += getRowX(cursorRow);
523 
524  // Calculate y coordinate of the cursor
525 
526  y = getRowY(cursorRow);
527 }
528 
529 /**
530  * Gets the index of the character at the specified x coordinate in the
531  * specified row.
532  *
533  * @param x X coordinate of the character.
534  * @param rowIndex Index of the row containing the character.
535  * @return The index of the character at the specified coordinate.
536  */
537 
538 int CMultiLineTextBox::getCharIndexAtCoordinate(nxgl_coord_t x, int rowIndex) const
539 {
540  // Locate the character within the row
541 
542  int startIndex = m_text->getLineStartIndex(rowIndex);
543  int stopIndex = m_text->getLineLength(rowIndex);
544  int width = getRowX(rowIndex);
545  int index = -1;
546 
548  iterator->moveTo(startIndex);
549 
550  width += m_text->getFont()->getCharWidth(iterator->getChar());
551 
552  for (int i = 0; i < stopIndex; ++i)
553  {
554  if (width > x)
555  {
556  if (i == 0)
557  {
558  // If the coordinate is on the left of the text, we add nothing
559  // to the index
560 
561  index = startIndex;
562  }
563  else
564  {
565  // Character within the row.
566  // This is the character that contains the coordinate.
567 
568  index = startIndex + i;
569  }
570  break;
571  }
572 
573  iterator->moveToNext();
574  width += m_text->getFont()->getCharWidth(iterator->getChar());
575  }
576 
577  delete iterator;
578 
579  // If the coordinate is past the last character, index will still be -1.
580  // We need to set it to the last character
581 
582  if (index == -1)
583  {
584  if (rowIndex == m_text->getLineCount() - 1)
585  {
586  // Index past the end point of the text, so return an index
587  // just past the text
588 
589  index = startIndex + stopIndex;
590  }
591  else
592  {
593  // Index at the end of a row, so return the last index of the
594  // row
595 
596  index = startIndex + stopIndex - 1;
597  }
598  }
599 
600  return index;
601 }
602 
603 /**
604  * Get the index of the character at the specified coordinates.
605  *
606  * @param x X coordinate of the character.
607  * @param y Y coordinate of the character.
608  * @return The index of the character at the specified coordinates.
609  */
610 
611 unsigned int
612 CMultiLineTextBox::getCharIndexAtCoordinates(nxgl_coord_t x, nxgl_coord_t y) const
613 {
614  int rowIndex = getRowContainingCoordinate(y);
615  return getCharIndexAtCoordinate(x, rowIndex);
616 }
617 
618 /**
619  * Get the row containing the specified Y coordinate.
620  *
621  * @param y Y coordinate to locate.
622  * @return The index of the row containing the specified Y coordinate.
623  */
624 
626 {
627  int row = -1;
628 
629  // Locate the row containing the character
630 
631  for (int i = 0; i < m_text->getLineCount(); ++i)
632  {
633  // Abort search if we've found the row below the y coordinate
634 
635  if (getRowY(i) > y)
636  {
637  if (i == 0)
638  {
639  // If the coordinate is above the text, we return the top
640  // row
641 
642  row = 0;
643  }
644  else
645  {
646  // Row within the text, so return the previous row - this is
647  // the row that contains the coordinate.
648 
649  row = i - 1;
650  }
651 
652  break;
653  }
654  }
655 
656  // If the coordinate is below the text, row will still be -1.
657  // We need to set it to the last row
658 
659  if (row == -1)
660  {
661  row = m_text->getLineCount() - 1;
662  }
663 
664  return row;
665 }
666 
667 /**
668  * Draw the area of this widget that falls within the clipping region.
669  * Called by the redraw() function to draw all visible regions.
670  *
671  * @param port The CGraphicsPort to draw to.
672  * @see redraw()
673  */
674 
676 {
677  drawText(port);
678 
679  // Draw the cursor
680 
681  drawCursor(port);
682 }
683 
684 /**
685  * Draw the area of this widget that falls within the clipping region.
686  * Called by the redraw() function to draw all visible regions.
687  *
688  * @param port The CGraphicsPort to draw to.
689  * @see redraw()
690  */
691 
693 {
694  port->drawFilledRect(getX(), getY(), getWidth(), getHeight(),
696 
697  // Stop drawing if the widget indicates it should not have an outline
698 
699  if (!isBorderless())
700  {
701  port->drawBevelledRect(getX(), getY(), getWidth(), getHeight(),
703  }
704 }
705 
706 /**
707  * Move cursor one character to the left.
708  */
709 
711 {
712  if (m_cursorPos > 0)
713  {
715  }
716 
717  jumpToCursor();
718 }
719 
720 /**
721  * Move cursor one character to the right.
722  */
723 
725 {
726  if (m_cursorPos < (int)m_text->getLength())
727  {
729  }
730 
731  jumpToCursor();
732 }
733 
734 /**
735  * Move cursor one row upwards.
736  */
737 
739 {
740  nxgl_coord_t cursorX = 0;
741  nxgl_coord_t cursorY = 0;
742 
743  getCursorCoordinates(cursorX, cursorY);
744 
745  // Get the midpoint of the cursor. We use the midpoint to ensure that
746  // the cursor does not drift off to the left as it moves up the text, which
747  // is a problem when we use the left edge as the reference point when the
748  // font is proportional
749 
750  cursorX += m_text->getFont()->getCharWidth(m_text->getCharAt(m_cursorPos)) >> 1;
751 
752  // Locate the character above the midpoint
753 
754  int index = getCharIndexAtCoordinates(cursorX, cursorY + m_text->getLineHeight());
755  moveCursorToPosition(index);
756  jumpToCursor();
757 }
758 
759 /**
760  * Move cursor one row downwards.
761  */
762 
764 {
765  nxgl_coord_t cursorX = 0;
766  nxgl_coord_t cursorY = 0;
767 
768  getCursorCoordinates(cursorX, cursorY);
769 
770  // Get the midpoint of the cursor. We use the midpoint to ensure that
771  // the cursor does not drift off to the left as it moves up the text, which
772  // is a problem when we use the left edge as the reference point when the
773  // font is proportional
774 
775  cursorX += m_text->getFont()->getCharWidth(m_text->getCharAt(m_cursorPos)) >> 1;
776 
777  // Locate the character above the midpoint
778 
779  int index = getCharIndexAtCoordinates(cursorX, cursorY - m_text->getLineHeight());
780  moveCursorToPosition(index);
781  jumpToCursor();
782 }
783 
784 /**
785  * Ensures that the textbox only contains the maximum allowed
786  * number of rows by culling any excess rows from the top of
787  * the text.
788  *
789  * @return True if lines were removed from the text; false if not.
790  */
791 
793 {
794  // Ensure that we have the correct number of rows
795 
796  if (m_text->getLineCount() > m_maxRows)
797  {
799  return true;
800  }
801 
802  return false;
803 }
804 
805 /**
806  * Ensures that the canvas height is the height of the widget,
807  * if the widget exceeds the size of the text, or the height of
808  * the text if the text exceeds the size of the widget.
809  */
810 
812 {
814 
815  CRect rect;
816  getClientRect(rect);
817  if (m_canvasHeight < rect.getHeight())
818  {
819  m_canvasHeight = rect.getHeight();
820  }
821 }
822 
823 /**
824  * Ensures that the canvas cannot scroll beyond its height.
825  */
826 
828 {
829  CRect rect;
830  getClientRect(rect);
831 
832  // Ensure that the visible portion of the canvas is not less than the
833  // height of the viewer window
834 
835  if (m_canvasY + m_canvasHeight < rect.getHeight())
836  {
838  }
839 }
840 
841 /**
842  * Jumps to the cursor coordinates of the text.
843  */
844 
846 {
847  // Get the co-odinates of the cursor
848 
849  nxgl_coord_t cursorX;
850  nxgl_coord_t cursorY;
851 
852  getCursorCoordinates(cursorX, cursorY);
853 
854  // Work out which row the cursor falls within
855 
857  nxgl_coord_t rowY = getRowY(cursorRow);
858 
859  // If the cursor is outside the visible portion of the canvas, jump to it
860 
861  CRect rect;
862  getClientRect(rect);
863 
864  if (rowY + m_text->getLineHeight() + m_canvasY > rect.getHeight())
865  {
866  // Cursor is below the visible portion of the canvas, so
867  // jump down so that the cursor's row is the bottom row of
868  // text
869 
870  jump(0, -(rowY + m_text->getLineHeight() - rect.getHeight()));
871  }
872  else if (rowY + m_canvasY < 0)
873  {
874  // Cursor is above the visible portion of the canvas, so
875  // jump up so that the cursor's row is the top row of text
876 
877  jump(0, -cursorY);
878  }
879 }
880 
881 /**
882  * Jumps to the bottom of the text.
883  */
884 
886 {
887  CRect rect;
888  getClientRect(rect);
889  jump(0, -(m_canvasHeight - rect.getHeight()));
890 }
891 
892 /**
893  * Resize the textbox to the new dimensions.
894  *
895  * @param width The new width.
896  * @param height The new height.
897  */
898 
899 void CMultiLineTextBox::onResize(nxgl_coord_t width, nxgl_coord_t height)
900 {
901  // Ensure the base class resize method is called
902 
903  CScrollingPanel::onResize(width, height);
904 
905  // Resize the canvas' width
906 
907  CRect rect;
908  getClientRect(rect);
909 
910  m_canvasWidth = rect.getWidth();
911  m_canvasHeight = rect.getHeight();
912  m_canvasX = 0;
913  m_canvasY = 0;
914 
916 
917  // Re-wrap the text
918 
920  m_text->wrap();
921 
922  bool raiseEvent = cullTopLines();
924  limitCanvasY();
925 
926  if (raiseEvent)
927  {
929  }
930 }
931 
932 /**
933  * Starts the dragging system.
934  *
935  * @param x The x coordinate of the click.
936  * @param y The y coordinate of the click.
937  */
938 
939 void CMultiLineTextBox::onClick(nxgl_coord_t x, nxgl_coord_t y)
940 {
941  startDragging(x, y);
942 
943  // Move cursor to clicked coordinates
944 
945  CRect rect;
946  getClientRect(rect);
947 
948  // Adjust x and y from screen coordinates to canvas coordinates
949 
950  nxgl_coord_t canvasRelativeX = x - getX() - rect.getX() - m_canvasX;
951  nxgl_coord_t canvasRelativeY = y - getY() - rect.getY() - m_canvasY;
952 
953  moveCursorToPosition(getCharIndexAtCoordinates(canvasRelativeX, canvasRelativeY));
954 }
955 
956 /**
957  * Opens the keyboard on the bottom display.
958  *
959  * @param x The x coordinates of the click.
960  * @param y The y coordinates of the click.
961  */
962 
963 void CMultiLineTextBox::onDoubleClick(nxgl_coord_t x, nxgl_coord_t y)
964 {
965 }
966 
967 /**
968  * Handles cursor control events. Moves the cursor
969  * in the direction selected.
970  *
971  * @param key The key that was pressed.
972  */
973 
975 {
976  ECursorControl control = e.getCursorControl();
977 
978  switch (control)
979  {
980  case CURSOR_LEFT:
981  moveCursorLeft();
982  break;
983 
984  case CURSOR_RIGHT:
985  moveCursorRight();
986  break;
987 
988  case CURSOR_UP:
989  moveCursorDown();
990  break;
991 
992  case CURSOR_DOWN:
993  moveCursorUp();
994  break;
995 
996  default:
997  // Not interested in other controls
998 
999  break;
1000  }
1001 }
1002 
1003 /**
1004  * Handle a keyboard press event.
1005  *
1006  * @param e The event data.
1007  */
1008 
1009  void handleKeyPressEvent(const CWidgetEventArgs &e);
1010 
1011 /**
1012  * Gets the x position of a row of text based on the width of the row and the
1013  * type of horizontal alignment currently set.
1014  *
1015  * @param row The index of the row.
1016  * @return The x coordinate of the row.
1017  */
1018 
1019 nxgl_coord_t CMultiLineTextBox::getRowX(int row) const
1020 {
1021  CRect rect;
1022  getClientRect(rect);
1023 
1024  uint8_t rowLength = m_text->getLineTrimmedLength(row);
1025  uint8_t rowPixelWidth = m_text->getFont()->getStringWidth(*m_text, m_text->getLineStartIndex(row), rowLength);
1026 
1027  // Calculate horizontal position
1028 
1029  switch (m_hAlignment)
1030  {
1032  return (rect.getWidth() - rowPixelWidth) >> 1;
1033 
1035  return 0;
1036 
1038  return rect.getWidth() - rowPixelWidth;
1039  }
1040 
1041  // Will never be reached
1042 
1043  return 0;
1044 }
1045 
1046 /**
1047  * Gets the y position of the specified row of text based on the type of
1048  * vertical alignment currently set.
1049  *
1050  * @param row The row number to find the y coordinate of.
1051  * @return The y coordinate of the specified row of text.
1052  */
1053 
1054 nxgl_coord_t CMultiLineTextBox::getRowY(int row) const
1055 {
1056  // If the amount of text exceeds the size of the widget, force
1057  // the text to be top-aligned
1058 
1059  if (m_visibleRows <= m_text->getLineCount())
1060  {
1061  return row * m_text->getLineHeight();
1062  }
1063 
1064  // All text falls within the textbox, so obey the alignment
1065  // options
1066 
1067  nxgl_coord_t textY = 0;
1068  nxgl_coord_t startPos = 0;
1069 
1070  int canvasRows = 0;
1071  int textRows = 0;
1072 
1073  CRect rect;
1074  getClientRect(rect);
1075 
1076  // Calculate vertical position
1077 
1078  switch (m_vAlignment)
1079  {
1081 
1082  // Calculate the maximum number of rows
1083 
1084  canvasRows = m_canvasHeight / m_text->getLineHeight();
1085  textY = row * m_text->getLineHeight();
1086 
1087  // Get the number of rows of text
1088 
1089  textRows = m_text->getLineCount();
1090 
1091  // Ensure there's always one row
1092 
1093  if (textRows == 0)
1094  {
1095  textRows = 1;
1096  }
1097 
1098  // Calculate the start position of the block of text
1099 
1100  startPos = ((canvasRows - textRows) * m_text->getLineHeight()) >> 1;
1101 
1102  // Calculate the row Y coordinate
1103 
1104  textY = startPos + textY;
1105  break;
1106 
1108  textY = row * m_text->getLineHeight();
1109  break;
1110 
1112  textY = rect.getHeight() - (((m_text->getLineCount() - row) * m_text->getLineHeight()));
1113  break;
1114  }
1115 
1116  return textY;
1117 }
1118 
1119 /**
1120  * Return true if the cursor is visible
1121  */
1122 
1124 {
1125  return (m_showCursor == SHOW_CURSOR_ALWAYS ||
1127 }
1128 
1129 /**
1130  * Gets the character under the cursor.
1131  *
1132  * @return The character under the cursor.
1133  */
1134 
1136 {
1137  if (m_cursorPos < m_text->getLength())
1138  {
1139  return m_text->getCharAt(m_cursorPos);
1140  }
1141  else
1142  {
1143  return ' ';
1144  }
1145 }
1146 
1147 /**
1148  * Works out the number of visible rows within the textbox.
1149  */
1150 
1152 {
1153  CRect rect;
1154  getClientRect(rect);
1155 
1157 }
1158 
1159 /**
1160  * Draws text.
1161  *
1162  * @param port The CGraphicsPort to draw to.
1163  */
1164 
1166 {
1167  // Early exit if there is no text to display
1168 
1169  if (m_text->getLineCount() == 0)
1170  {
1171  return;
1172  }
1173 
1174  // Determine the top and bottom rows within the graphicsport's clip rect.
1175  // We only draw these rows in order to increase the speed of the routine.
1176 
1177  int regionY = -m_canvasY; // Y coord of canvas visible region
1178  int topRow = getRowContainingCoordinate(regionY);
1179  int bottomRow = getRowContainingCoordinate(regionY + m_rect.getHeight());
1180 
1181  // Early exit checks
1182 
1183  if ((topRow < 0) && (bottomRow < 0))
1184  {
1185  return;
1186  }
1187 
1188  if ((bottomRow >= m_text->getLineCount()) && (topRow >= m_text->getLineCount()))
1189  {
1190  return;
1191  }
1192 
1193  // Prevent overflows
1194 
1195  if (topRow < 0)
1196  {
1197  topRow = 0;
1198  }
1199 
1200  if (bottomRow >= m_text->getLineCount())
1201  {
1202  bottomRow = m_text->getLineCount() - 1;
1203  }
1204 
1205  // Draw lines of text
1206 
1207  int currentRow = topRow;
1208 
1209  // Draw all rows in this region
1210 
1211  while (currentRow <= bottomRow)
1212  {
1213  drawRow(port, currentRow);
1214  currentRow++;
1215  }
1216 }
1217 
1218 /**
1219  * Draws the cursor.
1220  *
1221  * @param port The CGraphicsPort to draw to.
1222  */
1223 
1225 {
1226  // Get the cursor coordinates
1227 
1228  if (isCursorVisible())
1229  {
1230  nxgl_coord_t cursorX = 0;
1231  nxgl_coord_t cursorY = 0;
1232 
1233  getCursorCoordinates(cursorX, cursorY);
1234 
1235  // Adjust for canvas offsets
1236 
1237  cursorX += m_canvasX;
1238  cursorY += m_canvasY;
1239 
1240  // Draw cursor
1241 
1242  port->invert(cursorX, cursorY,
1244  m_text->getFont()->getHeight());
1245  }
1246 }
1247 
1248 /**
1249  * Draws a single line of text.
1250  *
1251  * @param port The CGraphicsPort to draw to.
1252  * @param row The index of the row to draw.
1253  */
1254 
1256 {
1257  // Get the drawing region
1258 
1259  CRect rect;
1260  getRect(rect);
1261 
1262  uint8_t rowLength = m_text->getLineTrimmedLength(row);
1263 
1264  struct nxgl_point_s pos;
1265  pos.x = getRowX(row) + m_canvasX + rect.getX();
1266  pos.y = getRowY(row) + m_canvasY + rect.getY();
1267 
1268  // Determine the background and text color
1269 
1270  nxgl_mxpixel_t textColor;
1271 
1272  if (!isEnabled())
1273  {
1274  textColor = getDisabledTextColor();
1275  }
1276  else
1277  {
1278  textColor = getEnabledTextColor();
1279  }
1280 
1281  // And draw the text using the selected color
1282 
1283  port->drawText(&pos, &rect, m_text->getFont(), *m_text,
1284  m_text->getLineStartIndex(row), rowLength, textColor);
1285 }
const int getLineContainingCharIndex(const int index) const
Definition: ctext.cxx:609
void drawCursor(CGraphicsPort *port)
nxgl_coord_t getCharWidth(nxwidget_char_t letter) const
Definition: cnxfont.cxx:254
WidgetBorderSize m_borderSize
Definition: cnxwidget.hxx:208
const int getLineLength(const int lineNumber) const
Definition: ctext.cxx:240
bool isBorderless(void) const
Definition: cnxwidget.hxx:549
nxgl_mxpixel_t getShadowEdgeColor(void) const
Definition: cnxwidget.hxx:749
const uint8_t getLineHeight(void) const
Definition: ctext.hxx:316
nxgl_coord_t getHeight(void) const
Definition: crect.hxx:204
CGraphicsPort * getGraphicsPort(void)
void stripTopLines(const int lines)
Definition: ctext.cxx:334
virtual void removeText(const unsigned int startIndex)
void disableDrawing(void)
Definition: cnxwidget.hxx:908
void getRect(CRect &rect) const
Definition: cnxwidget.cxx:448
nxgl_coord_t getWidth(void) const
Definition: crect.hxx:181
void drawBevelledRect(nxgl_coord_t x, nxgl_coord_t y, nxgl_coord_t width, nxgl_coord_t height, nxgl_mxpixel_t shineColor, nxgl_mxpixel_t shadowColor)
nxgl_coord_t getX(void) const
Definition: crect.hxx:160
const int getLineTrimmedLength(const int lineNumber) const
Definition: ctext.cxx:258
virtual void onDoubleClick(nxgl_coord_t x, nxgl_coord_t y)
virtual void limitCanvasHeight(void)
void drawText(CGraphicsPort *port)
nxgl_mxpixel_t getBackgroundColor(void) const
Definition: cnxwidget.hxx:716
virtual void setFont(CNxFont *font)
virtual void setText(const CNxString &text)
Definition: ctext.cxx:115
virtual const int getCurrentPage(void) const
virtual void getCursorCoordinates(nxgl_coord_t &x, nxgl_coord_t &y) const
const int32_t getPixelHeight(void) const
Definition: ctext.hxx:282
virtual int getCharIndexAtCoordinate(nxgl_coord_t x, int rowIndex) const
virtual void setText(const CNxString &text)
bool isEnabled(void) const
Definition: cnxwidget.cxx:381
nxgl_coord_t getStringWidth(const CNxString &text) const
Definition: cnxfont.cxx:142
virtual void append(const CNxString &text)
Definition: ctext.cxx:151
virtual bool isCursorVisible(void) const
nxgl_mxpixel_t getEnabledTextColor(void) const
Definition: cnxwidget.hxx:782
virtual void onClick(nxgl_coord_t x, nxgl_coord_t y)
CWidgetControl * m_widgetControl
nxgl_coord_t getHeight(void) const
Definition: cnxwidget.hxx:593
virtual unsigned int getCharIndexAtCoordinates(nxgl_coord_t x, nxgl_coord_t y) const
virtual void setTextAlignmentHoriz(TextAlignmentHoriz alignment)
void getClientRect(CRect &rect) const
Definition: cnxwidget.cxx:421
void handleKeyPressEvent(const CWidgetEventArgs &e)
virtual void onResize(nxgl_coord_t width, nxgl_coord_t height)
virtual const int getPageCount(void) const
virtual void setTextAlignmentVert(TextAlignmentVert alignment)
uint16_t nxwidget_char_t
Definition: nxconfig.hxx:454
virtual void drawBorder(CGraphicsPort *port)
const int getLineStartIndex(const int line) const
Definition: ctext.hxx:382
CMultiLineTextBox(const CMultiLineTextBox &multiLineTextBox)
nxwidget_char_t getChar(void) const
void drawRow(CGraphicsPort *port, int row)
void drawFilledRect(nxgl_coord_t x, nxgl_coord_t y, nxgl_coord_t width, nxgl_coord_t height, nxgl_mxpixel_t color)
virtual void insertTextAtCursor(const CNxString &ext)
virtual void insert(const CNxString &text, const int index)
Definition: ctext.cxx:164
CNxFont * getFont(void) const
Definition: cnxwidget.hxx:705
const nxwidget_char_t getKey(void) const
CNxFont * getFont(void) const
Definition: ctext.cxx:323
void invert(nxgl_coord_t x, nxgl_coord_t y, nxgl_coord_t width, nxgl_coord_t height)
nxgl_mxpixel_t getShineEdgeColor(void) const
Definition: cnxwidget.hxx:738
virtual const int getTextLength(void) const
void wrap(void)
Definition: ctext.cxx:358
nxwidget_char_t getCursorChar(void) const
int getRowContainingCoordinate(nxgl_coord_t y) const
void setFont(CNxFont *font)
Definition: ctext.cxx:227
void enableDrawing(void)
Definition: cnxwidget.hxx:917
const uint8_t getHeight(void) const
Definition: cnxfont.hxx:229
CWidgetEventHandlerList * m_widgetEventHandlers
Definition: cnxwidget.hxx:191
virtual void appendText(const CNxString &text)
const unsigned int getLength(void) const
Definition: cnxstring.hxx:325
virtual void onResize(nxgl_coord_t width, nxgl_coord_t height)
Definition: cnxwidget.hxx:401
void setWidth(nxgl_coord_t width)
Definition: ctext.cxx:215
nxgl_coord_t getRowX(int row) const
virtual const int getCursorPosition(void) const
const nxwidget_char_t getCharAt(int index) const
Definition: cnxstring.cxx:467
nxgl_coord_t getY(void) const
Definition: cnxwidget.cxx:261
virtual void insertText(const CNxString &text, const unsigned int index)
nxgl_coord_t getWidth(void) const
Definition: cnxwidget.hxx:582
nxgl_coord_t getX(void) const
Definition: cnxwidget.cxx:245
CStringIterator * newStringIterator(void) const
Definition: cnxstring.cxx:156
virtual void moveCursorToPosition(const int position)
nxgl_coord_t getY(void) const
Definition: crect.hxx:170
CWidgetStyle m_style
Definition: cnxwidget.hxx:183
nxgl_mxpixel_t getDisabledTextColor(void) const
Definition: cnxwidget.hxx:771
const int getLineCount(void) const
Definition: ctext.hxx:327
void handleCursorControlEvent(const CWidgetEventArgs &e)
nxgl_coord_t getRowY(int row) const
void startDragging(nxgl_coord_t x, nxgl_coord_t y)
Definition: cnxwidget.cxx:1542
void drawText(struct nxgl_point_s *pos, CRect *bound, CNxFont *font, const CNxString &string)
virtual void drawContents(CGraphicsPort *port)
virtual void remove(const int startIndex)
Definition: ctext.cxx:176
virtual void jump(int32_t x, int32_t y)
const ECursorControl getCursorControl(void) const
bool hasFocus(void) const
Definition: cnxwidget.hxx:472