µGFX library fork

gwin_list.c 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. /*
  2. * This file is subject to the terms of the GFX License. If a copy of
  3. * the license was not distributed with this file, you can obtain one at:
  4. *
  5. * http://ugfx.org/license.html
  6. */
  7. /**
  8. * @file src/gwin/gwin_list.c
  9. * @brief GWIN list widget header file
  10. */
  11. #include "../../gfx.h"
  12. #if GFX_USE_GWIN && GWIN_NEED_LIST
  13. #include "gwin_class.h"
  14. #include <string.h>
  15. #include <stdlib.h>
  16. // user for the default drawing routine
  17. #define LST_SCROLLWIDTH 16 // the border from the scroll buttons to the frame
  18. #define LST_ARROW_SZ 10 // arrow side length
  19. #define LST_HORIZ_PAD 5 // extra horizontal padding for text
  20. #define LST_VERT_PAD 2 // extra vertical padding for text
  21. // Macro's to assist in data type conversions
  22. #define gh2obj ((GListObject *)gh)
  23. #define gw2obj ((GListObject *)gw)
  24. #define qi2li ((ListItem *)qi)
  25. #define qix2li ((ListItem *)qix)
  26. #define ple ((GEventGWinList *)pe)
  27. static void sendListEvent(GWidgetObject *gw, int item) {
  28. GSourceListener* psl;
  29. GEvent* pe;
  30. // Trigger a GWIN list event
  31. psl = 0;
  32. while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) {
  33. if (!(pe = geventGetEventBuffer(psl)))
  34. continue;
  35. ple->type = GEVENT_GWIN_LIST;
  36. ple->gwin = (GHandle)gw;
  37. ple->item = item;
  38. #if GWIN_WIDGET_TAGS
  39. ple->tag = gw->tag;
  40. #endif
  41. geventSendEvent(psl);
  42. }
  43. }
  44. #if GINPUT_NEED_MOUSE
  45. static void ListMouseSelect(GWidgetObject* gw, coord_t x, coord_t y) {
  46. const gfxQueueASyncItem* qi;
  47. int item, i;
  48. coord_t iheight;
  49. (void) x;
  50. iheight = gdispGetFontMetric(gw->g.font, fontHeight) + LST_VERT_PAD;
  51. // Handle click over the list area
  52. item = (gw2obj->top + y) / iheight;
  53. if (item < 0 || item >= gw2obj->cnt)
  54. return;
  55. for(qi = gfxQueueASyncPeek(&gw2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
  56. if ((gw->g.flags & GLIST_FLG_MULTISELECT)) {
  57. if (item == i) {
  58. qi2li->flags ^= GLIST_FLG_SELECTED;
  59. break;
  60. }
  61. } else {
  62. if (item == i)
  63. qi2li->flags |= GLIST_FLG_SELECTED;
  64. else
  65. qi2li->flags &=~ GLIST_FLG_SELECTED;
  66. }
  67. }
  68. _gwinUpdate(&gw->g);
  69. sendListEvent(gw, item);
  70. }
  71. // a mouse down has occurred over the list area
  72. static void ListMouseDown(GWidgetObject* gw, coord_t x, coord_t y) {
  73. coord_t iheight, pgsz;
  74. // Save our mouse start position
  75. gw2obj->start_mouse_x = x;
  76. gw2obj->start_mouse_y = y;
  77. gw2obj->last_mouse_y = y;
  78. // For smooth scrolling, scrolling is done in the ListMouseMove and selection is done on ListMouseUp
  79. if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH)
  80. return;
  81. // Some initial stuff
  82. iheight = gdispGetFontMetric(gw->g.font, fontHeight) + LST_VERT_PAD;
  83. pgsz = gw->g.height-2;
  84. // Handle click over the scroll bar
  85. if (x >= gw->g.width-(LST_SCROLLWIDTH+2) && (gw2obj->cnt > pgsz/iheight || (gw->g.flags & GLIST_FLG_SCROLLALWAYS))) {
  86. if (y < 2*LST_ARROW_SZ) {
  87. if (gw2obj->top > 0) {
  88. gw2obj->top -= iheight;
  89. if (gw2obj->top < 0)
  90. gw2obj->top = 0;
  91. _gwinUpdate(&gw->g);
  92. }
  93. } else if (y >= gw->g.height - 2*LST_ARROW_SZ) {
  94. if (gw2obj->top < gw2obj->cnt * iheight - pgsz) {
  95. gw2obj->top += iheight;
  96. if (gw2obj->top > gw2obj->cnt * iheight - pgsz)
  97. gw2obj->top = gw2obj->cnt * iheight - pgsz;
  98. _gwinUpdate(&gw->g);
  99. }
  100. } else if (y < gw->g.height/2) {
  101. if (gw2obj->top > 0) {
  102. if (gw2obj->top > pgsz)
  103. gw2obj->top -= pgsz;
  104. else
  105. gw2obj->top = 0;
  106. _gwinUpdate(&gw->g);
  107. }
  108. } else {
  109. if (gw2obj->top < gw2obj->cnt * iheight - pgsz) {
  110. if (gw2obj->top < gw2obj->cnt * iheight - 2*pgsz)
  111. gw2obj->top += pgsz;
  112. else
  113. gw2obj->top = gw2obj->cnt * iheight - pgsz;
  114. _gwinUpdate(&gw->g);
  115. }
  116. }
  117. return;
  118. }
  119. ListMouseSelect(gw, x, y);
  120. }
  121. static void ListMouseUp(GWidgetObject* gw, coord_t x, coord_t y) {
  122. // Only act when we are a smooth scrolling list
  123. if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH))
  124. return;
  125. // Only allow selection when we did not scroll
  126. if (abs(gw2obj->start_mouse_x - x) > 4 || abs(gw2obj->start_mouse_y - y) > 4)
  127. return;
  128. ListMouseSelect(gw, x, y);
  129. }
  130. static void ListMouseMove(GWidgetObject* gw, coord_t x, coord_t y) {
  131. int iheight, oldtop;
  132. (void) x;
  133. if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH)) return;
  134. if (gw2obj->last_mouse_y != y) {
  135. oldtop = gw2obj->top;
  136. iheight = gdispGetFontMetric(gw->g.font, fontHeight) + LST_VERT_PAD;
  137. gw2obj->top -= y - gw2obj->last_mouse_y;
  138. if (gw2obj->top >= gw2obj->cnt * iheight - (gw->g.height-2))
  139. gw2obj->top = gw2obj->cnt * iheight - (gw->g.height-2) - 1;
  140. if (gw2obj->top < 0)
  141. gw2obj->top = 0;
  142. gw2obj->last_mouse_y = y;
  143. if (oldtop != gw2obj->top)
  144. _gwinUpdate(&gw->g);
  145. }
  146. }
  147. #endif
  148. #if GINPUT_NEED_TOGGLE
  149. // a toggle-on has occurred
  150. static void ListToggleOn(GWidgetObject *gw, uint16_t role) {
  151. const gfxQueueASyncItem * qi;
  152. const gfxQueueASyncItem * qix;
  153. int i;
  154. coord_t iheight;
  155. iheight = gdispGetFontMetric(gw->g.font, fontHeight) + LST_VERT_PAD;
  156. switch (role) {
  157. // select down
  158. case 0:
  159. for (i = 0, qi = gfxQueueASyncPeek(&gw2obj->list_head); qi; qi = gfxQueueASyncNext(qi), i++) {
  160. if ((qi2li->flags & GLIST_FLG_SELECTED)) {
  161. qix = gfxQueueASyncNext(qi);
  162. if (qix) {
  163. qi2li->flags &=~ GLIST_FLG_SELECTED;
  164. qix2li->flags |= GLIST_FLG_SELECTED;
  165. //if we need to scroll down
  166. if (((i+2)*iheight - gw2obj->top) > gw->g.height){
  167. gw2obj->top += iheight;
  168. }
  169. _gwinUpdate(&gw->g);
  170. }
  171. break;
  172. }
  173. }
  174. break;
  175. // select up
  176. case 1:
  177. qi = gfxQueueASyncPeek(&gw2obj->list_head);
  178. qix = 0;
  179. for (i = 0; qi; qix = qi, qi = gfxQueueASyncNext(qi), i++) {
  180. if ((qi2li->flags & GLIST_FLG_SELECTED)) {
  181. if (qix) {
  182. qi2li->flags &=~ GLIST_FLG_SELECTED;
  183. qix2li->flags |= GLIST_FLG_SELECTED;
  184. //if we need to scroll up
  185. if (((i-1)*iheight) < gw2obj->top){
  186. gw2obj->top -= iheight;
  187. if (gw2obj->top < 0)
  188. gw2obj->top = 0;
  189. }
  190. _gwinUpdate(&gw->g);
  191. }
  192. break;
  193. }
  194. }
  195. break;
  196. }
  197. }
  198. static void ListToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) {
  199. if (role)
  200. gw2obj->t_up = instance;
  201. else
  202. gw2obj->t_dn = instance;
  203. }
  204. static uint16_t ListToggleGet(GWidgetObject *gw, uint16_t role) {
  205. return role ? gw2obj->t_up : gw2obj->t_dn;
  206. }
  207. #endif
  208. static void ListDestroy(GHandle gh) {
  209. const gfxQueueASyncItem* qi;
  210. while((qi = gfxQueueASyncGet(&gh2obj->list_head)))
  211. gfxFree((void *)qi);
  212. _gwidgetDestroy(gh);
  213. }
  214. static const gwidgetVMT listVMT = {
  215. {
  216. "List", // The class name
  217. sizeof(GListObject), // The object size
  218. ListDestroy, // The destroy routine
  219. _gwidgetRedraw, // The redraw routine
  220. 0, // The after-clear routine
  221. },
  222. gwinListDefaultDraw, // default drawing routine
  223. #if GINPUT_NEED_MOUSE
  224. {
  225. ListMouseDown,
  226. ListMouseUp,
  227. ListMouseMove,
  228. },
  229. #endif
  230. #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD
  231. {
  232. 0 // Process keyboard events
  233. },
  234. #endif
  235. #if GINPUT_NEED_TOGGLE
  236. {
  237. 2, // two toggle roles
  238. ListToggleAssign, // Assign toggles
  239. ListToggleGet, // Get toggles
  240. 0,
  241. ListToggleOn, // Process toggle on event
  242. },
  243. #endif
  244. #if GINPUT_NEED_DIAL
  245. {
  246. 0,
  247. 0,
  248. 0,
  249. 0,
  250. },
  251. #endif
  252. };
  253. GHandle gwinGListCreate(GDisplay *g, GListObject* gobj, GWidgetInit* pInit, bool_t multiselect) {
  254. if (!(gobj = (GListObject *)_gwidgetCreate(g, &gobj->w, pInit, &listVMT)))
  255. return 0;
  256. // initialize the item queue
  257. gfxQueueASyncInit(&gobj->list_head);
  258. gobj->cnt = 0;
  259. gobj->top = 0;
  260. if (multiselect)
  261. gobj->w.g.flags |= GLIST_FLG_MULTISELECT;
  262. gobj->w.g.flags |= GLIST_FLG_SCROLLALWAYS;
  263. gobj->w.g.flags |= GLIST_FLG_ENABLERENDER;
  264. gwinSetVisible(&gobj->w.g, pInit->g.show);
  265. return (GHandle)gobj;
  266. }
  267. void gwinListEnableRender(GHandle gh, bool_t ena) {
  268. // is it a valid handle?
  269. if (gh->vmt != (gwinVMT *)&listVMT)
  270. return;
  271. if (ena) {
  272. gh->flags |= GLIST_FLG_ENABLERENDER;
  273. gwinRedraw(gh);
  274. } else {
  275. gh->flags &=~ GLIST_FLG_ENABLERENDER;
  276. }
  277. }
  278. void gwinListSetScroll(GHandle gh, scroll_t flag) {
  279. // is it a valid handle?
  280. if (gh->vmt != (gwinVMT *)&listVMT)
  281. return;
  282. ((GListObject*)gh)->w.g.flags &=~(GLIST_FLG_SCROLLSMOOTH | GLIST_FLG_SCROLLALWAYS);
  283. switch (flag) {
  284. case scrollAlways:
  285. ((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLALWAYS;
  286. break;
  287. case scrollAuto:
  288. break;
  289. case scrollSmooth:
  290. ((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLSMOOTH;
  291. break;
  292. }
  293. }
  294. int gwinListAddItem(GHandle gh, const char* text, bool_t useAlloc) {
  295. ListItem *newItem;
  296. // is it a valid handle?
  297. if (gh->vmt != (gwinVMT *)&listVMT)
  298. return -1;
  299. if (useAlloc) {
  300. size_t len = strlen(text)+1;
  301. if (!(newItem = gfxAlloc(sizeof(ListItem) + len)))
  302. return -1;
  303. memcpy((char *)(newItem+1), text, len);
  304. text = (const char *)(newItem+1);
  305. } else {
  306. if (!(newItem = gfxAlloc(sizeof(ListItem))))
  307. return -1;
  308. }
  309. // the item is not selected when added
  310. newItem->flags = 0;
  311. newItem->param = 0;
  312. newItem->text = text;
  313. #if GWIN_NEED_LIST_IMAGES
  314. newItem->pimg = 0;
  315. #endif
  316. // select the item if it's the first in the list
  317. if (gh2obj->cnt == 0 && !(gh->flags & GLIST_FLG_MULTISELECT))
  318. newItem->flags |= GLIST_FLG_SELECTED;
  319. // add the new item to the list
  320. gfxQueueASyncPut(&gh2obj->list_head, &newItem->q_item);
  321. // increment the total amount of entries in the list widget
  322. gh2obj->cnt++;
  323. _gwinUpdate(gh);
  324. // return the position in the list (-1 because we start with index 0)
  325. return gh2obj->cnt-1;
  326. }
  327. void gwinListItemSetText(GHandle gh, int item, const char* text, bool_t useAlloc) {
  328. const gfxQueueASyncItem *qi;
  329. int i;
  330. ListItem *newItem;
  331. // is it a valid handle?
  332. if (gh->vmt != (gwinVMT *)&listVMT)
  333. return;
  334. // watch out for an invalid item
  335. if (item < 0 || item > (gh2obj->cnt) - 1)
  336. return;
  337. for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
  338. if (i == item) {
  339. // create the new object
  340. if (useAlloc) {
  341. size_t len = strlen(text)+1;
  342. if (!(newItem = gfxAlloc(sizeof(ListItem) + len)))
  343. return;
  344. memcpy((char *)(newItem+1), text, len);
  345. text = (const char *)(newItem+1);
  346. } else {
  347. if (!(newItem = gfxAlloc(sizeof(ListItem))))
  348. return;
  349. }
  350. // copy the info from the existing object
  351. newItem->flags = qi2li->flags;
  352. newItem->param = qi2li->param;
  353. newItem->text = text;
  354. #if GWIN_NEED_LIST_IMAGES
  355. newItem->pimg = qi2li->pimg;
  356. #endif
  357. // add the new item to the list and remove the old item
  358. gfxQueueASyncInsert(&gh2obj->list_head, &newItem->q_item, &qi2li->q_item);
  359. gfxQueueASyncRemove(&gh2obj->list_head, &qi2li->q_item);
  360. gfxFree(qi2li);
  361. _gwinUpdate(gh);
  362. break;
  363. }
  364. }
  365. }
  366. const char* gwinListItemGetText(GHandle gh, int item) {
  367. const gfxQueueASyncItem* qi;
  368. int i;
  369. // is it a valid handle?
  370. if (gh->vmt != (gwinVMT *)&listVMT)
  371. return 0;
  372. // watch out for an invalid item
  373. if (item < 0 || item >= gh2obj->cnt)
  374. return 0;
  375. for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
  376. if (i == item)
  377. return qi2li->text;
  378. }
  379. return 0;
  380. }
  381. int gwinListFindText(GHandle gh, const char* text) {
  382. const gfxQueueASyncItem* qi;
  383. int i;
  384. // is it a valid handle?
  385. if (gh->vmt != (gwinVMT *)&listVMT)
  386. return -1;
  387. // watch out for NULL pointers
  388. if (!text)
  389. return -1;
  390. for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
  391. if (strcmp(((ListItem *)qi)->text, text) == 0)
  392. return i;
  393. }
  394. return -1;
  395. }
  396. int gwinListGetSelected(GHandle gh) {
  397. const gfxQueueASyncItem * qi;
  398. int i;
  399. // is it a valid handle?
  400. if (gh->vmt != (gwinVMT *)&listVMT)
  401. return -1;
  402. // Multi-select always returns -1. Use gwinListItemIsSelected() instead
  403. if ((gh->flags & GLIST_FLG_MULTISELECT))
  404. return -1;
  405. for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
  406. if (qi2li->flags & GLIST_FLG_SELECTED)
  407. return i;
  408. }
  409. return -1;
  410. }
  411. void gwinListItemSetParam(GHandle gh, int item, uint16_t param) {
  412. const gfxQueueASyncItem * qi;
  413. int i;
  414. // is it a valid handle?
  415. if (gh->vmt != (gwinVMT *)&listVMT)
  416. return;
  417. // watch out for an invalid item
  418. if (item < 0 || item > (gh2obj->cnt) - 1)
  419. return;
  420. for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
  421. if (i == item) {
  422. qi2li->param = param;
  423. break;
  424. }
  425. }
  426. }
  427. void gwinListDeleteAll(GHandle gh) {
  428. gfxQueueASyncItem* qi;
  429. // is it a valid handle?
  430. if (gh->vmt != (gwinVMT *)&listVMT)
  431. return;
  432. while((qi = gfxQueueASyncGet(&gh2obj->list_head)))
  433. gfxFree(qi);
  434. gh->flags &= ~GLIST_FLG_HASIMAGES;
  435. gh2obj->cnt = 0;
  436. gh2obj->top = 0;
  437. _gwinUpdate(gh);
  438. }
  439. void gwinListItemDelete(GHandle gh, int item) {
  440. const gfxQueueASyncItem * qi;
  441. int i;
  442. // is it a valid handle?
  443. if (gh->vmt != (gwinVMT *)&listVMT)
  444. return;
  445. // watch out for an invalid item
  446. if (item < 0 || item >= gh2obj->cnt)
  447. return;
  448. for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
  449. if (i == item) {
  450. gfxQueueASyncRemove(&gh2obj->list_head, (gfxQueueASyncItem*)qi);
  451. gfxFree((void *)qi);
  452. gh2obj->cnt--;
  453. if (gh2obj->top >= item && gh2obj->top)
  454. gh2obj->top--;
  455. _gwinUpdate(gh);
  456. break;
  457. }
  458. }
  459. }
  460. uint16_t gwinListItemGetParam(GHandle gh, int item) {
  461. const gfxQueueASyncItem * qi;
  462. int i;
  463. // is it a valid handle?
  464. if (gh->vmt != (gwinVMT *)&listVMT)
  465. return 0;
  466. // watch out for an invalid item
  467. if (item < 0 || item > (gh2obj->cnt) - 1)
  468. return 0;
  469. for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
  470. if (i == item)
  471. return qi2li->param;
  472. }
  473. return 0;
  474. }
  475. bool_t gwinListItemIsSelected(GHandle gh, int item) {
  476. const gfxQueueASyncItem * qi;
  477. int i;
  478. // is it a valid handle?
  479. if (gh->vmt != (gwinVMT *)&listVMT)
  480. return FALSE;
  481. // watch out for an invalid item
  482. if (item < 0 || item > (gh2obj->cnt) - 1)
  483. return FALSE;
  484. for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
  485. if (i == item)
  486. return (qi2li->flags & GLIST_FLG_SELECTED) ? TRUE : FALSE;
  487. }
  488. return FALSE;
  489. }
  490. int gwinListItemCount(GHandle gh) {
  491. // is it a valid handle?
  492. if (gh->vmt != (gwinVMT *)&listVMT)
  493. return 0;
  494. return gh2obj->cnt;
  495. }
  496. const char* gwinListGetSelectedText(GHandle gh) {
  497. // is it a valid handle?
  498. if (gh->vmt != (gwinVMT *)&listVMT)
  499. return 0;
  500. // return NULL if nothing is selected (or multi-select)
  501. if (gwinListGetSelected(gh) < 0)
  502. return 0;
  503. return gwinListItemGetText(gh, gwinListGetSelected(gh));
  504. }
  505. void gwinListSetSelected(GHandle gh, int item, bool_t doSelect) {
  506. const gfxQueueASyncItem * qi;
  507. int i;
  508. // is it a valid handle?
  509. if (gh->vmt != (gwinVMT *)&listVMT)
  510. return;
  511. // watch out for an invalid item
  512. if (item < 0 || item >= gh2obj->cnt)
  513. return;
  514. // If not a multiselect mode - clear previous selected item
  515. if (doSelect && !(gh->flags & GLIST_FLG_MULTISELECT)) {
  516. for(qi = gfxQueueASyncPeek(&gh2obj->list_head); qi; qi = gfxQueueASyncNext(qi)) {
  517. if (qi2li->flags & GLIST_FLG_SELECTED) {
  518. qi2li->flags &= ~GLIST_FLG_SELECTED;
  519. break;
  520. }
  521. }
  522. }
  523. // Find item and set selected or not
  524. for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
  525. if (i == item) {
  526. if (doSelect)
  527. qi2li->flags |= GLIST_FLG_SELECTED;
  528. else
  529. qi2li->flags &= ~GLIST_FLG_SELECTED;
  530. break;
  531. }
  532. }
  533. _gwinUpdate(gh);
  534. }
  535. void gwinListViewItem(GHandle gh, int item) {
  536. coord_t iheight;
  537. // is it a valid handle?
  538. if (gh->vmt != (gwinVMT *)&listVMT)
  539. return;
  540. // watch out for an invalid item
  541. if (item < 0 || item >= gh2obj->cnt)
  542. return;
  543. // Work out a possible new top for the list
  544. iheight = gdispGetFontMetric(gh->font, fontHeight) + LST_VERT_PAD;
  545. gh2obj->top = iheight * item;
  546. // Adjust the list
  547. if (gh2obj->top > gh2obj->cnt * iheight - gh->height-2)
  548. gh2obj->top = gh2obj->cnt * iheight - gh->height-2;
  549. if (gh2obj->top < 0)
  550. gh2obj->top = 0;
  551. _gwinUpdate(gh);
  552. }
  553. #if GWIN_NEED_LIST_IMAGES
  554. void gwinListItemSetImage(GHandle gh, int item, gdispImage *pimg) {
  555. const gfxQueueASyncItem * qi;
  556. int i;
  557. // is it a valid handle?
  558. if (gh->vmt != (gwinVMT *)&listVMT)
  559. return;
  560. // watch out for an invalid item
  561. if (item < 0 || item > (gh2obj->cnt) - 1)
  562. return;
  563. for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
  564. if (i == item) {
  565. qi2li->pimg = pimg;
  566. if (pimg)
  567. gh->flags |= GLIST_FLG_HASIMAGES;
  568. break;
  569. }
  570. }
  571. }
  572. #endif
  573. void gwinListDefaultDraw(GWidgetObject* gw, void* param) {
  574. const gfxQueueASyncItem* qi;
  575. int i;
  576. coord_t x, y, iheight, iwidth;
  577. color_t fill;
  578. const GColorSet * ps;
  579. #if GWIN_NEED_LIST_IMAGES
  580. coord_t sy;
  581. #endif
  582. #if GDISP_NEED_CONVEX_POLYGON
  583. static const point upArrow[] = { {0, LST_ARROW_SZ}, {LST_ARROW_SZ, LST_ARROW_SZ}, {LST_ARROW_SZ/2, 0} };
  584. static const point downArrow[] = { {0, 0}, {LST_ARROW_SZ, 0}, {LST_ARROW_SZ/2, LST_ARROW_SZ} };
  585. #endif
  586. (void)param;
  587. // is it a valid handle?
  588. if (gw->g.vmt != (gwinVMT *)&listVMT)
  589. return;
  590. // don't render if render has been disabled
  591. if (!(gw->g.flags & GLIST_FLG_ENABLERENDER))
  592. return;
  593. ps = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled;
  594. iheight = gdispGetFontMetric(gw->g.font, fontHeight) + LST_VERT_PAD;
  595. x = 1;
  596. // the scroll area
  597. if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH) {
  598. iwidth = gw->g.width - 2 - 4;
  599. if (gw2obj->cnt > 0) {
  600. int max_scroll_value = gw2obj->cnt * iheight - gw->g.height-2;
  601. if (max_scroll_value > 0) {
  602. int bar_height = (gw->g.height-2) * (gw->g.height-2) / (gw2obj->cnt * iheight);
  603. gdispGFillArea(gw->g.display, gw->g.x + gw->g.width-4, gw->g.y + 1, 2, gw->g.height-1, gw->pstyle->background);
  604. 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);
  605. }
  606. }
  607. } else if ((gw2obj->cnt > (gw->g.height-2) / iheight) || (gw->g.flags & GLIST_FLG_SCROLLALWAYS)) {
  608. iwidth = gw->g.width - (LST_SCROLLWIDTH+3);
  609. 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));
  610. 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);
  611. #if GDISP_NEED_CONVEX_POLYGON
  612. 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);
  613. 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);
  614. #else
  615. #if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT
  616. #warning "GWIN: Lists display better when GDISP_NEED_CONVEX_POLYGON is turned on"
  617. #elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO
  618. COMPILER_WARNING("GWIN: Lists display better when GDISP_NEED_CONVEX_POLYGON is turned on")
  619. #endif
  620. 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);
  621. 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);
  622. #endif
  623. } else
  624. iwidth = gw->g.width - 2;
  625. #if GWIN_NEED_LIST_IMAGES
  626. if ((gw->g.flags & GLIST_FLG_HASIMAGES)) {
  627. x += iheight;
  628. iwidth -= iheight;
  629. }
  630. #endif
  631. // Find the top item
  632. for (qi = gfxQueueASyncPeek(&gw2obj->list_head), i = iheight - 1; i < gw2obj->top && qi; qi = gfxQueueASyncNext(qi), i+=iheight);
  633. // the list frame
  634. gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, ps->edge);
  635. // Set the clipping region so we do not override the frame.
  636. #if GDISP_NEED_CLIP
  637. gdispGSetClip(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2);
  638. #endif
  639. // Draw until we run out of room or items
  640. for (y = 1-(gw2obj->top%iheight); y < gw->g.height-2 && qi; qi = gfxQueueASyncNext(qi), y += iheight) {
  641. fill = (qi2li->flags & GLIST_FLG_SELECTED) ? ps->fill : gw->pstyle->background;
  642. gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, iwidth, iheight, fill);
  643. #if GWIN_NEED_LIST_IMAGES
  644. if ((gw->g.flags & GLIST_FLG_HASIMAGES)) {
  645. // Clear the image area
  646. if (qi2li->pimg && gdispImageIsOpen(qi2li->pimg)) {
  647. // Calculate which image
  648. sy = (qi2li->flags & GLIST_FLG_SELECTED) ? 0 : (iheight-LST_VERT_PAD);
  649. if (!(gw->g.flags & GWIN_FLG_SYSENABLED))
  650. sy += 2*(iheight-LST_VERT_PAD);
  651. while (sy > qi2li->pimg->height)
  652. sy -= iheight-LST_VERT_PAD;
  653. // Draw the image
  654. gdispImageSetBgColor(qi2li->pimg, fill);
  655. gdispGImageDraw(gw->g.display, qi2li->pimg, gw->g.x+1, gw->g.y+y, iheight-LST_VERT_PAD, iheight-LST_VERT_PAD, 0, sy);
  656. }
  657. }
  658. #endif
  659. 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);
  660. }
  661. // Fill any remaining item space
  662. if (y < gw->g.height-1)
  663. gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, iwidth, gw->g.height-1-y, gw->pstyle->background);
  664. }
  665. #undef gh2obj
  666. #undef gw2obj
  667. #undef qi2li
  668. #undef qix2li
  669. #undef ple
  670. #endif // GFX_USE_GWIN && GWIN_NEED_LIST