
/* 
A* -------------------------------------------------------------------
B* This file contains source code for the PyMOL computer program
C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific. 
D* -------------------------------------------------------------------
E* It is unlawful to modify or remove this copyright notice.
F* -------------------------------------------------------------------
G* Please see the accompanying LICENSE file for further information. 
H* -------------------------------------------------------------------
I* Additional authors of this source file include:
-* 
-* 
-*
Z* -------------------------------------------------------------------
*/
#include"os_python.h"
#include"os_predef.h"
#include"os_std.h"
#include"os_gl.h"


#include "Err.h"

#include "main.h"
#include "Base.h"
#include "Pop.h"
#include "PopUp.h"
#include "Ortho.h"
#include "Util.h"
#include "P.h"
#include "Util.h"
#include "Color.h"
#include "Text.h"
#include "PyMOL.h"
#include "CGO.h"
#include "Block.h"

#define cPopUpLineHeight DIP2PIXEL(17)
#define cPopUpTitleHeight DIP2PIXEL(19)
#define cPopUpBarHeight DIP2PIXEL(4)
#define cPopUpCharWidth DIP2PIXEL(8)
#define cPopUpCharMargin DIP2PIXEL(2)
#define cPopUpCharLift DIP2PIXEL(2)

#define cChildDelay 0.25
#define cPassiveDelay 0.45
#define cDirtyDelay 0.05

struct CPopUp : public Block {
  Block *Parent {};
  Block *Child {};
  int ChildLine {};
  int LastX {}, LastY {};
  int StartX {}, StartY {};
  int Selected {};
  int Width {}, Height {};
  int NLine {};
  PyObject **Sub {};
  char **Command {};
  char **Text {};
  int *Code {};
  double ChildDelay {};
  double DirtyDelay {};
  double PassiveDelay {};
  int DirtyDelayFlag {};
  int NeverDragged {};
  int PlacementAffinity {};

  CPopUp(PyMOLGlobals * G) : Block(G){}

  void draw(CGO *orthoCGO) override;
  int drag(int x, int y, int mod) override;
  int release(int button, int x, int y, int mod) override;
  void reshape(int width, int height) override {};
};

static
int PopUpConvertY(CPopUp * I, int value, int mode);

/*========================================================================
 * If Sub[a] is a list, return it, otherwise assume it's callable so
 * call it, assign the result to Sub[a] and return the result.
 *
 * @param Sub : array of lists or callables
 * @param a   : array index
 */
static PyObject * SubGetItem(PyMOLGlobals * G, PyObject ** Sub, const int a) {
#ifndef _PYMOL_NOPY
  PyObject *elem = Sub[a];
  ok_assert(1, elem);

  if(!PyList_Check(elem)) {
    PBlock(G);
    elem = PyObject_CallObject(elem, nullptr);

    if(PyErr_Occurred())
      PyErr_Print();

    Py_DECREF(Sub[a]);
    Sub[a] = elem;

    PUnblock(G);
  }

  return elem;

ok_except1:
#endif
  return nullptr;
}

/*========================================================================*/
static Block *PopUpRecursiveFind(Block * block, int x, int y)
{
  PyMOLGlobals *G = block->m_G;
  CPopUp *I = (CPopUp *) block->reference;
  if(I->Child) {                /* favor the child */
    if(PopUpRecursiveFind(I->Child, x, y) == I->Child)
      return block;
  }
  if(block->recursiveFind(x, y) == block) {
    OrthoGrab(G, block);
    return block;
  }
  return nullptr;
}


/*========================================================================*/
Block *PopUpNew(PyMOLGlobals * G, int x, int y, int last_x, int last_y,
                int passive, PyObject * list, Block * parent)
{
#ifdef _PYMOL_NOPY
  return nullptr;
#else
  /* assumes blocked threads (calls the Python C API) */

  int mx, a, l, cl, cmx;
  int dim[2];
  PyObject *elem;
  const char *str, *c;
  int blocked = PAutoBlock(G);
  auto ui_light_bg = SettingGet<InternalGUIMode>(cSetting_internal_gui_mode, G->Setting);
  CPopUp *I = new CPopUp(G);

  I->reference = (void *) I;
  I->active = false;
  I->TextColor[0] = 1.0F;
  I->TextColor[1] = 1.0F;
  I->TextColor[2] = 1.0F;

  I->BackColor[0] = 0.1F;
  I->BackColor[1] = 0.1F;
  I->BackColor[2] = 0.1F;

  if(ui_light_bg != InternalGUIMode::Default) {
    I->TextColor[0] = 0.0F;
    I->TextColor[1] = 0.0F;
    I->TextColor[2] = 0.0F;
    I->BackColor[0] = 1.0F;
    I->BackColor[1] = 1.0F;
    I->BackColor[2] = 1.0F;
  }

  I->Parent = parent;
  I->Child = nullptr;
  I->NLine = PyList_Size(list);
  I->Text = nullptr;
  I->Command = nullptr;
  I->Code = nullptr;
  I->Selected = -1;
  I->StartX = (I->LastX = last_x);
  I->StartY = (I->LastY = last_y);
  I->ChildDelay = UtilGetSeconds(G) + cChildDelay * 2.5;
  I->PassiveDelay = UtilGetSeconds(G) + cPassiveDelay;
  I->DirtyDelay = false;
  I->DirtyDelayFlag = false;
  I->NeverDragged = true;
  I->PlacementAffinity = 0;
  mx = 1;
  cmx = 1;
  for(a = 0; a < I->NLine; a++) {
    PyObject* item = PyList_GetItem(list, a);
    if (PyList_Size(item) < 2) {
      continue;
    }
    elem = PyList_GetItem(item, 1);
    l = PyString_Size(elem);
    str = PyString_AsString(elem);
    cl = l;
    c = str;
    while(*c) {
      if(TextStartsWithColorCode(c)) {  /* discount the markup */
          c += 3;
          cl -= 4;
      }
      c++;
    }
    if(cl > cmx)
      cmx = cl;
    if(l > mx)
      mx = l;
  }
  I->Width = (cmx * cPopUpCharWidth) + 2 * cPopUpCharMargin;

  dim[0] = I->NLine + 1;
  dim[1] = mx + 1;
  I->Text = (char **) UtilArrayCalloc((unsigned int *) (void *) dim, 2, 1);

  mx = 1;
  for(a = 0; a < I->NLine; a++) {
    PyObject* item = PyList_GetItem(list, a);
    if (PyList_Size(item) > 2) {
      PyObject* command = PyList_GetItem(item, 2);
      if(PyString_Check(command)) {
	l = PyString_Size(command);
	if(l > mx)
	  mx = l;
      }
    }
  }
  dim[0] = I->NLine + 1;
  dim[1] = mx + 1;
  I->Command = (char **) UtilArrayCalloc((unsigned int *) (void *) dim, 2, 1);

  I->Code = pymol::malloc<int>(I->NLine + 1);
  I->Sub = pymol::calloc<PyObject *>(I->NLine + 1);

  for(a = 0; a < I->NLine; a++) {
    PyObject *command;
    elem = PyList_GetItem(list, a);
    I->Code[a] = PyInt_AsLong(PyList_GetItem(elem, 0));
    if (I->Code[a] == 0) {
      continue;
    }
    strcpy(I->Text[a], PyString_AsString(PyList_GetItem(elem, 1)));
    if (I->Code[a] == 2) {
      continue;
    }
    command = PyList_GetItem(elem, 2);
    if(command) {
      if(PyString_Check(command)) {
	strcpy(I->Command[a], PyString_AsString(command));
      } else {
	Py_INCREF(command);
	I->Sub[a] = command;
      }
    }
  }

  /* compute height */

  I->Height = 1 * cPopUpCharMargin + PopUpConvertY(I, I->NLine, true);

  I->rect.top = y;
  I->rect.bottom = y - I->Height;
  I->rect.left = x - (I->Width) / 3;
  I->rect.right = x + (2 * I->Width) / 3;

  PopFitBlock(I);

  OrthoAttach(G, I, cOrthoTool);
  I->active = true;
  OrthoGrab(G, I);
  OrthoDirty(G);

  if(passive)
    PyMOL_SetPassive(G->PyMOL, true);

  PAutoUnblock(G, blocked);

  OrthoInvalidateDoDraw(G);  
  return I;
#endif

}


/*========================================================================*/
int PopUpConvertY(CPopUp * I, int value, int mode)
{
  int result;
  int a;
  int flag;
  if(mode) {
    result = 0;

    /* line to height */
    for(a = 0; a < I->NLine; a++) {
      if(a >= value)
        break;
      switch (I->Code[a]) {
      case 0:
        result += cPopUpBarHeight;
        break;
      case 1:
        result += cPopUpLineHeight;
        break;
      case 2:
        result += cPopUpTitleHeight;
        break;
      }
    }
  } else {
    flag = false;
    /* height to line */
    if(value < 0) {
      result = -1;
    } else {
      result = 0;
      for(a = 0; a < I->NLine; a++) {
        switch (I->Code[a]) {
        case 0:
          if(value < cPopUpBarHeight)
            flag = true;
          value -= cPopUpBarHeight;
          break;
        case 1:
          if(value < cPopUpLineHeight)
            flag = true;
          value -= cPopUpLineHeight;
          break;
        case 2:
          if(value < cPopUpLineHeight)
            flag = true;
          value -= cPopUpTitleHeight;
          break;
        }
        if(flag)
          break;
        result++;
      }
      if(!flag)
        result = -1;
      else if(result && !I->Code[result])
        result--;
    }
    /* height to line */
  }
  return (result);
}


/*========================================================================*/

static void PopUpDetachRecursiveChild(Block * block)
{
  PyMOLGlobals *G = block->m_G;
  CPopUp *I = (CPopUp *) block->reference;

  OrthoDetach(G, block);
  if(I->Child)
    PopUpDetachRecursiveChild(I->Child);
}


/*========================================================================*/

static void PopUpForgetChild(Block * block)
{

  CPopUp *I = (CPopUp *) block->reference;
  I->Child = nullptr;
}

static void PopUpRecursiveDetach(Block * block)
{
  PyMOLGlobals *G = block->m_G;
  CPopUp *I = (CPopUp *) block->reference;
  OrthoDetach(G, block);
  if(I->Child)
    PopUpDetachRecursiveChild(I->Child);
  if(I->Parent) {
    PopUpForgetChild(I->Parent);
    PopUpRecursiveDetach(I->Parent);
  }
}

static void PopUpFree(Block * block)
{
  PyMOLGlobals *G = block->m_G;
  CPopUp *I = (CPopUp *) block->reference;

#ifndef _PYMOL_NOPY
  {                             /* purge code */
    int a;
    int blocked = PAutoBlock(G);
    for(a = 0; a < I->NLine; a++)
      if(I->Sub[a]) {
        Py_DECREF(I->Sub[a]);
      }
    PAutoUnblock(G, blocked);
  }
#endif

  OrthoDetach(G, I);
  FreeP(I->Sub);
  FreeP(I->Code);
  FreeP(I->Command);
  FreeP(I->Text);
  DeleteP(I);

}

static void PopUpRecursiveFree(Block * block)
{

  CPopUp *I = (CPopUp *) block->reference;

  if(I->Child)
    PopUpFree(I->Child);
  I->Child = nullptr;
  if(I->Parent) {
    PopUpForgetChild(I->Parent);
    PopUpRecursiveFree(I->Parent);
  }
  PopUpFree(block);
}

static void PopUpFreeRecursiveChild(Block * block)
{
  CPopUp *I = (CPopUp *) block->reference;
  if(I->Child)
    PopUpFreeRecursiveChild(I->Child);
  I->Child = nullptr;
  PopUpFree(block);
}


/*========================================================================*/
int CPopUp::release(int button, int x, int y, int mod)
{
  PyMOLGlobals *G = m_G;
  CPopUp *I = (CPopUp *) reference;
  int gone_passive = false;

  int scroll_dy = 10;
  switch (button) {
    case PYMOL_BUTTON_SCROLL_FORWARD:
      scroll_dy *= -1;
    case PYMOL_BUTTON_SCROLL_REVERSE:
      translate(0, scroll_dy);
      return 1;
  }

  if(I->NeverDragged) {
    if(I->PassiveDelay > UtilGetSeconds(G)) {
      gone_passive = true;
      I->PassiveDelay = UtilGetSeconds(G);      /* kill any further delay */
    }
  }
  if(!gone_passive) {
    if(!I->NeverDragged)
      drag(x, y, mod);

    /* go passive if we click and release on a sub-menu */

    if((I->Selected >= 0) && (I->Sub[I->Selected])) {
      if((x >= I->rect.left) && (x <= I->rect.right)) {
        gone_passive = true;
      }
    }
  }
  if(gone_passive) {
    PyMOL_SetPassive(G->PyMOL, true);
  } else {
    OrthoUngrab(G);
    PopUpRecursiveDetach(this);
    if(!I->NeverDragged)
      if((I->Selected >= 0) && (!I->Sub[I->Selected])) {
        PLog(G, I->Command[I->Selected], cPLog_pym);
        PParse(G, I->Command[I->Selected]);
        PFlush(G);
      }
    PopUpRecursiveFree(this);
  }
  OrthoDirty(G);
  return (1);
}


/*========================================================================*/
int CPopUp::drag(int x, int y, int mod)
{
  PyMOLGlobals *G = m_G;
  CPopUp *I = (CPopUp *) reference;

  int a;
  int was = I->Selected;

  if((!I->NeverDragged) && (((I->StartX - x) > 4) || ((I->StartY - y) > 4)))
    I->NeverDragged = false;

  I->LastX = x;
  I->LastY = y;

  x -= I->rect.left;
  y = (I->rect.top - cPopUpCharMargin) - y - 1;

  if((x < -2) || (x > (I->Width + 2))) {
    int handled_flag = false;
    if(I->Child) {
      if(PopUpRecursiveFind(I->Child, I->LastX, I->LastY) == I->Child) {
        I->Selected = I->ChildLine;
        handled_flag = true;
      }
    }
    if(!handled_flag) {
      if(I->Parent) {           /* are we back in the parent window? */
        I->Selected = -1;
        return I->Parent->drag(I->LastX, I->LastY, mod);
      } else if(!I->Child) {
        I->Selected = -1;
      }
    }
  } else {
    OrthoGrab(G, this);
    a = PopUpConvertY(I, y, false);
    if(I->NLine && (a == I->NLine))
      if((y - a * cPopUpLineHeight) < 4)
        a = I->NLine - 1;
    if((a < 0) || (a >= I->NLine))
      I->Selected = -1;
    else {
      if(I->Code[a] == 1) {
        if((I->Child) && (I->ChildLine != a)) {
          if(I->ChildDelay < UtilGetSeconds(G)) {
            PopUpDetachRecursiveChild(I->Child);
            PopUpFreeRecursiveChild(I->Child);
            I->Child = nullptr;
            I->ChildLine = -1;
            OrthoDirty(G);
	    OrthoInvalidateDoDraw(G);
          } else {
            I->Selected = a;
          }
          PyMOL_NeedFakeDrag(G->PyMOL);
        }
      }

      if(I->Code[a] != 1)
        I->Selected = -1;
      else {
        /* activate submenu */
        PyObject * sub_a = SubGetItem(G, I->Sub, a);
        if(!sub_a) {
          // do nothing
        } else if(!I->Child) {
          I->ChildLine = a;
          if(I->ChildDelay > UtilGetSeconds(G)) {
            PyMOL_NeedFakeDrag(G->PyMOL);       /* keep coming back here... */
          } else {
            I->Child = PopUpNew(G, I->LastX - 300, I->LastY, I->LastX, I->LastY,
                                false, sub_a, I);
            {
              int target_y =
                rect.top - (PopUpConvertY(I, a, true) + cPopUpCharMargin);
              CPopUp *child = (CPopUp *) (I->Child->reference);
              if(child->NLine)
                if(child->Code[0] != 1)
                  target_y += cPopUpTitleHeight + 2;
              child->PlacementAffinity = PopPlaceChild(I->Child, rect.left - 5,
                                                       rect.right + 5, target_y,
                                                       I->PlacementAffinity);
            }

            OrthoGrab(G, this);
            I->ChildDelay = UtilGetSeconds(G) + cChildDelay;    /* leave child up for a while */
          }
          PyMOL_NeedFakeDrag(G->PyMOL); /* keep coming back here... */
        } else if(I->ChildLine == a) {  /* on correct line */
          I->ChildDelay = UtilGetSeconds(G) + cChildDelay;      /* keep child here for a while */
        }
        I->Selected = a;
      }
    }
  }
  /* delay updates, etc. so that child menus 
     can be comfortably accessed with sloppy mousing */

  if((I->Child) && (I->Selected != I->ChildLine))
    PyMOL_NeedFakeDrag(G->PyMOL);

  if(was != I->Selected) {

    I->NeverDragged = false;
    if(!I->Child) {
      /* we moved, so renew the child delay */
      I->ChildDelay = UtilGetSeconds(G) + cChildDelay;
      PyMOL_NeedFakeDrag(G->PyMOL);
    }

    if((I->Child) && (I->Selected != I->ChildLine)) {
      I->DirtyDelayFlag = true;
      I->DirtyDelay = UtilGetSeconds(G) + cDirtyDelay;
    }
    if(!I->DirtyDelayFlag){
      OrthoDirty(G);
      OrthoInvalidateDoDraw(G);
    }
  }
  if(I->DirtyDelayFlag && (I->DirtyDelay < UtilGetSeconds(G))) {
    I->DirtyDelayFlag = false;
    OrthoDirty(G);
    OrthoInvalidateDoDraw(G);
  }
  return (1);
}


/*========================================================================*/
void CPopUp::draw(CGO* orthoCGO)
{
  CPopUp *I = this; // TODO: Remove I during PopUp refactor
  PyMOLGlobals *G = m_G;
  int x, y, a, xx;
  char *c;

  if(G->HaveGUI && G->ValidContext) {

    if((I->Child) && (I->Selected != I->ChildLine))
      PyMOL_NeedFakeDrag(G->PyMOL);

    /* put raised border around pop-up menu */

    if (orthoCGO){
      CGOColor(orthoCGO, 0.2F, 0.2F, 0.4F);
      CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
      CGOVertex(orthoCGO, rect.left - 2, rect.bottom - 2, 0.f);
      CGOVertex(orthoCGO, rect.right + 2, rect.bottom - 2, 0.f);
      CGOVertex(orthoCGO, rect.left - 2, rect.bottom + 1, 0.f);
      CGOVertex(orthoCGO, rect.right + 2, rect.bottom + 1, 0.f);
      CGOEnd(orthoCGO);
    } else {
      glColor3f(0.2F, 0.2F, 0.4F);
      glBegin(GL_POLYGON);
      glVertex2i(rect.left - 2, rect.bottom - 2);
      glVertex2i(rect.right + 2, rect.bottom - 2);
      glVertex2i(rect.right + 2, rect.bottom + 1);
      glVertex2i(rect.left - 2, rect.bottom + 1);
      glEnd();
    }

    if (orthoCGO){
      CGOColor(orthoCGO, 0.4F, 0.4F, 0.6F);
      CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
      CGOVertex(orthoCGO, rect.left - 1, rect.bottom - 1, 0.f);
      CGOVertex(orthoCGO, rect.right + 1, rect.bottom - 1, 0.f);
      CGOVertex(orthoCGO, rect.left - 1, rect.bottom + 1, 0.f);
      CGOVertex(orthoCGO, rect.right + 1, rect.bottom + 1, 0.f);
      CGOEnd(orthoCGO);
    } else {
      glColor3f(0.4F, 0.4F, 0.6F);
      glBegin(GL_POLYGON);
      glVertex2i(rect.left - 1, rect.bottom - 1);
      glVertex2i(rect.right + 1, rect.bottom - 1);
      glVertex2i(rect.right + 1, rect.bottom + 1);
      glVertex2i(rect.left - 1, rect.bottom + 1);
      glEnd();
    }
    /* right */

    if (orthoCGO){
      CGOColor(orthoCGO, 0.2F, 0.2F, 0.4F);
      CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
      CGOVertex(orthoCGO, rect.right, rect.bottom - 2, 0.f);
      CGOVertex(orthoCGO, rect.right + 2, rect.bottom - 2, 0.f);
      CGOVertex(orthoCGO, rect.right, rect.top, 0.f);
      CGOVertex(orthoCGO, rect.right + 2, rect.top, 0.f);
      CGOEnd(orthoCGO);
    } else {
      glColor3f(0.2F, 0.2F, 0.4F);
      glBegin(GL_POLYGON);
      glVertex2i(rect.right, rect.bottom - 2);
      glVertex2i(rect.right + 2, rect.bottom - 2);
      glVertex2i(rect.right + 2, rect.top);
      glVertex2i(rect.right, rect.top);
      glEnd();
    }

    if (orthoCGO){
      CGOColor(orthoCGO, 0.4F, 0.4F, 0.6F);
      CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
      CGOVertex(orthoCGO, rect.right, rect.bottom - 1, 0.f);
      CGOVertex(orthoCGO, rect.right + 1, rect.bottom - 1, 0.f);
      CGOVertex(orthoCGO, rect.right, rect.top, 0.f);
      CGOVertex(orthoCGO, rect.right + 1, rect.top, 0.f);
      CGOEnd(orthoCGO);
    } else {
      glColor3f(0.4F, 0.4F, 0.6F);
      glBegin(GL_POLYGON);
      glVertex2i(rect.right, rect.bottom - 1);
      glVertex2i(rect.right + 1, rect.bottom - 1);
      glVertex2i(rect.right + 1, rect.top);
      glVertex2i(rect.right, rect.top);
      glEnd();
    }
    /* top */

    if (orthoCGO){
      CGOColor(orthoCGO, 0.5F, 0.5F, 0.7F);
      CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
      CGOVertex(orthoCGO, rect.left - 2, rect.top + 2, 0.f);
      CGOVertex(orthoCGO, rect.right + 2, rect.top + 2, 0.f);
      CGOVertex(orthoCGO, rect.left - 2, rect.top, 0.f);
      CGOVertex(orthoCGO, rect.right + 2, rect.top, 0.f);
      CGOEnd(orthoCGO);
    } else {
      glColor3f(0.5F, 0.5F, 0.7F);
      glBegin(GL_POLYGON);
      glVertex2i(rect.left - 2, rect.top + 2);
      glVertex2i(rect.right + 2, rect.top + 2);
      glVertex2i(rect.right + 2, rect.top);
      glVertex2i(rect.left - 2, rect.top);
      glEnd();
    }

    if (orthoCGO){
      CGOColor(orthoCGO, 0.6F, 0.6F, 0.8F);
      CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
      CGOVertex(orthoCGO, rect.left - 1, rect.top + 1, 0.f);
      CGOVertex(orthoCGO, rect.right + 1, rect.top + 1, 0.f);
      CGOVertex(orthoCGO, rect.left - 1, rect.top, 0.f);
      CGOVertex(orthoCGO, rect.right + 1, rect.top, 0.f);
      CGOEnd(orthoCGO);
    } else {
      glColor3f(0.6F, 0.6F, 0.8F);
      glBegin(GL_POLYGON);
      glVertex2i(rect.left - 1, rect.top + 1);
      glVertex2i(rect.right + 1, rect.top + 1);
      glVertex2i(rect.right + 1, rect.top);
      glVertex2i(rect.left - 1, rect.top);
      glEnd();
    }

    /* left */
    if (orthoCGO){
      CGOColor(orthoCGO, 0.5F, 0.5F, 0.7F);
      CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
      CGOVertex(orthoCGO, rect.left - 2, rect.bottom - 2, 0.f);
      CGOVertex(orthoCGO, rect.left, rect.bottom, 0.f);
      CGOVertex(orthoCGO, rect.left - 2, rect.top, 0.f);
      CGOVertex(orthoCGO, rect.left, rect.top, 0.f);
      CGOEnd(orthoCGO);
    } else {
      glColor3f(0.5F, 0.5F, 0.7F);
      glBegin(GL_POLYGON);
      glVertex2i(rect.left - 2, rect.bottom - 2);
      glVertex2i(rect.left, rect.bottom);
      glVertex2i(rect.left, rect.top);
      glVertex2i(rect.left - 2, rect.top);
      glEnd();
    }

    if (orthoCGO){
      CGOColor(orthoCGO, 0.6F, 0.6F, 0.8F);
      CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
      CGOVertex(orthoCGO, rect.left - 1, rect.bottom - 1, 0.f);
      CGOVertex(orthoCGO, rect.left, rect.bottom - 1, 0.f);
      CGOVertex(orthoCGO, rect.left, rect.top, 0.f);
      CGOVertex(orthoCGO, rect.left - 1, rect.top, 0.f);
      CGOEnd(orthoCGO);
    } else {
      glColor3f(0.6F, 0.6F, 0.8F);
      glBegin(GL_POLYGON);
      glVertex2i(rect.left - 1, rect.bottom - 1);
      glVertex2i(rect.left, rect.bottom - 1);
      glVertex2i(rect.left, rect.top);
      glVertex2i(rect.left - 1, rect.top);
      glEnd();
    }

    if (orthoCGO)
      CGOColorv(orthoCGO, BackColor);
#ifndef PURE_OPENGL_ES_2
    else
      glColor3fv(BackColor);
#endif
    fill(orthoCGO);

    if (orthoCGO)
      CGOColorv(orthoCGO, TextColor);
#ifndef PURE_OPENGL_ES_2
    else
      glColor3fv(TextColor);
#endif

    if(I->Selected >= 0) {

      x = rect.left;
      y = rect.top - PopUpConvertY(I, I->Selected, true) - cPopUpCharMargin;

      y += 2;
    if (orthoCGO){
      CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
      CGOVertex(orthoCGO, x, y, 0.f);
      CGOVertex(orthoCGO, x + I->Width - 1, y, 0.f);
      CGOVertex(orthoCGO, x, y - (cPopUpLineHeight + 3), 0.f);
      CGOVertex(orthoCGO, x + I->Width - 1, y - (cPopUpLineHeight + 3), 0.f);
      CGOEnd(orthoCGO);
    } else {
      glBegin(GL_POLYGON);
      glVertex2i(x, y);
      glVertex2i(x + I->Width - 1, y);
      glVertex2i(x + I->Width - 1, y - (cPopUpLineHeight + 3));
      glVertex2i(x, y - (cPopUpLineHeight + 3));
      glEnd();
    }
    }
    if(I->Code[0] == 2) {       /* menu name */

      if (SettingGet<InternalGUIMode>(cSetting_internal_gui_mode, G->Setting) ==
          InternalGUIMode::Default)
      {
        if (orthoCGO)
          CGOColor(orthoCGO, 0.3F, 0.3F, 0.6F);
        else
          glColor3f(0.3F, 0.3F, 0.6F);
      } else {
	if (orthoCGO)
	  CGOColor(orthoCGO, 1.0F, 1.0F, 1.0F);
	else
	  glColor3f(1.0F, 1.0F, 1.0F);
      }
      x = rect.left;
      y = rect.top;

      if (orthoCGO){
	CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
	CGOVertex(orthoCGO, x, y, 0.f);
	CGOVertex(orthoCGO, x + I->Width, y, 0.f);
	CGOVertex(orthoCGO, x, y - (cPopUpLineHeight + cPopUpCharMargin), 0.f);
	CGOVertex(orthoCGO, x + I->Width, y - (cPopUpLineHeight + cPopUpCharMargin), 0.f);
	CGOEnd(orthoCGO);
      } else {
	glBegin(GL_POLYGON);
	glVertex2i(x, y);
	glVertex2i(x + I->Width, y);
	glVertex2i(x + I->Width, y - (cPopUpLineHeight + cPopUpCharMargin));
	glVertex2i(x, y - (cPopUpLineHeight + cPopUpCharMargin));
	glEnd();
      }

      if (orthoCGO){
	CGOColor(orthoCGO, 0.2F, 0.2F, 0.4F);
	CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
	CGOVertex(orthoCGO, x + I->Width - 1, y - (cPopUpLineHeight + cPopUpCharMargin), 0.f);
	CGOVertex(orthoCGO, x + I->Width - 1, y - (cPopUpLineHeight + cPopUpCharMargin) - 1.f, 0.f);
	CGOVertex(orthoCGO, x, y - (cPopUpLineHeight + cPopUpCharMargin), 0.f);
	CGOVertex(orthoCGO, x, y - (cPopUpLineHeight + cPopUpCharMargin) - 1.f, 0.f);
	CGOEnd(orthoCGO);
      } else {
	glColor3f(0.2F, 0.2F, 0.4F);
	glBegin(GL_LINES);
	glVertex2i(x + I->Width - 1, y - (cPopUpLineHeight + cPopUpCharMargin));
	glVertex2i(x, y - (cPopUpLineHeight + cPopUpCharMargin));
	glEnd();
      }
    }

    x = rect.left + cPopUpCharMargin;
    y = (rect.top - cPopUpLineHeight) - cPopUpCharMargin + 2;

    for(a = 0; a < I->NLine; a++) {
      auto text_color = (a == I->Selected) ? BackColor : TextColor;
      TextSetColor(G, text_color);
      if(I->Code[a]) {
        c = I->Text[a];
        xx = x;
        while(*c) {
          // note: previously also supported "\\+++red", but was never used
          if(TextSetColorFromCode(G, c, text_color)) {
            c += 4;
          }

          TextSetPos2i(G, xx, y + cPopUpCharLift);
          TextDrawChar(G, *(c++), orthoCGO);
          xx = xx + DIP2PIXEL(8);
        }

        if(I->Sub[a]) {

	  if (orthoCGO){
	    CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
	    CGOColor(orthoCGO, 0.4F, 0.4F, 0.4F);
	    CGOVertex(orthoCGO, rect.left - 3, y + ((cPopUpLineHeight)) - 4, 0.f);
	    CGOColor(orthoCGO, 0.4F, 0.4F, 0.4F);
	    CGOVertex(orthoCGO, rect.left - 3, y + 1, 0.f);
	    CGOColor(orthoCGO, 0.1F, 0.1F, 0.1F);
	    CGOVertex(orthoCGO, rect.left, y + ((cPopUpLineHeight)) - 4, 0.f);
	    CGOColor(orthoCGO, 0.1F, 0.1F, 0.1F);
	    CGOVertex(orthoCGO, rect.left, y + 1, 0.f);
	    CGOEnd(orthoCGO);
	  } else {
	    glBegin(GL_POLYGON);
	    glColor3f(0.4F, 0.4F, 0.4F);
	    glVertex2i(rect.left - 3, y + 1);
	    glColor3f(0.1F, 0.1F, 0.1F);
	    glVertex2i(rect.left, y + 1);
	    glVertex2i(rect.left, y + ((cPopUpLineHeight)) - 4);
	    glColor3f(0.4F, 0.4F, 0.4F);
	    glVertex2i(rect.left - 3, y + ((cPopUpLineHeight)) - 4);
	    glEnd();
	  }
	  if (orthoCGO){
	    CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
	    CGOColor(orthoCGO, 0.1F, 0.2F, 0.2F);
	    CGOVertex(orthoCGO, rect.right, y + 1, 0.f);
	    CGOColor(orthoCGO, 0.4F, 0.4F, 0.4F);
	    CGOVertex(orthoCGO, rect.right + 3, y + 1, 0.f);
	    CGOColor(orthoCGO, 0.1F, 0.2F, 0.2F);
	    CGOVertex(orthoCGO, rect.right, y + ((cPopUpLineHeight)) - 4, 0.f);
	    CGOColor(orthoCGO, 0.4F, 0.4F, 0.4F);
	    CGOVertex(orthoCGO, rect.right + 3, y + ((cPopUpLineHeight)) - 4, 0.f);
	    CGOEnd(orthoCGO);
	  } else {
	    glBegin(GL_POLYGON);
	    glColor3f(0.1F, 0.2F, 0.2F);
	    glVertex2i(rect.right, y + 1);
	    glColor3f(0.4F, 0.4F, 0.4F);
	    glVertex2i(rect.right + 3, y + 1);
	    glVertex2i(rect.right + 3, y + ((cPopUpLineHeight)) - 4);
	    glColor3f(0.1F, 0.2F, 0.2F);
	    glVertex2i(rect.right, y + ((cPopUpLineHeight)) - 4);
	    glEnd();
	  }
        }

        y -= cPopUpLineHeight;
        if(I->Code[a] == 2)
          y -= 2;
      } else {
	  if (orthoCGO){
	    /* two lines between sections in the menu, one light, one dark */
	    CGOColor(orthoCGO, 0.3F, 0.3F, 0.5F);
	    CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
	    CGOVertex(orthoCGO, rect.right,
		      y + ((cPopUpLineHeight + cPopUpCharMargin) / 2) + 4, 0.f);
	    CGOVertex(orthoCGO, rect.right,
		      y + ((cPopUpLineHeight + cPopUpCharMargin) / 2) + 3, 0.f);
	    CGOVertex(orthoCGO, rect.left,
		      y + ((cPopUpLineHeight + cPopUpCharMargin) / 2) + 4, 0.f);
	    CGOVertex(orthoCGO, rect.left,
		      y + ((cPopUpLineHeight + cPopUpCharMargin) / 2) + 3, 0.f);
	    CGOEnd(orthoCGO);
	    CGOColor(orthoCGO, 0.6F, 0.6F, 0.8F);
	    CGOBegin(orthoCGO, GL_TRIANGLE_STRIP);
	    CGOVertex(orthoCGO, rect.right,
		      y + ((cPopUpLineHeight + cPopUpCharMargin) / 2) + 5, 0.f);
	    CGOVertex(orthoCGO, rect.right,
		      y + ((cPopUpLineHeight + cPopUpCharMargin) / 2) + 4, 0.f);
	    CGOVertex(orthoCGO, rect.left,
		      y + ((cPopUpLineHeight + cPopUpCharMargin) / 2) + 5, 0.f);
	    CGOVertex(orthoCGO, rect.left,
		      y + ((cPopUpLineHeight + cPopUpCharMargin) / 2) + 4, 0.f);
	    CGOEnd(orthoCGO);
	  } else {
	    glBegin(GL_LINES);
	    glColor3f(0.3F, 0.3F, 0.5F);
	    glVertex2i(rect.left,
		       y + ((cPopUpLineHeight + cPopUpCharMargin) / 2) + 3);
	    glVertex2i(rect.right,
		       y + ((cPopUpLineHeight + cPopUpCharMargin) / 2) + 3);
	    glColor3f(0.6F, 0.6F, 0.8F);
	    glVertex2i(rect.left,
		       y + ((cPopUpLineHeight + cPopUpCharMargin) / 2) + 4);
	    glVertex2i(rect.right,
		       y + ((cPopUpLineHeight + cPopUpCharMargin) / 2) + 4);
	    glEnd();
	  }
        y -= cPopUpBarHeight;
      }
    }
    if (orthoCGO)
      CGOColorv(orthoCGO, TextColor);
#ifndef PURE_OPENGL_ES_2
    else
      glColor3fv(TextColor);
#endif
    /*    BlockOutline(block); */
  }
}
