summaryrefslogtreecommitdiff
path: root/grlib/listbox.c
diff options
context:
space:
mode:
authorYuval Adam <yuval@y3xz.com>2015-03-13 12:24:02 +0200
committerYuval Adam <yuval@y3xz.com>2015-03-13 12:24:02 +0200
commit788db64b8642bf31de6930d18a62177c64163ee0 (patch)
tree38e8291d1fcfceac7ad2a393ad74396ed6453d24 /grlib/listbox.c
parentc686407d271989d5c4bcc1b3c4f5bb6fcd1846a8 (diff)
Add grlib, fixes #5
Diffstat (limited to 'grlib/listbox.c')
-rw-r--r--grlib/listbox.c750
1 files changed, 750 insertions, 0 deletions
diff --git a/grlib/listbox.c b/grlib/listbox.c
new file mode 100644
index 0000000..dc1f4f3
--- /dev/null
+++ b/grlib/listbox.c
@@ -0,0 +1,750 @@
+//*****************************************************************************
+//
+// listbox.c - A listbox widget.
+//
+// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved.
+// Software License Agreement
+//
+// Texas Instruments (TI) is supplying this software for use solely and
+// exclusively on TI's microcontroller products. The software is owned by
+// TI and/or its suppliers, and is protected under applicable copyright
+// laws. You may not combine this software with "viral" open-source
+// software in order to form a larger program.
+//
+// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
+// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
+// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
+// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+// DAMAGES, FOR ANY REASON WHATSOEVER.
+//
+// This is part of revision 2.1.0.12573 of the Tiva Graphics Library.
+//
+//*****************************************************************************
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "driverlib/debug.h"
+#include "grlib/grlib.h"
+#include "grlib/widget.h"
+#include "grlib/listbox.h"
+
+//*****************************************************************************
+//
+//! \addtogroup listbox_api
+//! @{
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Make sure that the abs() macro is defined.
+//
+//*****************************************************************************
+#ifndef abs
+#define abs(a) (((a) >= 0) ? (a) : (-(a)))
+#endif
+
+//*****************************************************************************
+//
+// Make sure min and max are defined.
+//
+//*****************************************************************************
+#ifndef min
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a, b) (((a) < (b)) ? (b) : (a))
+#endif
+
+//*****************************************************************************
+//
+//! Draws the contents of a listbox.
+//!
+//! \param psWidget is a pointer to the listbox widget to be drawn.
+//!
+//! This function draws the contents of a listbox on the display. This is
+//! called in response to a \b #WIDGET_MSG_PAINT message.
+//!
+//! \return None.
+//
+//*****************************************************************************
+static void
+ListBoxPaint(tWidget *psWidget)
+{
+ tListBoxWidget *pListBox;
+ tContext sCtx;
+ tRectangle sWidgetRect, sLineRect;
+ int16_t i16Height;
+ int32_t i32Width;
+ uint16_t ui16String;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(psWidget);
+
+ //
+ // Convert the generic widget pointer into a listbox widget pointer.
+ //
+ pListBox = (tListBoxWidget *)psWidget;
+
+ //
+ // Initialize a drawing context.
+ //
+ GrContextInit(&sCtx, psWidget->psDisplay);
+ GrContextFontSet(&sCtx, pListBox->psFont);
+
+ //
+ // Initialize the clipping region based on the extents of this listbox.
+ //
+ sWidgetRect = psWidget->sPosition;
+ GrContextClipRegionSet(&sCtx, &sWidgetRect);
+
+ //
+ // See if the listbox outline style is selected.
+ //
+ if(pListBox->ui32Style & LISTBOX_STYLE_OUTLINE)
+ {
+ //
+ // Outline the listbox with the outline color.
+ //
+ GrContextForegroundSet(&sCtx, pListBox->ui32OutlineColor);
+ GrRectDraw(&sCtx, &(psWidget->sPosition));
+
+ //
+ // Shrink the widget region by one pixel on each side and draw another
+ // rectangle, this time in the background color. This ensures that the
+ // text will not interfere with the colored border.
+ //
+ sWidgetRect.i16XMin++;
+ sWidgetRect.i16YMin++;
+ sWidgetRect.i16XMax--;
+ sWidgetRect.i16YMax--;
+ GrContextForegroundSet(&sCtx, pListBox->ui32BackgroundColor);
+ GrRectDraw(&sCtx, &sWidgetRect);
+
+ //
+ // Reduce the size of the rectangle by another pixel to get the final
+ // area into which we will put the text.
+ //
+ sWidgetRect.i16XMin++;
+ sWidgetRect.i16YMin++;
+ sWidgetRect.i16XMax--;
+ sWidgetRect.i16YMax--;
+ GrContextClipRegionSet(&sCtx, &sWidgetRect);
+ }
+
+ //
+ // Start drawing at the top of the widget.
+ //
+ sLineRect = sWidgetRect;
+ ui16String = pListBox->ui16StartEntry;
+ i16Height = GrFontHeightGet(pListBox->psFont);
+
+ //
+ // Keep drawing until we reach the bottom of the listbox or run out of
+ // strings to draw.
+ //
+ while((sLineRect.i16YMin < sWidgetRect.i16YMax) &&
+ (ui16String < pListBox->ui16Populated))
+ {
+ //
+ // Calculate the rectangle that will enclose this line of text.
+ //
+ sLineRect.i16YMax = sLineRect.i16YMin + i16Height - 1;
+
+ //
+ // Set foreground and background colors appropriately.
+ //
+ GrContextBackgroundSet(&sCtx, ((ui16String == pListBox->i16Selected) ?
+ pListBox->ui32SelectedBackgroundColor :
+ pListBox->ui32BackgroundColor));
+ GrContextForegroundSet(&sCtx, ((ui16String == pListBox->i16Selected) ?
+ pListBox->ui32SelectedTextColor :
+ pListBox->ui32TextColor));
+
+ //
+ // Draw the text.
+ //
+ GrStringDraw(&sCtx, pListBox->ppcText[ui16String], -1,
+ sLineRect.i16XMin, sLineRect.i16YMin, 1);
+
+ //
+ // Determine the width of the string we just rendered.
+ //
+ i32Width = GrStringWidthGet(&sCtx, pListBox->ppcText[ui16String], -1);
+
+ //
+ // Do we need to clear the area to the right of the string?
+ //
+ if(i32Width < (sLineRect.i16XMax - sLineRect.i16XMin + 1))
+ {
+ //
+ // Yes - we need to fill the right side of this string with
+ // background color.
+ //
+ GrContextForegroundSet(&sCtx,
+ ((ui16String == pListBox->i16Selected) ?
+ pListBox->ui32SelectedBackgroundColor :
+ pListBox->ui32BackgroundColor));
+ sLineRect.i16XMin += i32Width;
+ GrRectFill(&sCtx, &sLineRect);
+ sLineRect.i16XMin = sWidgetRect.i16XMin;
+ }
+
+ //
+ // Move on to the next string, wrapping if necessary.
+ //
+ ui16String++;
+ if(ui16String == pListBox->ui16MaxEntries)
+ {
+ ui16String = 0;
+ }
+ sLineRect.i16YMin += i16Height;
+
+ //
+ // If we are wrapping and got back at the oldest entry, we drop out.
+ //
+ if(ui16String == pListBox->ui16OldestEntry)
+ {
+ break;
+ }
+ }
+
+ //
+ // Fill the remainder of the listbox area with the background color.
+ //
+ if(sLineRect.i16YMin < sWidgetRect.i16YMax)
+ {
+ //
+ // Determine the rectangle to be filled.
+ //
+ sLineRect.i16YMax = sWidgetRect.i16YMax;
+
+ //
+ // Fill the rectangle with the background color.
+ //
+ GrContextForegroundSet(&sCtx, pListBox->ui32BackgroundColor);
+ GrRectFill(&sCtx, &sLineRect);
+ }
+}
+
+//*****************************************************************************
+//
+// Handles pointer messages for a listbox widget.
+//
+// \param pListBox is a pointer to the listbox widget.
+// \param ui32Msg is the message.
+// \param i32X is the X coordinate of the pointer.
+// \param i32Y is the Y coordinate of the pointer.
+//
+// This function receives pointer messages intended for this listbox widget
+// and processes them accordingly.
+//
+// \return Returns a value appropriate to the supplied message.
+//
+//*****************************************************************************
+static int32_t
+ListBoxPointer(tListBoxWidget *pListBox, uint32_t ui32Msg, int32_t i32X,
+ int32_t i32Y)
+{
+ int32_t i32LineNum, i32Entry, i32Visible, i32MaxUp, i32MaxDown, i32Scroll;
+
+ switch(ui32Msg)
+ {
+ //
+ // The touchscreen has been pressed.
+ //
+ case WIDGET_MSG_PTR_DOWN:
+ {
+ //
+ // Is the pointer press within the bounds of this widget?
+ //
+ if(!GrRectContainsPoint(&(pListBox->sBase.sPosition), i32X, i32Y))
+ {
+ //
+ // This is not a message for us so return 0 to indicate that
+ // we did not process it.
+ //
+ return(0);
+ }
+ else
+ {
+ //
+ // The pointer was pressed within this control. Remember the Y
+ // coordinate and reset or scrolling flag.
+ //
+ pListBox->ui16Scrolled = 0;
+ pListBox->i32PointerY = i32Y;
+
+ //
+ // Return 1 to indicate to the widget manager that we processed
+ // the message. This widget will now receive all pointer move
+ // messages until the pointer is released.
+ //
+ return(1);
+ }
+ }
+
+ //
+ // The touchscreen has been released.
+ //
+ case WIDGET_MSG_PTR_UP:
+ {
+ //
+ // If the pointer is still within the bounds of the control and
+ // we have not scrolled the contents since the last time the
+ // pointer was pressed, we assume that this is a tap rather than
+ // a drag and select the element that falls beneath the current
+ // pointer position. If the pointer is outside our control, if
+ // we have scrolled already or if the control is locked, don't
+ // change the selection.
+ //
+ if((pListBox->ui16Scrolled == 0) &&
+ !(pListBox->ui32Style & LISTBOX_STYLE_LOCKED) &&
+ GrRectContainsPoint(&(pListBox->sBase.sPosition), i32X, i32Y))
+ {
+ //
+ // It seems we need to change the selected element. What is
+ // the display line number that has been clicked on?
+ //
+ i32LineNum = (i32Y -
+ (int32_t)pListBox->sBase.sPosition.i16YMin) /
+ GrFontHeightGet(pListBox->psFont);
+
+ //
+ // We now know the location of the click as a number of text
+ // lines from the top of the list box. Now determine what
+ // entry is shown there, remembering that the index may wrap.
+ //
+ i32Entry = ((int32_t)pListBox->ui16StartEntry + i32LineNum) %
+ pListBox->ui16MaxEntries;
+
+ //
+ // If this is an unpopulated entry or the current selection,
+ // clear the selection.
+ //
+ if((i32Entry >= (int32_t)pListBox->ui16Populated) ||
+ (i32Entry == (int32_t)pListBox->i16Selected))
+ {
+ //
+ // Yes - update the selection and force a repaint.
+ //
+ pListBox->i16Selected = (int16_t)0xFFFF;
+ }
+ else
+ {
+ //
+ // The pointer was tapped on a valid entry other than the
+ // current selection so change the selection.
+ //
+ pListBox->i16Selected = (int16_t)i32Entry;
+ }
+
+ //
+ // Force a repaint of the widget.
+ //
+ WidgetPaint((tWidget *)pListBox);
+
+ //
+ // Tell the client that the selection changed.
+ //
+ if(pListBox->pfnOnChange)
+ {
+ (pListBox->pfnOnChange)((tWidget *)pListBox,
+ pListBox->i16Selected);
+ }
+ }
+
+ //
+ // We process all pointer up messages so return 1 to tell the
+ // widget manager this.
+ //
+ return(1);
+ }
+
+ //
+ // The pointer is moving while pressed.
+ //
+ case WIDGET_MSG_PTR_MOVE:
+ {
+ //
+ // How far has the pointer moved vertically from the point where it
+ // was pressed or where we last registered a scroll? i32LineNum
+ // will be negative for downward scrolling.
+ //
+ i32LineNum = pListBox->i32PointerY - i32Y;
+
+ //
+ // If this distance is greater than or equal to the height of a
+ // line of text, we need to check to see if we need to scroll the
+ // list box contents.
+ //
+ if(abs(i32LineNum) >= GrFontHeightGet(pListBox->psFont))
+ {
+ //
+ // We have to scroll if this is possible. How many lines can
+ // be visible on the display?
+ //
+ i32Visible = (pListBox->sBase.sPosition.i16YMax -
+ pListBox->sBase.sPosition.i16YMin) /
+ (int32_t)GrFontHeightGet(pListBox->psFont);
+
+ //
+ // If we have fewer strings in the listbox than there are lines
+ // on the display, scrolling is not possible so give up now.
+ //
+ if(i32Visible > (int32_t)pListBox->ui16Populated)
+ {
+ return(1);
+ }
+
+ //
+ // How many lines of scrolling does the latest pointer position
+ // indicate? A negative value implies downward scrolling (i.e.
+ // showing earlier strings).
+ //
+ i32Scroll = i32LineNum /
+ (int32_t)GrFontHeightGet(pListBox->psFont);
+
+ //
+ // What is the farthest we could scroll downwards (i.e. moving
+ // the pointer towards the bottom of the screen)? Note - this
+ // will be negative or 0.
+ //
+ i32MaxDown =
+ (pListBox->ui16StartEntry >= pListBox->ui16OldestEntry) ?
+ (pListBox->ui16OldestEntry - pListBox->ui16StartEntry ) :
+ ((pListBox->ui16OldestEntry - pListBox->ui16StartEntry) -
+ pListBox->ui16MaxEntries);
+
+ //
+ // What is the farthest we could scroll upwards? Note - this
+ // will be a positive number.
+ //
+ i32MaxUp = ((int32_t)pListBox->ui16Populated - i32Visible) +
+ i32MaxDown;
+
+ //
+ // Determine the actual scroll distance given the maximum
+ // distances calculated.
+ //
+ i32Scroll = min(i32Scroll, i32MaxUp);
+ i32Scroll = max(i32Scroll, i32MaxDown);
+
+ if(i32Scroll)
+ {
+ int32_t i32Temp;
+
+ //
+ // Adjust the start entry appropriately, taking care to
+ // handle the wrap case. The use of a temporary variable
+ // here is required to work around a compiler bug which
+ // resulted in an invalid value of pListBox->ui16StartEntry
+ // following the calculation.
+ //
+ i32Temp = pListBox->ui16StartEntry;
+ i32Temp += i32Scroll;
+ i32Temp %= (int32_t)pListBox->ui16MaxEntries;
+ pListBox->ui16StartEntry = (uint16_t)i32Temp;
+
+ //
+ // Remember that we scrolled.
+ //
+ pListBox->ui16Scrolled = 1;
+
+ //
+ // Adjust the pointer position we record to take into account
+ // the amount we just scrolled.
+ //
+ pListBox->i32PointerY -= (i32Scroll *
+ GrFontHeightGet(pListBox->psFont));
+
+ //
+ // Repaint the contents of the widget.
+ //
+ WidgetPaint((tWidget *)pListBox);
+ }
+ }
+
+ return(1);
+ }
+ }
+
+ //
+ // We don't handle any other messages so return 0 if we get these.
+ //
+ return(0);
+}
+
+//*****************************************************************************
+//
+//! Handles messages for a listbox widget.
+//!
+//! \param psWidget is a pointer to the listbox widget.
+//! \param ui32Msg is the message.
+//! \param ui32Param1 is the first parameter to the message.
+//! \param ui32Param2 is the second parameter to the message.
+//!
+//! This function receives messages intended for this listbox widget and
+//! processes them accordingly. The processing of the message varies based on
+//! the message in question.
+//!
+//! Unrecognized messages are handled by calling WidgetDefaultMsgProc().
+//!
+//! \return Returns a value appropriate to the supplied message.
+//
+//*****************************************************************************
+int32_t
+ListBoxMsgProc(tWidget *psWidget, uint32_t ui32Msg, uint32_t ui32Param1,
+ uint32_t ui32Param2)
+{
+ tListBoxWidget *pListBox;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(psWidget);
+
+ //
+ // Convert the generic pointer to a list box pointer.
+ //
+ pListBox = (tListBoxWidget *)psWidget;
+
+ //
+ // Determine which message is being sent.
+ //
+ switch(ui32Msg)
+ {
+ //
+ // A pointer message has been received.
+ //
+ case WIDGET_MSG_PTR_DOWN:
+ case WIDGET_MSG_PTR_UP:
+ case WIDGET_MSG_PTR_MOVE:
+ return(ListBoxPointer(pListBox, ui32Msg, (int32_t)ui32Param1,
+ (int32_t)ui32Param2));
+
+ //
+ // The widget paint request has been sent.
+ //
+ case WIDGET_MSG_PAINT:
+ {
+ //
+ // Handle the widget paint request.
+ //
+ ListBoxPaint(psWidget);
+
+ //
+ // Return one to indicate that the message was successfully
+ // processed.
+ //
+ return(1);
+ }
+
+ //
+ // An unknown request has been sent.
+ //
+ default:
+ {
+ //
+ // Let the default message handler process this message.
+ //
+ return(WidgetDefaultMsgProc(psWidget, ui32Msg, ui32Param1,
+ ui32Param2));
+ }
+ }
+}
+
+//*****************************************************************************
+//
+//! Initializes a listbox widget.
+//!
+//! \param psWidget is a pointer to the listbox widget to initialize.
+//! \param psDisplay is a pointer to the display on which to draw the listbox.
+//! \param ppcText is a pointer to an array of character pointers which will
+//! hold the strings that the listbox displays.
+//! \param ui16MaxEntries provides the total number of entries in the
+//! \e ppcText array.
+//! \param ui16PopulatedEntries provides the number of entries in the
+//! \e ppcText array which are populated.
+//! \param i32X is the X coordinate of the upper left corner of the listbox.
+//! \param i32Y is the Y coordinate of the upper left corner of the listbox.
+//! \param i32Width is the width of the listbox.
+//! \param i32Height is the height of the listbox.
+//!
+//! This function initializes the provided listbox widget.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+ListBoxInit(tListBoxWidget *psWidget, const tDisplay *psDisplay,
+ const char **ppcText, uint16_t ui16MaxEntries,
+ uint16_t ui16PopulatedEntries, int32_t i32X, int32_t i32Y,
+ int32_t i32Width, int32_t i32Height)
+{
+ uint32_t ui32Idx;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(psWidget);
+ ASSERT(psDisplay);
+
+ //
+ // Clear out the widget structure.
+ //
+ for(ui32Idx = 0; ui32Idx < sizeof(tListBoxWidget); ui32Idx += 4)
+ {
+ ((uint32_t *)psWidget)[ui32Idx / 4] = 0;
+ }
+
+ //
+ // Set the size of the listbox widget structure.
+ //
+ psWidget->sBase.i32Size = sizeof(tListBoxWidget);
+
+ //
+ // Mark this widget as fully disconnected.
+ //
+ psWidget->sBase.psParent = 0;
+ psWidget->sBase.psNext = 0;
+ psWidget->sBase.psChild = 0;
+
+ //
+ // Save the display pointer.
+ //
+ psWidget->sBase.psDisplay = psDisplay;
+
+ //
+ // Set the extents of this listbox.
+ //
+ psWidget->sBase.sPosition.i16XMin = i32X;
+ psWidget->sBase.sPosition.i16YMin = i32Y;
+ psWidget->sBase.sPosition.i16XMax = i32X + i32Width - 1;
+ psWidget->sBase.sPosition.i16YMax = i32Y + i32Height - 1;
+
+ //
+ // Use the listbox message handler to process messages to this listbox.
+ //
+ psWidget->sBase.pfnMsgProc = ListBoxMsgProc;
+
+ //
+ // Initialize some of the widget fields that are not accessible via
+ // macros.
+ //
+ psWidget->ppcText = ppcText;
+ psWidget->ui16MaxEntries = ui16MaxEntries;
+ psWidget->ui16Populated = ui16PopulatedEntries;
+ psWidget->i16Selected = (int16_t)0xFFFF;
+}
+
+//*****************************************************************************
+//
+//! Adds a line of text to a listbox.
+//!
+//! \param pListBox is a pointer to the listbox widget that is to receive the
+//! new text string.
+//! \param pcTxt is a pointer to the string that is to be added to the listbox.
+//!
+//! This function adds a new string to the listbox. If the listbox has
+//! style \b #LISTBOX_STYLE_WRAP and the current string table is full, this
+//! function will discard the oldest string and replace it with the one passed
+//! here. If this style flag is absent, the function will return -1 if no
+//! empty entries exist in the string table for the widget.
+//!
+//! The display is not automatically updated as a result of this function call.
+//! An application must call WidgetPaint() to update the display after adding
+//! a new string to the listbox.
+//!
+//! \note To replace the string associated with a particular, existing element
+//! in the listbox, use ListBoxTextSet().
+//!
+//! \return Returns the string table index into which the new string has been
+//! placed if successful or -1 if the string table is full and
+//! \b #LISTBOX_STYLE_WRAP is not set.
+//
+//*****************************************************************************
+int32_t ListBoxTextAdd(tListBoxWidget *pListBox, const char *pcTxt)
+{
+ uint32_t ui32Index;
+
+ //
+ // Is the list box full?
+ //
+ if(pListBox->ui16Populated == pListBox->ui16MaxEntries)
+ {
+ //
+ // The box is already full. If the wrap style is not set, fail
+ // the call.
+ //
+ if(!(pListBox->ui32Style & LISTBOX_STYLE_WRAP))
+ {
+ //
+ // The listbox is full and it is not configured to wrap so we can't
+ // add another string to it.
+ //
+ return(-1);
+ }
+ else
+ {
+ //
+ // We are wrapping so replace the oldest entry in the box.
+ //
+ ui32Index = pListBox->ui16OldestEntry;
+
+ //
+ // Check to see if we are displaying the oldest entry and, if so,
+ // move the start entry on by one to keep the display order
+ // correct.
+ //
+ if(pListBox->ui16OldestEntry == pListBox->ui16StartEntry)
+ {
+ pListBox->ui16StartEntry++;
+ if(pListBox->ui16StartEntry == pListBox->ui16MaxEntries)
+ {
+ pListBox->ui16StartEntry = 0;
+ }
+ }
+
+ //
+ // The new oldest entry is the next one. Update the index and
+ // take care to wrap if we reach the end of the string table.
+ //
+ pListBox->ui16OldestEntry++;
+ if(pListBox->ui16OldestEntry == pListBox->ui16MaxEntries)
+ {
+ pListBox->ui16OldestEntry = 0;
+ }
+ }
+ }
+ else
+ {
+ //
+ // The listbox is not full so add the new string to the first free
+ // slot in the string table.
+ //
+ ui32Index = pListBox->ui16Populated;
+ pListBox->ui16Populated++;
+ }
+
+ //
+ // Save the new string in the appropriate string table entry.
+ //
+ pListBox->ppcText[ui32Index] = pcTxt;
+
+ //
+ // Tell the caller which string table entry was added.
+ //
+ return((int32_t)ui32Index);
+}
+
+//*****************************************************************************
+//
+// Close the Doxygen group.
+//! @}
+//
+//*****************************************************************************