| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735 |
- //*****************************************************************************
- //
- // listbox.c - A listbox widget.
- //
- // Copyright (c) 2008-2010 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 6288 of the Stellaris Graphics Library.
- //
- //*****************************************************************************
- #include "debug.h"
- #include "grlib.h"
- #include "widget.h"
- #include "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 pWidget 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 *pWidget)
- {
- tListBoxWidget *pListBox;
- tContext sCtx;
- tRectangle sWidgetRect, sLineRect;
- short sHeight;
- int lWidth;
- unsigned short usCount, usString;
- //
- // Check the arguments.
- //
- ASSERT(pWidget);
- //
- // Convert the generic widget pointer into a listbox widget pointer.
- //
- pListBox = (tListBoxWidget *)pWidget;
- //
- // Initialize a drawing context.
- //
- GrContextInit(&sCtx, pWidget->pDisplay);
- GrContextFontSet(&sCtx, pListBox->pFont);
- //
- // Initialize the clipping region based on the extents of this listbox.
- //
- sWidgetRect = pWidget->sPosition;
- GrContextClipRegionSet(&sCtx, &sWidgetRect);
- //
- // See if the listbox outline style is selected.
- //
- if(pListBox->ulStyle & LISTBOX_STYLE_OUTLINE)
- {
- //
- // Outline the listbox with the outline color.
- //
- GrContextForegroundSet(&sCtx, pListBox->ulOutlineColor);
- GrRectDraw(&sCtx, &(pWidget->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.sXMin++;
- sWidgetRect.sYMin++;
- sWidgetRect.sXMax--;
- sWidgetRect.sYMax--;
- GrContextForegroundSet(&sCtx, pListBox->ulBackgroundColor);
- 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.sXMin++;
- sWidgetRect.sYMin++;
- sWidgetRect.sXMax--;
- sWidgetRect.sYMax--;
- GrContextClipRegionSet(&sCtx, &sWidgetRect);
- }
- //
- // Start drawing at the top of the widget.
- //
- sLineRect = sWidgetRect;
- usCount = 0;
- usString = pListBox->usStartEntry;
- sHeight = GrFontHeightGet(pListBox->pFont);
- //
- // Keep drawing until we reach the bottom of the listbox or run out of
- // strings to draw.
- //
- while((sLineRect.sYMin < sWidgetRect.sYMax) &&
- (usCount < pListBox->usPopulated))
- {
- //
- // Calculate the rectangle that will enclose this line of text.
- //
- sLineRect.sYMax = sLineRect.sYMin + sHeight - 1;
- //
- // Set foreground and background colors appropriately.
- //
- GrContextBackgroundSet(&sCtx, ((usString == pListBox->sSelected) ?
- pListBox->ulSelectedBackgroundColor :
- pListBox->ulBackgroundColor));
- GrContextForegroundSet(&sCtx, ((usString == pListBox->sSelected) ?
- pListBox->ulSelectedTextColor :
- pListBox->ulTextColor));
- //
- // Draw the text.
- //
- GrStringDraw(&sCtx, pListBox->ppcText[usString], -1, sLineRect.sXMin,
- sLineRect.sYMin, 1);
- //
- // Determine the width of the string we just rendered.
- //
- lWidth = GrStringWidthGet(&sCtx, pListBox->ppcText[usString], -1);
- //
- // Do we need to clear the area to the right of the string?
- //
- if(lWidth < (sLineRect.sXMax - sLineRect.sXMin + 1))
- {
- //
- // Yes - we need to fill the right side of this string with
- // background color.
- //
- GrContextForegroundSet(&sCtx, ((usString == pListBox->sSelected) ?
- pListBox->ulSelectedBackgroundColor :
- pListBox->ulBackgroundColor));
- sLineRect.sXMin += lWidth;
- GrRectFill(&sCtx, &sLineRect);
- sLineRect.sXMin = sWidgetRect.sXMin;
- }
- //
- // Move on to the next string.
- //
- usCount++;
- usString++;
- if(usString == pListBox->usMaxEntries)
- {
- usString = 0;
- }
- sLineRect.sYMin += sHeight;
- }
- //
- // Fill the remainder of the listbox area with the background color.
- //
- if(sLineRect.sYMin < sWidgetRect.sYMax)
- {
- //
- // Determine the rectangle to be filled.
- //
- sLineRect.sYMax = sWidgetRect.sYMax;
- //
- // Fill the rectangle with the background color.
- //
- GrContextForegroundSet(&sCtx, pListBox->ulBackgroundColor);
- GrRectFill(&sCtx, &sLineRect);
- }
- }
- //*****************************************************************************
- //
- // Handles pointer messages for a listbox widget.
- //
- // \param pListBox is a pointer to the listbox widget.
- // \param ulMsg is the message.
- // \param lX is the X coordinate of the pointer.
- // \param lY 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 int
- ListBoxPointer(tListBoxWidget *pListBox, unsigned int ulMsg, int lX, int lY)
- {
- int lLineNum, lEntry, lVisible, lMaxUp, lMaxDown, lScroll;
- switch(ulMsg)
- {
- //
- // 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), lX, lY))
- {
- //
- // 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->usScrolled = 0;
- pListBox->lPointerY = lY;
- //
- // 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->usScrolled == 0) &&
- !(pListBox->ulStyle & LISTBOX_STYLE_LOCKED) &&
- GrRectContainsPoint(&(pListBox->sBase.sPosition), lX, lY))
- {
- //
- // It seems we need to change the selected element. What is
- // the display line number that has been clicked on?
- //
- lLineNum = (lY - (int)pListBox->sBase.sPosition.sYMin) /
- GrFontHeightGet(pListBox->pFont);
- //
- // 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.
- //
- lEntry = ((int)pListBox->usStartEntry + lLineNum) %
- pListBox->usMaxEntries;
- //
- // If this is an unpopulated entry or the current selection,
- // clear the selection.
- //
- if((lEntry >= (int)pListBox->usPopulated) ||
- (lEntry == (int)pListBox->sSelected))
- {
- //
- // Yes - update the selection and force a repaint.
- //
- pListBox->sSelected = (short)0xFFFF;
- }
- else
- {
- //
- // The pointer was tapped on a valid entry other than the
- // current selection so change the selection.
- //
- pListBox->sSelected = (short)lEntry;
- }
- //
- // Force a repaint of the widget.
- //
- WidgetPaint((tWidget *)pListBox);
- //
- // Tell the client that the selection changed.
- //
- if(pListBox->pfnOnChange)
- {
- (pListBox->pfnOnChange)((tWidget *)pListBox,
- pListBox->sSelected);
- }
- }
- //
- // 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? lLineNum will
- // be negative for downward scrolling.
- //
- lLineNum = pListBox->lPointerY - lY;
- //
- // 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(lLineNum) >= GrFontHeightGet(pListBox->pFont))
- {
- //
- // We have to scroll if this is possible. How many lines can
- // be visible on the display?
- //
- lVisible = (pListBox->sBase.sPosition.sYMax -
- pListBox->sBase.sPosition.sYMin) /
- (int)GrFontHeightGet(pListBox->pFont);
- //
- // If we have fewer strings in the listbox than there are lines
- // on the display, scrolling is not possible so give up now.
- //
- if(lVisible > (int)pListBox->usPopulated)
- {
- return(1);
- }
- //
- // How many lines of scrolling does the latest pointer position
- // indicate? A negative value implies downward scrolling (i.e.
- // showing earlier strings).
- //
- lScroll = lLineNum / (int)GrFontHeightGet(pListBox->pFont);
- //
- // 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.
- //
- lMaxDown = (pListBox->usStartEntry >= pListBox->usOldestEntry) ?
- (pListBox->usOldestEntry - pListBox->usStartEntry ) :
- ((pListBox->usOldestEntry - pListBox->usStartEntry) -
- pListBox->usMaxEntries);
- //
- // What is the farthest we could scroll upwards? Note - this
- // will be a positive number.
- //
- lMaxUp = ((int)pListBox->usPopulated - lVisible) + lMaxDown;
- //
- // Determine the actual scroll distance given the maximum
- // distances calculated.
- //
- lScroll = min(lScroll, lMaxUp);
- lScroll = max(lScroll, lMaxDown);
- if(lScroll)
- {
- int lTemp;
-
- //
- // 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->usStartEntry following the
- // calculation.
- //
- lTemp = pListBox->usStartEntry;
- lTemp += lScroll;
- lTemp %= (int)pListBox->usMaxEntries;
- pListBox->usStartEntry = (unsigned short)lTemp;
-
- //
- // Remember that we scrolled.
- //
- pListBox->usScrolled = 1;
- //
- // Adjust the pointer position we record to take into account
- // the amount we just scrolled.
- //
- pListBox->lPointerY -= (lScroll *
- GrFontHeightGet(pListBox->pFont));
- //
- // 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 pWidget is a pointer to the listbox widget.
- //! \param ulMsg is the message.
- //! \param ulParam1 is the first parameter to the message.
- //! \param ulParam2 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.
- //
- //*****************************************************************************
- int
- ListBoxMsgProc(tWidget *pWidget, unsigned int ulMsg, unsigned int ulParam1,
- unsigned int ulParam2)
- {
- tListBoxWidget *pListBox;
- //
- // Check the arguments.
- //
- ASSERT(pWidget);
- //
- // Convert the generic pointer to a list box pointer.
- //
- pListBox = (tListBoxWidget *)pWidget;
- //
- // Determine which message is being sent.
- //
- switch(ulMsg)
- {
- //
- // A pointer message has been received.
- //
- case WIDGET_MSG_PTR_DOWN:
- case WIDGET_MSG_PTR_UP:
- case WIDGET_MSG_PTR_MOVE:
- return(ListBoxPointer(pListBox, ulMsg, (int)ulParam1,
- (int)ulParam2));
- //
- // The widget paint request has been sent.
- //
- case WIDGET_MSG_PAINT:
- {
- //
- // Handle the widget paint request.
- //
- ListBoxPaint(pWidget);
- //
- // 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(pWidget, ulMsg, ulParam1, ulParam2));
- }
- }
- }
- //*****************************************************************************
- //
- //! Initializes a listbox widget.
- //!
- //! \param pWidget is a pointer to the listbox widget to initialize.
- //! \param pDisplay 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 usMaxEntries provides the total number of entries in the \e ppcText
- //! array.
- //! \param usPopulatedEntries provides the number of entries in the \e ppcText
- //! array which are populated.
- //! \param lX is the X coordinate of the upper left corner of the listbox.
- //! \param lY is the Y coordinate of the upper left corner of the listbox.
- //! \param lWidth is the width of the listbox.
- //! \param lHeight is the height of the listbox.
- //!
- //! This function initializes the provided listbox widget.
- //!
- //! \return None.
- //
- //*****************************************************************************
- void
- ListBoxInit(tListBoxWidget *pWidget, const tDisplay *pDisplay,
- const char **ppcText, unsigned short usMaxEntries,
- unsigned short usPopulatedEntries, int lX, int lY, int lWidth,
- int lHeight)
- {
- unsigned int ulIdx;
- //
- // Check the arguments.
- //
- ASSERT(pWidget);
- ASSERT(pDisplay);
- //
- // Clear out the widget structure.
- //
- for(ulIdx = 0; ulIdx < sizeof(tListBoxWidget); ulIdx += 4)
- {
- ((unsigned int *)pWidget)[ulIdx / 4] = 0;
- }
- //
- // Set the size of the listbox widget structure.
- //
- pWidget->sBase.lSize = sizeof(tListBoxWidget);
- //
- // Mark this widget as fully disconnected.
- //
- pWidget->sBase.pParent = 0;
- pWidget->sBase.pNext = 0;
- pWidget->sBase.pChild = 0;
- //
- // Save the display pointer.
- //
- pWidget->sBase.pDisplay = pDisplay;
- //
- // Set the extents of this listbox.
- //
- pWidget->sBase.sPosition.sXMin = lX;
- pWidget->sBase.sPosition.sYMin = lY;
- pWidget->sBase.sPosition.sXMax = lX + lWidth - 1;
- pWidget->sBase.sPosition.sYMax = lY + lHeight - 1;
- //
- // Use the listbox message handler to process messages to this listbox.
- //
- pWidget->sBase.pfnMsgProc = ListBoxMsgProc;
- //
- // Initialize some of the widget fields that are not accessible via
- // macros.
- //
- pWidget->ppcText = ppcText;
- pWidget->usMaxEntries = usMaxEntries;
- pWidget->usPopulated = usPopulatedEntries;
- pWidget->sSelected = (short)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.
- //
- //*****************************************************************************
- int ListBoxTextAdd(tListBoxWidget *pListBox, const char *pcTxt)
- {
- unsigned int ulIndex;
- //
- // Is the list box full?
- //
- if(pListBox->usPopulated == pListBox->usMaxEntries)
- {
- //
- // The box is already full. If the wrap style is not set, fail
- // the call.
- //
- if(!(pListBox->ulStyle & 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.
- //
- ulIndex = pListBox->usOldestEntry;
- //
- // 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->usOldestEntry == pListBox->usStartEntry)
- {
- pListBox->usStartEntry++;
- if(pListBox->usStartEntry == pListBox->usMaxEntries)
- {
- pListBox->usStartEntry = 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->usOldestEntry++;
- if(pListBox->usOldestEntry == pListBox->usMaxEntries)
- {
- pListBox->usOldestEntry = 0;
- }
- }
- }
- else
- {
- //
- // The listbox is not full so add the new string to the first free
- // slot in the string table.
- //
- ulIndex = pListBox->usPopulated;
- pListBox->usPopulated++;
- }
- //
- // Save the new string in the appropriate string table entry.
- //
- pListBox->ppcText[ulIndex] = pcTxt;
- //
- // Tell the caller which string table entry was added.
- //
- return((int)ulIndex);
- }
- //*****************************************************************************
- //
- // Close the Doxygen group.
- //! @}
- //
- //*****************************************************************************
|