/* canvas-type selection-list
 *   [CV-LIST]
 *   name: <arbitrary unique name>
 *   position-itag: (position): <box-tag of an element in the main text>
 *   position-rect: (position): <rectangle (<x>+<w>,<y>+<h>) within the box-tag or the main text>
 *   disable: <whether is disabled: 1 = yes, 0 = no, or missing = enabled>
 *   spacing: <space in pixels between list-items, (default: 0), may have appended a '+' to arrange few list-items in the box>
 *   font: <fontname, (default: default font)>
 *   orientation: (according to text-header): <"left", "center", "right" or "block", (default: left)>
 *   bgcolor: <background color (0xRRGGBB or "transparent") for background, (default: transparent)>
 *   bgcolor-dfl: <background color (0xRRGGBB or "transparent") for default list-items, (default: transparent)>
 *   bgcolor-act: <background color (0xRRGGBB or "transparent") for activated list-item, (default: transparent)>
 *   fgcolor-dfl: <text color (0xRRGGBB) for default list-items, (default: white)>
 *   fgcolor-act: <text color (0xRRGGBB) for activated list-item, (default: white)>
 *   item1.bgcolor-dfl: <background color (0xRRGGBB or "transparent") for default odd list-items, (default: transparent)>
 *   item1.bgcolor-act: <background color (0xRRGGBB or "transparent") for activated odd list-item, (default: transparent)>
 *   item1.fgcolor-dfl: <text color (0xRRGGBB) for default odd list-items, (default: white)>
 *   item1.fgcolor-act: <text color (0xRRGGBB) for activated odd list-item, (default: white)>
 *   item2.bgcolor-dfl: <background color (0xRRGGBB or "transparent") for default even list-items, (default: transparent)>
 *   item2.bgcolor-act: <background color (0xRRGGBB or "transparent") for activated even list-item, (default: transparent)>
 *   item2.fgcolor-dfl: <text color (0xRRGGBB) for default even list-items, (default: white)>
 *   item2.fgcolor-act: <text color (0xRRGGBB) for activated even list-item, (default: white)>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "canvas.h"

static int type_no = 0;

struct cv_type {
  struct VG_Image *limg;             /* image with all list-items */
  int ypos;                          /* y-start-position for limg */
  int repw;                          /* waiting value before repeating */
  int active;                        /* activated list-item (1-max), or 0 = none */
  struct vgi_canvas_arrow arw_up;    /* arrow up */
  struct vgi_canvas_arrow arw_down;  /* arrow down */
  struct VG_Hash *hparam;            /* default parameters for text */
  int max;                           /* number of list-items */
  struct {                           /* list-items */
    struct VG_Image *img_dfl;          /* default list-item image */
    struct VG_Image *img_act;          /* activated list-item image */
    char *name;                        /* name of list-item */
    struct VG_Rect rect;               /* position within limg */
  } *e;
};

void cvinit_list(struct vgi_canvas_inittype *, int);

static void * t_read_section(const struct VG_Canvas *, struct vgi_canvas_item *, const char *);
static void t_warp_mouse(struct VG_Canvas *, struct vgi_canvas_item *, int, int, int *, int *);
static void t_pre_exec(struct VG_Canvas *, struct vgi_canvas_item *);
static void t_draw(struct vgi_canvas_data *, struct vgi_canvas_item *, const char **);
static void t_destroy(struct vgi_canvas_item *);
static void t_dump(FILE *, struct vgi_canvas_item *);

static void canvas_list_clear(struct VG_Canvas *, const char *);
static VG_BOOL canvas_list_add(struct VG_Canvas *, const char *, const char *, const char *);
static void canvas_list_set_activated(struct VG_Canvas *, const char *, const char *);
static const char * canvas_list_get_activated(struct VG_Canvas *, const char *);


void
cvinit_list(struct vgi_canvas_inittype *cvinittype, int type)
{
  if (cvinittype == NULL) { return; }

  type_no = type;

  /* cvinit */
  cvinittype->mouse_opaque = VG_FALSE;
  cvinittype->uses_cursor = VG_TRUE;
  cvinittype->read_section = t_read_section;
  cvinittype->get_itag_rect = NULL;
  cvinittype->warp_mouse = t_warp_mouse;
  cvinittype->pre_exec = t_pre_exec;
  cvinittype->draw = t_draw;
  cvinittype->destroy = t_destroy;
  cvinittype->dump = t_dump;

  /* functions */
  vg4->canvas->list_clear = canvas_list_clear;
  vg4->canvas->list_add = canvas_list_add;
  vg4->canvas->list_set_activated = canvas_list_set_activated;
  vg4->canvas->list_get_activated = canvas_list_get_activated;
} /* Ende cvinit_list */


static void *
t_read_section(const struct VG_Canvas *cvas, struct vgi_canvas_item *cvitem, const char *dtptr)
{
  struct SML3_gummi gm1 = SML3_GUM_INITIALIZER;
  struct cv_type *cvtype;
  char ename[64], bval[256], *pval;
  const char *namptr;
  char *nptr, bkey[32];

  if (cvas == NULL || cvitem == NULL || dtptr == NULL) { return NULL; }

  cvtype = SML3_calloc(1, sizeof(*cvtype));

  cvtype->hparam = vg4_hash_create_nolist();

  namptr = "item1";
  nptr = (char *)dtptr;
  while (canvas_get_lineval(nptr, namptr, ename, sizeof(ename), NULL, 0, bval, sizeof(bval), &nptr)) {
    vg4->misc->strccat(bkey, sizeof(bkey), namptr, ".", ename, NULL);
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    vg4->hash->setstr(cvtype->hparam, bkey, pval);
  }

  namptr = "item2";
  nptr = (char *)dtptr;
  while (canvas_get_lineval(nptr, namptr, ename, sizeof(ename), NULL, 0, bval, sizeof(bval), &nptr)) {
    vg4->misc->strccat(bkey, sizeof(bkey), namptr, ".", ename, NULL);
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    vg4->hash->setstr(cvtype->hparam, bkey, pval);
  }

  namptr = "spacing";
  if (canvas_get_lineval(dtptr, namptr, NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    vg4->hash->setstr(cvtype->hparam, namptr, pval);
  }

  namptr = "font";
  if (canvas_get_lineval(dtptr, namptr, NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    vg4->hash->setstr(cvtype->hparam, namptr, pval);
  }

  namptr = "orientation";
  if (canvas_get_lineval(dtptr, namptr, NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    vg4->hash->setstr(cvtype->hparam, namptr, pval);
  }

  namptr = "bgcolor";
  if (canvas_get_lineval(dtptr, namptr, NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    vg4->hash->setstr(cvtype->hparam, namptr, pval);
  }

  namptr = "bgcolor-dfl";
  if (canvas_get_lineval(dtptr, namptr, NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    vg4->hash->setstr(cvtype->hparam, namptr, pval);
  }

  namptr = "bgcolor-act";
  if (canvas_get_lineval(dtptr, namptr, NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    vg4->hash->setstr(cvtype->hparam, namptr, pval);
  }

  namptr = "fgcolor-dfl";
  if (canvas_get_lineval(dtptr, namptr, NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    vg4->hash->setstr(cvtype->hparam, namptr, pval);
  }

  namptr = "fgcolor-act";
  if (canvas_get_lineval(dtptr, namptr, NULL, 0, NULL, 0, bval, sizeof(bval), NULL)) {
    vg4_intern_str_with_var(&gm1, bval, cvas->cve.hvar, NULL); pval = SML3_gumgetval(&gm1);
    vg4->hash->setstr(cvtype->hparam, namptr, pval);
  }

  SML3_gumdest(&gm1);

  return cvtype;
} /* Ende t_read_section */


static void
t_warp_mouse(struct VG_Canvas *cvas, struct vgi_canvas_item *cvitem, int xadd, int yadd, int *xm, int *ym)
{
  struct cv_type *cvtype;

  if (cvas == NULL || cvitem == NULL) { return; }
  if (xm == NULL || ym == NULL) { return; }

  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  *xm = xadd + cvitem->rect.x + cvitem->rect.w / 2;
  if (cvtype->active > 0) {
    *ym = yadd + cvitem->rect.y
          + cvtype->e[cvtype->active - 1].rect.y
          - cvtype->ypos
          + cvtype->e[cvtype->active - 1].rect.h / 2;
  } else {
    *ym = yadd + cvitem->rect.y + cvitem->rect.h / 2;
  }
} /* Ende t_warp_mouse */


static void
t_pre_exec(struct VG_Canvas *cvas, struct vgi_canvas_item *cvitem)
{
  struct cv_type *cvtype;
  int uelm, h1, imgw, imgh;
  char *bgcol;
  struct vgi_canvas_dirs *arw_dirs;
  VG_BOOL ishigh;
  struct VG_Position posb;

  if (cvas == NULL || cvitem == NULL) { return; }

  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  /* +++ create image with all list-items for a selection-list +++ */

  if (cvtype->limg != NULL) {
    vg4->image->destroy(cvtype->limg);
    cvtype->limg = NULL;
  }

  /* calculate image size for image with all list-items */
  imgw = cvitem->rect.w;
  imgh = 0;
  for (uelm = 0; uelm < cvtype->max; uelm++) {
    vg4->image->getsize(cvtype->e[uelm].img_dfl, NULL, NULL, &h1);
    cvtype->e[uelm].rect.x = 0;
    cvtype->e[uelm].rect.w = imgw;
    cvtype->e[uelm].rect.y = imgh;
    cvtype->e[uelm].rect.h = h1;
    imgh += h1;
  }
  if (imgh == 0) {
    char *valptr = (char *)vg4->hash->get(cvtype->hparam, "spacing", NULL);
    if (valptr != NULL && valptr[strlen(valptr) - 1] == '+') {  /* use height of box */
      imgh = cvitem->rect.h;
    } else {
      cvitem->isdisabled = VG_TRUE;
      return;
    }
  }

  /* if box-height is greater than image of list-items, correct spacing if it has appended a '+' */
  if (imgh < cvitem->rect.h) {
    char *valptr = (char *)vg4->hash->get(cvtype->hparam, "spacing", NULL);
    if (valptr != NULL && valptr[strlen(valptr) - 1] == '+') {  /* correct spacing to height of box */
      int iplus = (cvitem->rect.h - imgh) / cvtype->max;
      if (iplus > 0) {
        int eplus = iplus / 2;
        cvtype->e[0].rect.y += eplus;
        eplus += iplus;
        for (uelm = 1; uelm < cvtype->max; uelm++) {
          cvtype->e[uelm].rect.y += eplus;
          eplus += iplus;
        }
        imgh = cvitem->rect.h;
      }
    }
  }

  /* draw all list-items onto image */
  cvtype->limg = vg4_image_create_nolist(imgw, imgh);
  bgcol = (char *)vg4->hash->get(cvtype->hparam, "bgcolor", NULL);
  if (bgcol != NULL && strcmp(bgcol, "transparent") != 0) {
    int colb = (int)strtol(bgcol, NULL, 16);
    vg4->image->fill(cvtype->limg, colb);
  }
  for (uelm = 0; uelm < cvtype->max; uelm++) {
    posb.pos = VG_POS_UPPER_LEFT;
    posb.x = cvtype->e[uelm].rect.x;
    posb.y = cvtype->e[uelm].rect.y;
    vg4->image->copy(cvtype->limg, cvtype->e[uelm].img_dfl, &posb, NULL);
  }

  /* +++ set arrow-images +++ */

  ishigh = cvas->cve.ishigh;

#if 0
  { int fontw;
    vg4->font->maxsize(vg4->font->load((char *)vg4->hash->get(cvtype->hparam, "font", NULL)), &fontw, NULL);
    if (fontw > 0) {
      if (fontw < 16) { ishigh = VG_FALSE; } else { ishigh = VG_TRUE; }
    }
  }
#endif

  if (cvas->cve.arrow.user_set) {
    arw_dirs = &cvas->cve.arrow.user;
  } else if (ishigh) {
    arw_dirs = &cvas->cve.arrow.high;
  } else {
    arw_dirs = &cvas->cve.arrow.low;
  }
  cvtype->arw_up.img = &arw_dirs->up;
  cvtype->arw_down.img = &arw_dirs->down;
} /* Ende t_pre_exec */


static void
t_draw(struct vgi_canvas_data *cvdata, struct vgi_canvas_item *cvitem, const char **selname)
{
  struct cv_type *cvtype;
  int updown;

  if (cvdata == NULL || cvdata->cvas == NULL || cvitem == NULL || selname == NULL) { return; }
  if (cvitem->rimg == NULL) { return; }
  if (cvitem->isdisabled) { return; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  /* check if select next/previous entry: 1=next, 2=page-down, 3=end, -1=previous, -2=page-up, -3=home */
  updown = 0;
  if (cvdata->hasfocus) {  /* has focus */
    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_UP])) {
      updown = -1;
      cvtype->repw = VGI_CANVAS_REPWMAX;
    }
    if (VGI_CVKEY_PRESSED(cvdata->keys.k[CV_KEY_UP])) {
      if (cvtype->repw > 0) { cvtype->repw--; } else { updown = -1; }
    }
    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_PGUP])) { updown = -2; }
    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_HOME])) { updown = -3; }

    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_DOWN])) {
      updown = 1;
      cvtype->repw = VGI_CANVAS_REPWMAX;
    }
    if (VGI_CVKEY_PRESSED(cvdata->keys.k[CV_KEY_DOWN])) {
      if (cvtype->repw > 0) { cvtype->repw--; } else { updown = 1; }
    }
    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_PGDOWN])) { updown = 2; }
    if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_END])) { updown = 3; }
  }

  /* draw */
  { struct VG_Image *imgp;
    struct VG_Position posb;
    struct VG_Rect rect;
    int *active;

    active = &cvtype->active;

    if (cvdata->hasfocus) {  /* has focus */
      VG_BOOL keyok;
  
      if (VGI_CVKEY_NEWPRESSED(cvdata->keys.k[CV_KEY_RETURN]) || VGI_CVKEY_NEWPRESSED(cvdata->keys.m_left)) {
        keyok = VG_TRUE;
      } else {
        keyok = VG_FALSE;
      }

      /* activate list-item according to keypress up and down */
      if (updown < 0) {  /* up */
        if (*active > 1) {
          if (updown == -3) {  /* home */
            *active = 1;
          } else if (updown == -2) {  /* page up */
            int atmp;
            rect.y = cvtype->e[*active - 1].rect.y;
            rect.h = cvitem->rect.h;
            for (atmp = *active; atmp > 0; atmp--) {
              if (cvtype->e[atmp - 1].rect.y <= rect.y - rect.h) { atmp++; break; }
            }
            if (atmp == 0) { atmp = 1; }
            *active = atmp;
          } else {  /* step up */
            (*active)--;
          }
        } else if (*active == 0) {
          *active = cvtype->max;
        }

      } else if (updown > 0) {  /* down */
        if (*active == 0) {
          if (cvtype->max > 0) { *active = 1; }
        } else if (*active < cvtype->max) {
          if (updown == 3) {  /* end */
            *active = cvtype->max;
          } else if (updown == 2) {  /* page down */
            int atmp;
            rect.y = cvtype->e[*active - 1].rect.y;
            rect.h = cvitem->rect.h;
            for (atmp = *active; atmp <= cvtype->max; atmp++) {
              if (cvtype->e[atmp - 1].rect.y >= rect.y + rect.h) { atmp--; break; }
            }
            if (atmp > cvtype->max) { atmp = cvtype->max; }
            *active = atmp;
          } else {  /* step down */
            (*active)++;
          }
        }
      }

      if (*active > 0) {
        /* correct ypos to show activated list-item completely */
        rect.y = cvtype->e[*active - 1].rect.y - cvtype->ypos;
        rect.h = cvtype->e[*active - 1].rect.h;
        if (rect.y < 0) {
          cvtype->ypos += rect.y;
        } else if (rect.y > cvitem->rect.h - rect.h) {
          cvtype->ypos += (rect.y - (cvitem->rect.h - rect.h));
        }
        /* warp mouse to activated list-item */
        if (updown != 0) {
          int xm, ym;
          t_warp_mouse(cvdata->cvas, cvitem, cvdata->xadd, cvdata->yadd, &xm, &ym);
          vg4->input->mouse_warp(xm, ym);
        }
      }

      /* activate list-item according to mouse position */
      if (cvdata->cvas->cve.mouse.show) {
        int xm, ym;
        if (vg4->input->mouse_position(&xm, &ym)) {
          int uelm;
          VG_BOOL iscorr;
          const int maxcnt = CV_WAIT_TIME * 2;  /* milliseconds between scrolling to next list-item (refer also to repw) */

          for (uelm = 0; uelm < cvtype->max; uelm++) {
            rect.y = cvtype->e[uelm].rect.y - cvtype->ypos;
            rect.h = cvtype->e[uelm].rect.h;
            if (rect.y < 0) { rect.h += rect.y; rect.y = 0; }
            if (rect.y + rect.h > cvitem->rect.h) { rect.h = rect.y + rect.h - cvitem->rect.h; }
            if (rect.y + rect.h <= 0 || rect.y >= cvitem->rect.h) { continue; }
            if (ym >= cvdata->yadd + cvitem->rect.y + rect.y && ym < cvdata->yadd + cvitem->rect.y + rect.y + rect.h) {
              *active = uelm + 1;
              break;
            }
          }

          /* check for moving up-down */
          iscorr = VG_FALSE;
          if (IS_IN_RECT(&cvtype->arw_up.rect, xm, ym, cvdata->xadd, cvdata->yadd)) {
            if (VGI_CVKEY_PRESSED(cvdata->keys.m_left)) {
              if (keyok) { cvtype->arw_up.scrollcount = CV_WAIT_TIME; }
              if ((cvtype->arw_up.scrollcount -= CV_WAIT_TIME) <= 0) {
                cvtype->arw_up.scrollcount += maxcnt;
                if (*active > 1) { (*active)--; }
              }
              keyok = VG_FALSE;
              iscorr = VG_TRUE;
            }
          } else if (IS_IN_RECT(&cvtype->arw_down.rect, xm, ym, cvdata->xadd, cvdata->yadd)) {
            if (VGI_CVKEY_PRESSED(cvdata->keys.m_left)) {
              if (keyok) { cvtype->arw_down.scrollcount = CV_WAIT_TIME; }
              if ((cvtype->arw_down.scrollcount -= CV_WAIT_TIME) <= 0) {
                cvtype->arw_down.scrollcount += maxcnt;
                if (*active < cvtype->max) { (*active)++; }
              }
              keyok = VG_FALSE;
              iscorr = VG_TRUE;
            }
          }

          if (*active > 0) {
            /* correct ypos to show activated list-item completely */
            rect.y = cvtype->e[*active - 1].rect.y - cvtype->ypos;
            rect.h = cvtype->e[*active - 1].rect.h;
            if (rect.y < 0) {
              cvtype->ypos += rect.y;
            } else if (rect.y > cvitem->rect.h - rect.h) {
              cvtype->ypos += (rect.y - (cvitem->rect.h - rect.h));
            }
            /* warp mouse to activated list-item */
            if (iscorr && rect.h < cvtype->arw_up.rect.h) {
              int xm, ym;
              t_warp_mouse(cvdata->cvas, cvitem, cvdata->xadd, cvdata->yadd, &xm, &ym);
              vg4->input->mouse_warp(xm, ym);
            }
          }
        }
      }

      if (keyok && *active > 0) { *selname = cvitem->name; }
    }

    /* copy relevant rectangle from limg */
    rect.x = 0;
    rect.w = cvitem->rect.w;
    rect.y = cvtype->ypos;
    rect.h = cvitem->rect.h;
    imgp = vg4_image_clone_nolist(cvtype->limg, &rect, NULL);

    /* draw activated list-item onto it */
    if (*active > 0) {
      if (cvdata->hasfocus) {  /* has focus */
        posb.pos = VG_POS_UPPER_LEFT;
        posb.x = cvtype->e[*active - 1].rect.x;
        posb.y = cvtype->e[*active - 1].rect.y - cvtype->ypos;
        vg4->image->copy(imgp, cvtype->e[*active - 1].img_act, &posb, NULL);
      } else {
        rect.x = cvtype->e[*active - 1].rect.x;
        rect.w = cvtype->e[*active - 1].rect.w;
        rect.y = cvtype->e[*active - 1].rect.y - cvtype->ypos;
        rect.h = cvtype->e[*active - 1].rect.h;
        vg4->image->draw_rect(imgp, &rect, VG_COLOR_RGB(128, 128, 128), VG_FALSE);
      }
    }

    /* draw out */
    vg4->image->clear(cvitem->rimg);
    posb.pos = VG_POS_UPPER_LEFT;
    posb.x = posb.y = 0;
    vg4->image->copy(cvitem->rimg, imgp, &posb, NULL);
    posb.pos = VG_POS_UPPER_LEFT;
    posb.x = cvdata->xadd + cvitem->rect.x;
    posb.y = cvdata->yadd + cvitem->rect.y;
    vg4->window->copy(cvitem->rimg, &posb, NULL);
    vg4->image->destroy(imgp);
    if (cvdata->hasfocus) {  /* has focus */
      canvas_draw_actborder(&cvitem->rect, cvdata->xadd, cvdata->yadd);
    }

    /* draw arrow-images */
    if (cvdata->hasfocus && cvtype->max > 0) {
      int h1;
      vg4->image->getsize(cvtype->limg, NULL, NULL, &h1);
      /* upper image */
      if (h1 <= cvitem->rect.h) {
        CLEAR_RECT(&cvtype->arw_up.rect);
      } else if (cvtype->ypos > cvtype->e[0].rect.y) {
        canvas_draw_arrow(cvdata->cvas, &cvtype->arw_up, cvitem, cvdata->keys.m_left, cvdata->xadd, cvdata->yadd, VG_FALSE, 1);
      } else {
        canvas_draw_arrow(cvdata->cvas, &cvtype->arw_up, cvitem, cvdata->keys.m_left, cvdata->xadd, cvdata->yadd, VG_TRUE, 1);
      }

      /* lower image */
      if (h1 <= cvitem->rect.h) {
        CLEAR_RECT(&cvtype->arw_down.rect);
      } else if (cvtype->ypos + cvitem->rect.h < cvtype->e[cvtype->max - 1].rect.y + cvtype->e[cvtype->max - 1].rect.h) {
        canvas_draw_arrow(cvdata->cvas, &cvtype->arw_down, cvitem, cvdata->keys.m_left, cvdata->xadd, cvdata->yadd, VG_FALSE, 2);
      } else {
        canvas_draw_arrow(cvdata->cvas, &cvtype->arw_down, cvitem, cvdata->keys.m_left, cvdata->xadd, cvdata->yadd, VG_TRUE, 2);
      }
    }
  }
} /* Ende t_draw */


static void
t_destroy(struct vgi_canvas_item *cvitem)
{
  struct cv_type *cvtype;
  int uelm;

  if (cvitem == NULL) { return; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  if (cvtype->limg != NULL) { vg4->image->destroy(cvtype->limg); }
  if (cvtype->hparam != NULL) { vg4->hash->destroy(cvtype->hparam); }
  for (uelm = 0; uelm < cvtype->max; uelm++) {
    if (cvtype->e[uelm].img_dfl != NULL) {
      vg4->image->destroy(cvtype->e[uelm].img_dfl);
    }
    if (cvtype->e[uelm].img_act != NULL) {
      vg4->image->destroy(cvtype->e[uelm].img_act);
    }
    if (cvtype->e[uelm].name != NULL) { free(cvtype->e[uelm].name); }
  }
  if (cvtype->max > 0) { free(cvtype->e); }

  free(cvtype);
} /* Ende t_destroy */


static void
t_dump(FILE *outfp, struct vgi_canvas_item *cvitem)
{
  struct cv_type *cvtype;
  const char *namptr;
  int uelm;

  if (outfp == NULL || cvitem == NULL) { return; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  fprintf(outfp, "    type=selection-list\n");
  namptr = vg4->image->getname(cvtype->limg);
  fprintf(outfp, "    limg=%s\n", namptr);
  fprintf(outfp, "    ypos=%d\n", cvtype->ypos);
  fprintf(outfp, "    active=%d\n", cvtype->active);
  fprintf(outfp, "    hparam=%s\n", (vg4->hash->is_empty(cvtype->hparam) ? "[unused]" : "[used]"));
  { void *vpos;
    const char *key;
    vpos = NULL;
    for (key = vg4->hash->list(cvtype->hparam, &vpos); vpos != NULL; key = vg4->hash->list(cvtype->hparam, &vpos)) {
      fprintf(outfp, "     - %s=%s\n", key, (char *)vg4->hash->get(cvtype->hparam, key, NULL));
    }
  }
  fprintf(outfp, "    list-items=%d:\n", cvtype->max);
  for (uelm = 0; uelm < cvtype->max; uelm++) {
    fprintf(outfp, "    - [%d]\n", uelm + 1);
    fprintf(outfp, "      ename=%s\n", cvtype->e[uelm].name);
    namptr = vg4->image->getname(cvtype->e[uelm].img_dfl);
    fprintf(outfp, "      img_dfl=%s\n", namptr);
    namptr = vg4->image->getname(cvtype->e[uelm].img_act);
    fprintf(outfp, "      img_act=%s\n", namptr);
    fprintf(outfp, "      rect={%d+%d,%d+%d}\n",
            cvtype->e[uelm].rect.x,
            cvtype->e[uelm].rect.w,
            cvtype->e[uelm].rect.y,
            cvtype->e[uelm].rect.h);
  }
} /* Ende t_dump */


/* canvas_list_clear:
 * clear list
 * @param cvas   canvas
 * @param iname  name of list
 */
static void
canvas_list_clear(struct VG_Canvas *cvas, const char *iname)
{
  struct vgi_canvas_item *cvitem;
  struct cv_type *cvtype;
  int uelm;

  if (cvas == NULL || iname == NULL || *iname == '\0') { return; }

  cvitem = cvtype_get_item(cvas, iname, type_no);
  if (cvitem == NULL) { return; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  if (cvtype->limg != NULL) {
    vg4->image->destroy(cvtype->limg);
    cvtype->limg = NULL;
  }

  cvtype->ypos = 0;
  for (uelm = 0; uelm < cvtype->max; uelm++) {
    if (cvtype->e[uelm].img_dfl != NULL) {
      vg4->image->destroy(cvtype->e[uelm].img_dfl);
    }
    if (cvtype->e[uelm].img_act != NULL) {
      vg4->image->destroy(cvtype->e[uelm].img_act);
    }
    if (cvtype->e[uelm].name != NULL) {
      free(cvtype->e[uelm].name);
    }
  }

  free(cvtype->e);
  cvtype->e = NULL;
  cvtype->max = 0;
  cvtype->active = 0;
} /* Ende canvas_list_clear */


/* canvas_list_add:
 * add list-item to list
 * @param cvas   canvas
 * @param iname  name of list
 * @param text   text of list-item
 * @param ename  name of list-item
 * @return  VG_TRUE = added or VG_FALSE = not added
 */
static VG_BOOL
canvas_list_add(struct VG_Canvas *cvas, const char *iname, const char *text, const char *ename)
{
  struct vgi_canvas_item *cvitem;
  struct cv_type *cvtype;
  int uelm;
  VG_BOOL retw;

  if (cvas == NULL || iname == NULL || *iname == '\0'
      || text == NULL || *text == '\0'
      || ename == NULL || *ename == '\0') { return VG_FALSE; }

  cvitem = cvtype_get_item(cvas, iname, type_no);
  if (cvitem == NULL) { return VG_FALSE; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return VG_FALSE; }

  retw = VG_FALSE;

  uelm = cvtype->max++;
  if (uelm == 0) {
    cvtype->e = SML3_malloc(sizeof(*cvtype->e));
  } else {
    cvtype->e = SML3_realloc(cvtype->e, (uelm + 1) * sizeof(*cvtype->e));
  }
  memset(&cvtype->e[uelm], 0, sizeof(*cvtype->e));

  /* create images */
  { struct SML3_gummi gm1 = SML3_GUM_INITIALIZER;
    int gmpos, gmcommon;
    char *valptr, *bgcol, bkey[32];
    char *fgcol_dfl, *fgcol_act, *bgcol_dfl, *bgcol_act;

    /* get background color */
    bgcol = (char *)vg4->hash->get(cvtype->hparam, "bgcolor", NULL);
    if (bgcol != NULL && strcmp(bgcol, "transparent") == 0) { bgcol = NULL; }

    /* get actual bgcolor-dfl */
    snprintf(bkey, sizeof(bkey), "item%d.bgcolor-dfl", uelm % 2 + 1);
    bgcol_dfl = (char *)vg4->hash->get(cvtype->hparam, bkey, NULL);
    if (bgcol_dfl != NULL && strcmp(bgcol_dfl, "transparent") == 0) { bgcol_dfl = NULL; }
    if (bgcol_dfl == NULL) {
      bgcol_dfl = (char *)vg4->hash->get(cvtype->hparam, "bgcolor-dfl", NULL);
      if (bgcol_dfl != NULL && strcmp(bgcol_dfl, "transparent") == 0) { bgcol_dfl = NULL; }
    }

    /* get actual bgcolor-act */
    snprintf(bkey, sizeof(bkey), "item%d.bgcolor-act", uelm % 2 + 1);
    bgcol_act = (char *)vg4->hash->get(cvtype->hparam, bkey, NULL);
    if (bgcol_act != NULL && strcmp(bgcol_act, "transparent") == 0) { bgcol_act = NULL; }
    if (bgcol_act == NULL) {
      bgcol_act = (char *)vg4->hash->get(cvtype->hparam, "bgcolor-act", NULL);
      if (bgcol_act != NULL && strcmp(bgcol_act, "transparent") == 0) { bgcol_act = NULL; }
    }

    /* get actual fgcolor-dfl */
    snprintf(bkey, sizeof(bkey), "item%d.fgcolor-dfl", uelm % 2 + 1);
    fgcol_dfl = (char *)vg4->hash->get(cvtype->hparam, bkey, NULL);
    if (fgcol_dfl != NULL && strcmp(fgcol_dfl, "transparent") == 0) { fgcol_dfl = NULL; }
    if (fgcol_dfl == NULL) {
      fgcol_dfl = (char *)vg4->hash->get(cvtype->hparam, "fgcolor-dfl", NULL);
      if (fgcol_dfl != NULL && strcmp(fgcol_dfl, "transparent") == 0) { fgcol_dfl = NULL; }
    }

    /* get actual fgcolor-act */
    snprintf(bkey, sizeof(bkey), "item%d.fgcolor-act", uelm % 2 + 1);
    fgcol_act = (char *)vg4->hash->get(cvtype->hparam, bkey, NULL);
    if (fgcol_act != NULL && strcmp(fgcol_act, "transparent") == 0) { fgcol_act = NULL; }
    if (fgcol_act == NULL) {
      fgcol_act = (char *)vg4->hash->get(cvtype->hparam, "fgcolor-act", NULL);
      if (fgcol_act != NULL && strcmp(fgcol_act, "transparent") == 0) { fgcol_act = NULL; }
    }

    /* start */
    gmpos = SML3_gumprintf(&gm1, 0, "[boxwidth=%d", cvitem->rect.w);

    /* font */
    valptr = (char *)vg4->hash->get(cvtype->hparam, "font", NULL);
    if (valptr != NULL) { gmpos += SML3_gumprintf(&gm1, gmpos, " font=%s", valptr); }

    /* orientation */
    valptr = (char *)vg4->hash->get(cvtype->hparam, "orientation", NULL);
    if (valptr != NULL) { gmpos += SML3_gumprintf(&gm1, gmpos, " orientation=%s", valptr); }

    /* create default text image */
    gmcommon = gmpos;
    if (fgcol_dfl != NULL) { gmpos += SML3_gumprintf(&gm1, gmpos, " fgcolor=%s", fgcol_dfl); }
    if (bgcol_dfl != NULL) { gmpos += SML3_gumprintf(&gm1, gmpos, " bgcolor=%s", bgcol_dfl); }
    gmpos += (int)SML3_gumcpy(&gm1, gmpos, "]");
    { char afont[64];
      vg4->font->getdefault(afont, sizeof(afont));
      if (*cvas->cve.dflfont != '\0') { vg4->font->setdefault(cvas->cve.dflfont); }
      cvtype->e[uelm].img_dfl = vg4_font_totext_nolist(text, SML3_gumgetval(&gm1), cvas->cve.dirname, cvas->cve.hvar, NULL);
      vg4->font->setdefault(afont);
    }

    /* create activated text image */
    gmpos = gmcommon;
    if (fgcol_act != NULL) { gmpos += SML3_gumprintf(&gm1, gmpos, " fgcolor=%s", fgcol_act); }
    if (bgcol_act != NULL) { gmpos += SML3_gumprintf(&gm1, gmpos, " bgcolor=%s", bgcol_act); }
    gmpos += (int)SML3_gumcpy(&gm1, gmpos, "]");
    { char afont[64];
      vg4->font->getdefault(afont, sizeof(afont));
      if (*cvas->cve.dflfont != '\0') { vg4->font->setdefault(cvas->cve.dflfont); }
      cvtype->e[uelm].img_act = vg4_font_totext_nolist(text, SML3_gumgetval(&gm1), cvas->cve.dirname, cvas->cve.hvar, NULL);
      vg4->font->setdefault(afont);
    }

    /* spacing */
    valptr = (char *)vg4->hash->get(cvtype->hparam, "spacing", NULL);
    if (valptr != NULL) {
      int sp = atoi(valptr);
      if (sp > 0) {
        struct VG_Image *imgp;
        int w1, h1, colb;
        if (bgcol != NULL) { colb = (int)strtol(bgcol, NULL, 16); } else { colb = VG_COLOR_TRANSPARENT; }
        vg4->image->getsize(cvtype->e[uelm].img_dfl, NULL, &w1, &h1);
        /* default image */
        imgp = vg4_image_create_nolist(w1, h1 + sp);
        vg4->image->fill(imgp, colb);
        vg4->image->copy(imgp, cvtype->e[uelm].img_dfl, NULL, NULL);
        vg4->image->destroy(cvtype->e[uelm].img_dfl);
        cvtype->e[uelm].img_dfl = imgp;
        /* activated image */
        imgp = vg4_image_create_nolist(w1, h1 + sp);
        vg4->image->fill(imgp, colb);
        vg4->image->copy(imgp, cvtype->e[uelm].img_act, NULL, NULL);
        vg4->image->destroy(cvtype->e[uelm].img_act);
        cvtype->e[uelm].img_act = imgp;
      }
    }

    SML3_gumdest(&gm1);
  }

  if (cvtype->e[uelm].img_dfl == NULL || cvtype->e[uelm].img_act == NULL) {  /* remove item */
    vg4->image->destroy(cvtype->e[uelm].img_dfl);
    vg4->image->destroy(cvtype->e[uelm].img_act);
    if (--cvtype->max == 0) {
      free(cvtype->e); cvtype->e = NULL;
    }
  } else {
    cvtype->e[uelm].name = SML3_strdup(ename);
    retw = VG_TRUE;
  }

  return retw;
} /* Ende canvas_list_add */


/* canvas_list_set_activated:
 * activate list-item of a list
 * @param cvas     canvas
 * @param iname    name of list
 * @param ename    name of list-item to activate
 */
static void
canvas_list_set_activated(struct VG_Canvas *cvas, const char *iname, const char *ename)
{
  struct vgi_canvas_item *cvitem;
  struct cv_type *cvtype;
  int uelm;

  if (cvas == NULL || iname == NULL || *iname == '\0' || ename == NULL || *ename == '\0') { return; }

  cvitem = cvtype_get_item(cvas, iname, type_no);
  if (cvitem == NULL) { return; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return; }

  for (uelm = 0; uelm < cvtype->max; uelm++) {
    if (strcmp(ename, cvtype->e[uelm].name) == 0) {
      cvtype->active = uelm + 1;
      break;
    }
  }
  if (uelm == cvtype->max) { cvtype->active = 0; }
} /* Ende canvas_list_set_activated */


/* canvas_list_get_activated:
 * get activated list-item of a list
 * @param cvas     canvas
 * @param iname    name of list
 * @return   name of activated list-item, or NULL = none
 */
static const char *
canvas_list_get_activated(struct VG_Canvas *cvas, const char *iname)
{
  struct vgi_canvas_item *cvitem;
  struct cv_type *cvtype;
  const char *ename;
  int item_no;

  if (cvas == NULL || iname == NULL || *iname == '\0') { return NULL; }

  cvitem = cvtype_get_item(cvas, iname, type_no);
  if (cvitem == NULL) { return NULL; }
  cvtype = (struct cv_type *)cvitem->vstrct;
  if (cvtype == NULL) { return NULL; }

  ename = NULL;
  item_no = cvtype->active;
  if (item_no > 0) { ename = cvtype->e[item_no - 1].name; }

  return ename;
} /* Ende canvas_list_get_activated */
