µGFX  2.9
version 2.9
gwin_list.c
Go to the documentation of this file.
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.io/license.html
6  */
7 
8 /**
9  * @file src/gwin/gwin_list.c
10  * @brief GWIN list widget header file
11  */
12 
13 #include "../../gfx.h"
14 
15 #if GFX_USE_GWIN && GWIN_NEED_LIST
16 
17 #include "gwin_class.h"
18 #include <string.h>
19 #include <stdlib.h>
20 
21 // user for the default drawing routine
22 #define LST_SCROLLWIDTH 16 // the border from the scroll buttons to the frame
23 #define LST_ARROW_SZ 10 // arrow side length
24 #define LST_HORIZ_PAD 5 // extra horizontal padding for text
25 #define LST_VERT_PAD 2 // extra vertical padding for text
26 
27 // Macro's to assist in data type conversions
28 #define gh2obj ((GListObject *)gh)
29 #define gw2obj ((GListObject *)gw)
30 #define qi2li ((ListItem *)qi)
31 #define qix2li ((ListItem *)qix)
32 #define ple ((GEventGWinList *)pe)
33 
34 static void sendListEvent(GWidgetObject *gw, int item) {
35  GSourceListener* psl;
36  GEvent* pe;
37 
38  // Trigger a GWIN list event
39  psl = 0;
40 
41  while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) {
42  if (!(pe = geventGetEventBuffer(psl)))
43  continue;
44 
45  ple->type = GEVENT_GWIN_LIST;
46  ple->gwin = (GHandle)gw;
47  ple->item = item;
48  #if GWIN_WIDGET_TAGS
49  ple->tag = gw->tag;
50  #endif
51 
52  geventSendEvent(psl);
53  }
54 }
55 
56 #if GINPUT_NEED_MOUSE
57  static void ListMouseSelect(GWidgetObject* gw, gCoord x, gCoord y) {
58  const gfxQueueASyncItem* qi;
59  int item, i;
60  gCoord iheight;
61  (void) x;
62 
63  iheight = gdispGetFontMetric(gw->g.font, gFontHeight) + LST_VERT_PAD;
64 
65  // Handle click over the list area
66  item = (gw2obj->top + y) / iheight;
67 
68  if (item < 0 || item >= gw2obj->cnt)
69  return;
70 
71  for(qi = gfxQueueASyncPeek(&gw2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
72  if ((gw->g.flags & GLIST_FLG_MULTISELECT)) {
73  if (item == i) {
74  qi2li->flags ^= GLIST_FLG_SELECTED;
75  break;
76  }
77  } else {
78  if (item == i)
79  qi2li->flags |= GLIST_FLG_SELECTED;
80  else
81  qi2li->flags &=~ GLIST_FLG_SELECTED;
82  }
83  }
84 
85  _gwinUpdate(&gw->g);
86  sendListEvent(gw, item);
87 
88  }
89 
90  // a mouse down has occurred over the list area
91  static void ListMouseDown(GWidgetObject* gw, gCoord x, gCoord y) {
92  gCoord iheight, pgsz;
93 
94  // Save our mouse start position
95  gw2obj->start_mouse_x = x;
96  gw2obj->start_mouse_y = y;
97  gw2obj->last_mouse_y = y;
98 
99  // For smooth scrolling, scrolling is done in the ListMouseMove and selection is done on ListMouseUp
100  if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH)
101  return;
102 
103  // Some initial stuff
104  iheight = gdispGetFontMetric(gw->g.font, gFontHeight) + LST_VERT_PAD;
105  pgsz = gw->g.height-2;
106 
107  // Handle click over the scroll bar
108  if (x >= gw->g.width-(LST_SCROLLWIDTH+2) && (gw2obj->cnt > pgsz/iheight || (gw->g.flags & GLIST_FLG_SCROLLALWAYS))) {
109  if (y < 2*LST_ARROW_SZ) {
110  if (gw2obj->top > 0) {
111  gw2obj->top -= iheight;
112  if (gw2obj->top < 0)
113  gw2obj->top = 0;
114  _gwinUpdate(&gw->g);
115  }
116  } else if (y >= gw->g.height - 2*LST_ARROW_SZ) {
117  if (gw2obj->top < gw2obj->cnt * iheight - pgsz) {
118  gw2obj->top += iheight;
119  if (gw2obj->top > gw2obj->cnt * iheight - pgsz)
120  gw2obj->top = gw2obj->cnt * iheight - pgsz;
121  _gwinUpdate(&gw->g);
122  }
123  } else if (y < gw->g.height/2) {
124  if (gw2obj->top > 0) {
125  if (gw2obj->top > pgsz)
126  gw2obj->top -= pgsz;
127  else
128  gw2obj->top = 0;
129  _gwinUpdate(&gw->g);
130  }
131  } else {
132  if (gw2obj->top < gw2obj->cnt * iheight - pgsz) {
133  if (gw2obj->top < gw2obj->cnt * iheight - 2*pgsz)
134  gw2obj->top += pgsz;
135  else
136  gw2obj->top = gw2obj->cnt * iheight - pgsz;
137  _gwinUpdate(&gw->g);
138  }
139  }
140  return;
141  }
142 
143  ListMouseSelect(gw, x, y);
144  }
145 
146  static void ListMouseUp(GWidgetObject* gw, gCoord x, gCoord y) {
147  // Only act when we are a smooth scrolling list
148  if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH))
149  return;
150 
151  // Only allow selection when we did not scroll
152  if (abs(gw2obj->start_mouse_x - x) > 4 || abs(gw2obj->start_mouse_y - y) > 4)
153  return;
154 
155  ListMouseSelect(gw, x, y);
156  }
157 
158  static void ListMouseMove(GWidgetObject* gw, gCoord x, gCoord y) {
159  int iheight, oldtop;
160  (void) x;
161 
162  if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH)) return;
163 
164  if (gw2obj->last_mouse_y != y) {
165  oldtop = gw2obj->top;
166  iheight = gdispGetFontMetric(gw->g.font, gFontHeight) + LST_VERT_PAD;
167 
168  gw2obj->top -= y - gw2obj->last_mouse_y;
169  if (gw2obj->top >= gw2obj->cnt * iheight - (gw->g.height-2))
170  gw2obj->top = gw2obj->cnt * iheight - (gw->g.height-2) - 1;
171  if (gw2obj->top < 0)
172  gw2obj->top = 0;
173  gw2obj->last_mouse_y = y;
174  if (oldtop != gw2obj->top)
175  _gwinUpdate(&gw->g);
176  }
177  }
178 #endif
179 
180 #if GINPUT_NEED_TOGGLE
181  // a toggle-on has occurred
182  static void ListToggleOn(GWidgetObject *gw, gU16 role) {
183  const gfxQueueASyncItem * qi;
184  const gfxQueueASyncItem * qix;
185  int i;
186 
187  gCoord iheight;
188  iheight = gdispGetFontMetric(gw->g.font, gFontHeight) + LST_VERT_PAD;
189 
190  switch (role) {
191  // select down
192  case 0:
193  for (i = 0, qi = gfxQueueASyncPeek(&gw2obj->list_head); qi; qi = gfxQueueASyncNext(qi), i++) {
194  if ((qi2li->flags & GLIST_FLG_SELECTED)) {
195  qix = gfxQueueASyncNext(qi);
196  if (qix) {
197  qi2li->flags &=~ GLIST_FLG_SELECTED;
198  qix2li->flags |= GLIST_FLG_SELECTED;
199 
200  //if we need to scroll down
201  if (((i+2)*iheight - gw2obj->top) > gw->g.height){
202  gw2obj->top += iheight;
203  }
204 
205  _gwinUpdate(&gw->g);
206  }
207  break;
208  }
209  }
210  break;
211 
212  // select up
213  case 1:
214  qi = gfxQueueASyncPeek(&gw2obj->list_head);
215  qix = 0;
216 
217  for (i = 0; qi; qix = qi, qi = gfxQueueASyncNext(qi), i++) {
218  if ((qi2li->flags & GLIST_FLG_SELECTED)) {
219  if (qix) {
220  qi2li->flags &=~ GLIST_FLG_SELECTED;
221  qix2li->flags |= GLIST_FLG_SELECTED;
222 
223  //if we need to scroll up
224  if (((i-1)*iheight) < gw2obj->top){
225  gw2obj->top -= iheight;
226  if (gw2obj->top < 0)
227  gw2obj->top = 0;
228  }
229 
230  _gwinUpdate(&gw->g);
231  }
232  break;
233  }
234  }
235  break;
236  }
237  }
238 
239  static void ListToggleAssign(GWidgetObject *gw, gU16 role, gU16 instance) {
240  if (role)
241  gw2obj->t_up = instance;
242  else
243  gw2obj->t_dn = instance;
244  }
245 
246  static gU16 ListToggleGet(GWidgetObject *gw, gU16 role) {
247  return role ? gw2obj->t_up : gw2obj->t_dn;
248  }
249 #endif
250 
251 static void ListDestroy(GHandle gh) {
252  const gfxQueueASyncItem* qi;
253 
254  while((qi = gfxQueueASyncGet(&gh2obj->list_head)))
255  gfxFree((void *)qi);
256 
257  _gwidgetDestroy(gh);
258 }
259 
260 static const gwidgetVMT listVMT = {
261  {
262  "List", // The class name
263  sizeof(GListObject), // The object size
264  ListDestroy, // The destroy routine
265  _gwidgetRedraw, // The redraw routine
266  0, // The after-clear routine
267  },
268  gwinListDefaultDraw, // default drawing routine
269  #if GINPUT_NEED_MOUSE
270  {
271  ListMouseDown,
272  ListMouseUp,
273  ListMouseMove,
274  },
275  #endif
276  #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD
277  {
278  0 // Process keyboard events
279  },
280  #endif
281  #if GINPUT_NEED_TOGGLE
282  {
283  2, // two toggle roles
284  ListToggleAssign, // Assign toggles
285  ListToggleGet, // Get toggles
286  0,
287  ListToggleOn, // Process toggle on event
288  },
289  #endif
290  #if GINPUT_NEED_DIAL
291  {
292  0,
293  0,
294  0,
295  0,
296  },
297  #endif
298 };
299 
300 GHandle gwinGListCreate(GDisplay *g, GListObject* gobj, GWidgetInit* pInit, gBool multiselect) {
301  if (!(gobj = (GListObject *)_gwidgetCreate(g, &gobj->w, pInit, &listVMT)))
302  return 0;
303 
304  // initialize the item queue
305  gfxQueueASyncInit(&gobj->list_head);
306  gobj->cnt = 0;
307  gobj->top = 0;
308  if (multiselect)
309  gobj->w.g.flags |= GLIST_FLG_MULTISELECT;
310  gobj->w.g.flags |= GLIST_FLG_SCROLLALWAYS;
311  gobj->w.g.flags |= GLIST_FLG_ENABLERENDER;
312 
313  gwinSetVisible(&gobj->w.g, pInit->g.show);
314 
315  return (GHandle)gobj;
316 }
317 
318 void gwinListEnableRender(GHandle gh, gBool ena) {
319  // is it a valid handle?
320  if (gh->vmt != (gwinVMT *)&listVMT)
321  return;
322 
323  if (ena) {
324  gh->flags |= GLIST_FLG_ENABLERENDER;
325  gwinRedraw(gh);
326  } else {
327  gh->flags &=~ GLIST_FLG_ENABLERENDER;
328  }
329 }
330 
331 void gwinListSetScroll(GHandle gh, scroll_t flag) {
332  // is it a valid handle?
333  if (gh->vmt != (gwinVMT *)&listVMT)
334  return;
335 
336  ((GListObject*)gh)->w.g.flags &=~(GLIST_FLG_SCROLLSMOOTH | GLIST_FLG_SCROLLALWAYS);
337  switch (flag) {
338  case scrollAlways:
339  ((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLALWAYS;
340  break;
341 
342  case scrollAuto:
343  break;
344 
345  case scrollSmooth:
346  ((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLSMOOTH;
347  break;
348  }
349 }
350 
351 int gwinListAddItem(GHandle gh, const char* text, gBool useAlloc) {
352  ListItem *newItem;
353 
354  // is it a valid handle?
355  if (gh->vmt != (gwinVMT *)&listVMT)
356  return -1;
357 
358  if (useAlloc) {
359  gMemSize len = strlen(text)+1;
360  if (!(newItem = gfxAlloc(sizeof(ListItem) + len)))
361  return -1;
362 
363  memcpy((char *)(newItem+1), text, len);
364  text = (const char *)(newItem+1);
365  } else {
366  if (!(newItem = gfxAlloc(sizeof(ListItem))))
367  return -1;
368  }
369 
370  // the item is not selected when added
371  newItem->flags = 0;
372  newItem->param = 0;
373  newItem->text = text;
374  #if GWIN_NEED_LIST_IMAGES
375  newItem->pimg = 0;
376  #endif
377 
378  // select the item if it's the first in the list
379  if (gh2obj->cnt == 0 && !(gh->flags & GLIST_FLG_MULTISELECT))
380  newItem->flags |= GLIST_FLG_SELECTED;
381 
382  // add the new item to the list
383  gfxQueueASyncPut(&gh2obj->list_head, &newItem->q_item);
384 
385  // increment the total amount of entries in the list widget
386  gh2obj->cnt++;
387 
388  _gwinUpdate(gh);
389 
390  // return the position in the list (-1 because we start with index 0)
391  return gh2obj->cnt-1;
392 }
393 
394 void gwinListItemSetText(GHandle gh, int item, const char* text, gBool useAlloc) {
395  const gfxQueueASyncItem *qi;
396  int i;
397  ListItem *newItem;
398 
399  // is it a valid handle?
400  if (gh->vmt != (gwinVMT *)&listVMT)
401  return;
402 
403  // watch out for an invalid item
404  if (item < 0 || item > (gh2obj->cnt) - 1)
405  return;
406 
407  for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
408  if (i == item) {
409 
410  // create the new object
411  if (useAlloc) {
412  gMemSize len = strlen(text)+1;
413  if (!(newItem = gfxAlloc(sizeof(ListItem) + len)))
414  return;
415 
416  memcpy((char *)(newItem+1), text, len);
417  text = (const char *)(newItem+1);
418  } else {
419  if (!(newItem = gfxAlloc(sizeof(ListItem))))
420  return;
421  }
422 
423  // copy the info from the existing object
424  newItem->flags = qi2li->flags;
425  newItem->param = qi2li->param;
426  newItem->text = text;
427  #if GWIN_NEED_LIST_IMAGES
428  newItem->pimg = qi2li->pimg;
429  #endif
430 
431  // add the new item to the list and remove the old item
432  gfxQueueASyncInsert(&gh2obj->list_head, &newItem->q_item, &qi2li->q_item);
433  gfxQueueASyncRemove(&gh2obj->list_head, &qi2li->q_item);
434  gfxFree(qi2li);
435 
436  _gwinUpdate(gh);
437  break;
438  }
439  }
440 }
441 
442 const char* gwinListItemGetText(GHandle gh, int item) {
443  const gfxQueueASyncItem* qi;
444  int i;
445 
446  // is it a valid handle?
447  if (gh->vmt != (gwinVMT *)&listVMT)
448  return 0;
449 
450  // watch out for an invalid item
451  if (item < 0 || item >= gh2obj->cnt)
452  return 0;
453 
454  for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
455  if (i == item)
456  return qi2li->text;
457  }
458  return 0;
459 }
460 
461 int gwinListFindText(GHandle gh, const char* text) {
462  const gfxQueueASyncItem* qi;
463  int i;
464 
465  // is it a valid handle?
466  if (gh->vmt != (gwinVMT *)&listVMT)
467  return -1;
468 
469  // watch out for NULL pointers
470  if (!text)
471  return -1;
472 
473  for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
474  if (strcmp(((ListItem *)qi)->text, text) == 0)
475  return i;
476  }
477 
478  return -1;
479 }
480 
482  const gfxQueueASyncItem * qi;
483  int i;
484 
485  // is it a valid handle?
486  if (gh->vmt != (gwinVMT *)&listVMT)
487  return -1;
488 
489  // Multi-select always returns -1. Use gwinListItemIsSelected() instead
490  if ((gh->flags & GLIST_FLG_MULTISELECT))
491  return -1;
492 
493  for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
494  if (qi2li->flags & GLIST_FLG_SELECTED)
495  return i;
496  }
497 
498  return -1;
499 }
500 
501 void gwinListItemSetParam(GHandle gh, int item, gU16 param) {
502  const gfxQueueASyncItem * qi;
503  int i;
504 
505  // is it a valid handle?
506  if (gh->vmt != (gwinVMT *)&listVMT)
507  return;
508 
509  // watch out for an invalid item
510  if (item < 0 || item > (gh2obj->cnt) - 1)
511  return;
512 
513  for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
514  if (i == item) {
515  qi2li->param = param;
516  break;
517  }
518  }
519 }
520 
521 void gwinListDeleteAll(GHandle gh) {
522  gfxQueueASyncItem* qi;
523 
524  // is it a valid handle?
525  if (gh->vmt != (gwinVMT *)&listVMT)
526  return;
527 
528  while((qi = gfxQueueASyncGet(&gh2obj->list_head)))
529  gfxFree(qi);
530 
531  gh->flags &= ~GLIST_FLG_HASIMAGES;
532  gh2obj->cnt = 0;
533  gh2obj->top = 0;
534  _gwinUpdate(gh);
535 }
536 
537 void gwinListItemDelete(GHandle gh, int item) {
538  const gfxQueueASyncItem * qi;
539  int i;
540 
541  // is it a valid handle?
542  if (gh->vmt != (gwinVMT *)&listVMT)
543  return;
544 
545  // watch out for an invalid item
546  if (item < 0 || item >= gh2obj->cnt)
547  return;
548 
549  for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
550  if (i == item) {
551  gfxQueueASyncRemove(&gh2obj->list_head, (gfxQueueASyncItem*)qi);
552  gfxFree((void *)qi);
553  gh2obj->cnt--;
554  if (gh2obj->top >= item && gh2obj->top)
555  gh2obj->top--;
556  _gwinUpdate(gh);
557  break;
558  }
559  }
560 }
561 
562 gU16 gwinListItemGetParam(GHandle gh, int item) {
563  const gfxQueueASyncItem * qi;
564  int i;
565 
566  // is it a valid handle?
567  if (gh->vmt != (gwinVMT *)&listVMT)
568  return 0;
569 
570  // watch out for an invalid item
571  if (item < 0 || item > (gh2obj->cnt) - 1)
572  return 0;
573 
574  for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
575  if (i == item)
576  return qi2li->param;
577  }
578  return 0;
579 }
580 
581 gBool gwinListItemIsSelected(GHandle gh, int item) {
582  const gfxQueueASyncItem * qi;
583  int i;
584 
585  // is it a valid handle?
586  if (gh->vmt != (gwinVMT *)&listVMT)
587  return gFalse;
588 
589  // watch out for an invalid item
590  if (item < 0 || item > (gh2obj->cnt) - 1)
591  return gFalse;
592 
593  for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
594  if (i == item)
595  return (qi2li->flags & GLIST_FLG_SELECTED) ? gTrue : gFalse;
596  }
597  return gFalse;
598 }
599 
600 int gwinListItemCount(GHandle gh) {
601  // is it a valid handle?
602  if (gh->vmt != (gwinVMT *)&listVMT)
603  return 0;
604 
605  return gh2obj->cnt;
606 }
607 
608 const char* gwinListGetSelectedText(GHandle gh) {
609  // is it a valid handle?
610  if (gh->vmt != (gwinVMT *)&listVMT)
611  return 0;
612 
613  // return NULL if nothing is selected (or multi-select)
614  if (gwinListGetSelected(gh) < 0)
615  return 0;
616 
618 }
619 
620 void gwinListSetSelected(GHandle gh, int item, gBool doSelect) {
621  const gfxQueueASyncItem * qi;
622  int i;
623 
624  // is it a valid handle?
625  if (gh->vmt != (gwinVMT *)&listVMT)
626  return;
627 
628  // watch out for an invalid item
629  if (item < 0 || item >= gh2obj->cnt)
630  return;
631 
632  // If not a multiselect mode - clear previous selected item
633  if (doSelect && !(gh->flags & GLIST_FLG_MULTISELECT)) {
634  for(qi = gfxQueueASyncPeek(&gh2obj->list_head); qi; qi = gfxQueueASyncNext(qi)) {
635  if (qi2li->flags & GLIST_FLG_SELECTED) {
636  qi2li->flags &= ~GLIST_FLG_SELECTED;
637  break;
638  }
639  }
640  }
641 
642  // Find item and set selected or not
643  for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
644  if (i == item) {
645  if (doSelect)
646  qi2li->flags |= GLIST_FLG_SELECTED;
647  else
648  qi2li->flags &= ~GLIST_FLG_SELECTED;
649  break;
650  }
651  }
652  _gwinUpdate(gh);
653 }
654 
655 void gwinListViewItem(GHandle gh, int item) {
656  gCoord iheight;
657 
658  // is it a valid handle?
659  if (gh->vmt != (gwinVMT *)&listVMT)
660  return;
661 
662  // watch out for an invalid item
663  if (item < 0 || item >= gh2obj->cnt)
664  return;
665 
666  // Work out a possible new top for the list
667  iheight = gdispGetFontMetric(gh->font, gFontHeight) + LST_VERT_PAD;
668  gh2obj->top = iheight * item;
669 
670  // Adjust the list
671  if (gh2obj->top > gh2obj->cnt * iheight - gh->height-2)
672  gh2obj->top = gh2obj->cnt * iheight - gh->height-2;
673  if (gh2obj->top < 0)
674  gh2obj->top = 0;
675 
676  _gwinUpdate(gh);
677 }
678 
679 #if GWIN_NEED_LIST_IMAGES
680  void gwinListItemSetImage(GHandle gh, int item, gImage *pimg) {
681  const gfxQueueASyncItem * qi;
682  int i;
683 
684  // is it a valid handle?
685  if (gh->vmt != (gwinVMT *)&listVMT)
686  return;
687 
688  // watch out for an invalid item
689  if (item < 0 || item > (gh2obj->cnt) - 1)
690  return;
691 
692  for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
693  if (i == item) {
694  qi2li->pimg = pimg;
695  if (pimg)
696  gh->flags |= GLIST_FLG_HASIMAGES;
697  break;
698  }
699  }
700  }
701 #endif
702 
703 void gwinListDefaultDraw(GWidgetObject* gw, void* param) {
704  const gfxQueueASyncItem* qi;
705  int i;
706  gCoord x, y, iheight, iwidth;
707  gColor fill;
708  const GColorSet * ps;
709  #if GWIN_NEED_LIST_IMAGES
710  gCoord sy;
711  #endif
712  #if GDISP_NEED_CONVEX_POLYGON
713  static const gPoint upArrow[] = { {0, LST_ARROW_SZ}, {LST_ARROW_SZ, LST_ARROW_SZ}, {LST_ARROW_SZ/2, 0} };
714  static const gPoint downArrow[] = { {0, 0}, {LST_ARROW_SZ, 0}, {LST_ARROW_SZ/2, LST_ARROW_SZ} };
715  #endif
716 
717  (void)param;
718 
719  // is it a valid handle?
720  if (gw->g.vmt != (gwinVMT *)&listVMT)
721  return;
722 
723  // don't render if render has been disabled
724  if (!(gw->g.flags & GLIST_FLG_ENABLERENDER))
725  return;
726 
727  ps = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled;
728  iheight = gdispGetFontMetric(gw->g.font, gFontHeight) + LST_VERT_PAD;
729  x = 1;
730 
731  // the scroll area
732  if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH) {
733  iwidth = gw->g.width - 2 - 4;
734  if (gw2obj->cnt > 0) {
735  int max_scroll_value = gw2obj->cnt * iheight - gw->g.height-2;
736  if (max_scroll_value > 0) {
737  int bar_height = (gw->g.height-2) * (gw->g.height-2) / (gw2obj->cnt * iheight);
738  gdispGFillArea(gw->g.display, gw->g.x + gw->g.width-4, gw->g.y + 1, 2, gw->g.height-1, gw->pstyle->background);
739  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);
740  }
741  }
742  } else if ((gw2obj->cnt > (gw->g.height-2) / iheight) || (gw->g.flags & GLIST_FLG_SCROLLALWAYS)) {
743  iwidth = gw->g.width - (LST_SCROLLWIDTH+3);
744  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));
745  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);
746  #if GDISP_NEED_CONVEX_POLYGON
747  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);
748  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);
749  #else
750  #if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT
751  #warning "GWIN: Lists display better when GDISP_NEED_CONVEX_POLYGON is turned on"
752  #elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO
753  COMPILER_WARNING("GWIN: Lists display better when GDISP_NEED_CONVEX_POLYGON is turned on")
754  #endif
755  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);
756  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);
757  #endif
758  } else
759  iwidth = gw->g.width - 2;
760 
761  #if GWIN_NEED_LIST_IMAGES
762  if ((gw->g.flags & GLIST_FLG_HASIMAGES)) {
763  x += iheight;
764  iwidth -= iheight;
765  }
766  #endif
767 
768 
769  // Find the top item
770  for (qi = gfxQueueASyncPeek(&gw2obj->list_head), i = iheight - 1; i < gw2obj->top && qi; qi = gfxQueueASyncNext(qi), i+=iheight);
771 
772  // the list frame
773  gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, ps->edge);
774 
775  // Set the clipping region so we do not override the frame.
776  #if GDISP_NEED_CLIP
777  gdispGSetClip(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2);
778  #endif
779 
780  // Draw until we run out of room or items
781  for (y = 1-(gw2obj->top%iheight); y < gw->g.height-2 && qi; qi = gfxQueueASyncNext(qi), y += iheight) {
782  fill = (qi2li->flags & GLIST_FLG_SELECTED) ? ps->fill : gw->pstyle->background;
783  gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, iwidth, iheight, fill);
784  #if GWIN_NEED_LIST_IMAGES
785  if ((gw->g.flags & GLIST_FLG_HASIMAGES)) {
786  // Clear the image area
787  if (qi2li->pimg && gdispImageIsOpen(qi2li->pimg)) {
788  // Calculate which image
789  sy = (qi2li->flags & GLIST_FLG_SELECTED) ? 0 : (iheight-LST_VERT_PAD);
790  if (!(gw->g.flags & GWIN_FLG_SYSENABLED))
791  sy += 2*(iheight-LST_VERT_PAD);
792  while (sy > qi2li->pimg->height)
793  sy -= iheight-LST_VERT_PAD;
794  // Draw the image
795  gdispImageSetBgColor(qi2li->pimg, fill);
796  gdispGImageDraw(gw->g.display, qi2li->pimg, gw->g.x+1, gw->g.y+y, iheight-LST_VERT_PAD, iheight-LST_VERT_PAD, 0, sy);
797  }
798  }
799  #endif
800  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, gJustifyLeft);
801  }
802 
803  // Fill any remaining item space
804  if (y < gw->g.height-1)
805  gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, iwidth, gw->g.height-1-y, gw->pstyle->background);
806 }
807 
808 #undef gh2obj
809 #undef gw2obj
810 #undef qi2li
811 #undef qix2li
812 #undef ple
813 #endif // GFX_USE_GWIN && GWIN_NEED_LIST
COLOR_TYPE gColor
The color type definition.
Definition: gdisp_colors.h:437
gColor gdispBlendColor(gColor fg, gColor bg, gU8 alpha)
Blend 2 colors according to the alpha.
gCoord gdispGetFontMetric(gFont font, gFontmetric metric)
Get a metric of a font.
void gdispGSetClip(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy)
Clip all drawing to the defined area.
void gdispGFillArea(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, gColor color)
Fill an area with a color.
void gdispGDrawLine(GDisplay *g, gCoord x0, gCoord y0, gCoord x1, gCoord y1, gColor color)
Draw a line.
void gdispGFillStringBox(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, const char *str, gFont font, gColor color, gColor bgColor, gJustify justify)
Draw a text string vertically centered within the specified box. The box background is filled with th...
gI16 gCoord
The type for a coordinate or length on the screen.
Definition: gdisp.h:39
void gdispGFillConvexPoly(GDisplay *g, gCoord tx, gCoord ty, const gPoint *pntarray, unsigned cnt, gColor color)
Fill a convex polygon.
void gdispGDrawBox(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, gColor color)
Draw a rectangular box.
@ gJustifyLeft
Definition: gdisp.h:61
@ gFontHeight
Definition: gdisp.h:80
GEvent * geventGetEventBuffer(GSourceListener *psl)
Get the event buffer from the GSourceListener.
Definition: gevent.c:187
void geventSendEvent(GSourceListener *psl)
Called by a source to indicate the listener's event buffer has been filled.
Definition: gevent.c:200
GSourceListener * geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr)
Called by a source with a possible event to get a listener record.
Definition: gevent.c:163
void * gfxAlloc(gMemSize sz)
Allocate memory.
void gfxFree(void *ptr)
Free memory.
gdispImageError gdispGImageDraw(GDisplay *g, gImage *img, gCoord x, gCoord y, gCoord cx, gCoord cy, gCoord sx, gCoord sy)
Draw the image.
gBool gdispImageIsOpen(gImage *img)
Is an image open.
void gdispImageSetBgColor(gImage *img, gColor bgcolor)
Set the background color of the image.
const char * gwinListItemGetText(GHandle gh, int item)
Get the name behind an item with a given ID.
gU16 gwinListItemGetParam(GHandle gh, int item)
Get the custom parameter of an item with a given ID.
int gwinListGetSelected(GHandle gh)
Get the ID of the selected item.
void gwinListItemSetParam(GHandle gh, int item, gU16 param)
Set the custom parameter of an item with a given ID.
const char * gwinListGetSelectedText(GHandle gh)
Get the text of the selected item.
scroll_t
Enum to change the behaviour of the scroll bar.
Definition: gwin_list.h:77
void gwinListItemSetText(GHandle gh, int item, const char *text, gBool useAlloc)
Set the custom parameter of an item with a given ID.
void gwinListViewItem(GHandle gh, int item)
Scroll the list so the specified item is in view.
void gwinListSetSelected(GHandle gh, int item, gBool doSelect)
Set whether a specific item is selected or not.
int gwinListFindText(GHandle gh, const char *text)
Get the ID of an item with a given name.
gBool gwinListItemIsSelected(GHandle gh, int item)
Check if an item with a given ID is selected.
#define GLIST_FLG_MULTISELECT
The internal list object flags.
Definition: gwin_list.h:84
int gwinListAddItem(GHandle gh, const char *text, gBool useAlloc)
Add an item to the list.
GHandle gwinGListCreate(GDisplay *g, GListObject *widget, GWidgetInit *pInit, gBool multiselect)
Create a list widget.
void gwinListDeleteAll(GHandle gh)
Delete all the items of the list.
void gwinListItemSetImage(GHandle gh, int item, gImage *pimg)
Set the image for a list item.
int gwinListItemCount(GHandle gh)
Get the amount of items within the list.
void gwinListSetScroll(GHandle gh, scroll_t flag)
Change the behaviour of the scroll bar.
void gwinListItemDelete(GHandle gh, int item)
Delete an item from the list.
void gwinListEnableRender(GHandle gh, gBool ena)
Enable or disable the rendering of the list.
#define GEVENT_GWIN_LIST
The event type for a list event.
Definition: gwin_list.h:36
void gwinListDefaultDraw(GWidgetObject *gw, void *param)
The default rendering function for the list widget.
void gwinRedraw(GHandle gh)
Redraw a window.
void gwinSetVisible(GHandle gh, gBool visible)
Sets whether a window is visible or not.
The GColorSet structure.
Definition: gwin_widget.h:37
gColor fill
Definition: gwin_widget.h:40
gColor text
Definition: gwin_widget.h:38
gColor edge
Definition: gwin_widget.h:39
The structure to initialise a widget.
Definition: gwin_widget.h:97
GWindowInit g
Definition: gwin_widget.h:98
The GWIN Widget structure.
Definition: gwin_widget.h:118
GWindowObject g
Definition: gwin_widget.h:119
const GWidgetStyle * pstyle
Definition: gwin_widget.h:123
WidgetTag tag
Definition: gwin_widget.h:125
gColor background
Definition: gwin_widget.h:53
GColorSet disabled
Definition: gwin_widget.h:56
GColorSet enabled
Definition: gwin_widget.h:55
gBool show
Definition: gwin.h:80
A window object structure.
Definition: gwin.h:40
GDisplay * display
Definition: gwin.h:46
gCoord x
Definition: gwin.h:47
gCoord width
Definition: gwin.h:49
const struct gwinVMT * vmt
Definition: gwin.h:45
gU32 flags
Definition: gwin.h:53
gCoord y
Definition: gwin.h:48
gCoord height
Definition: gwin.h:50
The internal list item structure.
Definition: gwin_list.h:95
The structure for an image.
Definition: gdisp_image.h:59
Type for a 2D point on the screen.
Definition: gdisp.h:51
A queue item.
Definition: gqueue.h:40
The Virtual Method Table for a widget.
Definition: gwin_class.h:85
The Virtual Method Table for a GWIN window.
Definition: gwin_class.h:55