// a1.cpp
// Copyleft by Yuki Kamino
#include "stdafx.h"

#include "Storm.h"

TCHAR BD[256];
BOOL bFullBD;
static double offX, offY;
static BOOL bColAra;
static COLORREF colAra;
static OnClick *araOnClick;


void ClearBD()
{
  BD[0] = 0;
  bFullBD = FALSE;
}

void AddBD(TCHAR *str)
{
  if (!str)
    str = _T(" -null- ");
  if (!bFullBD)
  {
    if (lstrlen(BD) + lstrlen(str) < 200)
    {
      lstrcat(BD, _T(" _ "));
      lstrcat(BD, str);
    }
    else
    {
      lstrcat(BD, _T("..."));
      bFullBD = TRUE;
    }
  }
}

void ShowBD()
{
  SetWindowText(hMainWnd, BD);
}

HFONT GetFont(TCHAR *name, double size, BOOL bBold, BOOL bItalic, BOOL bUnder, BOOL bPrn, BOOL bFix /* = FALSE */)
{
  if(!bFix)
    size *= measFactor;
  TCHAR buf[256];
  lstrcpy(buf, name);
  if (lstrlen(buf) > 31)
    buf[31] = 0;
  LOGFONT lf;
  GetObject(GetStockObject(SYSTEM_FONT), sizeof(LOGFONT), &lf);
  lstrcpy(lf.lfFaceName, buf);
  lf.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
  lf.lfItalic = bItalic ? TRUE : FALSE;
  lf.lfUnderline = bUnder ? TRUE : FALSE;
  // lf.lfCharSet ‚́A•Ê“rl‚¦‚é

  int res = bPrn ? prnYRes : crtYRes;
  lf.lfHeight = -int(res*size/72);
  lf.lfWidth = 0;
  return CreateFontIndirect(&lf);
}

HFONT GetDefFont(BOOL bPrn, BOOL bFix)
{
  return GetFont(_T("‚l‚r –¾’©"), defFontSize, FALSE, FALSE, FALSE, bPrn, bFix);
}

HFONT GetBodyFont(BOOL bPrn, double size, BOOL bUnder)
{
  return GetFont(_T("‚l‚r –¾’©"), size, FALSE, FALSE, bUnder, bPrn, FALSE);
}

HFONT GetHyperFont()
{
  return GetFont(_T("‚l‚r ƒSƒVƒbƒN"), 10, FALSE, FALSE, TRUE, FALSE, 0);
}

void AddHyper(Ara *pa)
{
  if (!pa->pMom->pHyper)
    return;
  if (numHyper >= maxHyper)
  {
    maxHyper += 32;
    PARA *p = new PARA[maxHyper];
    ZeroMemory(p, sizeof(PARA)*maxHyper);
    for (int i = 0; i < numHyper; i++)
      p[i] = pHyper[i];
    if (pHyper)
      delete [] pHyper;
    pHyper = p;
  }
  pHyper[numHyper] = pa;
  numHyper++;
}

void AddAra(int type, Eva *pMom)
{
  if (numAra >= maxAra)
  {
    maxAra += 32;
    Ara *p = new Ara[maxAra];
    ZeroMemory(p, sizeof(Ara)*maxAra);
    for (int i = 0; i < numAra; i++)
      p[i] = pAra[i];
    if (pAra)
      delete [] pAra;
    pAra = p;
  }
  Ara *pa = &pAra[numAra];
  pa->pMom = pMom;
  pa->type = type;
  pa->rec = curFor ? curFor->rec : 0;
  pa->x = offX;
  pa->y = offY;
  pa->bCol = bColAra;
  pa->col = colAra;
  pa->pOnClick = araOnClick;
  numAra++;
  AddHyper(pa);
}

void ClearAra()
{
  numAra = 0;
}

int PathWid(Path *p)
{
  if (!p)
    return 0;
  int n = 0;
  DataSet *pD = p->pD;
  for (int i = 0; i < p->len; i++)
  {
    int at = p->ip[i];
    if (pD->item[at].type == FT_LINK)
    {
      pD = pD->item[at].link;
    }
  }
  if (pD)
  {
    n = pD->RecNum;
  }
  return n;
}

BOOL bUpSort;
Ope *pOpeSort;

int cmp(const void *p, const void *q)
{
  RecNo *p1 = (RecNo*)p;
  RecNo *p2 = (RecNo*)q;
  int type = PathType(pOpeSort->pPath);
  int ret = 0;
  if (type == FT_DBL)
  {
    double x1 = GetPathDbl(pOpeSort->pPath, p1->rec);
    double x2 = GetPathDbl(pOpeSort->pPath, p2->rec);
    if (x1 < x2)
      ret = -1;
    else if (x1 > x2)
      ret = 1;
    else
      ret = 0;
  }
  else if (type == FT_STR)
  {
    TCHAR *str1 = GetPathStr(pOpeSort->pPath, p1->rec);
    TCHAR *str2 = GetPathStr(pOpeSort->pPath, p2->rec);
    ret = lstrcmp(str1, str2);
  }
  else
  {
    int n1 = GetPathInt(pOpeSort->pPath, p1->rec);
    int n2 = GetPathInt(pOpeSort->pPath, p2->rec);
    ret = n1 - n2;
  }
  if (ret == 0)
    ret = p1->no - p2->no;
  if (!bUpSort)
    ret = -ret;
  return ret;
}

void OpeSort(int& nShow, int*& pShow, BOOL bUp, Ope *pOpe)
{
  if (nShow < 2)
    return;
  bUpSort = bUp;
  pOpeSort = pOpe;
  RecNo *p = new RecNo[nShow];
  for (int i = 0; i < nShow; i++)
  {
    p[i].no = i;
    p[i].rec = pShow[i];
  }
  qsort(p, nShow, sizeof(RecNo), cmp);
  for (int i = 0; i < nShow; i++)
    pShow[i] = p[i].rec;
  delete [] p;
}

void OpeSelect(DataSet *pD, int& nShow, int*& pShow, Ope *pOpe)
{
  int *ip = new int[pD->RecNum];
  int n = 0;
  for (int i = 0; i < nShow; i++)
  {
    evalRec = pShow[i];
    int ret = Eval(&pOpe->expr);
    if (ret == EX_DBL)
    {
      if (dVal == 0)
        continue;
    }
    else if (ret == EX_STR)
    {
      //////////////////////
    }
    else
    {
      if (nVal == 0)
        continue;
    }
    ip[n++] = evalRec;
  }
  for (int i = 0; i < n; i++)
    pShow[i] = ip[i];
  nShow = n;
  delete [] ip;
}

void DoOpe(DataSet *pD, int& nShow, int*& pShow, Ope *pOpe)
{
  switch (pOpe->type)
  {
  case OP_SELECT:
    OpeSelect(pD, nShow, pShow, pOpe);
    break;
  case OP_SORTUP:
    OpeSort(nShow, pShow, TRUE, pOpe);
    break;
  case OP_SORTDOWN:
    OpeSort(nShow, pShow, FALSE, pOpe);
    break;
  }
}

void ClearOpe(DataSet *pD, int& nShow, int*& pShow)
{
  if (pShow)
    delete [] pShow;
  int num = pD->RecNum;
  nShow = num;
  pShow = new int[num];
  for (int i = 0; i < num; i++)
    pShow[i] = i;
}

void RenewSub(int numCode, Eva **pEva)
{
  int ret, num;
  static TableItem *pTab;
  static int nTab, maxTab;
  static int nCount;
  for (int i = 0; i < numCode; i++)
  {
    Eva *pp = pEva[i];
    switch (pp->type)
    {
    case ET_SETCHARSIZE:
      curPoint = pp->y;
      break;
    case ET_TEXT:
      offX = nCount*pp->dx;
      offY = nCount*pp->dy;
      AddAra(ET_TEXT, pp);
      if (pp->pHyper && curHyperAra && curHyperAra->pMom == pp)
      {
        RenewSub(pp->pHyper->numCode, pp->pHyper->pEva);
      }
      break;
    case ET_RECT:
      AddAra(ET_RECT, pp);
      break;
    case ET_ELLIPSE:
      AddAra(ET_ELLIPSE, pp);
      break;
    case ET_HYPER:
      AddAra(ET_HYPER, pp);
      break;
    case ET_CREATURE:
      offX = pp->x;
      offY = pp->y;
      bColAra = pp->bCol;
      colAra = pp->col;
      araOnClick = pp->pOnClick;
      RenewSub(pp->pCreature->numCode, pp->pCreature->pEva);
      offX = 0;
      offY = 0;
      bColAra = FALSE;
      colAra = RGB(0, 0, 0);
      araOnClick = NULL;
      break;
    case ET_CELL:
      AddAra(ET_CELL, pp);
      if (pp->pCell->topOpe)
      {
        ClearOpe(pp->pCell->pD, pp->pCell->nShow, pp->pCell->pShow);
        for (Ope *p = pp->pCell->topOpe; p; p = p->next)
        {
          DoOpe(pp->pCell->pD, pp->pCell->nShow, pp->pCell->pShow, p);
        }
      }
      if (pp->sub == ST_CROSS)
      {
        CrossItem *pC = pp->pCell->pCrossItem;
        int add = pC->bOnOff ? 0 : 1;
        if (pC->pX)
        {
          if (pC->pY)
          {
            pp->pCell->nW = PathWid(pC->pX) + add + 1;
            pp->pCell->nH = PathWid(pC->pY) + add + 1;
          }
          else
          {
            pp->pCell->nW = PathWid(pC->pX) + add;
            pp->pCell->nH = add + 1;
          }
        }
        else
        {
          if (pC->pY)
          {
            pp->pCell->nW = add + 1;
            pp->pCell->nH = PathWid(pC->pY) + add;
          }
          else
          {
            pp->pCell->nW = add;
            pp->pCell->nH = add;
          }
        }
      }
      break;
    case ET_ASSIGN:
      if (pp->sub == ST_PATH)
      {
        ret = Eval(&pp->expr);
        int type = PathType(pp->pPath);
        if (type == FT_DBL)
          SetPathDbl(pp->pPath, evalRec, dVal);
        else
          SetPathInt(pp->pPath, evalRec, nVal);
      }
      else if (!pp->pAssign->bOnce || !pp->pAssign->bAlready)
      {
        if (pp->sub == ST_STR)
        {
          if (pp->pAssign->pVar->str)
            delete [] pp->pAssign->pVar->str;
          pp->pAssign->pVar->str = new TCHAR[lstrlen(pp->pAssign->str) + 1];
          lstrcpy(pp->pAssign->pVar->str, pp->pAssign->str);
          if (pp->pAssign->bOnce)
            pp->pAssign->bAlready = TRUE;
        }
        else
        {
          ret = Eval(&pp->pAssign->expr);
          pp->pAssign->pVar->dVal = dVal;
          pp->pAssign->pVar->nVal = nVal;
          if (pp->pAssign->bOnce)
            pp->pAssign->bAlready = TRUE;
        }
      }
      break;
    case ET_IF:
      switch (pp->sub)
      {
      case ST_IF:
        ret = Eval(&pp->expr);
        if (ret != EX_DBL && nVal == 0 || ret == EX_DBL && dVal == 0)
          i += pp->skip;
        break;
      case ST_ELSE:
        i += pp->skip;
        break;
      }
      break;
    case ET_FOR:
      num = pp->pFor->pD->RecNum; ///////////////////
      if (pp->pFor->topOpe)
      {
        ClearOpe(pp->pFor->pD, pp->pFor->nShow, pp->pFor->pShow);
        for (Ope *p = pp->pFor->topOpe; p; p = p->next)
        {
          DoOpe(pp->pFor->pD, pp->pFor->nShow, pp->pFor->pShow, p);
        }
        num = pp->pFor->nShow;
      }
      curFor = pp->pFor;
      for (int i = 0; i < num; i++)
      {
        int at = pp->pFor->topOpe ? pp->pFor->pShow[i] : i;
        if (curFor)
          curFor->rec = at;
        else
          SetWindowText(hMainWnd, _T("no curFor")); //////////////////
        nCount = i;
        RenewSub(pp->pFor->numCode, pp->pFor->pEva);
      }
      curFor = NULL;
      break;
    default:
      SetWindowText(hMainWnd, _T("not define Eva"));
      break;
    }
  }
}

void Renew()
{
  numHyper = 0;
  ClearAra();
  for (DataSet *pD = pDataSetTop; pD; pD = pD->next)
  {
    if (pD->numCode > 0 && pD->pEva)
    {
      for (int i = 0; i < pD->RecNum; i++)
      {
        evalRec = i;
        RenewSub(pD->numCode, pD->pEva);
      }
    }
  }
  evalRec = -1;
  RenewSub(topSet->numCode, topSet->pEva);
  if (curSet != topSet)
    RenewSub(curSet->numCode, curSet->pEva);
}

int PixX(double x, BOOL bPrn)
{
  x *= measFactor;
  int res = bPrn ? prnXRes : crtXRes;
  switch (curMeasUnit)
  {
  case MU_CHAR:
    return int(x*curPoint*res/144);
  case MU_WCHAR:
    return int(x*curPoint*res/72);
  case MU_MM:
    return int(x*res/25.4);
  case MU_INCH:
    return int(x*res);
  }
  return 0;
}

int PixY(double y, BOOL bPrn, BOOL bAdd)
{
  y *= measFactor;
  topOffset = topSet->next ? int(defFontSize*crtYRes*5/72/2) : 0;
  int off = bAdd ? topOffset : 0;
  int res = bPrn ? prnYRes : crtYRes;
  int yy = 0;
  switch (curMeasUnit)
  {
  case MU_CHAR:
    yy = int(y*curPoint*res/72);
    break;
  case MU_WCHAR:
    yy = int(y*curPoint*res/72);
    break;
  case MU_MM:
    yy = int(y*res/25.4);
    break;
  case MU_INCH:
    yy = int(y*res);
    break;
  }
  if (!bPrn)
    yy += off;
  return yy;
}

int maxColor, spColor;
COLORREF *stackColor;  // •Ð‚¯‚͏I—¹Žž

void ClearColor()
{
  delete [] stackColor;
  stackColor = NULL;
  maxColor = spColor = 0;
}

void PushColor(COLORREF col)
{
  if (spColor >= maxColor)
  {
    maxColor += 32;
    COLORREF *p = new COLORREF[maxColor];
    for (int i = 0; i < spColor; i++)
      p[i] = stackColor[i];
    if (stackColor)
      delete [] stackColor;
    stackColor = p;
  }
  stackColor[spColor++] = col;
}

COLORREF PopColor()
{
  if (spColor  > 0)
  {
    return stackColor[--spColor];
  }
  return RGB(0, 0, 0);
}

int maxFont, spFont;
HFONT *stackFont;                // ‚²‚ݏW‚߂͏I—¹Žž

void ClearFont()
{
  for (int i = 0; i < spFont; i++)
  {
    if (stackFont[i])
      DeleteObject(stackFont[i]);
  }
  delete [] stackFont;
  stackFont = NULL;
  maxFont = spFont = 0;
}

void PushFont(HFONT hFont, HDC hDC)
{
  SelectObject(hDC, hFont);
  if (spFont < 0)
    spFont = 0;
  if (spFont >= maxFont)
  {
    maxFont += 32;
    HFONT *p = new HFONT[maxFont];
    for (int i = 0; i < spFont; i++)
      p[i] = stackFont[i];
    if (stackFont)
      delete [] stackFont;
    stackFont = p;
  }
  stackFont[spFont++] = hFont;
}

HFONT PopFont(HDC hDC)
{
  HFONT hFont = NULL;
  if (spFont > 0)
  {
    hFont = stackFont[--spFont];
    HFONT hOld = (HFONT)SelectObject(hDC, hFont);
    DeleteObject(hOld);
  }
  else
  {
    if (spFont == 0)
    {
      HFONT hOld = (HFONT)SelectObject(hDC, GetStockObject(SYSTEM_FONT));
      DeleteObject(hOld);
    }
    spFont--;
  }
  return hFont ? hFont : (HFONT)GetStockObject(SYSTEM_FONT);
}

TCHAR *ValToStr(double x, int dig, BOOL bAdd)
{
  static TCHAR buf[100];
  BOOL bNeg = x < 0;
  if (x < 0)
    x = -x;
  if (dig < 99)
  {
    double f = 1;
    for (int i = 0; i < dig; i++)
      f *= 10;
    x = floor(f*x + 0.5)/f;
  }
  if (bNeg)
    x = -x;
  _stprintf_s(buf, _T("%.5g"), x);
  if (bAdd)
  {
    TCHAR buf2[100];
    lstrcpy(buf2, buf);
    int len = lstrlen(buf2);
    TCHAR *p = _tcschr(buf2, '.');
    if (!p)
      p = buf2 + len;
    TCHAR *q = p - 1;
    int n;
    for (n = 0; q >= buf2; q--)
    {
      if (_istdigit(*q))
        n++;
      else
        break;
    }
    int m = n > 0 ? (n-1)/3 : 0;
    q = buf + m + len;
    n = 0;
    for (TCHAR *pp = buf2 + len; pp >= buf2 && q >= buf; pp--, q--)
    {
      *q = *pp;
      if (pp < p && _istdigit(*pp))
      {
        if (++n > 2 && m-- > 0)
        {
          n = 0;
          if (q > buf)
            *--q = ',';
        }
      }
    }
  }
  return buf;
}

void OnPaintText(Ara *p, int& xx, int& yy, COLORREF& color, int& textY, int& dy, HDC hDC, HFONT& hFont, BOOL bPrn)
{
  Eva *pp = p->pMom;
  double size = pp->size == 0 ? 10 : pp->size;
  HFONT hFont2 = GetBodyFont(bPrn, size, pp->pHyper != NULL);
  HFONT hOld = NULL;
  if (hFont2)
    hOld = (HFONT)SelectObject(hDC, hFont2);
  if (pp->sub == ST_FIRST || pp->sub == ST_FOLLOW || pp->sub == ST_VAR1 || pp->sub == ST_VAR2 || pp->sub == ST_TIME || pp->sub == ST_PATH1 || pp->sub == ST_PATH2)
  {
    color = pp->col;
    SIZE sz;
    int y;
    TCHAR buf[100], *str2;
    if (pp->sub == ST_VAR1 || pp->sub == ST_VAR2)
    {
      str2 = buf;
      if (pp->pVar->type == VT_INT)
        wsprintf(buf, _T("%d"), pp->pVar->nVal);
      else if (pp->pVar->type == VT_DBL)
        lstrcpy(buf, ValToStr(pp->pVar->dVal, pp->dig, TRUE));
      else if (pp->pVar->type == VT_STR)
        str2 = pp->pVar->str ? pp->pVar->str : _T("xx");
      else
        wsprintf(buf, _T("b???"));
    }
    else if (pp->sub == ST_TIME)
    {
  //    SetWindowText(hMainWnd, _T("hhh")); ///////////////
      xx = PixX(pp->x + p->x, bPrn);
      yy = PixY(pp->y + p->y, bPrn, TRUE);
      wsprintf(buf, _T("%d"), p->pOnClick ? p->pOnClick->time : 999);
    }
    if (pp->sub == ST_PATH1 || pp->sub == ST_PATH2)
    {
      str2 = GetPathAsStr(pp->pPath, p->rec);
    }
    TCHAR *str = pp->sub == ST_FIRST || pp->sub == ST_FOLLOW ? pp->text : str2;
    if (pp->sub == ST_TIME)
      str = buf;
    GetTextExtentPoint32(hDC, str, lstrlen(str), &sz);
    if (pp->sub == ST_FIRST || pp->sub == ST_VAR1 || pp->sub == ST_TIME || pp->sub == ST_PATH1)
    {
      xx = PixX(pp->x + p->x, bPrn);// - sz.cx/2;   ///////////////////////////////////////////////
      yy = PixY(pp->y + p->y, bPrn, TRUE);
      y = yy;
      textY = ST_TOP;
      dy = sz.cy;
    }
    SetTextColor(hDC, color);
    if (pp->sub == ST_FOLLOW || pp->sub == ST_VAR2 || pp->sub == ST_PATH2)
    {
      y = yy;
      switch (textY)
      {
      case ST_TOP:
        // do nothing
        break;
      case ST_CENTER:
        y -= (sz.cy - dy) / 2;
        break;
      case ST_BOTTOM:
        y -= (sz.cy - dy);
        break;
      default:
        break;
      }
    }
    if (pp->pHyper)
    {
      pp->pHyper->x1 = xx;
      pp->pHyper->y1 = y;
      pp->pHyper->x2 = xx + sz.cx;
      pp->pHyper->y2 = y + sz.cy;
    }

    if (pp->pCreature && pp->pCreature->bCol)
      SetTextColor(hDC, pp->pCreature->col);
    if (p->bCol)
      SetTextColor(hDC, p->col);
    if (pp->sub ==ST_TIME)
      SetTextColor(hDC, RGB(0, 0, 0)); ///////////
    TextOut(hDC, xx, y, str, lstrlen(str)); 
    if (pp->topRuby)
    {
   //   ClearBD(); ///////////////////////////
      double size = pp->size*0.5;
      HFONT hFont = GetBodyFont(bPrn, size, FALSE);
      HFONT hOld = (HFONT)SelectObject(hDC, hFont);
      for (Ruby *p = pp->topRuby; p; p = p->next)
      {
  //      AddBD(p->ruby); /////////////////////////
        SelectObject(hDC, hOld);
        SIZE sz;
        GetTextExtentPoint32(hDC, pp->text, p->parent - pp->text, &sz);
        int x1 = xx + sz.cx;
        GetTextExtentPoint32(hDC, p->parent, p->len, &sz);
        x1 += sz.cx/2;    // parent center
        SelectObject(hDC, hFont);
        GetTextExtentPoint32(hDC, p->ruby, lstrlen(p->ruby), &sz);
        x1 -= sz.cx/2;    // ruby left
        int y1 = y - sz.cy*3/2;
        TextOut(hDC, x1, y1, p->ruby, lstrlen(p->ruby));
      }
      DeleteObject(SelectObject(hDC, hOld));
  //    ShowBD(); /////////////
    }
    xx += sz.cx;
  }
  else
  {
    if (pp->sub == ST_COLORIN)
    {
      PushColor(color);
      color = pp->col;
    }
    else if (pp->sub == ST_COLOROUT)
    {
      color = PopColor();
    }
    else if (pp->sub == ST_FONTIN)
    {
      PushFont(hFont, hDC);
      FontData *p = pp->pFontData;
      hFont = GetFont(p->name, p->deciPt*0.1, p->bBold, p->bItalic, p->bUnder, bPrn);
      SelectObject(hDC, hFont);
    }
    else if (pp->sub == ST_FONTOUT)
    {
      hFont = PopFont(hDC);
      SelectObject(hDC, hFont);
    }
    else if (pp->sub == ST_TOP || pp->sub == ST_CENTER || pp->sub == ST_BOTTOM)
    {
      textY = pp->sub;
    }
  }
  if (hFont2)
  {
    SelectObject(hDC, hOld);
    DeleteObject(hFont2);
  }
}

int CellWid(Cell *pCell)
{
  if (pCell->type == ST_TABLE && pCell->pD)
  {
    if (pCell->nItem > 0)
      return pCell->nItem;
    int num = pCell->pD->num + 1;
    for (int i = pCell->pD->num - 1; i >= 0; i--)
    {
      if (pCell->pD->item[i].bTemp)
        num--;
      else
        break;
    }
    return num;
  }
  else
    return pCell->nW;
}

int CellLen(Cell *pCell)
{
  if (pCell->type == ST_TABLE && pCell->pD)
  {
    if (pCell->pShow)
      return pCell->nShow + 1;
    return pCell->pD->RecNum + 1;
  }
  else
    return pCell->nH;
}

TCHAR *CellStrTable(Cell *pCell, int x, int y, TCHAR *buf)
{
  DataSet *pD = pCell->pD;
  TCHAR *str = _T("");
  int rec = y - 1;
  if (pCell->pShow && rec < pCell->nShow)
    rec = pCell->pShow[rec];
  if (pCell->pTableItem && pCell->nItem > 0 && x < pCell->nItem)
  {
    TableItem *pTab = &pCell->pTableItem[x];
    if (pTab->type == TI_PATH)
    {
      if (y > 0)
        str = GetPathAsStr(pTab->pPath, rec);
      else
        str = GetPathName(pTab->pPath);
    }
    else if (pTab->type == TI_EXPR)
    {
      if (y == 0)
      {
        str = pTab->str ? pTab->str : _T("ŒvŽZŽ®");
      }
      else
      {
        evalRec = rec;
        int ret = Eval(&pTab->expr);
        if (ret == EX_DBL)
        {
          lstrcpy(buf, ValToStr(dVal, 3, FALSE));
          str = buf;
        }
        else
        {
          wsprintf(buf, _T("%d"), nVal);
          str = buf;
        }
      }
    }
    else if (pTab->type == TI_NUM)
    {
      if (y > 0)
      {
        wsprintf(buf, _T("%d"), y);
        str = buf;
      }
      else if (pD->type == TK_DATA)
        str = _T("(new)");
      else
        str = _T("");
    }
    else if (pTab->type == TI_RANK)
    {
      if (y > 0)
      {
        wsprintf(buf, _T("%d"), y);
        str = buf;
      }
      else
        str = _T("");
    }
    else
      str = _T("c???"); ////////////////
    return str;
  }
  if (x > 0 && y > 0)
  {
    str = GetDataAsStr(pD, rec, x-1, FALSE);
  }
  else if (x > 0)
  {
    str = pD->item[x - 1].name;
  }
  else if (y > 0)
  {
    wsprintf(buf, _T("%d"), y);
    str = buf;
  }
  else
  {
    if (pD->type == TK_DATA)
      str = _T("(new)");
    else
      str = _T("");
  }
  return str;
}

TCHAR *GetTermData(Path *p, int rec)
{
  DataSet *pD = p->pD;
  for (int i = 0; i < p->len - 1; i++)
  {
    pD = pD->item[p->ip[i]].link;
  }
  int pos = p->ip[p->len - 1];
  int type = pD->item[pos].type;
  if (type == FT_LINK)
  {
    pD = pD->item[pos].link;
    pos = pD->posName < 0 ? 0 : pD->posName;
  }
  return GetDataAsStr(pD, rec, pos, FALSE);
}

int GetTermRec(Path *p, int rec)
{
  DataSet *pD = p->pD;
  for (int i = 0; i < p->len - 1; i++)
  {
    int id = GetDataInt(pD, rec, p->ip[i]);
    pD = pD->item[p->ip[i]].link;
    rec = FindIDData(pD, id);
  }
  int pos = p->ip[p->len - 1];
  int type = pD->item[pos].type;
  if (type == FT_LINK)
  {
    int id = GetDataInt(pD, rec, pos);
    pD = pD->item[pos].link;
    rec = FindIDData(pD, id);
  }
  return rec;
}

TCHAR *SumStr(DataSet *pD, CrossItem *pC, int x, int y)
{
  static TCHAR buf[100];
  Path *pX = pC->pX;
  Path *pY = pC->pY;
  Path *pV = pC->pV;
  int nx = PathWid(pX);
  int ny = PathWid(pY);
  int n = 0;
  BOOL bDbl = FALSE;
  double dx = 0;
  int xx = 0;
  int type = pV ? GetPathType(pV) : FT_INT;
  for (int i = 0; i < pD->RecNum; i++)
  {
    if (x < nx)
    {
      int rec = GetTermRec(pX, i);
      if (rec != x)
        continue;
    }
    if (y < ny)
    {
      int rec = GetTermRec(pY, i);
      if (rec != y)
        continue;
    }
    if (pV)
    {
      int rec = GetTermRec(pV, i);
      if (type == FT_DBL)
      {
        dx += GetPathDbl(pV, rec);
      }
      else
      {
        xx += GetPathInt(pV, rec);
      }
    }
    else
      xx += 1;
    n++;
  }
  if (n == 0)
    return _T("");
  if (pC->bOnOff)
    return xx > 0 ? _T("›") : _T("");
  if (type == FT_DBL)
    wsprintf(buf, _T("%s"), ValToStr(dx, 0, FALSE));
  else
    wsprintf(buf, _T("%d"), xx);
  return buf;
}

TCHAR *CellStrCross(Cell *pCell, int x, int y)
{
  Path *pX = pCell->pCrossItem->pX;
  Path *pY = pCell->pCrossItem->pY;
  Path *pV = pCell->pCrossItem->pV;
  if (pX && pY)
  {
    if (x > 0)
    {
      if (y > 0)
      {
       return SumStr(pCell->pD, pCell->pCrossItem, x - 1, y - 1);
      }
      else
      {
        int at = x - 1;
        if (at < PathWid(pX))
          return GetTermData(pX, at);
        else
          return _T("‡Œv");
      }
    }
    else
    {
      if (y > 0)
      {
        int at = y - 1;
        if (at < PathWid(pY))
          return GetTermData(pY, at);
        else
          return _T("‡Œv");
      }
      else
      {
        return _T("");
      }
    }
  }
  if (pX)
  {
    if (x > 0 && y == 0)
    {
      int at = x - 1;
      if (at < PathWid(pX))
        return GetTermData(pX, at);
      else
        return _T("‡Œv");
    }
    return _T("ww"); 
  }
  return _T("oo");
}

double PathTermWid(Path *p)
{
  DataSet *pD = p->pD;
  for (int i = 0; i < p->len - 1; i++)
    pD = pD->item[p->ip[i]].link;
  return pD->item[p->ip[p->len - 1]].wid;
}

double CellWidOne(Cell *pCell, int x)
{
  double dx = defCellWid;
  if (pCell->type == ST_TABLE)
  {
    if (pCell->pTableItem && pCell->nItem > 0 && x < pCell->nItem)
    {
      TableItem *pTab = &pCell->pTableItem[x];
      if (pTab->wid != 0)
      {
        dx = pTab->wid;
      }
      else if (pTab->type == TI_PATH)
      {
        double wid = PathTermWid(pTab->pPath);
        if (wid != 0)
          dx = wid;
      }
      else if (pTab->type == TI_NUM || pTab->type == TI_RANK)
      {
        dx = -12;
      }
    }
    else
    {
      if (x > 0 && x-1 < pCell->pD->num)
      {
        double wid = pCell->pD->item[x-1].wid;
        if (wid != 0)
          dx = wid;
      }
      else if (x == 0)
        dx = -12;
    }
  }
  if (pCell->type == ST_CROSS)
  {
    if (x == 0)
    {
      if (pCell->pCrossItem->pY)
      {
        double wid = PathTermWid(pCell->pCrossItem->pY);
        if (wid != 0)
          return wid;
      }
    }
    else
    {
      if (pCell->pCrossItem->pX)
      {
        double wid = PathTermWid(pCell->pCrossItem->pX);
        if (wid != 0)
          return wid;
      }
    }
  }
  if (x == 0 && pCell->w1 != 0)
    return pCell->w1;
  if (x == pCell->nW-1 && pCell->w3 != 0)
    return pCell->w3;
  if (x > 0 && x < pCell->nW-1 && pCell->w2 != 0)
    return pCell->w2;
  if (x > 0 && x < pCell->nWid)
    dx = pCell->pWid[x];
  return dx;
}

void OnPaintCell(Ara *p, HDC hDC, BOOL bPrn)
{
  SetTextAlign(hDC, TA_LEFT);  // ‚¨‚Ü‚¶‚È‚¢
  double dx = defCellWid;
  int res = bPrn ? prnYRes : crtYRes;
  double dy = curMeasUnit == MU_CHAR ? 1.5 : int(curPoint*1.5*25.4/72); // text heigth * 1.5 : ƒCƒ“ƒ`‚Ȃǂ͑Ήž‚µ‚Ä‚¢‚È‚¢ !!!
  Cell *pCell = p->pMom->pCell;
  int type = pCell->type;
  DataSet *pD = pCell->pD;
  int x = PixX(p->pMom->x, bPrn);
  int y = PixY(p->pMom->y, bPrn, TRUE);
  int h = PixY(dy, bPrn, FALSE);
  int dd = h/4;                // text offset from left-top
  int nx = CellWid(pCell);
  int ny = CellLen(pCell);
  int nw = 0;
  for (int i = 0; i < nx; i++)
  {
    nw += PixX(fabs(CellWidOne(pCell, i)), bPrn);
  }
  for (int i = 0; i <= ny; i++)
  {
    MoveToEx(hDC, x, y + h*i, NULL);
    LineTo(hDC, x + nw, y + h*i);
  }
  int atW = 0;
  for (int i = 0; i <= nx; i++)
  {
    if (i > 0)
    {
      atW += PixX(fabs(CellWidOne(pCell, i-1)), bPrn);
    }
    MoveToEx(hDC, x + atW, y, NULL);
    LineTo(hDC, x + atW, y + h*ny);
  }

  RECT rc;
  int left = x;
  for (int i = 0; i < nx; i++)
  {
    rc.left = left + dd;
    rc.top = 0;
    double ww = CellWidOne(pCell, i);
    int align = ww < 0 ? TA_RIGHT : TA_LEFT;
    if (ww < 0)
      ww = -ww;
    for (int j = 0; j < ny; j++)
    {
      int at = j*nx + i;
      rc.right = rc.left + PixX(ww, bPrn)- dd*2;
      rc.top = y + h*j + dd;
      rc.bottom = rc.top + h;
      TCHAR *str = NULL;
      TCHAR buf[100];
      if (at < pCell->nItem && pCell->pCellItem)
      {
        CellItem *pp = pCell->pCellItem[at];
        if (pp->type == ITEM_STR)
        {
          str = pp->text;
        }
        else if (pp->type == ITEM_VAR)
        {
          switch (pp->pVar->type)
          {
          case VT_INT:
            wsprintf(buf, _T("%d"), pp->pVar->nVal);
            str = buf;
            break;
          case VT_DBL:
            _stprintf_s(buf, _T("%.15g"), pp->pVar->dVal);
            str = buf;
            break;
          case VT_STR:
            str = pp->pVar->str ? pp->pVar->str : _T("xx");
            break;
          default:
            str = _T("a???");
            break;
          }
        }
        else if (pp->type == ITEM_EXPR)
        {
          int ret = Eval(&pp->expr);
          switch (ret)
          {
          case EX_DBL:
            lstrcpy(buf, ValToStr(dVal, pp->dig, TRUE));
            break;
          case EX_INT:
            wsprintf(buf, _T("%d"), nVal);
            break;
          default:
            lstrcpy(buf, _T("d???"));
            break;
          }
          str = buf;
        }
        else
          str = _T("??");
      }
      else if (type == ST_TABLE)
      {
        str = CellStrTable(pCell, i, j, buf);
      }
      else if (type == ST_CROSS)
      {
        str = CellStrCross(pCell, i, j);
        if (i > 0)
          align = TA_RIGHT;
      }
      else
      {
        str = _T("");
      }
      if (str)
      {
        DrawText(hDC, str, -1, &rc, DT_SINGLELINE | DT_END_ELLIPSIS | align | DT_TOP);
      }
    }
    left += PixX(ww, bPrn);
  }
}

void OnPaintRect(Ara *p, HDC hDC, BOOL bPrn)
{
  COLORREF col = RGB(0, 0, 255);
  if (p->pMom->bCol)
    col = p->pMom->col;
  if (p->bCol)
    col = p->col;
  RECT rc;
  rc.left = PixX(p->pMom->x1 + p->x, bPrn);
  rc.top = PixY(p->pMom->y1 + p->y, bPrn, TRUE);
  rc.right = PixX(p->pMom->x2 + p->x, bPrn);
  rc.bottom = PixY(p->pMom->y2 + p->y, bPrn, TRUE);
  HBRUSH hBr = CreateSolidBrush(col);
  FillRect(hDC, &rc, hBr);
  DeleteObject(SelectObject(hDC, GetStockObject(WHITE_BRUSH)));
}

void OnPaintEllipse(Ara *p, HDC hDC, BOOL bPrn)
{
  COLORREF col = RGB(0, 0, 255);
  if (p->pMom->bCol)
    col = p->pMom->col;
  if (p->bCol)
    col = p->col;
  HBRUSH hBr = CreateSolidBrush(col);
  HBRUSH hOld = (HBRUSH)SelectObject(hDC, hBr);
  SelectObject(hDC, GetStockObject(NULL_PEN));
  int x1 = PixX(p->pMom->x1 + p->x, bPrn);
  int y1 = PixY(p->pMom->y1 + p->y, bPrn, TRUE);
  int x2 = PixX(p->pMom->x2 + p->x, bPrn);
  int y2 = PixY(p->pMom->y2 + p->y, bPrn, TRUE);
  Ellipse(hDC, x1, y1, x2, y2);
  DeleteObject(SelectObject(hDC, hOld));
  SelectObject(hDC, GetStockObject(BLACK_PEN));
}

void OnPaintExt(HDC hDC)
{
  for (Mic *p = micTop; p; p = p->next)
  {
    if (rcp.left > p->x + 100 || rcp.right < p->x - 100 || rcp.top > p->y + 100 || rcp.bottom < p->y - 100)
      continue;
    int xx = p->x - 75;
    int yy = p->y - 75;
    for (Parts *q = p->pParts; q; q = q->next)
    {
      int x = q->x1/3 + xx;
      int y = q->y1/3 + yy;
      HPEN hPen = GetPen(q->wid, q->col);
      HPEN hOld = (HPEN)SelectObject(hDC, hPen);
      MoveToEx(hDC, x, y, NULL);
      x = q->x2/3 + xx;
      y = q->y2/3 + yy;
      LineTo(hDC, x, y);
      SelectObject(hDC, hOld);
    }
  }
}

HPEN GetPen(int wid, COLORREF col)
{
  static HPEN hPen;
  static int w;
  static COLORREF c;
  if (!hPen || w != wid || c != col)
  {
    if (hPen)
      DeleteObject(hPen);
    hPen = CreatePen(PS_SOLID, wid, col);
    w = wid;
    c = col;
  }
  return hPen;
}

void OnPaintExt2(HDC hDC)
{
  int x = 100, y = 100;
  Rectangle(hDC, x, y, x + 450, y + 450);
  x = 600;
  Rectangle(hDC, x, y, x + 150, y + 150);
  x = 800;
  int xAt = partsAt / 3;
  int yAt = partsAt % 3;
  for (int i = 0; i < 3; i++)
  {
    for (int j = 0; j < 3; j++)
    {
      int xx = x + i*160;
      int yy = y + j*160;
      if (i == xAt && j == yAt)
        SelectObject(hDC, hPenRed);
      Rectangle(hDC, xx, yy, xx + 150, yy + 150);
      SelectObject(hDC, GetStockObject(BLACK_PEN));
    }
  }
  for (Parts *p = partsTop; p; p = p->next)
  {
    HPEN hPen = GetPen(p->wid*3, p->col);
    HPEN hOld = (HPEN)SelectObject(hDC, hPen);
    MoveToEx(hDC, p->x1 + 100, p->y1 + 100, NULL);
    LineTo(hDC, p->x2 + 100, p->y2 + 100);
    SelectObject(hDC, hOld);
  }
  for (Parts *p = partsTop; p; p = p->next)
  {
    HPEN hPen = GetPen(p->wid, p->col);
    HPEN hOld = (HPEN)SelectObject(hDC, hPen);
    MoveToEx(hDC, p->x1/3 + 600, p->y1/3 + 100, NULL);
    LineTo(hDC, p->x2/3 + 600, p->y2/3 + 100);
    SelectObject(hDC, hOld);
  }
  for (int i = 0; i < partsNum; i++)
  {
   int xx = 800 + (i/3)*160;
    int yy = 100 + (i%3)*160;
    for (Parts *p = partsArr[i]; p; p = p->next)
    {
      HPEN hPen = GetPen(p->wid, p->col);
      HPEN hOld = (HPEN)SelectObject(hDC, hPen);
      MoveToEx(hDC, p->x1/3 + xx, p->y1/3 + yy, NULL);
      LineTo(hDC, p->x2/3 + xx, p->y2/3 + yy);
      SelectObject(hDC, hOld);
    }
  }
}

void OnPaintHyper(Ara *p, HDC hDC)
{
  TCHAR *str = p->pMom->text;
  int x1 = (int)p->pMom->x1;
  int y1 = (int)p->pMom->y1;
  HFONT hFont = GetHyperFont();
  HFONT hOld = (HFONT)SelectObject(hDC, hFont);
  TextOut(hDC, x1, y1, str, _tcslen(str));
  DeleteObject(SelectObject(hDC, hOld));
}

BOOL CheckHyper(int x, int y)
{
  for (int i = 0; i < numAra; i++)
  {
    Ara *p = &pAra[i];
    if (p->type == ET_HYPER)
    {
      Eva *pp = p->pMom;
      if (pp->x1 < x && pp->y1 < y && x < pp->x2 && y < pp->y2)
      {
        SetCursor(LoadCursor(NULL, IDC_HAND));
        strHyper = pp->text;
  //      SetWindowText(hMainWnd, pp->text); //////////////////
        return TRUE;
      }
    }
  }
  SetCursor(LoadCursor(NULL, IDC_ARROW));
  strHyper = NULL;
 // SetWindowText(hMainWnd, _T("out")); ////////////////////
  return FALSE;
}

void OnPaint(HWND hWnd, HDC hDC, BOOL bPrn)
{
  if (curSet->bBack)
  {
    RECT rc;
    GetClientRect(hWnd, &rc);
    HBRUSH hBr = CreateSolidBrush(curSet->colBack);
    FillRect(hDC, &rc, hBr);
    DeleteObject(hBr);
  }
  SetTextColor(hDC, RGB(0, 0, 0));
  SetBkMode(hDC, TRANSPARENT);
  HFONT hFont = GetDefFont(bPrn);
  PushFont(hFont, hDC);
  PushColor(RGB(0, 0, 0));
  int xx, yy;
  COLORREF color = RGB(0, 0, 0);
  int textY = ST_TOP;
  int dy = 10;

  for (int i = 0; i < numAra; i++)
  {
    Ara *p = &pAra[i];
    if (p->type == ET_TEXT)
    {
      OnPaintText(p, xx, yy, color, textY, dy, hDC, hFont, bPrn);
    }
    else if (p->type == ET_CELL)
    {
      OnPaintCell(p, hDC, bPrn);
    }
    else if (p->type == ET_RECT)
    {
      OnPaintRect(p, hDC, bPrn);
    }
    else if (p->type == ET_ELLIPSE)
    {
      OnPaintEllipse(p, hDC, bPrn);
    }
    else if (p->type == ET_HYPER)
    {
      OnPaintHyper(p, hDC);
    }
  }
  PopFont(hDC);
  PopColor();

  if (!bPrn && topSet->next != NULL)
  {
    RECT rc;
    GetClientRect(hWnd, &rc);
    int ww = rc.right;
    int hh = int(defFontSize*crtYRes/72);
    int cw = hh/2;
    rc.bottom = hh*5/2;
    int y0 = rc.bottom;
    FillRect(hDC, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH));

    hFont = GetDefFont(bPrn, TRUE);
    PushFont(hFont, hDC);
    int w = 0;
    for (Set *p = topSet->next; p; p = p->next)
    {
      SIZE sz;
      TCHAR *name = p->name;
      GetTextExtentPoint32(hDC, name, lstrlen(name), &sz);
      if (p == curSet)
        SetTextColor(hDC, RGB(0, 0, 255));
      else
        SetTextColor(hDC, RGB(0, 0, 0));
      TextOut(hDC, w + hh, hh, p->name, lstrlen(p->name));
      if (p->x1 == 0 && p->x2 == 0)
      {
        p->x1 = w + hh;
        p->y1 = hh;
        p->x2 = p->x1 + sz.cx;
        p->y2 = p->y1 + sz.cy;
      }
      MoveToEx(hDC, w, y0, NULL);
      LineTo(hDC, w + cw, hh/2);
      LineTo(hDC, w + cw*3 + sz.cx, hh/2);
      LineTo(hDC, w + hh*2 + sz.cx, y0);
      if (p != curSet)
      {
        MoveToEx(hDC, w, y0, NULL);
        LineTo(hDC, w + hh*2 + sz.cx, y0);
      }
      w += hh*2 + sz.cx;
    }
    MoveToEx(hDC, w, y0, NULL);
    LineTo(hDC, rc.right, y0);
    PopFont(hDC);
  }
}

void ClearSet(Set *p)
{
  for (int i = 0; i < p->numCode; i++)
  {
    Eva *pp = p->pEva[i];
    switch (pp->type)
    {
    case ET_TEXT:
      if (pp->text)
        delete [] pp->text;
      break;
    }
    delete pp;
  }
  p->numCode = 0;

  Variable *q;
  for (Variable *pp = p->varTop; pp; pp =q)
  {
    q = pp->next;
    if (pp->str)
      delete [] pp->str;
    delete pp;
  }
  p->varTop = NULL;
}

Eva *NewEva(int type, int sub)
{
  Eva *p = new Eva;
  ZeroMemory(p, sizeof(Eva));
  p->type = type;
  p->sub = sub;
  return p;
}

Cell *curCell;

void AddEva(Eva *p)
{
  if (curData)
  {
    if (curData->numCode >= curData->maxCode)
    {
      curData->maxCode += 32;
      PEVA *pp = new PEVA[curData->maxCode];
      for (int i = 0; i < curData->numCode; i++)
        pp[i] = curData->pEva[i];
      if (curData->pEva)
        delete [] curData->pEva;
      curData->pEva = pp;
    }
    curData->pEva[curData->numCode] = p;
    codePos = curData->numCode;
    curData->numCode++;
  }
  else if (curCreature)
  {
    if (curCreature->numCode >= curCreature->maxCode)
    {
      curCreature->maxCode += 32;
      PEVA *pp = new PEVA[curCreature->maxCode];
      for (int i = 0; i < curCreature->numCode; i++)
        pp[i] = curCreature->pEva[i];
      if (curCreature->pEva)
        delete [] curCreature->pEva;
      curCreature->pEva = pp;
    }
    curCreature->pEva[curCreature->numCode] = p;
    codePos = curCreature->numCode;
    curCreature->numCode++;
  }
  else if (curFor)
  {
    if (curFor->numCode >= curFor->maxCode)
    {
      curFor->maxCode += 32;
      PEVA *pp = new PEVA[curFor->maxCode];
      for (int i = 0; i < curFor->numCode; i++)
        pp[i] = curFor->pEva[i];
      if (curFor->pEva)
        delete [] curFor->pEva;
      curFor->pEva = pp;
    }
    curFor->pEva[curFor->numCode] = p;
    codePos = curFor->numCode;
    curFor->numCode++;
  }
  else if (curHyper)
  {
    if (curHyper->numCode >= curHyper->maxCode)
    {
      curHyper->maxCode += 32;
      PEVA *pp = new PEVA[curHyper->maxCode];
      for (int i = 0; i < curHyper->numCode; i++)
        pp[i] = curHyper->pEva[i];
      if (curHyper->pEva)
        delete [] curHyper->pEva;
      curHyper->pEva = pp;
    }
    curHyper->pEva[curHyper->numCode] = p;
    codePos = curHyper->numCode;
    curHyper->numCode++;
  }
  else
  {
    Set *pSet = curSet;
    if (pSet->numCode >= pSet->maxCode)
    {
      pSet->maxCode += 32;
      PEVA *pp = new PEVA[pSet->maxCode];
      for (int i = 0; i < pSet->numCode; i++)
        pp[i] = pSet->pEva[i];
      if (pSet->pEva)
        delete [] pSet->pEva;
      pSet->pEva = pp;
    }
    pSet->pEva[pSet->numCode] = p;
    codePos = pSet->numCode;
    pSet->numCode++;
  }
}

BOOL GetColor(COLORREF& col)
{
  if (CheckToken(TK_INT))
    return TRUE;
  int r = nVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  int g = nVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  int b = nVal;
  col = RGB(r, g, b);
  if (CheckToken(TK_OUT))
    return TRUE;
  return FALSE;
}

BOOL GetFontData(TCHAR *name, int& deciPt, BOOL& bBold, BOOL& bItalic, BOOL& bUnder)
{
  if (CheckToken(TK_STR))
    return TRUE;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  deciPt = nVal;
  bBold = bItalic = bUnder = FALSE;
  while (1)
  {
    int token = GetToken();
    if (token == TK_OUT)
      break;
    if (token != TK_COMMA)
    {
      wsprintf(strErrMsg, _T("bad token %s in GetFontData"), TokenStr(token));
      return TRUE;
    }
    token = GetToken();
    switch (token)
    {
    case TK_BOLD:
      bBold = TRUE;
      break;
    case TK_ITALIC:
      bItalic = TRUE;
      break;
    case TK_UNDER:
      bUnder = TRUE;
      break;
    default:
      wsprintf(strErrMsg, _T("bad token %s in GetFontData 2"), TokenStr(token));
      return TRUE;
    }
  }
  return FALSE;
}

BOOL GetText2()
{
  if (CheckNum())
    return TRUE;
  double x = dVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckNum())
    return TRUE;
  double y = dVal;
  if (CheckToken(TK_ENDNAME))
    return TRUE;
  if (CheckToken(TK_IN))
    return TRUE;
  COLORREF col = RGB(0, 0, 0);
  BOOL bTop = TRUE;
  BOOL bOnClick = FALSE;
  Eva *p = NULL;
  int num = 0;
  Eva *arr[100];
  while (1)
  {
    int token = GetToken();
    if (token == TK_OUT)
      break;
    if (token == TK_STR)
    {
      TCHAR *str = new TCHAR[lstrlen(strIn) + 1];
      lstrcpy(str, strIn);
      COLORREF color = col;
      BOOL bCol = FALSE;
      int tk = GetToken();
      if (tk == TK_COLON)
      {
        int t = GetToken();
        if (t == TK_COLORVAL)
        {
          bCol = TRUE;
          color = colValue;
        }
        else
        {
          wsprintf(strErrMsg, _T("bad token %s in GetText2 ff"), TokenStr(t));
          return TRUE;
        }
      }
      else
        UngetToken(tk);
      p = NewEva(ET_TEXT, bTop ? ST_FIRST : ST_FOLLOW);
      arr[num++] = p;
      p->x = x;
      p->y = y;
      p->text= str;
      p->bCol = bCol;
      p->col = color;
      bTop = FALSE;
      AddEva(p);
    }
    else if (token == TK_BEGINNAME)
    {
      if (CheckToken(TK_STR))
        return TRUE;
      Path *pp = FindPath(curFor->pD, strIn);
      if (CheckToken(TK_ENDNAME))
        return TRUE;
      COLORREF color = col;
      BOOL bCol = FALSE;
      int tk = GetToken();
      if (tk == TK_COLON)
      {
        int t = GetToken();
        if (t == TK_COLORVAL)
        {
          bCol = TRUE;
          color = colValue;
        }
        else
        {
          wsprintf(strErrMsg, _T("bad token %s in GetText2 ff"), TokenStr(t));
          return TRUE;
        }
      }
      else
        UngetToken(tk);
      p = NewEva(ET_TEXT, bTop ? ST_PATH1 : ST_PATH2);
      arr[num++] = p;
      p->x = x;
      p->y = y;
      p->text = _T("xxx"); ///////////////////
      p->pPath = pp;
      p->bCol = bCol;
      p->col = color;
      bTop = FALSE;
      AddEva(p);
    }
    
    token = GetToken();
    if (token != TK_COMMA && token != TK_OUT)
    {
      wsprintf(strErrMsg, _T("token %s is not , or )"), TokenStr(token));
      return TRUE;
    }
    if (token == TK_OUT)
      UngetToken(token);
  }

  double size = 10;
  while (1)
  {
    int token = GetToken();
    if (token == TK_COLON)
    {
      int tk = GetToken();
      if (tk == TK_SIZE)
      {
        if (CheckToken(TK_IN))
          return TRUE;
        if (CheckNum())
          return TRUE;
        for (int i = 0; i < num; i++)
        {
          Eva *p = arr[i];
          p->size = size = dVal;
        }
        if (CheckToken(TK_OUT))
          return TRUE;
      }
      else if (tk == TK_COLORVAL)
      {
        for (int i = 0; i < num; i++)
        {
          Eva *p = arr[i];
          if (!p->bCol)
          {
            p->bCol = TRUE;
            p->col = colValue;
          }
        }
      }
      else if (tk == TK_STEP)
      {
        if (CheckToken(TK_IN))
          return TRUE;
        if (CheckNum())
          return TRUE;
        double x = dVal;
        if (CheckToken(TK_COMMA))
          return TRUE;
        if (CheckNum())
          return TRUE;
        double y = dVal;
        if (CheckToken(TK_OUT))
          return TRUE;
        for (int i = 0; i < num; i++)
        {
          Eva *p = arr[i];
          p->dx = x;
          p->dy = y;
        }
      }
      else if (tk == TK_ONCLICK)
      {
        p->bOnClick =TRUE;
        bOnClick = TRUE;
      }
      else
      {
        wsprintf(strErrMsg, _T("bad token %s in GetText2 aa"), TokenStr(tk));
        return TRUE;
      }
    }
    else
    {
      UngetToken(token);
      break;
    }
  }

  for (int ii = 0; ii < num; ii++)
  {
    Eva *p = arr[ii];
    TCHAR *p1 = NULL, *p2 = NULL, *p3 = NULL;
    for (TCHAR *q = p->text; *q; q = CharNext(q))
    {
      if (*q == '{')
      {
        p1 = q++;
        for (TCHAR *q2 = q; *q2; q2 = CharNext(q2))
        {
          if (*q2 == '^')
          {
            q = p2 = q2++;
            for (TCHAR *q3 = q2; *q3; q3 = CharNext(q3))
            {
              if (*q3 == '}')
              {
                q = p3 = q3++;
                Ruby *pp = new Ruby;
                ZeroMemory(pp, sizeof(Ruby));
                pp->parent = p1;
                pp->len = p2 - p1 - 1;
                int len = p3 - p2 - 1;
                pp->ruby = new TCHAR[len + 1];
                for (int i = 0; i < len; i++)
                  pp->ruby[i] = p2[i+1];
                pp->ruby[len] = 0;
                pp->next = p->topRuby;
                p->topRuby = pp;
                for (int i = 0; i < pp->len; i++)
                  p1[i] = p1[i + 1];
                lstrcpy(p1 + pp->len, p3 + 1);
                q = p1 + pp->len;
                break;
              }
            }
            break;
          }
        }
      }
    }
  }
  if (bOnClick)
  {
    Hyper *pc = new Hyper;
    ZeroMemory(pc, sizeof(Hyper));
    p->pHyper = pc;
    int token = GetToken();
    if (token == TK_BEGIN)
    {
      curHyper = pc;
      if (GetContents(FALSE))
        return TRUE;
      curHyper = NULL;
    }
    else
      UngetToken(token);
  }

  return FALSE;
}

BOOL GetText()
{
  int token = GetToken();
  if (token == TK_BEGINNAME)
  {
    return GetText2();
  }
  else if (token != TK_IN)
  {
    wsprintf(strErrMsg, _T("token %s is not ("), TokenStr(token));
    return TRUE;
  }
  if (CheckNum())
    return TRUE;
  if (CheckToken(TK_COMMA))
    return TRUE;
  double x = dVal;
  if (CheckNum())
    return TRUE;
  double y = dVal;
  BOOL bTop = TRUE;
  int nCol = 0;
  int nFont = 0;
  while (1)
  {
    int token = GetToken();
    if (token == TK_OUT)
    {
      break;
    }
    if (token == TK_COMMA)
    {
      int tk = GetToken();
      if (tk == TK_STR)
      {
        TCHAR *pp = new TCHAR[lstrlen(strIn) + 1];
        lstrcpy(pp, strIn);
        Eva *p = NewEva(ET_TEXT, bTop ? ST_FIRST : ST_FOLLOW);
        p->x = x;
        p->y = y;
        p->text= pp;
        bTop = FALSE;
        AddEva(p);
      }
      else if (tk == TK_TIME)
      {
        Eva *p = NewEva(ET_TEXT, ST_TIME);
        p->x = x;
        p->y = y;
        AddEva(p);
        bTop = FALSE;
     //   SetWindowText(hMainWnd, _T("ttt")); ///////////
      }
      else if (tk == TK_BEGINNAME)
      {
        if (CheckToken(TK_STR))
          return TRUE;
        TCHAR *str = strIn;
        TCHAR *pp = new TCHAR[lstrlen(str) + 1];
        lstrcpy(pp, str);
        Eva *p = NewEva(ET_TEXT, bTop ? ST_FIRST : ST_FOLLOW);
        p->x = x;
        p->y = y;
        p->text = pp;
        bTop = FALSE;
        AddEva(p);
        if (CheckToken(TK_ENDNAME))
          return TRUE;
      }
      else if (tk == TK_AT)
      {
        if (CheckToken(TK_IN))
          return TRUE;
        if (CheckToken(TK_STR))
          return TRUE;
        Variable *pVar = FindVar(strIn);
        if (!pVar)
        {
          wsprintf(strErrMsg, _T("variable %s not found in GetText"), strIn);
          return TRUE;
        }
        if (CheckToken(TK_OUT))
          return TRUE;
        tk = GetToken();
        int dig = 100, wid = 0;
        if (tk == TK_COLON || tk == TK_DOT)
        {
          if (tk == TK_COLON)
          {
            if (CheckToken(TK_INT, TRUE))
              return TRUE;
            wid = nVal;
            tk = GetToken();
            if (tk == TK_DOT)
            {
              if (CheckToken(TK_INT))
                return TRUE;
              dig = nVal;
            }
            else
              UngetToken(tk);
          }
          else
          {
            if (CheckToken(TK_INT))
              return TRUE;
            dig = nVal;
          }
        }
        else
        {
          dig = 100;
          UngetToken(tk);
        }
        Eva *p = NewEva(ET_TEXT, bTop ? ST_VAR1 : ST_VAR2);
        p->x = x;
        p->y = y;
        p->pVar = pVar;
        p->dig = dig;
        bTop = FALSE;
        AddEva(p);
      }
      else if (tk == TK_COLOR)
      {
        int tk2 = GetToken();
        if (tk2 == TK_IN)
        {
          nCol++;
          COLORREF col;
          if (GetColor(col))
            return TRUE;
          Eva *p = NewEva(ET_TEXT, ST_COLORIN);
          p->col = col;
          AddEva(p);
        }
        else
        {
          UngetToken(tk2);
          nCol--;
          Eva *p = NewEva(ET_TEXT, ST_COLOROUT);
          AddEva(p);
        }
      }
      else if (tk == TK_FONT)
      {
        int tk2 = GetToken();
        if (tk2 == TK_IN)
        {
          nFont++;
          TCHAR name[256];
          int deciPt;
          BOOL bBold, bItalic, bUnder;
          if (GetFontData(name, deciPt, bBold, bItalic, bUnder))
            return TRUE;
          Eva *p = NewEva(ET_TEXT, ST_FONTIN);
          p->pFontData = new FontData;
          p->pFontData->name = new TCHAR[lstrlen(name) + 1];
          lstrcpy(p->pFontData->name, name);
          p->pFontData->deciPt = deciPt;
          p->pFontData->bBold = bBold;
          p->pFontData->bItalic = bItalic;
          p->pFontData->bUnder = bUnder;
          AddEva(p);
        }
        else
        {
          UngetToken(tk2);
          nFont--;
          Eva *p = NewEva(ET_TEXT, ST_FONTOUT);
          AddEva(p);
        }
      }
      else if (tk == TK_TOP || tk == TK_CENTER || tk == TK_BOTTOM)
      {
        int tt = 0;
        switch (tk)
        {
        case TK_TOP:
          tt = ST_TOP;
          break;
        case TK_CENTER:
          tt = ST_CENTER;
          break;
        case TK_BOTTOM:
          tt = ST_BOTTOM;
          break;
        }
        Eva *p = NewEva(ET_TEXT, tt);
        AddEva(p);
      }
      else
      {
        wsprintf(strErrMsg, _T("bad token %s in Text after comma"), TokenStr(token));
      }
    }
    else
    {
      wsprintf(strErrMsg, _T("bad token %s in Text"), TokenStr(token));
      return TRUE;
    }
  }
  for (int i = 0; i < nCol; i++)
  {
    Eva *p = NewEva(ET_TEXT, ST_COLOROUT);
    AddEva(p);
  }
  for (int i = 0; i < nFont; i++)
  {
    Eva *p = NewEva(ET_TEXT, ST_FONTOUT);
    AddEva(p);
  }
  return FALSE;
}

BOOL GetFontBlock()
{
  if (CheckToken(TK_IN))
    return TRUE;

  TCHAR name[256];
  int deciPt;
  BOOL bBold, bItalic, bUnder;
  if (GetFontData(name, deciPt, bBold, bItalic, bUnder))
    return TRUE;
  Eva *p = NewEva(ET_TEXT, ST_FONTIN);
  p->pFontData = new FontData;
  p->pFontData->name = new TCHAR[lstrlen(name) + 1];
  lstrcpy(p->pFontData->name, name);
  p->pFontData->deciPt = deciPt;
  p->pFontData->bBold = bBold;
  p->pFontData->bItalic = bItalic;
  p->pFontData->bUnder = bUnder;
  AddEva(p);
  if (CheckToken(TK_BEGIN))
    return TRUE;
  if (GetContents(FALSE))
    return TRUE;
  p = NewEva(ET_TEXT, ST_FONTOUT);
  AddEva(p);

  return FALSE;
}

BOOL GetSetCharSize()
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckNum())
    return TRUE;
  double y = dVal;
  if (CheckToken(TK_OUT))
    return TRUE;
  Eva *p = NewEva(ET_SETCHARSIZE, ST_NONE);
  p->y = y;
  AddEva(p);
  return FALSE;
}

double *pWorkWid;   // •Ð‚¯‚͍Ōã‚É
int maxWid, numWid;

void AddWid(double w)
{
  if (numWid >= maxWid)
  {
    maxWid += 32;
    double *p = new double[maxWid];
    for (int i = 0; i < numWid; i++)
      p[i] = pWorkWid[i];
    if (pWorkWid)
      delete [] pWorkWid;
    pWorkWid = p;
  }
  pWorkWid[numWid++] = w;
}

typedef CellItem * PCellItem;
PCellItem *pWorkCellItem;  // •Ð‚¯‚͍Ōã‚É
int maxCellItem, numCellItem;

void AddCellItem(CellItem *p)
{
  if (numCellItem >= maxCellItem)
  {
    maxCellItem += 32;
    PCellItem *pp = new PCellItem[maxCellItem];
    for (int i = 0; i < numCellItem; i++)
      pp[i] = pWorkCellItem[i];
    if (pWorkCellItem)
      delete [] pWorkCellItem;
    pWorkCellItem = pp;
  }
  pWorkCellItem[numCellItem++] = p;
}

BOOL GetCellWid(Cell *pCell)
{
  if (CheckToken(TK_IN))
    return TRUE;
  numWid = 0;
  while (1)
  {
    int token = GetToken();
    if (token == TK_INT || token == TK_DBL)
    {
      AddWid(dVal);
      token = GetToken();
      if (token == TK_OUT)
        break;
      else if (token != TK_COMMA)
      {
        wsprintf(strErrMsg, _T("bad token %s after num in GetCellWid"), TokenStr(token));
        return TRUE;
      }
    }
    else
    {
      wsprintf(strErrMsg, _T("bad token %s in GetCellWid"), TokenStr(token));
      return TRUE;
    }
  }
  pCell->nWid = numWid;
  pCell->pWid = new double[numWid];
  for (int i = 0; i < numWid; i++)
    pCell->pWid[i] = pWorkWid[i];
  return FALSE;
}

BOOL GetCellCon(Cell *pCell)
{
  if (CheckToken(TK_IN))
    return TRUE;
  numCellItem = 0;
  while (1)
  {
    CellItem *p = new CellItem;
    ZeroMemory(p, sizeof(CellItem));
    int token = GetToken();
    if (token == TK_STR)
    {
      p->text = new TCHAR[lstrlen(strIn) + 1];
      lstrcpy(p->text, strIn);
      p->type = ITEM_STR;
    }
    else if (token == TK_AT)
    {
      if (CheckToken(TK_IN))
        return TRUE;
      if (CheckToken(TK_STR))
        return TRUE;
      Variable *pVar = FindVar(strIn);
      if (!pVar)
      {
        wsprintf(strErrMsg, _T("variable %s not found in GetCellCon"), strIn);
        return TRUE;
      }
      if (CheckToken(TK_OUT))
        return TRUE;
      p->type = ITEM_VAR;
      p->pVar = pVar;
    }
    else if (token == TK_EQ)
    {
      if (GetExpr(&p->expr))
        return TRUE;
      p->type = ITEM_EXPR;
      int tk = GetToken();
      if (tk == TK_COLON || tk == TK_DOT)
      {
        if (tk == TK_COLON)
        {
          if (CheckToken(TK_INT, TRUE))
            return TRUE;
          p->wid = nVal;
          tk = GetToken();
          if (tk == TK_DOT)
          {
            if (CheckToken(TK_INT))
              return TRUE;
            p->dig = nVal;
          }
          else
          {
            UngetToken(tk);
          }
        }
        else
        {
          if (CheckToken(TK_INT))
            return TRUE;
          p->dig = nVal;
        }
      }
      else
      {
        p->dig = 100;
        UngetToken(tk);
      }
    }
    else
    {
      wsprintf(strErrMsg, _T("bad token %s in GetCellCon"), TokenStr(token));
      return TRUE;
    }
    token = GetToken();
    AddCellItem(p);
    if (token == TK_OUT)
      break;
    else if (token != TK_COMMA)
    {
      wsprintf(strErrMsg, _T("bad token %s after item in GetCellCon"), TokenStr(token));
      return TRUE;
    }
  }
  pCell->nItem = numCellItem;
  pCell->pCellItem = new PCellItem[numCellItem];
  for (int i = 0; i < numCellItem; i++)
    pCell->pCellItem[i] = pWorkCellItem[i];
  return FALSE;
}

BOOL GetCellContents(Cell *pCell)
{
  int token = GetToken();
  if (token == TK_BEGIN)
  {
    while (1)
    {
      token = GetToken();
      if (token == TK_END)
        break;
      switch (token)
      {
      case TK_WID:
        if (GetCellWid(pCell))
          return TRUE;
        break;
      case TK_CONTENTS:
        if (GetCellCon(pCell))
          return TRUE;
        break;
      default:
        wsprintf(strErrMsg, _T("bad token %s in GetCellContents"), TokenStr(token));
        return TRUE;
      }
    }
  }
  else
  {
    UngetToken(token);
  }
  return FALSE;
}

static TCHAR *outQ;
static int numQ;
TCHAR FileName[_MAX_PATH + 16], FileTitle[_MAX_FNAME + _MAX_EXT + 12];


// add " on both, and make " to ""
TCHAR *EnQuote(TCHAR *str)
{
  if (!str || !*str)
    return _T("\"\"");

  int len = lstrlen(str) + 1;
  for (TCHAR *p = str; *p; p = CharNext(p))
  {
    if (*p == '"')
      len++;
  }

  if (len > numQ)
  {
    if (outQ)
      delete [] outQ;
    outQ = new TCHAR[len];
    numQ = len;
  }

  TCHAR *out = outQ;
  *out++ = '"';
  while (*str)
  {
    if (*str == '"')
    {
      *out++ = '"';
      *out++ = '"';
      str++;
    }
    else
    {
      TCHAR *p = CharNext(str);
      while (p > str)
      {
        *out++ = *str++;
      }
    }
  }
  *out++ = '"';
  *out = 0;
  return outQ;
}

BOOL parseDataFormat()
{
  if (CheckToken(TK_STR))
    return TRUE;
  DataSet *pD = FindDataSet(strIn);
  if (!pD)
  {
    wsprintf(strErrMsg, _T("cannot find dataset %s in parseDataFormat"), strIn);
    return TRUE;
  }
  if (CheckToken(TK_BEGIN))
    return TRUE;
  int n = 0;
  int p[1000];
  int at;
  DataSet *pDD;
  while (1)
  {
    int token = GetToken();
    if (token == TK_END)
      break;
    switch (token)
    {
    case TK_LINK:
      if (CheckToken(TK_IN))
        return TRUE;
      if (CheckToken(TK_STR))
        return TRUE;
      at = FindItem(pD, strIn);
      if (CheckToken(TK_COMMA))
        return TRUE;
      if (CheckToken(TK_STR))
        return TRUE;
      if (at >= 0)
      {
        pDD = FindDataSet(strIn);
        if (pDD)
          p[n] = at;
        else
          p[n] = -1;
      }
      else
        p[n] = -1;
      if (CheckToken(TK_OUT))
        return TRUE;
      break;
    case TK_NAME: case TK_STRITEM: case TK_STRMULITEM: case TK_IDNUM:
    case TK_VAL: case TK_NUM: case TK_DATE: case TK_TIME: case TK_WORD:
      if (CheckToken(TK_IN))
        return TRUE;
      if (CheckToken(TK_STR))
        return TRUE;
      at = FindItem(pD, strIn);
      p[n] = at;
      if (CheckToken(TK_OUT))
        return TRUE;
      break;
    default:
      wsprintf(strErrMsg, _T("bad token %s in parseDataFormat"), TokenStr(token));
      return TRUE;
    }
    n++;
  }
  if (n == 0)
  {
    wsprintf(strErrMsg, _T("no data item in format %s"), pD->name);
    return TRUE;
  }
  if (pD->pFile)
    delete [] pD->pFile;
  pD->pFile = new int[n];
  for (int i = 0; i < n; i ++)
    pD->pFile[i] = p[i];
  pD->nFile = n;
  return FALSE;
}

BOOL parseDataBody()
{
  if (CheckToken(TK_STR))
    return TRUE;
  DataSet *pD = FindDataSet(strIn);
  if (!pD)
  {
    wsprintf(strErrMsg, _T("cannot find dataset %s"), strIn);
    return TRUE;
  }
  if (CheckToken(TK_BEGIN))
    return TRUE;
  int rec = 0;
  int n = pD->num;
  int atID = -1;
  if (pD->nFile > 0 && pD->pFile)
  {
    n = pD->nFile;
    for (int i = 0; i < n; i++)
    {
      int at = pD->pFile[i];
      if (at == pD->posID)
        atID = at;
    }
  }
  int id = pD->nStep;
  while (1)
  {
    int tk = GetToken();
    if (tk == TK_END)
      break;
    if (tk == TK_EOF)
    {
      lstrcpy(strErrMsg, _T("end of data 2"));
      return TRUE;
    }
    UngetToken(tk);
    if (pD->posID >= 0 && atID < 0)
    {
      SetDataInt(pD, rec, pD->posID, id);
      id += pD->nStep;
    }
    for (int i = 0; i < n; i++)
    {
      int at = pD->pFile ? pD->pFile[i] : i;
      tk = GetToken();
      if (tk == TK_EOF || tk == TK_END)
        return TRUE;
      if (at >= 0)
      {
        int type = pD->item[at].type;
        if (type == FT_DBL)
          SetDataDbl(pD, rec, at, dVal);
        else if (type == FT_STR || type == FT_STRMUL)  // no strmul !!
          SetDataStr(pD, rec, at, strIn);
        else
          SetDataInt(pD, rec, at, nVal);
      }
    }
    rec++;
  }
  return FALSE;
}

BOOL parseData(TCHAR *in)
{
  topBuf = in;
  lineNo = 1;
  backedToken = -1;

  while (1)
  {
    int token = GetToken();
    if (token == TK_EOF)
      break;
    switch (token)
    {
    case TK_DATAFORMAT:
      if (parseDataFormat())
        return TRUE;
      break;
    case TK_DATA:
      if (parseDataBody())
        return TRUE;
      break;
    default:
      wsprintf(strErrMsg, _T("bad token %s in parseData"), TokenStr(token));
      return TRUE;
    }
  }

  return FALSE;
}

void OnOpenD(HWND hWnd)
{

  if (!GetOpenFile(hWnd, FALSE))
    return;

  HANDLE h = CreateFile(FileName, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); //////////////
  if (h == INVALID_HANDLE_VALUE)
  {
    MessageBox(hWnd, _T("cannot open file"), FileTitle, MB_OK);
    return;
  }
  int len = GetFileSize(h, NULL);
  int len2 = (len+1)/2;
  TCHAR *p = new TCHAR[len2 + 10];
  ReadFile(h, p, len, NULL, NULL);
  CloseHandle(h);
  p[len2] = 0;
  TCHAR *srcData = p;
  bDirtyFlagD = FALSE;
  if (parseData(srcData + 1))
  {
    MessageBox(hWnd, strErrMsg, _T("data file error"), MB_OK);
    return;
  }
  delete srcData;
  Renew();
  InvalidateRect(hWnd, NULL, TRUE);
}

TCHAR *pBuf;
int nBuf, lenBuf;

void FileOut(TCHAR *str)
{
  int len = lstrlen(str);
  if (lenBuf + len + 8 > nBuf)
  {
    nBuf += len + 8192;
    TCHAR *p = new TCHAR[nBuf];
    ZeroMemory(p, sizeof(TCHAR)*lenBuf);
    for (int i = 0; i < lenBuf; i++)
      p[i] = pBuf[i];
    if (pBuf)
      delete [] pBuf;
    pBuf = p;
  }
  lstrcpy(pBuf + lenBuf, str);
  lenBuf += len;
}

// convert string data to save-format
TCHAR *SaveStr(DataSet *pD, int rec, int pos)
{
  TCHAR *p = GetDataAsStr(pD, rec, pos, TRUE);
  if (!p)
    p = _T("???"); //////////
  int type = pD->item[pos].type;
  if (type == FT_STR)
    return EnQuote(p);
  else if (type == FT_STRMUL)
  {
    return _T("??");  // not implemented yet !!!!!!!!!!!!!!
  }
  return p;
}

void DoSaveD(HWND hWnd)
{
  TCHAR buf[10];
  buf[0] = 0xFEFF;
  buf[1] = 0;
  FileOut(buf);
  FileOut(_T("// "));
  FileOut(FileName);
  FileOut(_T("\r\n\r\n"));

  for (DataSet *pD = pDataSetTop; pD; pD = pD->next)
  {
    if (pD->type == TK_LIST)
      continue;

    // output data format
    FileOut(_T("DataFormat "));
    FileOut(EnQuote(pD->name));
    FileOut(_T("\r\n{\r\n"));
    for (int i = 0; i < pD->num; i++)
    {
      if (pD->item[i].bTemp)
        continue;
      switch (pD->item[i].type)
      {
      case FT_ID:
        FileOut(_T("  ID(")); FileOut(EnQuote(pD->item[i].name)); FileOut(_T(")\r\n"));
        break;
      case FT_STR:
        if (i == pD->posName)
        {
          FileOut(_T("  Name(")); FileOut(EnQuote(pD->item[i].name)); FileOut(_T(")\r\n"));
        }
        else
        {
          FileOut(_T("  Str(")); FileOut(EnQuote(pD->item[i].name)); FileOut(_T(")\r\n"));
        }
        break;
      case FT_STRMUL:
        FileOut(_T("  StrMul(")); FileOut(EnQuote(pD->item[i].name)); FileOut(_T(")\r\n"));
        break;
      case FT_DBL:
        FileOut(_T("  Val(")); FileOut(EnQuote(pD->item[i].name)); FileOut(_T(")\r\n"));
        break;
      case FT_INT:
        FileOut(_T("  Num(")); FileOut(EnQuote(pD->item[i].name)); FileOut(_T(")\r\n"));
        break;
      case FT_DATE:
        FileOut(_T("  Date(")); FileOut(EnQuote(pD->item[i].name)); FileOut(_T(")\r\n"));
        break;
      case FT_TIME:
        FileOut(_T("  Time(")); FileOut(EnQuote(pD->item[i].name)); FileOut(_T(")\r\n"));
        break;
      case FT_LINK:
        FileOut(_T("  Choice(")); FileOut(EnQuote(pD->item[i].name)); FileOut(_T(","));
        FileOut(EnQuote(pD->item[i].link->name));FileOut(_T(")\r\n"));
        break;
      }
    }
    FileOut(_T("}\r\n\r\n"));
    // output data body
    FileOut(_T("Data ")); FileOut(EnQuote(pD->name)); FileOut(_T("\r\n{\r\n"));
    for (int i = 0; i < pD->RecNum; i++)
    {
      for (int j = 0; j < pD->num; j++)
      {
        if (pD->item[j].bTemp)
          continue;
        FileOut(_T(" "));
        FileOut(SaveStr(pD, i, j));
      }
      FileOut(_T("\r\n"));
    }
    FileOut(_T("}\r\n\r\n"));
  }
  HANDLE h = CreateFile(FileName, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  if (h == INVALID_HANDLE_VALUE)
  {
    MessageBox(hWnd, _T("cannot open file"), FileName, MB_OK);
    return;
  }

  WriteFile(h, pBuf, sizeof(TCHAR)*lenBuf, NULL, NULL);
  CloseHandle(h);

  if (pBuf)
    delete [] pBuf;
  pBuf = NULL;
  nBuf = lenBuf = 0;
  bDirtyFlagD = FALSE;
}

void OnSaveD(HWND hWnd)
{
 if (!*FileName)
    OnSaveAsD(hWnd);
  else
    DoSaveD(hWnd);
}

void OnSaveAsD(HWND hWnd)
{
  if (!GetSaveFile(hWnd, FALSE))
    return;
  DoSaveD(hWnd);
}


static TableItem *pTab;
static int nTab, maxTab;

TableItem *AddTab()
{
  if (nTab >= maxTab)
  {
    maxTab += 256;
    TableItem *p = new TableItem[maxTab];
    ZeroMemory(p, sizeof(TableItem)*maxTab);
    for (int i = 0; i < nTab; i++)
      p[i] = pTab[i];
    if (pTab)
      delete [] pTab;
    pTab = p;
  }
  return &pTab[nTab++];
}

BOOL GetSet(Cell *pCell)
{
  if (!pCell || !pCell->pD)
  {
    lstrcpy(strErrMsg, _T("no Cell or no DataSet in GetSet"));
    return TRUE;
  }
  if (CheckToken(TK_IN))
    return TRUE;
  TableItem *p = AddTab();
  int token = GetToken();
  if (token == TK_BEGINNAME)
  {
    if (CheckToken(TK_STR))
      return TRUE;
    Path *pPath = FindPath(pCell->pD, strIn);
    if (!pPath)
    {
      lstrcpy(strErrMsg, _T("cannot find path in GetSet"));
      return TRUE;
    }
    p->type = TI_PATH;
    p->pPath = pPath;
    if (CheckToken(TK_ENDNAME))
      return TRUE;
  }
  else if (token == TK_EQ)
  {
    p->type = TI_EXPR;
    pDStd = pCell->pD;
    if (GetExpr(&p->expr))
      return TRUE;
    pDStd = NULL;
  }
  else
  {
    wsprintf(strErrMsg, _T("bad token %s in GetSet"), TokenStr(token));
    return TRUE;
  }
  token = GetToken();
  if (token == TK_COMMA)
  {
    if (CheckToken(TK_STR))
      return TRUE;
    if (p)
    {
      p->str = new TCHAR[lstrlen(strIn) + 1];
      lstrcpy(p->str, strIn);
    }
    if (CheckToken(TK_OUT))
      return TRUE;
  }
  else if (token != TK_OUT)
  {
    wsprintf(strErrMsg, _T("token %s is not )"), TokenStr(token));
    return TRUE;
  }
  token = GetToken();
  if (token == TK_COLON)
  {
    int tk = GetToken();
    if (tk == TK_WID)
    {
      if (CheckToken(TK_IN))
        return TRUE;
      if (CheckNum())
        return TRUE;
      p->wid = dVal;
      if (CheckToken(TK_OUT))
        return TRUE;
    }
    else
    {
      wsprintf(strErrMsg, _T("bad token %s in GetSet : "), TokenStr(tk));
      return TRUE;
    }
  }
  else
    UngetToken(token);
  return FALSE;
}

BOOL GetSetNum(BOOL bRank)
{
  TableItem *p = AddTab();
  p->type = bRank ? TI_RANK : TI_NUM;
  return FALSE;
}

BOOL GetTableItem(Cell *pCell)
{
  while (1)
  {
    int token = GetToken();
    if (token == TK_END)
      break;
    switch (token)
    {
    case TK_SET:
      if (GetSet(pCell))
        return TRUE;
      break;
    case TK_SETNUM:
      if (GetSetNum(FALSE))
        return TRUE;
      break;
    case TK_SETRANK:
      if (GetSetNum(TRUE))
        return TRUE;
      break;
    }
  }
  return FALSE;
}

BOOL GetTableContents(Cell *pCell)
{
  int token = GetToken();
  if (token == TK_BEGIN)
  {
    while (1)
    {
      token = GetToken();
      if (token == TK_END)
        break;
      switch (token)
      {
      case TK_ITEM:
        if (CheckToken(TK_BEGIN))
          return TRUE;
        nTab = 0;
        if (GetTableItem(pCell))
          return TRUE;
        if (nTab > 0)
        {
          pCell->nItem = nTab;
          if (pCell->pTableItem)
            delete [] pCell->pTableItem;
          pCell->pTableItem = new TableItem[nTab];
          for (int i = 0; i < nTab; i++)
            pCell->pTableItem[i] = pTab[i];
        }
        break;
      case TK_WID:
        if (GetTableContents(pCell))
          return TRUE;
        break;
      default:
        wsprintf(strErrMsg, _T("bad token %s in GetTableContents"), TokenStr(token));
        return TRUE;
      }
    }
  }
  else
  {
    UngetToken(token);
  }
  return FALSE;
}

BOOL GetSort(Cell *pCell, BOOL bUp)
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckToken(TK_BEGINNAME))
    return TRUE;
  if (CheckToken(TK_STR))
    return TRUE;
  Path *pPath = FindPath(pCell->pD, strIn);
  if (!pPath)
  {
    wsprintf(strErrMsg, _T("cannot find path in %s in GetSort"), pCell->pD->name);
    return TRUE;
  }
  if (CheckToken(TK_ENDNAME))
    return TRUE;
  if (CheckToken(TK_OUT))
    return TRUE;
  Ope *p = new Ope;
  ZeroMemory(p, sizeof(Ope));
  p->type = bUp ? OP_SORTUP : OP_SORTDOWN;
  p->pPath = pPath;
  p->next = NULL;
  if (pCell->topOpe)
  {
    Ope *q = NULL;
    for (q = pCell->topOpe; q->next; q = q->next)
      ;
    q->next = p;
  }
  else
  {
    pCell->topOpe = p;
  }
  return FALSE;
}

BOOL GetSelect(Cell *pCell)
{
  if (CheckToken(TK_IN))
    return TRUE;
  Expr expr;
  pDStd = pCell->pD;
  if (GetExpr(&expr))
    return TRUE;
  if (CheckToken(TK_OUT))
    return TRUE;
  Ope *p = new Ope;
  ZeroMemory(p, sizeof(Ope));
  p->type = OP_SELECT;
  p->expr = expr;
  p->next = NULL;
  if (pCell->topOpe)
  {
    Ope *q = NULL;
    for (q = pCell->topOpe; q->next; q = q->next)
      ;
    q->next = p;
  }
  else
  {
    pCell->topOpe = p;
  }
  return FALSE;
}

BOOL GetSelectFor(For *pFor)
{
  if (CheckToken(TK_IN))
    return TRUE;
  Expr expr;
  pDStd = pFor->pD;
  if (GetExpr(&expr))
    return TRUE;
  if (CheckToken(TK_OUT))
    return TRUE;
  Ope *p = new Ope;
  ZeroMemory(p, sizeof(Ope));
  p->type = OP_SELECT;
  p->expr = expr;
  p->next = NULL;
  if (pFor->topOpe)
  {
    Ope *q = NULL;
    for (q = pFor->topOpe; q->next; q = q->next)
      ;
    q->next = p;
  }
  else
  {
    pFor->topOpe = p;
  }
  return FALSE;
}

BOOL GetSortFor(For *pFor, BOOL bUp)
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckToken(TK_BEGINNAME))
    return TRUE;
  if (CheckToken(TK_STR))
    return TRUE;
  Path *pPath = FindPath(pFor->pD, strIn);
  if (!pPath)
  {
    wsprintf(strErrMsg, _T("cannot find path in %s in GetSortFor"), pFor->pD->name);
    return TRUE;
  }
  if (CheckToken(TK_ENDNAME))
    return TRUE;
  if (CheckToken(TK_OUT))
    return TRUE;
  Ope *p = new Ope;
  ZeroMemory(p, sizeof(Ope));
  p->type = bUp ? OP_SORTUP : OP_SORTDOWN;
  p->pPath = pPath;
  p->next = NULL;
  if (pFor->topOpe)
  {
    Ope *q = NULL;
    for (q = pFor->topOpe; q->next; q = q->next)
      ;
    q->next = p;
  }
  else
  {
    pFor->topOpe = p;
  }
  return FALSE;
}

BOOL GetTable()
{
  DataSet *pD = NULL;
  double x, y;
  int token = GetToken();
  if (token == TK_BEGINNAME)
  {
    int tk = GetToken();
    if (tk == TK_STR)
    {
      pD = FindDataSet(strIn);
      if (!pD)
      {
        wsprintf(strErrMsg, _T("dataset %s not found in GetCell"), strIn);
        return TRUE;
      }
      if (CheckToken(TK_ENDNAME))
        return TRUE;
      if (CheckToken(TK_IN))
        return TRUE;
      if (CheckNum())
        return TRUE;
      x = dVal;
      if (CheckToken(TK_COMMA))
        return TRUE;
      if (CheckNum())
        return TRUE;
      y = dVal;
    }
    else if (tk == TK_INT || tk == TK_DBL)
    {
      x = dVal;
      if (CheckToken(TK_COMMA))
        return TRUE;
      if (CheckNum())
        return TRUE;
      y = dVal;
      if (CheckToken(TK_ENDNAME))
        return TRUE;
      if (CheckToken(TK_IN))
        return TRUE;
      if (CheckToken(TK_STR))
        return TRUE;
      pD = FindDataSet(strIn);
      if (!pD)
      {
        wsprintf(strErrMsg, _T("dataset %s not found in GetCell"), strIn);
        return TRUE;
      }
    }
    else
    {
      wsprintf(strErrMsg, _T("bad token %s in GetTable"), TokenStr(tk));
      return TRUE;
    }
  }
  else if (token != TK_IN)
  {
    wsprintf(strErrMsg, _T("bad token %s in GetCell"), TokenStr(token));
    return TRUE;
  }
  int nW = 0, nH = 0;
  if (!pD)
  {
    if (CheckToken(TK_COMMA))
      return TRUE;
    if (CheckToken(TK_INT))
      return TRUE;
    nW = nVal;
    if (CheckToken(TK_COMMA))
      return TRUE;
    if (CheckToken(TK_INT))
      return TRUE;
    nH = nVal;
  }
  if (CheckToken(TK_OUT))
    return TRUE;
  
  Cell *pCell = new Cell;
  ZeroMemory(pCell, sizeof(Cell));
  pCell->type = ST_TABLE;
  pCell->pD = pD;

  while (1)
  {
    int token = GetToken();
    if (token == TK_COLON)
    {
      int tk = GetToken();
      switch (tk)
      {
      case TK_SELECT:
        if (GetSelect(pCell))
          return TRUE;
        break;
      case TK_SORTUP:
        if (GetSort(pCell, TRUE))
          return TRUE;
        break;
      case TK_SORTDOWN:
        if (GetSort(pCell, FALSE))
          return TRUE;
        break;
      }
    }
    else
    {
      UngetToken(token);
      break;
    }
  }
  if (GetTableContents(pCell))
    return TRUE;

  Eva *p = NewEva(ET_CELL, ST_TABLE);
  p->x = x;
  p->y = y;
  p->pCell = pCell;
  p->pCell->nW = nW;
  p->pCell->nH = nH;
  AddEva(p);
  return FALSE;
}

BOOL GetCell()
{
  DataSet *pD = NULL;
  int type = ST_FREE;
  int token = GetToken();
  if (token == TK_BEGINNAME)
  {
    if (CheckToken(TK_STR))
      return TRUE;
    pD = FindDataSet(strIn);
    if (!pD)
    {
      wsprintf(strErrMsg, _T("dataset %s not found in GetCell"), strIn);
      return TRUE;
    }
    if (CheckToken(TK_ENDNAME))
      return TRUE;
    if (CheckToken(TK_IN))
      return TRUE;
    type = ST_TABLE;
  }
  else if (token != TK_IN)
  {
    wsprintf(strErrMsg, _T("bad token %s in GetCell"), TokenStr(token));
    return TRUE;
  }
  if (CheckNum())
    return TRUE;
  double x = dVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckNum())
    return TRUE;
  double y = dVal;
  int nW = 0, nH = 0;
  if (!pD)
  {
    if (CheckToken(TK_COMMA))
      return TRUE;
    if (CheckToken(TK_INT))
      return TRUE;
    nW = nVal;
    if (CheckToken(TK_COMMA))
      return TRUE;
    if (CheckToken(TK_INT))
      return TRUE;
    nH = nVal;
  }
  if (CheckToken(TK_OUT))
    return TRUE;

  Cell *pCell = new Cell;
  pCell->type = type;
  pCell->pD = pD;
  if (GetCellContents(pCell))
    return TRUE;

  Eva *p = NewEva(ET_CELL, type);
  p->x = x;
  p->y = y;
  p->pCell = pCell;
  p->pCell->nW = nW;
  p->pCell->nH = nH;
  AddEva(p);
  return FALSE;
}

Variable *FindVar(TCHAR *name)
{
  Set *pSet = curSet;
  Variable *p = FindVariable(pSet, name);
  if (p)
    return p;
  if (pSet != topSet)
    p = FindVariable(topSet, name);
  return p;
}

BOOL GetLetData()
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckToken(TK_BEGINNAME))
    return TRUE;
  if (CheckToken(TK_STR))
    return TRUE;
  Path *pPath = FindPath(curData, strIn);
  if (!pPath)
  {
    wsprintf(strErrMsg, _T("cannot find path in %s in GetLetData"), curData->name);
    return TRUE;
  }
  if (CheckToken(TK_ENDNAME))
    return TRUE;
  if (CheckToken(TK_EQ))
    return TRUE;
  Eva *pe = NewEva(ET_ASSIGN, ST_PATH);
  pe->pPath = pPath;
  if (GetExpr(&pe->expr))
    return TRUE;
  if (CheckToken(TK_OUT))
    return TRUE;
  AddEva(pe);
  return FALSE;
}

BOOL GetLet()
{
  if (CheckToken(TK_IN))
    return TRUE;
  int token = GetToken();
  BOOL bAdd = FALSE;
  if (token == TK_AT)
  {
    bAdd = TRUE;
    if (CheckToken(TK_IN))
      return TRUE;
  }
  else
    UngetToken(token);
  if (CheckToken(TK_STR))
    return TRUE;
  Variable *pp = FindVar(strIn);
  if (!pp)
  {
    wsprintf(strErrMsg, _T("variable %s not found in GetLet"), strIn);
    return TRUE;
  }
  if (bAdd && CheckToken(TK_OUT))
    return TRUE;

  Eva *pe = NewEva(ET_ASSIGN, pp->type == VT_DBL ? ST_DBL : (pp->type == VT_STR ? ST_STR : ST_INT));
  pe->pAssign = new Assign;
  ZeroMemory(pe->pAssign, sizeof(Assign));
  pe->pAssign->pVar = pp;
  pe->pAssign->bOnce = FALSE;
  AddEva(pe);
  if (CheckToken(TK_EQ))
    return TRUE;
  if (pe->pAssign->pVar->type == VT_STR)
  {
    if (CheckToken(TK_STR))
      return TRUE;
    pe->pAssign->str = new TCHAR[lstrlen(strIn) + 1];
    lstrcpy(pe->pAssign->str, strIn);
  }
  else
  {
    if (GetExpr(&pe->pAssign->expr))
      return TRUE;
  }
  if (CheckToken(TK_OUT))
    return TRUE;
  return FALSE;
}

BOOL GetVariable(int type, BOOL bOnce)
{
  if (CheckToken(TK_IN))
    return TRUE;
  while (1)
  {
    int token = GetToken();
    Variable *pp = NULL;
    if (token == TK_STR || token == TK_ID)
    {
      pp = FindVar(textIn);
      if (pp)
      {
        wsprintf(strErrMsg, _T("variable dupe %s in GetVariable"), textIn);
        return TRUE;
      }
      TCHAR *p = new TCHAR[lstrlen(textIn) + 1];
      lstrcpy(p, textIn);
      pp = new Variable;
      ZeroMemory(pp, sizeof(Variable));
      pp->type = type;
      pp->name = p;
      AddVariable(curSet, pp);
    }
    else
    {
      wsprintf(strErrMsg, _T("bad token %s in GetVariable"), TokenStr(token));
      return TRUE;
    }
    Eva *pe = NewEva(ET_ASSIGN, type == VT_DBL ? ST_DBL : (type == VT_INT ? ST_INT : ST_STR));
    pe->pAssign = new Assign;
    ZeroMemory(pe->pAssign, sizeof(Assign));
    pe->pAssign->pVar = pp;
    pe->pAssign->bOnce = bOnce;
    AddEva(pe);
    token = GetToken();
    if (token == TK_EQ)
    {
      if (type == VT_STR)
      {
        if (CheckToken(TK_STR))
          return TRUE;
        pe->pAssign->str = new TCHAR[lstrlen(strIn) + 1];
        lstrcpy(pe->pAssign->str, strIn);
      }
      else
      {
        if (GetExpr(&pe->pAssign->expr))
          return TRUE;
      }
      token = GetToken();
    }
    if (token == TK_OUT)
    {
      break;
    }
    else if (token != TK_COMMA)
    {
      wsprintf(strErrMsg, _T("bad token %s in GetVariable 2"), TokenStr(token));
      return TRUE;
    }
  }
  return FALSE;
}

BOOL GetVal(BOOL bOnce)
{
  if (curData)
  {
    if (CheckToken(TK_IN))
      return TRUE;
    while (1)
    {
      if (CheckToken(TK_STR))
        return TRUE;
      DataItem *p = DblField(strIn);
      p->bTemp = TRUE;
      int token = GetToken();
      if (token == TK_OUT)
      {
        int tk = GetToken();
        if (tk == TK_COLON)
        {
          int tk2 = GetToken();
          if (tk2 == TK_WID)
          {
            if (CheckToken(TK_IN))
              return TRUE;
            if (CheckNum())
              return TRUE;
            p->wid = dVal;
            if (CheckToken(TK_OUT))
              return TRUE;
          }
          else
          {
            wsprintf(strErrMsg, _T("bad token %s in GetVal"), TokenStr(tk2));
            return TRUE;
          }
        }
        else
          UngetToken(tk);
        break;
      }
      else if (token != TK_COMMA)
      {
        wsprintf(strErrMsg, _T("bad token %s in GetVal"), TokenStr(token));
        return TRUE;
      }
    }
  }
  else
  {
    if (GetVariable(VT_DBL, bOnce))
      return TRUE;
  }
  return FALSE;
}

BOOL GetNum(BOOL bOnce)
{
  if (curData)
  {
    if (CheckToken(TK_IN))
      return TRUE;
    while (1)
    {
      if (CheckToken(TK_STR))
        return TRUE;
      DataItem *p = IntField(strIn);
      p->bTemp = TRUE;
      int token = GetToken();
      if (token == TK_OUT)
      {
        int tk = GetToken();
        if (tk == TK_COLON)
        {
          int tk2 = GetToken();
          if (tk2 == TK_WID)
          {
            if (CheckToken(TK_IN))
              return TRUE;
            if (CheckNum())
              return TRUE;
            p->wid = dVal;
            if (CheckToken(TK_OUT))
              return TRUE;
          }
          else
          {
            wsprintf(strErrMsg, _T("bad token %s in GetNum"), TokenStr(tk2));
            return TRUE;
          }
        }
        else
          UngetToken(tk);
        break;
      }
      else if (token != TK_COMMA)
      {
        wsprintf(strErrMsg, _T("bad token %s in GetNum"), TokenStr(token));
        return TRUE;
      }
    }
  }
  else
  {
    if (GetVariable(VT_INT, bOnce))
      return TRUE;
  }
  return FALSE;
}

BOOL GetWord(BOOL bOnce)
{
  if (curData)
  {
    lstrcpy(strErrMsg, _T("word in DB not yet"));
    return TRUE; //////////////////////////
  }
  if (GetVariable(VT_STR, bOnce))
    return TRUE;
  return FALSE;
}

BOOL GetFontSize()
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckNum())
    return TRUE;
  defFontSize = dVal;
  if (CheckToken(TK_OUT))
    return TRUE;
  return FALSE;
}

BOOL GetIf()
{
  Eva *p = NewEva(ET_IF, ST_IF);
  AddEva(p);
  if (CheckToken(TK_IN))
    return TRUE;
  if (GetExpr(&p->expr))
    return TRUE;
  if (CheckToken(TK_OUT))
    return TRUE;
  BOOL bSingle = FALSE;
  int token = GetToken();
  if (token != TK_BEGIN)
  {
    bSingle = TRUE;
    UngetToken(token);
  }
  int n1 = codePos;
  if (GetContents(bSingle))
    return TRUE;
  token = GetToken();
  if (token != TK_ELSE)
  {
    UngetToken(token);
    p->skip = codePos - n1;
    return FALSE;
  }
  p->skip = codePos - n1 + 1;
  p = NewEva(ET_IF, ST_ELSE);
  AddEva(p);
  token = GetToken();
  if (token != TK_BEGIN)
  {
    bSingle = TRUE;
    UngetToken(token);
  }
  else
    bSingle = FALSE;
  n1 = codePos;
  if (GetContents(bSingle))
    return TRUE;
  p->skip = codePos - n1;
  return FALSE;
}

BOOL GetListData()
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckToken(TK_STR))
    return TRUE;
  DataSet *pD = FindDataSet(strIn);
  if (!pD)
  {
    wsprintf(strErrMsg, _T("dataset %s cannot found"), strIn);
    return TRUE;
  }
  if (CheckToken(TK_OUT))
    return TRUE;
  if (CheckToken(TK_BEGIN))
    return TRUE;
  int num = pD->RecNum;
  int id = 0;
  while (1)
  {
    int token = GetToken();
    if (token == TK_END)
      break;
    UngetToken(token);
    for (int i = 0; i < pD->num; i++)
    {
      switch (pD->item[i].type)
      {
      case FT_DBL:
        if (CheckNum())
          return TRUE;
        SetDataDbl(pD, num, i, dVal);
        break;
      case FT_ID:
        if (CheckToken(TK_INT))
          return TRUE;
        if (nVal <= id)
        {
          lstrcpy(strErrMsg, _T("id not in order"));
          return TRUE;
        }
        id = nVal;
        SetDataInt(pD, num, i, nVal);
        break;
      case FT_LINK: case FT_DATE: case FT_TIME: case FT_SEC: case FT_INT:
        if(CheckToken(TK_INT))
          return TRUE;
        SetDataInt(pD, num, i, nVal);
        break;
      case FT_STR: case FT_STRMUL:
        if (CheckToken(TK_STR))
          return TRUE;
        SetDataStr(pD, num, i, strIn);
        break;
      default:
        lstrcpy(strErrMsg, _T("bug in GetListData"));
        return TRUE;
      }
      int tk = GetToken();
      if (i == pD->num - 1 && tk == TK_END || tk != TK_COMMA)
        UngetToken(tk);
    }
    num++;
  }
  pD->bReadOnly = TRUE;
  return FALSE;
}

BOOL GetMeasUnit()
{
  if (CheckToken(TK_IN))
    return TRUE;
  int token = GetToken();
  switch (token)
  {
  case TK_MU_CHAR:
    curMeasUnit = MU_CHAR;
    break;
  case TK_MU_WCHAR:
    curMeasUnit = MU_WCHAR;
    break;
  case TK_MU_MM:
    curMeasUnit = MU_MM;
    break;
  case TK_MU_INCH:
    curMeasUnit = MU_INCH;
    break;
  default:
    wsprintf(strErrMsg, _T("bad token %s in GetMeasUnit"), TokenStr(token));
    return TRUE;
  }
  token = GetToken();
  if (token == TK_COMMA)
  {
    if (CheckNum())
      return TRUE;
    measFactor = dVal;
    if (CheckToken(TK_OUT))
      return TRUE;
  }
  else if (token != TK_OUT)
  {
    wsprintf(strErrMsg, _T("bad token2 %s in GetMeasUnit"), TokenStr(token));
    return TRUE;
  }
  return FALSE;
}

BOOL GetCrossContents(Cell *pCell)
{
  Path *pPath;
  CrossItem *p = new CrossItem;
  ZeroMemory(p, sizeof(CrossItem));
  pCell->pCrossItem = p;
  while (1)
  {
    int token = GetToken();
    if (token == TK_COLON)
    {
      int tk = GetToken();
      switch (tk)
      {
      case TK_XITEM: case TK_YITEM: case TK_VITEM:
        if (CheckToken(TK_IN))
          return TRUE;
        if (CheckToken(TK_STR))
          return TRUE;
        pPath = FindPath(pCell->pD, strIn);
        if (!pPath)
        {
          wsprintf(strErrMsg, _T("bad path in GetCrossContents"));
          return TRUE;
        }
        if (CheckToken(TK_OUT))
          return TRUE;
        if (tk == TK_XITEM)
          p->pX = pPath;
        else if (tk == TK_YITEM)
          p->pY = pPath;
        else
          p->pV = pPath;
        break;
      case TK_ONOFF:
        p->bOnOff = TRUE;
        break;
      case TK_WID:
        if (CheckToken(TK_IN))
          return TRUE;
        if (CheckNum())
          return TRUE;
        pCell->w1 = dVal;
        token = GetToken();
        if (token == TK_OUT)
        {
          pCell->w2 = pCell->w3 = pCell->w1;
        }
        else if (token != TK_COMMA)
        {
          wsprintf(strErrMsg, _T("bad token %s in GetCrossContents"), TokenStr(token));
          return TRUE;
        }
        else
        {
          if (CheckNum())
            return TRUE;
          pCell->w2 = dVal;
          token = GetToken();
          if (token == TK_OUT)
          {
            pCell->w3 = pCell->w2;
          }
          else if (token != TK_COMMA)
          {
            wsprintf(strErrMsg, _T("bad token %s in GetCrossContents"), TokenStr(token));
            return TRUE;
          }
          else
          {
            if (CheckNum())
              return TRUE;
            pCell->w3 = dVal;
            if (CheckToken(TK_OUT))
              return TRUE;
          }
        }
        break;
      default:
        wsprintf(strErrMsg, _T("bad token %s in GetCrossContents"), TokenStr(tk));
        return TRUE;
      }
    }
    else if (token == TK_BEGIN)
    {
    }
    else
    {
      UngetToken(token);
      break;
    }
  }
  return FALSE;
}

BOOL GetCross()
{
  if (CheckToken(TK_BEGINNAME))
    return TRUE;
  int token = GetToken();
  double x = 0, y = 0;
  DataSet *pD = NULL;
  if (token == TK_STR)
  {
    pD = FindDataSet(strIn);
    if (CheckToken(TK_ENDNAME))
      return TRUE;
    if (CheckToken(TK_IN))
      return TRUE;
    x = dVal;
    if (CheckToken(TK_COMMA))
      return TRUE;
    y = dVal;
    if (CheckToken(TK_OUT))
      return TRUE;
  }
  else if (token == TK_INT || token == TK_DBL)
  {
    x = dVal;
    if (CheckToken(TK_COMMA))
      return TRUE;
    if (CheckNum())
      return TRUE;
    y = dVal;
    if (CheckToken(TK_ENDNAME))
      return TRUE;
    if (CheckToken(TK_IN))
      return TRUE;
    if (CheckToken(TK_STR))
      return TRUE;
    pD = FindDataSet(strIn);
    if (CheckToken(TK_OUT))
      return TRUE;
  }
  else
  {
    wsprintf(strErrMsg, _T("bad token %s in GetCross"), TokenStr(token));
    return TRUE;
  }
  if (!pD)
  {
    wsprintf(strErrMsg, _T("dataset %s cannot found in GetCross"), strIn);
    return TRUE;
  }

  Cell *pCell = new Cell;
  ZeroMemory(pCell, sizeof(Cell));
  pCell->type = ST_CROSS;
  pCell->pD = pD;
  pCell->nW = 1;
  pCell->nH = 1;
  if (GetCrossContents(pCell))
    return TRUE;

  Eva *p = NewEva(ET_CELL, ST_CROSS);
  p->x = x;
  p->y = y;
  p->pCell = pCell;
  AddEva(p);

  return FALSE;
}

BOOL GetRect()
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  int x1 = nVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  int y1 = nVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  int x2 = nVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  int y2 = nVal;
  if (CheckToken(TK_OUT))
    return TRUE;

  COLORREF col = RGB(255, 100, 100); //////////
  int token = GetToken();
  if (token == TK_COLON)
  {
    if (CheckToken(TK_COLOR))
      return TRUE;
    if (CheckToken(TK_IN))
      return TRUE;
    if (CheckToken(TK_INT))
      return TRUE;
    int r = nVal;
    if (CheckToken(TK_COMMA))
      return TRUE;
    if (CheckToken(TK_INT))
      return TRUE;
    int g = nVal;
    if (CheckToken(TK_COMMA))
      return TRUE;
    if (CheckToken(TK_INT))
      return TRUE;
    int b = nVal;
    if (CheckToken(TK_OUT))
      return TRUE;
    col = RGB(r, g, b);
  }
  else
    UngetToken(token);

  Eva *p = NewEva(ET_RECT, ST_NONE);
  p->x1 = x1;
  p->y1 = y1;
  p->x2 = x2;
  p->y2 = y2;
  p->col = col;
  AddEva(p);

  return FALSE;
}

BOOL GetEllipse()
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  int x1 = nVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  int y1 = nVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  int x2 = nVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  int y2 = nVal;
  if (CheckToken(TK_OUT))
    return TRUE;

  COLORREF col = RGB(255, 100, 100); //////////
  int token = GetToken();
  if (token == TK_COLON)
  {
    if (CheckToken(TK_COLOR))
      return TRUE;
    if (CheckToken(TK_IN))
      return TRUE;
    if (CheckToken(TK_INT))
      return TRUE;
    int r = nVal;
    if (CheckToken(TK_COMMA))
      return TRUE;
    if (CheckToken(TK_INT))
      return TRUE;
    int g = nVal;
    if (CheckToken(TK_COMMA))
      return TRUE;
    if (CheckToken(TK_INT))
      return TRUE;
    int b = nVal;
    if (CheckToken(TK_OUT))
      return TRUE;
    col = RGB(r, g, b);
  }
  else
    UngetToken(token);

  Eva *p = NewEva(ET_ELLIPSE, ST_NONE);
  p->x1 = x1;
  p->y1 = y1;
  p->x2 = x2;
  p->y2 = y2;
  p->col = col;
  AddEva(p);

  return FALSE;
}

BOOL GetHyper()
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  int x = nVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckToken(TK_INT))
    return TRUE;
  int y = nVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckToken(TK_STR))
    return TRUE;
  TCHAR *str = new TCHAR[_tcslen(strIn) + 1];
  lstrcpy(str, strIn);
  if (CheckToken(TK_OUT))
    return TRUE;
  HFONT hFont = GetHyperFont();
  HDC hDC = GetDC(hMainWnd);
  HFONT hOld = (HFONT)SelectObject(hDC, hFont);
  SIZE sz;
  GetTextExtentPoint32(hDC, str, _tcslen(str), &sz);
  int xx = x + sz.cx;
  int yy = y + sz.cy;

  DeleteObject(SelectObject(hDC, hOld));
  ReleaseDC(hMainWnd, hDC);

  Eva *p = NewEva(ET_HYPER, ST_NONE);
  p->text = str;
  p->x1 = x;
  p->y1 = y;
  p->x2 = xx;
  p->y2 = yy;
  AddEva(p);

  return FALSE;
}

Creature *NewCreature(TCHAR *name)
{
  Creature *p = new Creature;
  ZeroMemory(p, sizeof(Creature));
  p->name = new TCHAR[lstrlen(name) + 1];
  lstrcpy(p->name, name);
  p->next = topCreature;
  topCreature = p;
  return p;
}

BOOL GetCreature()
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckToken(TK_STR))
    return TRUE;
  Creature *p = NewCreature(strIn);
  p->bCol = FALSE;
  if (CheckToken(TK_OUT))
    return TRUE;

  while (1)
  {
    int tk = GetToken();
    if (tk == TK_COLON)
    {
      int t = GetToken();
      if (t == TK_COLOR)
      {
        if (CheckToken(TK_IN))
          return TRUE;
        if (CheckToken(TK_INT))
          return TRUE;
        int r = nVal;
        if (CheckToken(TK_COMMA))
          return TRUE;
        if (CheckToken(TK_INT))
          return TRUE;
        int g = nVal;
        if (CheckToken(TK_COMMA))
          return TRUE;
        if (CheckToken(TK_INT))
          return TRUE;
        int b = nVal;
        if (CheckToken(TK_OUT))
          return TRUE;
        COLORREF col = RGB(r, g, b);
        p->bCol = TRUE;
        p->col = col;
      }
      else if (t == TK_TIMER)
      {
        if (CheckToken(TK_IN))
          return TRUE;
        if (CheckToken(TK_STR))
          return TRUE;
        Timer *pt = new Timer;
        ZeroMemory(pt, sizeof(Timer));
        pt->name = new TCHAR[lstrlen(strIn) + 1];
        lstrcpy(pt->name, strIn);
        if (CheckToken(TK_OUT))
          return TRUE;
        p->pTimer = pt;
      }
      else
      {
        wsprintf(strErrMsg, _T("bad token %s in GetCreature"), TokenStr(t));
        return TRUE;
      }
    }
    else
    {
      UngetToken(tk);
      break;
    }
  }

  int token = GetToken();
  if (token == TK_BEGIN)
  {
    curCreature = p;
    if (GetContents(FALSE))
      return TRUE;
    curCreature = NULL;
  }
  else
    UngetToken(token);
  return FALSE;
}

Creature *FindCreature(TCHAR *name)
{
  for (Creature *p = topCreature; p; p = p->next)
  {
    if (!lstrcmp(p->name, name))
      return p;
  }
  return NULL;
}

BOOL GetConCreature()
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckToken(TK_STR))
    return TRUE;
  Creature *p = FindCreature(strIn);
  if (!p)
  {
    wsprintf(strErrMsg, _T("Creature %s not found"), strIn);
    return TRUE;
  }
  if (CheckToken(TK_OUT))
    return TRUE;
  Eva *pp = NewEva(ET_CREATURE, ST_NONE);
  pp->pCreature = p;
  pp->x = 0;
  pp->y = 0;
  pp->bCol = FALSE;
  AddEva(pp);
  while (1)
  {
    int token = GetToken();
    if (token == TK_COLON)
    {
      int tk = GetToken();
      if (tk == TK_DWELL)
      {
        if (CheckToken(TK_IN))
          return TRUE;
        if (CheckNum())
          return TRUE;
        pp->x = dVal;
        if (CheckToken(TK_COMMA))
          return TRUE;
        if (CheckNum())
          return TRUE;
        pp->y = dVal;
        if (CheckToken(TK_OUT))
          return TRUE;
      }
      else if (tk == TK_COLOR)
      {
        if (CheckToken(TK_IN))
          return TRUE;
        if (CheckToken(TK_INT))
          return TRUE;
        int r = nVal;
        if (CheckToken(TK_COMMA))
          return TRUE;
        if (CheckToken(TK_INT))
          return TRUE;
        int g = nVal;
        if (CheckToken(TK_COMMA))
          return TRUE;
        if (CheckToken(TK_INT))
          return TRUE;
        int b = nVal;
        if (CheckToken(TK_OUT))
          return TRUE;
        pp->bCol = TRUE;
        pp->col = RGB(r, g, b);
      }
      else
      {
        wsprintf(strErrMsg, _T("bad token %s in GetCreature"), TokenStr(tk));
        return TRUE;
      }
    }
    else
    {
      UngetToken(token);
      break;
    }
  }
  if (p->bOnClick)
  {
    OnClick *px = new OnClick;
    ZeroMemory(px, sizeof(OnClick));
    px->pCreature = p;
    px->x1 = PixX(p->cx1 + pp->x, FALSE);
    px->y1 = PixY(p->cy1 + pp->y, FALSE, TRUE);
    px->x2 = PixX(p->cx2 + pp->x, FALSE);
    px->y2 = PixY(p->cy2 + pp->y, FALSE, TRUE);
    pp->pOnClick = px;
    px->next = topOnClick;
    topOnClick = px;
  }
  return FALSE;
}

BOOL GetCreatureOnClick()
{
  curCreature->bOnClick = TRUE;
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckNum())
    return TRUE;
  curCreature->cx1 = dVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckNum())
    return TRUE;
  curCreature->cy1 = dVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckNum())
    return TRUE;
  curCreature->cx2 = dVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckNum())
    return TRUE;
  curCreature->cy2 = dVal;
  if (CheckToken(TK_OUT))
    return TRUE;
  return FALSE;
}

BOOL GetCircle()
{
  if (CheckToken(TK_BEGINNAME))
    return TRUE;
  if (CheckNum())
    return TRUE;
  double x = dVal;
  if (CheckToken(TK_COMMA))
    return TRUE;
  if (CheckNum())
    return TRUE;
  double y = dVal;
  if (CheckToken(TK_ENDNAME))
    return TRUE;
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckNum())
    return TRUE;
  double r = dVal;
  if (CheckToken(TK_OUT))
    return TRUE;
  COLORREF col = RGB(0, 0, 0);
  BOOL bCol = FALSE;
  int token = GetToken();
  if (token == TK_COLON)
  {
    int tk = GetToken();
    if (tk == TK_COLOR)
    {
      if (CheckToken(TK_IN))
        return TRUE;
      if (CheckToken(TK_INT))
        return TRUE;
      int r = nVal;
      if (CheckToken(TK_COMMA))
        return TRUE;
      if (CheckToken(TK_INT))
        return TRUE;
      int g = nVal;
      if (CheckToken(TK_COMMA))
        return TRUE;
      if (CheckToken(TK_INT))
        return TRUE;
      int b = nVal;
      if (CheckToken(TK_OUT))
        return TRUE;
      col = RGB(r, g, b);
      bCol = TRUE;
    }
    else if (tk == TK_COLORVAL)
    {
      col = colValue;
      bCol = TRUE;
    }
    else
    {
      wsprintf(strErrMsg, _T("bad token %s in GetCircle"), TokenStr(tk));
      return TRUE;
    }
  }
  else
    UngetToken(token);
  Eva *p = NewEva(ET_ELLIPSE, ST_NONE);
  p->x1 = x - r;
  p->y1 = y - r;
  p->x2 = x + r;
  p->y2 = y + r;
  p->col = col;
  p->bCol = bCol;
  AddEva(p);
  return FALSE;
}

BOOL GetFor()
{
  if (CheckToken(TK_IN))
    return FALSE;
  if (CheckToken(TK_STR))
    return TRUE;
  DataSet *pD = FindDataSet(strIn);
  if (!pD)
  {
    wsprintf(strErrMsg, _T("DataSet %s not found in GetFor"), strIn);
    return TRUE;
  }
  if (CheckToken(TK_OUT))
    return TRUE;
  For *p = new For;
  ZeroMemory(p, sizeof(For));
  p->pD = pD;
  while (1)
  {
    int token = GetToken();
    if (token != TK_COLON)
    {
      UngetToken(token);
      break;
    }
    int tk = GetToken();
    if (tk == TK_SELECT)
    {
      if (GetSelectFor(p))
        return TRUE;
    }
    else if (tk == TK_SORTUP || tk == TK_SORTDOWN)
    {
      if (GetSortFor(p, tk == TK_SORTUP))
        return TRUE;
    }
    else
    {
      wsprintf(strErrMsg, _T("bad token %s in GetFor"), TokenStr(tk));
      return TRUE;
    }
  }
  if (CheckToken(TK_BEGIN))
    return TRUE;
  Eva *pp = NewEva(ET_FOR, ST_NONE);
  pp->pFor = p;
  AddEva(pp);
  curFor = p;
  if (GetContents(FALSE))
    return TRUE;
  curFor = NULL;
  return FALSE;
}


BOOL GetContents(BOOL bSingle)
{
  while (1)
  {
    int token = GetToken();
    if (token == TK_END)
      break;

    switch (token)
    {
    case TK_TEXT:
      if (GetText())
        return TRUE;
      break;
    case TK_HYPER:
      if (GetHyper())
        return TRUE;
      break;
    case TK_RECT:
      if (GetRect())
        return TRUE;
      break;
    case TK_ELLIPSE:
      if (GetEllipse())
        return TRUE;
      break;
    case TK_CIRCLE:
      if (GetCircle())
        return TRUE;
      break;
    case TK_NUM:
      if (GetNum(TRUE))
        return TRUE;
      break;
    case TK_NUMLET:
      if (GetNum(FALSE))
        return TRUE;
      break;
    case TK_VAL:
      if (GetVal(TRUE))
        return TRUE;
      break;
    case TK_VALLET:
      if (GetVal(FALSE))
        return TRUE;
      break;
    case TK_WORD:
      if (GetWord(TRUE))
        return TRUE;
      break;
    case TK_WORDLET:
      if (GetWord(FALSE))
        return TRUE;
      break;
    case TK_LET:
      if (curData)
      {
        if (GetLetData())
          return TRUE;
      }
      else
      {
        if (GetLet())
          return TRUE;
      }
      break;
    case TK_TABLE:
      if (GetTable())
        return TRUE;
      break;
    case TK_CROSS:
      if (GetCross())
        return TRUE;
      break;
    case TK_CELL:
      if (GetCell())
        return TRUE;
      break;
    case TK_SETCHARSIZE:
      if (GetSetCharSize())
        return TRUE;
      break;
    case TK_FONTSIZE:
      if (GetFontSize())
        return TRUE;
      break;
    case TK_IF:
      if (GetIf())
        return TRUE;
      break;
    case TK_FOR:
      if (GetFor())
        return TRUE;
      break;
    case TK_SETMEASUREUNIT:
      if (GetMeasUnit())
        return TRUE;
      break;
    case TK_FONT:
      if (GetFontBlock())
        return TRUE;
      break;
    case TK_CREATURE:
      if (GetConCreature())
        return TRUE;
      break;
    case TK_ONCLICK:
      if (curCreature)
      {
        if (GetCreatureOnClick())
          return TRUE;
      }
      else
      {
        wsprintf(strErrMsg, _T("no creature / OnClick"));
        return TRUE;
      }
      break;
    default:
      wsprintf(strErrMsg, _T("bad token %s in GetContents"), TokenStr(token));
      return TRUE;
    }
    
    if (bSingle)
      break;
  }
  return FALSE;
}

BOOL GetCommon()
{
  if (CheckToken(TK_BEGIN))
    return TRUE;
  if (GetContents(FALSE))
    return TRUE;
  return FALSE;
}

BOOL GetScene()
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckToken(TK_STR))
    return TRUE;
  TCHAR *name = new TCHAR[lstrlen(strIn) + 1];
  lstrcpy(name, strIn);
  if (CheckToken(TK_OUT))
    return TRUE;
  Set *p = new Set;
  ZeroMemory(p, sizeof(Set));
  p->name = name;

  Set*q = NULL;
  for (q = topSet; q->next; q = q->next)
    ;
  q->next = p;
  curSet = p;
  if (CheckToken(TK_BEGIN))
    return TRUE;
  if (GetContents(FALSE))
    return TRUE;

  return FALSE;
}

BOOL ParseAaf(TCHAR *in)
{
  topBuf = in;
  lineNo = 1;
  backedToken = -1;

  ClearSet(topSet);
  Set *pp = topSet->next, *qq;
  for ( ; pp; pp = qq)
  {
    qq = pp->next;
    ClearSet(pp);
    delete pp;
  }
  topSet->next = NULL;
  curSet = topSet;

  BOOL bIn = TRUE;
  while (bIn)
  {
    int token = GetToken();
    switch (token)
    {
    case TK_EOF:
      bIn = FALSE;
      break;
    case TK_LIST: case TK_DATA: case TK_MEMO:
      if (GetDataSet(token))
        return FALSE;
      break;
    case TK_LISTDATA:
      if (GetListData())
        return TRUE;
      break;
    case TK_COMMON:
      if (GetCommon())
        return TRUE;
      break;
    case TK_SCENE:
      if (GetScene())
        return TRUE;
      break;
    case TK_CREATURE:
      if (GetCreature())
        return TRUE;
      break;
    default:
      wsprintf(strErrMsg, _T("bad token %s in ParseAaf"), TokenStr(token));
      return TRUE;
    }
  }
  curSet = topSet->next ? topSet->next : topSet;
  Renew();
  bDirtyFlagD = FALSE;
  InvalidateRect(hMainWnd, NULL, TRUE);
  HMENU hMenu = GetMenu(hMainWnd);
  EnableMenuItem(hMenu, IDM_OPEN, MF_GRAYED);
  EnableMenuItem(hMenu, IDM_OPEN_D, MF_ENABLED);
  EnableMenuItem(hMenu, IDM_SAVE_D, MF_ENABLED);
  EnableMenuItem(hMenu, IDM_SAVEAS_D, MF_ENABLED);

  startTime = GetTickCount();
  return FALSE;
}

HANDLE hMode, hNames;

void OnPrint(HWND hWnd)
{
  int nPage = 1; //////////////////

  PRINTDLG pd;
  ZeroMemory(&pd, sizeof(pd));
  pd.lStructSize = sizeof(pd);
  pd.hwndOwner = hWnd;
  pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE | PD_HIDEPRINTTOFILE | PD_NOSELECTION;
  pd.hDevMode = hMode;
  pd.hDevNames;
  pd.nMinPage = 1;
  pd.nFromPage = 1;
  pd.nMaxPage = nPage;
  pd.nToPage = nPage;

  int ret = PrintDlg(&pd);
  if (!ret)
    return;

  if (hMode != pd.hDevMode)
    hMode = pd.hDevMode;
  if (hNames != pd.hDevNames)
    hNames = pd.hDevNames;

  HDC hDC = pd.hDC;
  prnXRes = GetDeviceCaps(hDC, LOGPIXELSX);
  prnYRes = GetDeviceCaps(hDC, LOGPIXELSY);

  int st = 0;
  int en = nPage;

  if (pd.Flags & PD_PAGENUMS)
  {
    st = pd.nFromPage - 1;
    en = pd.nToPage;
  }
  static DOCINFO di = { sizeof(DOCINFO), _T("Storm"), NULL };
  di.lpszDocName = _T("Storm");
  StartDoc(hDC, &di);
  for (int i = st; i < en; i++)
  {
    StartPage(hDC);
    OnPaint(hWnd, hDC, TRUE);
    EndPage(hDC);
  }
  EndDoc(hDC);
  DeleteDC(hDC);
}