aboutsummaryrefslogtreecommitdiffstats
path: root/src/gwin/gwin_list.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gwin/gwin_list.c')
-rw-r--r--src/gwin/gwin_list.c696
1 files changed, 696 insertions, 0 deletions
diff --git a/src/gwin/gwin_list.c b/src/gwin/gwin_list.c
new file mode 100644
index 00000000..265053e1
--- /dev/null
+++ b/src/gwin/gwin_list.c
@@ -0,0 +1,696 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+/**
+ * @file src/gwin/gwin_list.c
+ * @brief GWIN list widget header file
+ */
+
+#include "gfx.h"
+
+#if GFX_USE_GWIN && GWIN_NEED_LIST
+
+#include "gwin_class.h"
+#include <string.h>
+#include <stdlib.h>
+
+// user for the default drawing routine
+#define SCROLLWIDTH 16 // the border from the scroll buttons to the frame
+#define ARROW 10 // arrow side length
+#define HORIZONTAL_PADDING 5 // extra horizontal padding for text
+#define VERTICAL_PADDING 2 // extra vertical padding for text
+
+// Macro's to assist in data type conversions
+#define gh2obj ((GListObject *)gh)
+#define gw2obj ((GListObject *)gw)
+#define qi2li ((ListItem *)qi)
+#define qix2li ((ListItem *)qix)
+#define ple ((GEventGWinList *)pe)
+
+// Flags for the GListObject
+#define GLIST_FLG_MULTISELECT (GWIN_FIRST_CONTROL_FLAG << 0)
+#define GLIST_FLG_HASIMAGES (GWIN_FIRST_CONTROL_FLAG << 1)
+#define GLIST_FLG_SCROLLALWAYS (GWIN_FIRST_CONTROL_FLAG << 2)
+#define GLIST_FLG_SCROLLSMOOTH (GWIN_FIRST_CONTROL_FLAG << 3)
+#define GLIST_FLG_ENABLERENDER (GWIN_FIRST_CONTROL_FLAG << 4)
+
+// Flags on a ListItem.
+#define GLIST_FLG_SELECTED 0x0001
+
+typedef struct ListItem {
+ gfxQueueASyncItem q_item; // This must be the first member in the struct
+
+ uint16_t flags;
+ uint16_t param; // A parameter the user can specify himself
+ const char* text;
+ #if GWIN_NEED_LIST_IMAGES
+ gdispImage* pimg;
+ #endif
+} ListItem;
+
+static void sendListEvent(GWidgetObject *gw, int item) {
+ GSourceListener* psl;
+ GEvent* pe;
+
+ // Trigger a GWIN list event
+ psl = 0;
+
+ while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) {
+ if (!(pe = geventGetEventBuffer(psl)))
+ continue;
+
+ ple->type = GEVENT_GWIN_LIST;
+ ple->gwin = (GHandle)gw;
+ ple->item = item;
+ #if GWIN_WIDGET_TAGS
+ ple->tag = gw->tag;
+ #endif
+
+ geventSendEvent(psl);
+ }
+}
+
+static void gwinListDefaultDraw(GWidgetObject* gw, void* param);
+
+#if GINPUT_NEED_MOUSE
+ static void MouseSelect(GWidgetObject* gw, coord_t x, coord_t y) {
+ const gfxQueueASyncItem* qi;
+ int item, i;
+ coord_t iheight;
+ (void) x;
+
+ iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING;
+
+ // Handle click over the list area
+ item = (gw2obj->top + y) / iheight;
+
+ if (item < 0 || item >= gw2obj->cnt)
+ return;
+
+ for(qi = gfxQueueASyncPeek(&gw2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
+ if ((gw->g.flags & GLIST_FLG_MULTISELECT)) {
+ if (item == i) {
+ qi2li->flags ^= GLIST_FLG_SELECTED;
+ break;
+ }
+ } else {
+ if (item == i)
+ qi2li->flags |= GLIST_FLG_SELECTED;
+ else
+ qi2li->flags &=~ GLIST_FLG_SELECTED;
+ }
+ }
+
+ _gwinUpdate(&gw->g);
+ sendListEvent(gw, item);
+
+ }
+
+ // a mouse down has occurred over the list area
+ static void MouseDown(GWidgetObject* gw, coord_t x, coord_t y) {
+ coord_t iheight, pgsz;
+
+ // Save our mouse start position
+ gw2obj->start_mouse_x = x;
+ gw2obj->start_mouse_y = y;
+ gw2obj->last_mouse_y = y;
+
+ // For smooth scrolling, scrolling is done in the MouseMove and selection is done on MouseUp
+ if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH)
+ return;
+
+ // Some initial stuff
+ iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING;
+ pgsz = gw->g.height-2;
+
+ // Handle click over the scroll bar
+ if (x >= gw->g.width-(SCROLLWIDTH+2) && (gw2obj->cnt > pgsz/iheight || (gw->g.flags & GLIST_FLG_SCROLLALWAYS))) {
+ if (y < 2*ARROW) {
+ if (gw2obj->top > 0) {
+ gw2obj->top -= iheight;
+ if (gw2obj->top < 0)
+ gw2obj->top = 0;
+ _gwinUpdate(&gw->g);
+ }
+ } else if (y >= gw->g.height - 2*ARROW) {
+ if (gw2obj->top < gw2obj->cnt * iheight - pgsz) {
+ gw2obj->top += iheight;
+ if (gw2obj->top > gw2obj->cnt * iheight - pgsz)
+ gw2obj->top = gw2obj->cnt * iheight - pgsz;
+ _gwinUpdate(&gw->g);
+ }
+ } else if (y < gw->g.height/2) {
+ if (gw2obj->top > 0) {
+ if (gw2obj->top > pgsz)
+ gw2obj->top -= pgsz;
+ else
+ gw2obj->top = 0;
+ _gwinUpdate(&gw->g);
+ }
+ } else {
+ if (gw2obj->top < gw2obj->cnt * iheight - pgsz) {
+ if (gw2obj->top < gw2obj->cnt * iheight - 2*pgsz)
+ gw2obj->top += pgsz;
+ else
+ gw2obj->top = gw2obj->cnt * iheight - pgsz;
+ _gwinUpdate(&gw->g);
+ }
+ }
+ return;
+ }
+
+ MouseSelect(gw, x, y);
+ }
+
+ static void MouseUp(GWidgetObject* gw, coord_t x, coord_t y) {
+ // Only act when we are a smooth scrolling list
+ if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH))
+ return;
+
+ // Only allow selection when we did not scroll
+ if (abs(gw2obj->start_mouse_x - x) > 4 || abs(gw2obj->start_mouse_y - y) > 4)
+ return;
+
+ MouseSelect(gw, x, y);
+ }
+
+ static void MouseMove(GWidgetObject* gw, coord_t x, coord_t y) {
+ int iheight, oldtop;
+ (void) x;
+
+ if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH)) return;
+
+ if (gw2obj->last_mouse_y != y) {
+ oldtop = gw2obj->top;
+ iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING;
+
+ gw2obj->top -= y - gw2obj->last_mouse_y;
+ if (gw2obj->top >= gw2obj->cnt * iheight - (gw->g.height-2))
+ gw2obj->top = gw2obj->cnt * iheight - (gw->g.height-2) - 1;
+ if (gw2obj->top < 0)
+ gw2obj->top = 0;
+ gw2obj->last_mouse_y = y;
+ if (oldtop != gw2obj->top)
+ _gwinUpdate(&gw->g);
+ }
+ }
+#endif
+
+#if GINPUT_NEED_TOGGLE
+ // a toggle-on has occurred
+ static void ToggleOn(GWidgetObject *gw, uint16_t role) {
+ const gfxQueueASyncItem * qi;
+ const gfxQueueASyncItem * qix;
+ int i;
+
+ switch (role) {
+ // select down
+ case 0:
+ for (i = 0, qi = gfxQueueASyncPeek(&gw2obj->list_head); qi; qi = gfxQueueASyncNext(qi), i++) {
+ if ((qi2li->flags & GLIST_FLG_SELECTED)) {
+ qix = gfxQueueASyncNext(qi);
+ if (qix) {
+ qi2li->flags &=~ GLIST_FLG_SELECTED;
+ qix2li->flags |= GLIST_FLG_SELECTED;
+ _gwinUpdate(&gw->g);
+ }
+ break;
+ }
+ }
+ break;
+
+ // select up
+ case 1:
+ qi = gfxQueueASyncPeek(&gw2obj->list_head);
+ qix = 0;
+
+ for (i = 0; qi; qix = qi, qi = gfxQueueASyncNext(qi), i++) {
+ if ((qi2li->flags & GLIST_FLG_SELECTED))
+ if (qix) {
+ qi2li->flags &=~ GLIST_FLG_SELECTED;
+ qix2li->flags |= GLIST_FLG_SELECTED;
+ _gwinUpdate(&gw->g);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ static void ToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) {
+ if (role)
+ gw2obj->t_up = instance;
+ else
+ gw2obj->t_dn = instance;
+ }
+
+ static uint16_t ToggleGet(GWidgetObject *gw, uint16_t role) {
+ return role ? gw2obj->t_up : gw2obj->t_dn;
+ }
+#endif
+
+static void _destroy(GHandle gh) {
+ const gfxQueueASyncItem* qi;
+
+ while((qi = gfxQueueASyncGet(&gh2obj->list_head)))
+ gfxFree((void *)qi);
+
+ _gwidgetDestroy(gh);
+}
+
+static const gwidgetVMT listVMT = {
+ {
+ "List", // The class name
+ sizeof(GListObject), // The object size
+ _destroy, // The destroy routine
+ _gwidgetRedraw, // The redraw routine
+ 0, // The after-clear routine
+ },
+ gwinListDefaultDraw, // default drawing routine
+ #if GINPUT_NEED_MOUSE
+ {
+ MouseDown,
+ MouseUp,
+ MouseMove,
+ },
+ #endif
+ #if GINPUT_NEED_TOGGLE
+ {
+ 2, // two toggle roles
+ ToggleAssign, // Assign toggles
+ ToggleGet, // get toggles
+ 0,
+ ToggleOn, // process toggle on event
+ },
+ #endif
+ #if GINPUT_NEED_DIAL
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ },
+ #endif
+};
+
+GHandle gwinGListCreate(GDisplay *g, GListObject* gobj, GWidgetInit* pInit, bool_t multiselect) {
+ if (!(gobj = (GListObject *)_gwidgetCreate(g, &gobj->w, pInit, &listVMT)))
+ return 0;
+
+ // initialize the item queue
+ gfxQueueASyncInit(&gobj->list_head);
+ gobj->cnt = 0;
+ gobj->top = 0;
+ if (multiselect)
+ gobj->w.g.flags |= GLIST_FLG_MULTISELECT;
+ gobj->w.g.flags |= GLIST_FLG_SCROLLALWAYS;
+ gobj->w.g.flags |= GLIST_FLG_ENABLERENDER;
+
+ gwinSetVisible(&gobj->w.g, pInit->g.show);
+
+ return (GHandle)gobj;
+}
+
+void gwinListEnableRender(GHandle gh, bool_t ena) {
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return;
+
+ if (ena) {
+ gh->flags |= GLIST_FLG_ENABLERENDER;
+ gwinRedraw(gh);
+ } else {
+ gh->flags &=~ GLIST_FLG_ENABLERENDER;
+ }
+}
+
+void gwinListSetScroll(GHandle gh, scroll_t flag) {
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return;
+
+ ((GListObject*)gh)->w.g.flags &=~(GLIST_FLG_SCROLLSMOOTH | GLIST_FLG_SCROLLALWAYS);
+ switch (flag) {
+ case scrollAlways:
+ ((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLALWAYS;
+ break;
+
+ case scrollAuto:
+ break;
+
+ case scrollSmooth:
+ ((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLSMOOTH;
+ break;
+ }
+}
+
+int gwinListAddItem(GHandle gh, const char* item_name, bool_t useAlloc) {
+ ListItem *newItem;
+
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return -1;
+
+ if (useAlloc) {
+ size_t len = strlen(item_name)+1;
+ if (!(newItem = gfxAlloc(sizeof(ListItem) + len)))
+ return -1;
+
+ memcpy((char *)(newItem+1), item_name, len);
+ item_name = (const char *)(newItem+1);
+ } else {
+ if (!(newItem = gfxAlloc(sizeof(ListItem))))
+ return -1;
+ }
+
+ // the item is not selected when added
+ newItem->flags = 0;
+ newItem->param = 0;
+ newItem->text = item_name;
+ #if GWIN_NEED_LIST_IMAGES
+ newItem->pimg = 0;
+ #endif
+
+ // select the item if it's the first in the list
+ if (gh2obj->cnt == 0 && !(gh->flags & GLIST_FLG_MULTISELECT))
+ newItem->flags |= GLIST_FLG_SELECTED;
+
+ // add the new item to the list
+ gfxQueueASyncPut(&gh2obj->list_head, &newItem->q_item);
+
+ // increment the total amount of entries in the list widget
+ gh2obj->cnt++;
+
+ _gwinUpdate(gh);
+
+ // return the position in the list (-1 because we start with index 0)
+ return gh2obj->cnt-1;
+}
+
+const char* gwinListItemGetText(GHandle gh, int item) {
+ const gfxQueueASyncItem* qi;
+ int i;
+
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return 0;
+
+ // watch out for an invalid item
+ if (item < 0 || item >= gh2obj->cnt)
+ return 0;
+
+ for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
+ if (i == item)
+ return qi2li->text;
+ }
+ return 0;
+}
+
+int gwinListFindText(GHandle gh, const char* text) {
+ const gfxQueueASyncItem* qi;
+ int i;
+
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return -1;
+
+ // watch out for NULL pointers
+ if (!text)
+ return -1;
+
+ for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
+ if (strcmp(((ListItem *)qi)->text, text) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+int gwinListGetSelected(GHandle gh) {
+ const gfxQueueASyncItem * qi;
+ int i;
+
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return -1;
+
+ // Multi-select always returns -1. Use gwinListItemIsSelected() instead
+ if ((gh->flags & GLIST_FLG_MULTISELECT))
+ return -1;
+
+ for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
+ if (qi2li->flags & GLIST_FLG_SELECTED)
+ return i;
+ }
+
+ return -1;
+}
+
+void gwinListItemSetParam(GHandle gh, int item, uint16_t param) {
+ const gfxQueueASyncItem * qi;
+ int i;
+
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return;
+
+ // watch out for an invalid item
+ if (item < 0 || item > (gh2obj->cnt) - 1)
+ return;
+
+ for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
+ if (i == item) {
+ qi2li->param = param;
+ break;
+ }
+ }
+}
+
+void gwinListDeleteAll(GHandle gh) {
+ gfxQueueASyncItem* qi;
+
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return;
+
+ while((qi = gfxQueueASyncGet(&gh2obj->list_head)))
+ gfxFree(qi);
+
+ gh->flags &= ~GLIST_FLG_HASIMAGES;
+ gh2obj->cnt = 0;
+ gh2obj->top = 0;
+ _gwinUpdate(gh);
+}
+
+void gwinListItemDelete(GHandle gh, int item) {
+ const gfxQueueASyncItem * qi;
+ int i;
+
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return;
+
+ // watch out for an invalid item
+ if (item < 0 || item >= gh2obj->cnt)
+ return;
+
+ for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
+ if (i == item) {
+ gfxQueueASyncRemove(&gh2obj->list_head, (gfxQueueASyncItem*)qi);
+ gfxFree((void *)qi);
+ if (gh2obj->top >= item && gh2obj->top)
+ gh2obj->top--;
+ _gwinUpdate(gh);
+ break;
+ }
+ }
+}
+
+uint16_t gwinListItemGetParam(GHandle gh, int item) {
+ const gfxQueueASyncItem * qi;
+ int i;
+
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return 0;
+
+ // watch out for an invalid item
+ if (item < 0 || item > (gh2obj->cnt) - 1)
+ return 0;
+
+ for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
+ if (i == item)
+ return qi2li->param;
+ }
+ return 0;
+}
+
+bool_t gwinListItemIsSelected(GHandle gh, int item) {
+ const gfxQueueASyncItem * qi;
+ int i;
+
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return FALSE;
+
+ // watch out for an invalid item
+ if (item < 0 || item > (gh2obj->cnt) - 1)
+ return FALSE;
+
+ for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
+ if (i == item)
+ return (qi2li->flags & GLIST_FLG_SELECTED) ? TRUE : FALSE;
+ }
+ return FALSE;
+}
+
+int gwinListItemCount(GHandle gh) {
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return 0;
+
+ return gh2obj->cnt;
+}
+
+const char* gwinListGetSelectedText(GHandle gh) {
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return 0;
+
+ // return NULL if nothing is selected (or multi-select)
+ if (gwinListGetSelected(gh) < 0)
+ return 0;
+
+ return gwinListItemGetText(gh, gwinListGetSelected(gh));
+}
+
+#if GWIN_NEED_LIST_IMAGES
+ void gwinListItemSetImage(GHandle gh, int item, gdispImage *pimg) {
+ const gfxQueueASyncItem * qi;
+ int i;
+
+ // is it a valid handle?
+ if (gh->vmt != (gwinVMT *)&listVMT)
+ return;
+
+ // watch out for an invalid item
+ if (item < 0 || item > (gh2obj->cnt) - 1)
+ return;
+
+ for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
+ if (i == item) {
+ qi2li->pimg = pimg;
+ if (pimg)
+ gh->flags |= GLIST_FLG_HASIMAGES;
+ break;
+ }
+ }
+ }
+#endif
+
+static void gwinListDefaultDraw(GWidgetObject* gw, void* param) {
+ (void)param;
+
+ #if GDISP_NEED_CONVEX_POLYGON
+ static const point upArrow[] = { {0, ARROW}, {ARROW, ARROW}, {ARROW/2, 0} };
+ static const point downArrow[] = { {0, 0}, {ARROW, 0}, {ARROW/2, ARROW} };
+ #endif
+
+ const gfxQueueASyncItem* qi;
+ int i;
+ coord_t x, y, iheight, iwidth;
+ color_t fill;
+ const GColorSet * ps;
+ #if GWIN_NEED_LIST_IMAGES
+ coord_t sy;
+ #endif
+
+ // is it a valid handle?
+ if (gw->g.vmt != (gwinVMT *)&listVMT)
+ return;
+
+ // don't render if render has been disabled
+ if (!(gw->g.flags & GLIST_FLG_ENABLERENDER))
+ return;
+
+ ps = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled;
+ iheight = gdispGetFontMetric(gw->g.font, fontHeight) + VERTICAL_PADDING;
+ x = 1;
+
+ // the scroll area
+ if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH) {
+ iwidth = gw->g.width - 2 - 4;
+ if (gw2obj->cnt > 0) {
+ int max_scroll_value = gw2obj->cnt * iheight - gw->g.height-2;
+ if (max_scroll_value > 0) {
+ int bar_height = (gw->g.height-2) * (gw->g.height-2) / (gw2obj->cnt * iheight);
+ gdispGFillArea(gw->g.display, gw->g.x + gw->g.width-4, gw->g.y + 1, 2, gw->g.height-1, gw->pstyle->background);
+ gdispGFillArea(gw->g.display, gw->g.x + gw->g.width-4, gw->g.y + gw2obj->top * ((gw->g.height-2)-bar_height) / max_scroll_value, 2, bar_height, ps->edge);
+ }
+ }
+ } else if ((gw2obj->cnt > (gw->g.height-2) / iheight) || (gw->g.flags & GLIST_FLG_SCROLLALWAYS)) {
+ iwidth = gw->g.width - (SCROLLWIDTH+3);
+ gdispGFillArea(gw->g.display, gw->g.x+iwidth+2, gw->g.y+1, SCROLLWIDTH, gw->g.height-2, gdispBlendColor(ps->fill, gw->pstyle->background, 128));
+ gdispGDrawLine(gw->g.display, gw->g.x+iwidth+1, gw->g.y+1, gw->g.x+iwidth+1, gw->g.y+gw->g.height-2, ps->edge);
+ #if GDISP_NEED_CONVEX_POLYGON
+ gdispGFillConvexPoly(gw->g.display, gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+(ARROW/2+1), upArrow, 3, ps->fill);
+ gdispGFillConvexPoly(gw->g.display, gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+gw->g.height-(ARROW+ARROW/2+1), downArrow, 3, ps->fill);
+ #else
+ #warning "GWIN: Lists display better when GDISP_NEED_CONVEX_POLYGON is turned on"
+ gdispGFillArea(gw->g.display, gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+(ARROW/2+1), ARROW, ARROW, ps->fill);
+ gdispGFillArea(gw->g.display, gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+gw->g.height-(ARROW+ARROW/2+1), ARROW, ARROW, ps->fill);
+ #endif
+ } else
+ iwidth = gw->g.width - 2;
+
+ #if GWIN_NEED_LIST_IMAGES
+ if ((gw->g.flags & GLIST_FLG_HASIMAGES)) {
+ x += iheight;
+ iwidth -= iheight;
+ }
+ #endif
+
+
+ // Find the top item
+ for (qi = gfxQueueASyncPeek(&gw2obj->list_head), i = iheight - 1; i < gw2obj->top && qi; qi = gfxQueueASyncNext(qi), i+=iheight);
+
+ // the list frame
+ gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, ps->edge);
+
+ // Set the clipping region so we do not override the frame.
+ gdispGSetClip(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2);
+
+ // Draw until we run out of room or items
+ for (y = 1-(gw2obj->top%iheight); y < gw->g.height-2 && qi; qi = gfxQueueASyncNext(qi), y += iheight) {
+ fill = (qi2li->flags & GLIST_FLG_SELECTED) ? ps->fill : gw->pstyle->background;
+ gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, iwidth, iheight, fill);
+ #if GWIN_NEED_LIST_IMAGES
+ if ((gw->g.flags & GLIST_FLG_HASIMAGES)) {
+ // Clear the image area
+ if (qi2li->pimg && gdispImageIsOpen(qi2li->pimg)) {
+ // Calculate which image
+ sy = (qi2li->flags & GLIST_FLG_SELECTED) ? 0 : (iheight-VERTICAL_PADDING);
+ if (!(gw->g.flags & GWIN_FLG_SYSENABLED))
+ sy += 2*(iheight-VERTICAL_PADDING);
+ while (sy > qi2li->pimg->height)
+ sy -= iheight-VERTICAL_PADDING;
+ // Draw the image
+ gdispImageSetBgColor(qi2li->pimg, fill);
+ gdispGImageDraw(gw->g.display, qi2li->pimg, gw->g.x+1, gw->g.y+y, iheight-VERTICAL_PADDING, iheight-VERTICAL_PADDING, 0, sy);
+ }
+ }
+ #endif
+ gdispGFillStringBox(gw->g.display, gw->g.x+x+HORIZONTAL_PADDING, gw->g.y+y, iwidth-HORIZONTAL_PADDING, iheight, qi2li->text, gw->g.font, ps->text, fill, justifyLeft);
+ }
+
+ // Fill any remaining item space
+ if (y < gw->g.height-1)
+ gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, iwidth, gw->g.height-1-y, gw->pstyle->background);
+}
+
+#endif // GFX_USE_GWIN && GWIN_NEED_LIST