aboutsummaryrefslogtreecommitdiffstats
path: root/src/gwin/list.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gwin/list.c')
-rw-r--r--src/gwin/list.c443
1 files changed, 432 insertions, 11 deletions
diff --git a/src/gwin/list.c b/src/gwin/list.c
index eae9bb46..a938150f 100644
--- a/src/gwin/list.c
+++ b/src/gwin/list.c
@@ -20,34 +20,255 @@
#if GFX_USE_GWIN && GWIN_NEED_LIST
#include "gwin/class_gwin.h"
+#include <string.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 TEXTGAP 1 // extra vertical padding for text
+
+#define gh2obj ((GListObject *)gh)
+#define gw2obj ((GListObject *)gw)
+#define qi2li ((ListItem *)qi)
+#define qix2li ((ListItem *)qix)
+#define ple ((GEventGWinList *)pe)
+
+#define GLIST_FLG_MULTISELECT (GWIN_FIRST_CONTROL_FLAG << 0)
+#define GLIST_FLG_HASIMAGES (GWIN_FIRST_CONTROL_FLAG << 1)
+#define GLIST_FLG_SELECTED (GWIN_FIRST_CONTROL_FLAG << 2)
+
+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_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->list = (GHandle)gw;
+ ple->item = item;
+
+ geventSendEvent(psl);
+ }
+}
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 y, iheight, iwidth;
+ const GColorSet * ps;
+
+ ps = (gw->g.flags & GWIN_FLG_ENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled;
+ iheight = gdispGetFontMetric(gwinGetDefaultFont(), fontHeight) + TEXTGAP;
+
+ // the scroll area
+ if (gw2obj->cnt > (gw->g.height-2) / iheight) {
+ iwidth = gw->g.width - (SCROLLWIDTH+3);
+ gdispFillArea(gw->g.x+iwidth+2, gw->g.y+1, SCROLLWIDTH, gw->g.height-2, gdispBlendColor(ps->fill, gw->pstyle->background, 128));
+ gdispDrawLine(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
+ gdispFillConvexPoly(gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+(ARROW/2+1), upArrow, 3, ps->fill);
+ gdispFillConvexPoly(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_POLGON is turned on"
+ gdispFillArea(gw->g.x+iwidth+((SCROLLWIDTH-ARROW)/2+2), gw->g.y+(ARROW/2+1), ARROW, ARROW, ps->fill);
+ gdispFillArea(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;
+
+
+ // Find the top item
+ for (qi = gfxQueueASyncPeek(&gw2obj->list_head), i = 0; i < gw2obj->top && qi; qi = gfxQueueASyncNext(qi), i++);
+
+ // Draw until we run out of room or items
+ for (y=1; y+iheight < gw->g.height-1 && qi; qi = gfxQueueASyncNext(qi), y += iheight) {
+ if (qi2li->flags & GLIST_FLG_SELECTED) {
+ //gdispFillStringBox(gw->g.x+1, gw->g.y+y, iwidth, iheight, qi2li->text, gwinGetDefaultFont(), gw->pstyle->background, ps->text, justifyLeft);
+ gdispFillStringBox(gw->g.x+1, gw->g.y+y, iwidth, iheight, qi2li->text, gwinGetDefaultFont(), ps->text, ps->fill, justifyLeft);
+ } else {
+ gdispFillStringBox(gw->g.x+1, gw->g.y+y, iwidth, iheight, qi2li->text, gwinGetDefaultFont(), ps->text, gw->pstyle->background, justifyLeft);
+ }
+ }
+
+ // Fill any remaining item space
+ if (y < gw->g.height-1)
+ gdispFillArea(gw->g.x+1, gw->g.y+y, iwidth, gw->g.height-1-y, gw->pstyle->background);
+
+ // the list frame
+ gdispDrawBox(gw->g.x, gw->g.y, gw->g.width, gw->g.height, ps->edge);
+}
+
+#if GINPUT_NEED_MOUSE
+ // a mouse down has occurred over the list area
+ static void MouseDown(GWidgetObject* gw, coord_t x, coord_t y) {
+ const gfxQueueASyncItem* qi;
+ int item, i, pgsz;
+ coord_t iheight;
+ (void) x;
+
+ iheight = gdispGetFontMetric(gwinGetDefaultFont(), fontHeight) + TEXTGAP;
+ pgsz = (gw->g.height-2)/iheight;
+ if (pgsz < 1) pgsz = 1;
+
+ // Handle click over the scroll bar
+ if (gw2obj->cnt > pgsz && x >= gw->g.width-(SCROLLWIDTH+2)) {
+ if (y < 2*ARROW) {
+ if (gw2obj->top > 0) {
+ gw2obj->top--;
+ _gwidgetRedraw(&gw->g);
+ }
+ } else if (y >= gw->g.height - 2*ARROW) {
+ if (gw2obj->top < gw2obj->cnt - pgsz) {
+ gw2obj->top++;
+ _gwidgetRedraw(&gw->g);
+ }
+ } else if (y < gw->g.height/2) {
+ if (gw2obj->top > 0) {
+ if (gw2obj->top > pgsz)
+ gw2obj->top -= pgsz;
+ else
+ gw2obj->top = 0;
+ _gwidgetRedraw(&gw->g);
+ }
+ } else {
+ if (gw2obj->top < gw2obj->cnt - pgsz) {
+ if (gw2obj->top < gw2obj->cnt - 2*pgsz)
+ gw2obj->top += pgsz;
+ else
+ gw2obj->top = gw2obj->cnt - pgsz;
+ _gwidgetRedraw(&gw->g);
+ }
+ }
+ return;
+ }
+ // 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 (item == i)
+ qi2li->flags |= GLIST_FLG_SELECTED;
+ else
+ qi2li->flags &=~ GLIST_FLG_SELECTED;
+ }
+
+ _gwidgetRedraw(&gw->g);
+ sendListEvent(gw, item);
+ }
+#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;
+ _gwidgetRedraw(&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;
+ _gwidgetRedraw(&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
- _gwidgetDestroy, // The destroy routine
+ _destroy, // The destroy routine
_gwidgetRedraw, // The redraw routine
0, // The after-clear routine
},
gwinListDefaultDraw, // default drawing routine
- #if GWINPUT_NEED_MOUSE
+ #if GINPUT_NEED_MOUSE
{
- 0,
+ MouseDown,
0,
0,
},
#endif
#if GINPUT_NEED_TOGGLE
{
+ 2, // two toggle roles
+ ToggleAssign, // Assign toggles
+ ToggleGet, // get toggles
0,
- 0,
- 0,
- 0,
- 0,
+ ToggleOn, // process toggle on event
},
#endif
#if GINPUT_NEED_DIAL
@@ -60,13 +281,213 @@ static const gwidgetVMT listVMT = {
#endif
};
-GHandle gwinListCreate(GListObject* widget, GWidgetInit* pInit) {
- if (!(widget = (GListObject *)_gwidgetCreate(&widget->w, pInit, &listVMT)))
+GHandle gwinListCreate(GListObject* gobj, GWidgetInit* pInit) {
+ if (!(gobj = (GListObject *)_gwidgetCreate(&gobj->w, pInit, &listVMT)))
return 0;
- gwinSetVisible(&widget->w.g, pInit->g.show);
+ // initialize the item queue
+ gfxQueueASyncInit(&gobj->list_head);
+ gobj->cnt = 0;
+ gobj->top = 0;
+
+ gwinSetVisible(&gobj->w.g, pInit->g.show);
+
+ return (GHandle)gobj;
+}
+
+int gwinListAddItem(GHandle gh, const char* item_name, bool_t useAlloc) {
+ ListItem *newItem;
+
+ if (useAlloc) {
+ if (!(newItem = (ListItem *)gfxAlloc(sizeof(ListItem)+strlen(item_name)+1)))
+ return -1;
+
+ strcpy((char *)(newItem+1), item_name);
+ item_name = (const char *)(newItem+1);
+ } else {
+ if (!(newItem = (ListItem *)gfxAlloc(sizeof(ListItem))))
+ return -1;
+ }
+
+ // the item is not selected when added
+ newItem->flags = 0;
+ newItem->param = 0;
+ newItem->text = item_name;
+
+ // select the item if it's the first in the list
+ if (gh2obj->cnt == 0)
+ 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++;
+
+ _gwidgetRedraw(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;
+
+ 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);
+
+ gh2obj->cnt = 0;
+ gh2obj->top = 0;
+ _gwidgetRedraw(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--;
+ _gwidgetRedraw(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 (GHandle)widget;
+ return gh2obj->cnt;
}
#endif // GFX_USE_GWIN && GWIN_NEED_LIST