2013-07-17 15:49:21 +00:00
|
|
|
/*
|
|
|
|
* 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:
|
|
|
|
*
|
2013-07-21 20:20:37 +00:00
|
|
|
* http://ugfx.org/license.html
|
2013-07-17 15:49:21 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file include/gwin/list.h
|
|
|
|
* @brief GWIN list widget header file.
|
|
|
|
*
|
|
|
|
* @defgroup List List
|
|
|
|
* @ingroup GWIN
|
|
|
|
*
|
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "gfx.h"
|
|
|
|
|
|
|
|
#if GFX_USE_GWIN && GWIN_NEED_LIST
|
|
|
|
|
|
|
|
#include "gwin/class_gwin.h"
|
2013-07-25 17:15:51 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
2013-07-27 20:55:32 +00:00
|
|
|
#define BORDER 3
|
|
|
|
|
2013-07-25 17:15:51 +00:00
|
|
|
#define widget(gh) ((GListObject *)gh)
|
|
|
|
|
|
|
|
#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)
|
|
|
|
|
|
|
|
/*
|
|
|
|
Use gw->pstyle->background for the unselected fill.
|
|
|
|
Use gw->pstyle->enabled/disabled->text for unselected text color
|
|
|
|
Use gw->pstyle->enabled/disabled->edge for the surounding box
|
|
|
|
Use gw->pstyle->enabled/disabled->fill for the selected text fill
|
|
|
|
or gw->pstyle->pressed->fill for the selected text fill (which ever looks best)
|
|
|
|
and use gw->pstyle->pressed->text for the selected text color
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct ListItem {
|
|
|
|
gfxQueueASyncItem q_item; // This must be the first member in the struct
|
|
|
|
|
|
|
|
uint16_t flags;
|
|
|
|
#define LISTITEM_ALLOCEDTEXT 0x0001
|
2013-07-27 13:23:52 +00:00
|
|
|
uint16_t param; // A parameter the user can specify himself
|
2013-07-25 17:15:51 +00:00
|
|
|
const char* text;
|
|
|
|
#if GWIN_LIST_IMAGES
|
|
|
|
gdispImage* pimg;
|
|
|
|
#endif
|
|
|
|
} ListItem;
|
|
|
|
|
2013-07-27 12:26:16 +00:00
|
|
|
static void sendListEvent(GWidgetObject *gw, int item) {
|
2013-07-25 17:15:51 +00:00
|
|
|
GSourceListener* psl;
|
|
|
|
GEvent* pe;
|
|
|
|
#define pse ((GEventGWinList *)pe)
|
|
|
|
|
|
|
|
// Trigger a GWIN list event
|
|
|
|
psl = 0;
|
2013-07-27 13:23:52 +00:00
|
|
|
|
2013-07-25 17:15:51 +00:00
|
|
|
while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) {
|
|
|
|
if (!(pe = geventGetEventBuffer(psl)))
|
2013-07-27 12:26:16 +00:00
|
|
|
continue;
|
2013-07-25 17:15:51 +00:00
|
|
|
|
2013-07-27 12:26:16 +00:00
|
|
|
pse->type = GEVENT_GWIN_LIST;
|
2013-07-25 17:15:51 +00:00
|
|
|
pse->list = (GHandle)gw;
|
2013-07-27 12:26:16 +00:00
|
|
|
pse->item = item;
|
2013-07-25 17:15:51 +00:00
|
|
|
|
|
|
|
geventSendEvent(psl);
|
|
|
|
}
|
2013-07-27 13:23:52 +00:00
|
|
|
|
2013-07-25 17:15:51 +00:00
|
|
|
#undef pse
|
|
|
|
}
|
2013-07-17 15:49:21 +00:00
|
|
|
|
|
|
|
static void gwinListDefaultDraw(GWidgetObject* gw, void* param) {
|
2013-07-25 17:15:51 +00:00
|
|
|
#define gcw ((GListObject *)gw)
|
|
|
|
(void)param;
|
|
|
|
|
|
|
|
uint16_t i, fheight;
|
2013-07-27 23:15:36 +00:00
|
|
|
const gfxQueueASyncItem* qi;
|
2013-07-25 17:15:51 +00:00
|
|
|
|
|
|
|
fheight = gdispGetFontMetric(gwinGetDefaultFont(), fontHeight);
|
2013-07-17 15:49:21 +00:00
|
|
|
|
2013-07-28 00:06:27 +00:00
|
|
|
gdispDrawBox(gw->g.x, gw->g.y, gw->g.width, gw->g.height, gw->pstyle->enabled.edge);
|
2013-07-25 17:15:51 +00:00
|
|
|
|
2013-07-27 20:55:32 +00:00
|
|
|
for (qi = gfxQueueASyncPeek(&gcw->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i += fheight + 2*BORDER) {
|
2013-07-28 00:06:27 +00:00
|
|
|
if (((ListItem*)qi)->flags & GLIST_FLG_SELECTED) {
|
|
|
|
gdispFillStringBox(gw->g.x + BORDER, gw->g.y + BORDER + i, gw->g.width - 2*BORDER, fheight, ((ListItem*)qi)->text, gwinGetDefaultFont(), gw->pstyle->background, gw->pstyle->enabled.text, justifyLeft);
|
|
|
|
} else {
|
|
|
|
gdispFillStringBox(gw->g.x + BORDER, gw->g.y + BORDER + i, gw->g.width - 2*BORDER, fheight, ((ListItem*)qi)->text, gwinGetDefaultFont(), gw->pstyle->enabled.text, gw->pstyle->background, justifyLeft);
|
|
|
|
}
|
2013-07-25 17:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#undef gcw
|
2013-07-17 15:49:21 +00:00
|
|
|
}
|
|
|
|
|
2013-07-25 17:15:51 +00:00
|
|
|
#if GINPUT_NEED_MOUSE
|
|
|
|
// A mouse down has occured over the list area
|
|
|
|
static void MouseDown(GWidgetObject* gw, coord_t x, coord_t y) {
|
|
|
|
#define gcw ((GListObject *)gw)
|
2013-07-27 12:26:16 +00:00
|
|
|
#define li ((ListItem *)qi)
|
|
|
|
(void) x;
|
2013-07-25 17:15:51 +00:00
|
|
|
|
|
|
|
uint16_t i, item_id, item_height;
|
2013-07-27 23:15:36 +00:00
|
|
|
const gfxQueueASyncItem* qi;
|
2013-07-25 17:15:51 +00:00
|
|
|
|
2013-07-27 20:55:32 +00:00
|
|
|
item_height = gdispGetFontMetric(gwinGetDefaultFont(), fontHeight) + 2*BORDER;
|
2013-07-25 17:15:51 +00:00
|
|
|
|
|
|
|
item_id = (y - gw->g.y) / item_height;
|
|
|
|
|
|
|
|
for(qi = gfxQueueASyncPeek(&gcw->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
|
|
|
|
if (item_id == i)
|
2013-07-27 12:26:16 +00:00
|
|
|
li->flags |= GLIST_FLG_SELECTED;
|
2013-07-25 17:15:51 +00:00
|
|
|
else
|
2013-07-27 12:26:16 +00:00
|
|
|
li->flags &=~ GLIST_FLG_SELECTED;
|
2013-07-25 17:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_gwidgetRedraw((GHandle)gw);
|
2013-07-27 20:55:32 +00:00
|
|
|
|
|
|
|
// Do not generate an event if an empty section of the list has has been selected
|
|
|
|
if (item_id < 0 || item_id > gcw->cnt - 1)
|
|
|
|
return;
|
|
|
|
|
2013-07-27 12:26:16 +00:00
|
|
|
sendListEvent(gw, item_id);
|
2013-07-25 17:15:51 +00:00
|
|
|
|
|
|
|
#undef gcw
|
2013-07-27 12:26:16 +00:00
|
|
|
#undef li
|
2013-07-25 17:15:51 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-07-27 12:26:16 +00:00
|
|
|
static void _destroy(GHandle gh) {
|
2013-07-27 23:15:36 +00:00
|
|
|
const gfxQueueASyncItem* qi;
|
2013-07-27 12:26:16 +00:00
|
|
|
|
|
|
|
while((qi = gfxQueueASyncGet(&((GListObject *)gh)->list_head)))
|
|
|
|
gfxFree((void *)qi);
|
2013-07-27 20:55:32 +00:00
|
|
|
|
2013-07-27 12:26:16 +00:00
|
|
|
_gwidgetDestroy(gh);
|
|
|
|
}
|
|
|
|
|
2013-07-17 15:49:21 +00:00
|
|
|
static const gwidgetVMT listVMT = {
|
|
|
|
{
|
|
|
|
"List", // The class name
|
|
|
|
sizeof(GListObject), // The object size
|
2013-07-27 20:55:32 +00:00
|
|
|
_destroy, // The destroy routine
|
2013-07-17 15:49:21 +00:00
|
|
|
_gwidgetRedraw, // The redraw routine
|
|
|
|
0, // The after-clear routine
|
|
|
|
},
|
|
|
|
gwinListDefaultDraw, // default drawing routine
|
2013-07-25 17:15:51 +00:00
|
|
|
#if GINPUT_NEED_MOUSE
|
2013-07-17 15:49:21 +00:00
|
|
|
{
|
2013-07-25 17:15:51 +00:00
|
|
|
MouseDown,
|
2013-07-17 15:49:21 +00:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#if GINPUT_NEED_TOGGLE
|
|
|
|
{
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#if GINPUT_NEED_DIAL
|
|
|
|
{
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
},
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
GHandle gwinListCreate(GListObject* widget, GWidgetInit* pInit) {
|
|
|
|
if (!(widget = (GListObject *)_gwidgetCreate(&widget->w, pInit, &listVMT)))
|
|
|
|
return 0;
|
|
|
|
|
2013-07-25 17:15:51 +00:00
|
|
|
// initialize the item queue
|
|
|
|
gfxQueueASyncInit(&(widget->list_head));
|
|
|
|
widget->cnt = 0;
|
|
|
|
|
2013-07-17 15:49:21 +00:00
|
|
|
gwinSetVisible(&widget->w.g, pInit->g.show);
|
|
|
|
|
|
|
|
return (GHandle)widget;
|
|
|
|
}
|
|
|
|
|
2013-07-25 17:15:51 +00:00
|
|
|
int gwinListAddItem(GHandle gh, const char* item_name, bool_t useAlloc) {
|
2013-07-27 12:26:16 +00:00
|
|
|
size_t sz;
|
|
|
|
ListItem *newItem;
|
2013-07-25 17:15:51 +00:00
|
|
|
|
2013-07-27 12:26:16 +00:00
|
|
|
sz = useAlloc ? sizeof(ListItem)+strlen(item_name)+1 : sizeof(ListItem);
|
2013-07-25 17:15:51 +00:00
|
|
|
|
2013-07-27 12:26:16 +00:00
|
|
|
if (!(newItem = (ListItem *)gfxAlloc(sz)))
|
|
|
|
return -1;
|
2013-07-25 17:15:51 +00:00
|
|
|
|
2013-07-27 12:26:16 +00:00
|
|
|
if (useAlloc) {
|
|
|
|
strcpy((char *)(newItem+1), item_name);
|
|
|
|
item_name = (const char *)(newItem+1);
|
2013-07-25 17:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// the item is not selected when added
|
2013-07-27 12:26:16 +00:00
|
|
|
newItem->flags = 0;
|
|
|
|
newItem->param = 0;
|
|
|
|
newItem->text = item_name;
|
2013-07-25 17:15:51 +00:00
|
|
|
|
|
|
|
// add the new item to the list
|
2013-07-27 12:26:16 +00:00
|
|
|
gfxQueueASyncPut(&widget(gh)->list_head, &newItem->q_item);
|
2013-07-25 17:15:51 +00:00
|
|
|
|
|
|
|
// increment the total amount of entries in the list widget
|
|
|
|
widget(gh)->cnt++;
|
2013-07-27 12:26:16 +00:00
|
|
|
|
2013-07-27 20:55:32 +00:00
|
|
|
// return the position in the list (-1 because we start with index 0)
|
2013-07-27 12:26:16 +00:00
|
|
|
return widget(gh)->cnt-1;
|
2013-07-25 17:15:51 +00:00
|
|
|
}
|
|
|
|
|
2013-07-27 20:55:32 +00:00
|
|
|
char* gwinListItemGetText(GHandle gh, int item) {
|
2013-07-27 23:15:36 +00:00
|
|
|
const gfxQueueASyncItem* qi;
|
2013-07-27 20:55:32 +00:00
|
|
|
uint16_t i;
|
|
|
|
|
|
|
|
// is it a valid handle?
|
|
|
|
if (gh->vmt != (gwinVMT *)&listVMT)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// watch out for an invalid item
|
|
|
|
if (item < 0 || item > (widget(gh)->cnt) - 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
qi = gfxQueueASyncPeek(&widget(gh)->list_head);
|
|
|
|
|
|
|
|
for (i = 0; i < item; i++) {
|
|
|
|
qi = gfxQueueASyncNext(qi);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ((ListItem*)qi)->text;
|
|
|
|
}
|
|
|
|
|
|
|
|
int gwinListFindText(GHandle gh, const char* text) {
|
2013-07-27 23:15:36 +00:00
|
|
|
const gfxQueueASyncItem* qi;
|
2013-07-27 20:55:32 +00:00
|
|
|
uint16_t i;
|
|
|
|
|
|
|
|
// is it a valid handle?
|
|
|
|
if (gh->vmt != (gwinVMT *)&listVMT)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
// watch out for NULL pointers
|
|
|
|
if (!text)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
qi = gfxQueueASyncPeek(&widget(gh)->list_head);
|
|
|
|
|
|
|
|
for(qi = gfxQueueASyncPeek(&widget(gh)->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
|
|
|
|
if (strcmp(((ListItem *)qi)->text, text) == 0)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-07-25 17:15:51 +00:00
|
|
|
int gwinListGetSelected(GHandle gh) {
|
2013-07-27 23:15:36 +00:00
|
|
|
const gfxQueueASyncItem *qi;
|
2013-07-27 12:26:16 +00:00
|
|
|
int i;
|
2013-07-25 17:15:51 +00:00
|
|
|
|
2013-07-27 20:55:32 +00:00
|
|
|
// is it a valid handle?
|
2013-07-27 12:26:16 +00:00
|
|
|
if (gh->vmt != (gwinVMT *)&listVMT)
|
|
|
|
return -1;
|
2013-07-25 17:15:51 +00:00
|
|
|
|
2013-07-27 12:26:16 +00:00
|
|
|
for(qi = gfxQueueASyncPeek(&widget(gh)->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
|
|
|
|
if (((ListItem*)qi)->flags & GLIST_FLG_SELECTED)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
2013-07-25 17:15:51 +00:00
|
|
|
}
|
|
|
|
|
2013-07-27 20:55:32 +00:00
|
|
|
void gwinListItemSetParam(GHandle gh, int item, uint16_t param) {
|
2013-07-27 23:15:36 +00:00
|
|
|
const gfxQueueASyncItem* qi;
|
2013-07-27 20:55:32 +00:00
|
|
|
uint16_t i;
|
|
|
|
|
|
|
|
// is it a valid handle?
|
|
|
|
if (gh->vmt != (gwinVMT *)&listVMT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// watch out for an invalid item
|
|
|
|
if (item < 0 || item > (widget(gh)->cnt) - 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
qi = gfxQueueASyncPeek(&widget(gh)->list_head);
|
|
|
|
|
|
|
|
for (i = 0; i < item; i++) {
|
|
|
|
((ListItem*)qi)->param = param;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gwinListDeleteAll(GHandle gh) {
|
2013-07-27 23:15:36 +00:00
|
|
|
const gfxQueueASyncItem* qi;
|
2013-07-27 20:55:32 +00:00
|
|
|
|
|
|
|
while((qi = gfxQueueASyncGet(&((GListObject *)gh)->list_head)))
|
|
|
|
gfxFree((void *)qi);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gwinListItemDelete(GHandle gh, int item) {
|
2013-07-27 23:15:36 +00:00
|
|
|
const gfxQueueASyncItem* qi;
|
2013-07-27 20:55:32 +00:00
|
|
|
uint16_t i;
|
|
|
|
|
|
|
|
// is it a valid handle?
|
|
|
|
if (gh->vmt != (gwinVMT *)&listVMT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// watch out for an invalid item
|
|
|
|
if (item < 0 || item > (widget(gh)->cnt) - 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
qi = gfxQueueASyncPeek(&widget(gh)->list_head);
|
|
|
|
|
|
|
|
// get our item pointer
|
|
|
|
for (i = 0; i < item; i++) {
|
|
|
|
qi = gfxQueueASyncNext(qi);
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxQueueASyncRemove(&widget(gh)->list_head, qi);
|
|
|
|
gfxFree((void*)qi);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t gwinListItemGetParam(GHandle gh, int item) {
|
2013-07-27 23:15:36 +00:00
|
|
|
const gfxQueueASyncItem* qi;
|
2013-07-27 20:55:32 +00:00
|
|
|
uint16_t i;
|
|
|
|
|
|
|
|
// is it a valid handle?
|
|
|
|
if (gh->vmt != (gwinVMT *)&listVMT)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// watch out for an invalid item
|
|
|
|
if (item < 0 || item > (widget(gh)->cnt) - 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
qi = gfxQueueASyncPeek(&widget(gh)->list_head);
|
|
|
|
|
|
|
|
for (i = 0; i < item; i++) {
|
|
|
|
qi = gfxQueueASyncNext(qi);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ((ListItem*)qi)->param;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool_t gwinListItemIsSelected(GHandle gh, int item) {
|
2013-07-27 23:15:36 +00:00
|
|
|
const gfxQueueASyncItem* qi;
|
2013-07-27 20:55:32 +00:00
|
|
|
uint16_t i;
|
|
|
|
|
|
|
|
// is it a valid handle?
|
|
|
|
if (gh->vmt != (gwinVMT *)&listVMT)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
// watch out for an invalid item
|
|
|
|
if (item < 0 || item > (widget(gh)->cnt) - 1)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
qi = gfxQueueASyncPeek(&widget(gh)->list_head);
|
|
|
|
|
|
|
|
for (i = 0; i < item; i++) {
|
|
|
|
qi = gfxQueueASyncNext(qi);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (((ListItem*)qi)->flags & GLIST_FLG_SELECTED)
|
|
|
|
return TRUE;
|
|
|
|
else
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int gwinListItemCount(GHandle gh) {
|
|
|
|
// is it a valid handle?
|
|
|
|
if (gh->vmt != (gwinVMT *)&listVMT)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return widget(gh)->cnt;
|
|
|
|
}
|
|
|
|
|
2013-07-17 15:49:21 +00:00
|
|
|
#endif // GFX_USE_GWIN && GWIN_NEED_LIST
|
2013-07-21 20:02:57 +00:00
|
|
|
/** @} */
|
2013-07-17 15:49:21 +00:00
|
|
|
|