814 lines
22 KiB
C
814 lines
22 KiB
C
/*
|
|
* 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 LST_SCROLLWIDTH 16 // the border from the scroll buttons to the frame
|
|
#define LST_ARROW_SZ 10 // arrow side length
|
|
#define LST_HORIZ_PAD 5 // extra horizontal padding for text
|
|
#define LST_VERT_PAD 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)
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
#if GINPUT_NEED_MOUSE
|
|
static void ListMouseSelect(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) + LST_VERT_PAD;
|
|
|
|
// 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 ListMouseDown(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 ListMouseMove and selection is done on ListMouseUp
|
|
if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH)
|
|
return;
|
|
|
|
// Some initial stuff
|
|
iheight = gdispGetFontMetric(gw->g.font, fontHeight) + LST_VERT_PAD;
|
|
pgsz = gw->g.height-2;
|
|
|
|
// Handle click over the scroll bar
|
|
if (x >= gw->g.width-(LST_SCROLLWIDTH+2) && (gw2obj->cnt > pgsz/iheight || (gw->g.flags & GLIST_FLG_SCROLLALWAYS))) {
|
|
if (y < 2*LST_ARROW_SZ) {
|
|
if (gw2obj->top > 0) {
|
|
gw2obj->top -= iheight;
|
|
if (gw2obj->top < 0)
|
|
gw2obj->top = 0;
|
|
_gwinUpdate(&gw->g);
|
|
}
|
|
} else if (y >= gw->g.height - 2*LST_ARROW_SZ) {
|
|
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;
|
|
}
|
|
|
|
ListMouseSelect(gw, x, y);
|
|
}
|
|
|
|
static void ListMouseUp(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;
|
|
|
|
ListMouseSelect(gw, x, y);
|
|
}
|
|
|
|
static void ListMouseMove(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) + LST_VERT_PAD;
|
|
|
|
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 ListToggleOn(GWidgetObject *gw, uint16_t role) {
|
|
const gfxQueueASyncItem * qi;
|
|
const gfxQueueASyncItem * qix;
|
|
int i;
|
|
|
|
coord_t iheight;
|
|
iheight = gdispGetFontMetric(gw->g.font, fontHeight) + LST_VERT_PAD;
|
|
|
|
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;
|
|
|
|
//if we need to scroll down
|
|
if (((i+2)*iheight - gw2obj->top) > gw->g.height){
|
|
gw2obj->top += iheight;
|
|
}
|
|
|
|
_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;
|
|
|
|
//if we need to scroll up
|
|
if (((i-1)*iheight) < gw2obj->top){
|
|
gw2obj->top -= iheight;
|
|
if (gw2obj->top < 0)
|
|
gw2obj->top = 0;
|
|
}
|
|
|
|
_gwinUpdate(&gw->g);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void ListToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) {
|
|
if (role)
|
|
gw2obj->t_up = instance;
|
|
else
|
|
gw2obj->t_dn = instance;
|
|
}
|
|
|
|
static uint16_t ListToggleGet(GWidgetObject *gw, uint16_t role) {
|
|
return role ? gw2obj->t_up : gw2obj->t_dn;
|
|
}
|
|
#endif
|
|
|
|
static void ListDestroy(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
|
|
ListDestroy, // The destroy routine
|
|
_gwidgetRedraw, // The redraw routine
|
|
0, // The after-clear routine
|
|
},
|
|
gwinListDefaultDraw, // default drawing routine
|
|
#if GINPUT_NEED_MOUSE
|
|
{
|
|
ListMouseDown,
|
|
ListMouseUp,
|
|
ListMouseMove,
|
|
},
|
|
#endif
|
|
#if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD
|
|
{
|
|
0 // Process keyboard events
|
|
},
|
|
#endif
|
|
#if GINPUT_NEED_TOGGLE
|
|
{
|
|
2, // two toggle roles
|
|
ListToggleAssign, // Assign toggles
|
|
ListToggleGet, // Get toggles
|
|
0,
|
|
ListToggleOn, // Process toggle on event
|
|
},
|
|
#endif
|
|
#if GINPUT_NEED_DIAL
|
|
{
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
},
|
|
#endif
|
|
};
|
|
|
|
GHandle gwinGListCreate(GDisplay *g, GListObject* gobj, GWidgetInit* pInit, gBool 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, gBool 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* text, gBool useAlloc) {
|
|
ListItem *newItem;
|
|
|
|
// is it a valid handle?
|
|
if (gh->vmt != (gwinVMT *)&listVMT)
|
|
return -1;
|
|
|
|
if (useAlloc) {
|
|
size_t len = strlen(text)+1;
|
|
if (!(newItem = gfxAlloc(sizeof(ListItem) + len)))
|
|
return -1;
|
|
|
|
memcpy((char *)(newItem+1), text, len);
|
|
text = (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 = text;
|
|
#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;
|
|
}
|
|
|
|
void gwinListItemSetText(GHandle gh, int item, const char* text, gBool useAlloc) {
|
|
const gfxQueueASyncItem *qi;
|
|
int i;
|
|
ListItem *newItem;
|
|
|
|
// 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) {
|
|
|
|
// create the new object
|
|
if (useAlloc) {
|
|
size_t len = strlen(text)+1;
|
|
if (!(newItem = gfxAlloc(sizeof(ListItem) + len)))
|
|
return;
|
|
|
|
memcpy((char *)(newItem+1), text, len);
|
|
text = (const char *)(newItem+1);
|
|
} else {
|
|
if (!(newItem = gfxAlloc(sizeof(ListItem))))
|
|
return;
|
|
}
|
|
|
|
// copy the info from the existing object
|
|
newItem->flags = qi2li->flags;
|
|
newItem->param = qi2li->param;
|
|
newItem->text = text;
|
|
#if GWIN_NEED_LIST_IMAGES
|
|
newItem->pimg = qi2li->pimg;
|
|
#endif
|
|
|
|
// add the new item to the list and remove the old item
|
|
gfxQueueASyncInsert(&gh2obj->list_head, &newItem->q_item, &qi2li->q_item);
|
|
gfxQueueASyncRemove(&gh2obj->list_head, &qi2li->q_item);
|
|
gfxFree(qi2li);
|
|
|
|
_gwinUpdate(gh);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
gh2obj->cnt--;
|
|
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;
|
|
}
|
|
|
|
gBool gwinListItemIsSelected(GHandle gh, int item) {
|
|
const gfxQueueASyncItem * qi;
|
|
int i;
|
|
|
|
// is it a valid handle?
|
|
if (gh->vmt != (gwinVMT *)&listVMT)
|
|
return gFalse;
|
|
|
|
// watch out for an invalid item
|
|
if (item < 0 || item > (gh2obj->cnt) - 1)
|
|
return gFalse;
|
|
|
|
for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
|
|
if (i == item)
|
|
return (qi2li->flags & GLIST_FLG_SELECTED) ? gTrue : gFalse;
|
|
}
|
|
return gFalse;
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
void gwinListSetSelected(GHandle gh, int item, gBool doSelect) {
|
|
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;
|
|
|
|
// If not a multiselect mode - clear previous selected item
|
|
if (doSelect && !(gh->flags & GLIST_FLG_MULTISELECT)) {
|
|
for(qi = gfxQueueASyncPeek(&gh2obj->list_head); qi; qi = gfxQueueASyncNext(qi)) {
|
|
if (qi2li->flags & GLIST_FLG_SELECTED) {
|
|
qi2li->flags &= ~GLIST_FLG_SELECTED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find item and set selected or not
|
|
for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
|
|
if (i == item) {
|
|
if (doSelect)
|
|
qi2li->flags |= GLIST_FLG_SELECTED;
|
|
else
|
|
qi2li->flags &= ~GLIST_FLG_SELECTED;
|
|
break;
|
|
}
|
|
}
|
|
_gwinUpdate(gh);
|
|
}
|
|
|
|
void gwinListViewItem(GHandle gh, int item) {
|
|
coord_t iheight;
|
|
|
|
// is it a valid handle?
|
|
if (gh->vmt != (gwinVMT *)&listVMT)
|
|
return;
|
|
|
|
// watch out for an invalid item
|
|
if (item < 0 || item >= gh2obj->cnt)
|
|
return;
|
|
|
|
// Work out a possible new top for the list
|
|
iheight = gdispGetFontMetric(gh->font, fontHeight) + LST_VERT_PAD;
|
|
gh2obj->top = iheight * item;
|
|
|
|
// Adjust the list
|
|
if (gh2obj->top > gh2obj->cnt * iheight - gh->height-2)
|
|
gh2obj->top = gh2obj->cnt * iheight - gh->height-2;
|
|
if (gh2obj->top < 0)
|
|
gh2obj->top = 0;
|
|
|
|
_gwinUpdate(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
|
|
|
|
void gwinListDefaultDraw(GWidgetObject* gw, void* param) {
|
|
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
|
|
#if GDISP_NEED_CONVEX_POLYGON
|
|
static const point upArrow[] = { {0, LST_ARROW_SZ}, {LST_ARROW_SZ, LST_ARROW_SZ}, {LST_ARROW_SZ/2, 0} };
|
|
static const point downArrow[] = { {0, 0}, {LST_ARROW_SZ, 0}, {LST_ARROW_SZ/2, LST_ARROW_SZ} };
|
|
#endif
|
|
|
|
(void)param;
|
|
|
|
// 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) + LST_VERT_PAD;
|
|
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 - (LST_SCROLLWIDTH+3);
|
|
gdispGFillArea(gw->g.display, gw->g.x+iwidth+2, gw->g.y+1, LST_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+((LST_SCROLLWIDTH-LST_ARROW_SZ)/2+2), gw->g.y+(LST_ARROW_SZ/2+1), upArrow, 3, ps->fill);
|
|
gdispGFillConvexPoly(gw->g.display, gw->g.x+iwidth+((LST_SCROLLWIDTH-LST_ARROW_SZ)/2+2), gw->g.y+gw->g.height-(LST_ARROW_SZ+LST_ARROW_SZ/2+1), downArrow, 3, ps->fill);
|
|
#else
|
|
#if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT
|
|
#warning "GWIN: Lists display better when GDISP_NEED_CONVEX_POLYGON is turned on"
|
|
#elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO
|
|
COMPILER_WARNING("GWIN: Lists display better when GDISP_NEED_CONVEX_POLYGON is turned on")
|
|
#endif
|
|
gdispGFillArea(gw->g.display, gw->g.x+iwidth+((LST_SCROLLWIDTH-LST_ARROW_SZ)/2+2), gw->g.y+(LST_ARROW_SZ/2+1), LST_ARROW_SZ, LST_ARROW_SZ, ps->fill);
|
|
gdispGFillArea(gw->g.display, gw->g.x+iwidth+((LST_SCROLLWIDTH-LST_ARROW_SZ)/2+2), gw->g.y+gw->g.height-(LST_ARROW_SZ+LST_ARROW_SZ/2+1), LST_ARROW_SZ, LST_ARROW_SZ, 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.
|
|
#if GDISP_NEED_CLIP
|
|
gdispGSetClip(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2);
|
|
#endif
|
|
|
|
// 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-LST_VERT_PAD);
|
|
if (!(gw->g.flags & GWIN_FLG_SYSENABLED))
|
|
sy += 2*(iheight-LST_VERT_PAD);
|
|
while (sy > qi2li->pimg->height)
|
|
sy -= iheight-LST_VERT_PAD;
|
|
// Draw the image
|
|
gdispImageSetBgColor(qi2li->pimg, fill);
|
|
gdispGImageDraw(gw->g.display, qi2li->pimg, gw->g.x+1, gw->g.y+y, iheight-LST_VERT_PAD, iheight-LST_VERT_PAD, 0, sy);
|
|
}
|
|
}
|
|
#endif
|
|
gdispGFillStringBox(gw->g.display, gw->g.x+x+LST_HORIZ_PAD, gw->g.y+y, iwidth-LST_HORIZ_PAD, 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);
|
|
}
|
|
|
|
#undef gh2obj
|
|
#undef gw2obj
|
|
#undef qi2li
|
|
#undef qix2li
|
|
#undef ple
|
|
#endif // GFX_USE_GWIN && GWIN_NEED_LIST
|