/*――――――――――――――――――――――――――――――――――――――//
    houtei.c
//――――――――――――――――――――――――――――――――――――――*/

#include <windows.h>

#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <bolalg.h>
#include <bolbas.h>
#include <boldbe.h>
#include <bolfro.h>
#include <bolgui.h>
#include "houtei.h"

#define  MODNAM  PRGNAM
#define  MODVER  PRGVER
#define  PRGTIT  "支払調書作成"
#define  PRGDSC  "マイナンバー版支払調書作成"

//――――――――――――――――――――――――――――――――――――――//

  // テキストウィンドウ情報
typedef struct {
  HBLK  xb;
  HBLK  bk;
  HWND  hprn;
} TXWE;
typedef TXWE *pTXWE;

iANY LOCALF TXT_settext(HWND hwnd, HBLK xb)
{ // テキスト情報設定
  pTXWE we;

  we = halloc(sizeof(TXWE), TRUE); if (!we) { return (iANY)ierr(E_MEM);}
  SetWindowLong(hwnd, sizeof(iANY), (iANY)we);
  we->xb = xb;
  we->bk = DuplicateTXT(we->xb);
  return DefBOLTXTProc(hwnd, TXCM_SETTEXT, (uANY)xb, FALSE);
}

void LOCALF cmd_savetxt(HWND hwnd)
{ // 上書保存
  pTXWE we = (pTXWE)GetWindowLong(hwnd, sizeof(iANY));
  I4B re;

  if (SendMessage(hwnd, TXCM_GETMODE, 0, 0) & TXCS_DED) return;
  BusyMsg(hFRM(hwnd), "保存中...");
  reset_error();
  re = SaveTXT(we->xb, WindowText(hwnd));
  MoveTXT(we->bk, DuplicateTXT(we->xb));
  EndBusy(hFRM(hwnd));
  if (!re) {
    ErrorBox(hFRM(hwnd), format("'%s'の保存に失敗しました。", WindowText(hwnd)), NULL);
  }
}

#define  TFILTER  "テキストファイル (*.txt)\0*.txt;*.ini;*.log\0" "すべてのファイル (*.*)\0*.*\0"

void LOCALF cmd_saveas(HWND hwnd)
{ // 名前を付けて保存
  pTXWE we = (pTXWE)GetWindowLong(hwnd, sizeof(iANY));
  I4B re;
  STRBUF nam, bak;
  static OFNS ofn = { TFILTER, 1, NULL, OFN_HIDEREADONLY };

  if (SendMessage(hwnd, TXCM_GETMODE, 0, 0) & TXCS_DED) return;
  get_working_path(bak, sizeof(bak));
  (ofn.nam = nam)[0] = '\0';
  if (!SaveFileDlgBox(hwnd, &ofn)) goto _10;
  if (file_exists(nam)) {
    if (YesNoBox(hFRM(hwnd), format("'%s'は既に存在します。上書きしますか？", nam), NULL) != IDYES) goto _10;
  }
  BusyMsg(hFRM(hwnd), "保存中...");
  reset_error();
  re = SaveTXT(we->xb, nam);
  MoveTXT(we->bk, DuplicateTXT(we->xb));
  SetWindowText(hwnd, nam);
  EndBusy(hFRM(hwnd));
  if (!re) {
    ErrorBox(hFRM(hwnd), format("'%s'の保存に失敗しました。", nam), NULL);
  }
 _10:
  set_working_path(bak);
}

BOOL LOCALF frepTXT(HREP rp, I4B msg, I4B pg, U4B pa)
{ // テキスト描画
  I4B i;
  HWND hwnd = (HWND)pa;
  HBLK xb;
  CELL cel;
  static rSTR pt;
  static STRBUF bak;

  xb = (HBLK)SendMessage(hwnd, TXCM_GETTEXT, 0, 0);
  if (msg == REPM_PAGESETUP) {
    BusyMsg(hFRM(hwnd), "処理中...");
    get_working_path(bak, sizeof(bak));
    set_working_path(dirnam(WindowText(hwnd)));
    SetCanvasHDC(rp, NULL, PRNDPI);
    StartFlowText(rp);
    for (pt = Ptr(xb), i = 0; *pt != '\0'; i++) {
      FlowText(rp, &pt, BodyCell(&cel, rp));
    }
    SetNPages(rp, i);
    set_working_path(bak);
    EndBusy(hFRM(hwnd));
  } else if (msg == REPM_STARTPRINT) {
    get_working_path(bak, sizeof(bak));
    set_working_path(dirnam(WindowText(hwnd)));
    StartFlowText(rp);
    pt = Ptr(xb);
  } else if (msg == REPM_DRAWPAGE || msg == REPM_SKIPPAGE) {
    FlowText(rp, &pt, BodyCell(&cel, rp));
  } else if (msg == REPM_ENDPRINT) {
    set_working_path(bak);
  }
  return TRUE;
}

HWND LOCALF CreatePrintPreview(HWND hwnd, pREPI ri);

void LOCALF cmd_printtxt(HWND hwnd)
{ // テキスト印刷
  pTXWE we = (pTXWE)GetWindowLong(hwnd, sizeof(iANY));
  static REPI rpi = { NULL, PSZ_A4P, frepTXT, 0 };

  rpi.tit = basnamx(WindowText(hwnd));
  rpi.pa = (U4B)hwnd;
  we->hprn = CreatePrintPreview(hFRM(hwnd), &rpi);
}

void LOCALF cmd_searchtxt(HWND hwnd, BOOL sw)
{ // 文字列検索
  static STXP stx;

  if (sw == 0) {
    if (!SearchTextDlgBox(hwnd, &stx, 0)) return;
  }
  SendMessage(hwnd, TXCM_SEARCH, (uANY)stx.str, stx.flg);
}

iANY LOCALF TXT_command(HWND hwnd, uANY wpa, iANY lpa)
{ // コマンド
  switch (LOU2B(wpa)) {
  case IDM_SAVE:
    cmd_savetxt(hwnd); break;
  case IDM_SAVEAS:
    cmd_saveas(hwnd); break;
  case IDM_PRINT:
    cmd_printtxt(hwnd); break;
  case IDM_CUT:
    SendMessage(hwnd, WM_CUT, 0, 0); break;
  case IDM_COPY:
    SendMessage(hwnd, WM_COPY, 0, 0); break;
  case IDM_PASTE:
    SendMessage(hwnd, WM_PASTE, 0, 0); break;
  case IDM_DELETE:
    SendMessage(hwnd, WM_CLEAR, 0, 0); break;
  case IDM_SEARCH:
    cmd_searchtxt(hwnd, 0); break;
  case IDM_NSEARCH:
    cmd_searchtxt(hwnd, 1); break;
  }
  return 0;
}

iANY LOCALF TXT_close(HWND hwnd)
{ // テキストウィンドウを閉じる
  pTXWE we = (pTXWE)GetWindowLong(hwnd, sizeof(iANY));
  I4B sv, re;

  sv = !(SendMessage(hwnd, TXCM_GETMODE, 0, 0) & TXCS_DED) && !!CompareTXT(we->xb, we->bk);
  if (CurAuth() < 2) { sv = FALSE;}
  if (sv) {
    re = YesNoCanBox(hFRM(hwnd), format("'%s'は変更されています。保存しますか？", WindowText(hwnd)), NULL);
    switch (re) {
    case IDYES: sv = TRUE;  break;
    case IDNO:  sv = FALSE; break;
    default: return 0;
    }
  }
  if (sv) {
    BusyMsg(hFRM(hwnd), "保存中...");
    reset_error();
    re = SaveTXT(we->xb, WindowText(hwnd));
    EndBusy(hFRM(hwnd));
    if (!re) {
      ErrorBox(hFRM(hwnd), format("'%s'の保存に失敗しました。", WindowText(hwnd)), NULL);
    }
  }
  return DefBOLTXTProc(hwnd, WM_CLOSE, 0, 0);
}

iANY LOCALF TXT_destroy(HWND hwnd)
{ // テキストウィンドウ終了処理
  pTXWE we = (pTXWE)GetWindowLong(hwnd, sizeof(iANY));

  if (IsWindow(we->hprn)) { SendMessage(hFRM(hwnd), FRMM_DESTROYCHILD, (uANY)we->hprn, 0);}
  DeleteTXT(we->bk);
  UnloadTXT(we->xb);
  hfree(we);
  return DefBOLTXTProc(hwnd, WM_DESTROY, 0, 0);
}

static I1B  TXT_POS[32];

void LOCALF dispcurpos(HWND hwnd)
{ // カーソル位置表示
  I4B ib = error_message_index_base();
  U4B rc;
  HWND htob;

  htob = GetDlgItem(hFRM(hwnd), IDTOB); if (!htob) return;
  rc = SendMessage(hwnd, TXCM_GETCUR, 0, 0);
  sprintfs(TXT_POS, "%3d 行、%d 列", LOU2B(rc) + ib, HIU2B(rc) + ib);
  SetDlgItemText(htob, IDPOS, TXT_POS);
}

iANY LOCALF TXT_WndProc(HWND hwnd, U4B umsg, uANY wpa, iANY lpa)
{ // ウィンドウプロシージャ
  switch (umsg) {
  case TXCM_SETTEXT:
    return TXT_settext(hwnd, (HBLK)wpa);
  case TXCM_SETCUR:
  case TXCM_SETTOP:
  case TXCM_UPDATE:
  case TXCM_MOVECUR:
    DefBOLTXTProc(hwnd, umsg, wpa, lpa);
    dispcurpos(hwnd);
    return 0;
  case WM_COMMAND:
    return TXT_command(hwnd, wpa, lpa);
  case WM_CLOSE:
    return TXT_close(hwnd);
  case WM_DESTROY:
    return TXT_destroy(hwnd);
  }
  return DefBOLTXTProc(hwnd, umsg, wpa, lpa);
}

I4B  LOCALF TXT_appstatus(HWND hwnd)
{ // テキストウィンドウの状態の取得
  dispcurpos(hwnd);
  return 0;
}

//――――――――――――――――――――――――――――――――――――――//

void LOCALF cmd_prevpage(HWND hwnd)
{ // 前ページ
  I4B ip;

  ip = SendMessage(hwnd, PRCM_GETPAGE, 0, 0);
  if (ip > 0) { SendMessage(hwnd, PRCM_SETPAGE, ip - 1, TRUE);}
}

void LOCALF cmd_nextpage(HWND hwnd)
{ // 次ページ
  I4B ip, np;

  ip = SendMessage(hwnd, PRCM_GETPAGE, 0, 0);
  np = SendMessage(hwnd, PRCM_NPAGES, 0, 0);
  if (ip < np - 1) { SendMessage(hwnd, PRCM_SETPAGE, ip + 1, TRUE);}
}

  // 拡大縮小倍率
static I4B  ZSCL[] = { 25, 50, 75, 100, 125, 150, 200, 300, 400 };

void LOCALF cmd_zoomin(HWND hwnd)
{ // 拡大
  I4B i, s1, s2, xy, x0, y0;
  RECT rct;

  GetClientRect(hwnd, &rct);
  xy = SendMessage(hwnd, PRCM_GETORIGIN, 0, 0);
  s1 = SendMessage(hwnd, PRCM_GETSCALE, 0, 0);
  for (i = 0; i < _countof(ZSCL) - 1; i++) {
    if (s1 < ZSCL[i]) break;
  }
  s2 = ZSCL[i];
  x0 = ((I2B)LOU2B(xy) + rct.rgt / 2) * s2 / s1 - rct.rgt / 2;
  y0 = ((I2B)HIU2B(xy) + rct.btm / 2) * s2 / s1 - rct.btm / 2;
  SendMessage(hwnd, PRCM_SETORIGIN, MKU4B(x0, y0), FALSE);
  SendMessage(hwnd, PRCM_SETSCALE, s2, TRUE);
}

void LOCALF cmd_zoomout(HWND hwnd)
{ // 縮小
  I4B i, s1, s2, xy, x0, y0;
  RECT rct;

  GetClientRect(hwnd, &rct);
  xy = SendMessage(hwnd, PRCM_GETORIGIN, 0, 0);
  s1 = SendMessage(hwnd, PRCM_GETSCALE, 0, 0);
  for (i = _countof(ZSCL) - 1; i > 0; i--) {
    if (s1 > ZSCL[i]) break;
  }
  s2 = ZSCL[i];
  x0 = ((I2B)LOU2B(xy) + rct.rgt / 2) * s2 / s1 - rct.rgt / 2;
  y0 = ((I2B)HIU2B(xy) + rct.btm / 2) * s2 / s1 - rct.btm / 2;
  SendMessage(hwnd, PRCM_SETORIGIN, MKU4B(x0, y0), FALSE);
  SendMessage(hwnd, PRCM_SETSCALE, s2, TRUE);
}

void LOCALF cmd_samescale(HWND hwnd)
{ // 等倍表示
  SendMessage(hwnd, PRCM_SETORIGIN, 0, FALSE);
  SendMessage(hwnd, PRCM_SETSCALE, 100, TRUE);
}

void LOCALF cmd_autoscale(HWND hwnd)
{ // 全体表示
  I4B pw, ph, sc;
  RECT rct;

  GetClientRect(hwnd, &rct);
  pw = SendMessage(hwnd, PRCM_PAPERWIDTH, 0, 0);
  ph = SendMessage(hwnd, PRCM_PAPERHEIGHT, 0, 0);
  if (rct.rgt * ph < rct.btm * pw) {
    sc = rct.rgt * 100 / pw;
  } else {
    sc = rct.btm * 100 / ph;
  }
  if (sc > 100) { sc = 100;}
  SendMessage(hwnd, PRCM_SETSCALE, sc, TRUE);
}

void LOCALF cmd_invert(HWND hwnd)
{ // 拡大縮小(等倍表示/全体表示切替)
  I4B sc;

  sc = SendMessage(hwnd, PRCM_GETSCALE, 0, 0);
  if (sc == 100) { cmd_autoscale(hwnd);} else { cmd_samescale(hwnd);}
}

void LOCALF cmd_setprn(HWND hwnd)
{ // 用紙設定
  HREP rp;

  rp = (HREP)SendMessage(hwnd, PRCM_GETREPORT, 0, 0);
  PageSetup(hwnd, rp);
  SendMessage(hwnd, PRCM_SETREPORT, (uANY)rp, TRUE);
}

void LOCALF cmd_print(HWND hwnd)
{ // 印刷
  HREP rp;

  rp = (HREP)SendMessage(hwnd, PRCM_GETREPORT, 0, 0);
  PrintReport(hwnd, rp);
}

iANY LOCALF PRN_command(HWND hwnd, uANY wpa, iANY lpa)
{ // コマンド
  switch (LOU2B(wpa)) {
  case IDM_PREVPAGE:
    cmd_prevpage(hwnd); break;
  case IDM_NEXTPAGE:
    cmd_nextpage(hwnd); break;
  case IDM_ZOOMIN:
    cmd_zoomin(hwnd); break;
  case IDM_ZOOMOUT:
    cmd_zoomout(hwnd); break;
  case IDM_SAMESCALE:
    cmd_samescale(hwnd); break;
  case IDM_AUTOSCALE:
    cmd_autoscale(hwnd); break;
  case IDM_INVERT:
    cmd_invert(hwnd); break;
  case IDM_SETPRN:
    cmd_setprn(hwnd); break;
  case IDM_PRINT:
    cmd_print(hwnd); break;
  }
  return 0;
}

iANY LOCALF PRN_WndProc(HWND hwnd, U4B umsg, uANY wpa, iANY lpa)
{ // ウィンドウプロシージャ
  switch (umsg) {
  case WM_COMMAND:
    return PRN_command(hwnd, wpa, lpa);
  case WM_DESTROY:
    DeleteReport((HREP)SendMessage(hwnd, PRCM_GETREPORT, 0, 0)); break;
  }
  return DefBOLPRNProc(hwnd, umsg, wpa, lpa);
}

#define  APPS_PREVPAGEAVAILABLE    0x00000010
#define  APPS_NEXTPAGEAVAILABLE    0x00000020
#define  APPS_LOWERLIMITSCALE      0x00000040
#define  APPS_UPPERLIMITSCALE      0x00000080
#define  APPS_SKIPPAGESETUP        0x00000100

#define  APPS_PVP  APPS_PREVPAGEAVAILABLE
#define  APPS_NXP  APPS_NEXTPAGEAVAILABLE
#define  APPS_LSC  APPS_LOWERLIMITSCALE
#define  APPS_USC  APPS_UPPERLIMITSCALE
#define  APPS_SKP  APPS_SKIPPAGESETUP

static I1B  PRN_PAG[32];
static I1B  PRN_SCL[32];

I4B  LOCALF PRN_appstatus(HWND hwnd)
{ // 印刷プレビューウィンドウの状態の取得
  I4B ip, np, sc, sta = 0;
  HREP rp;

  rp = (HREP)SendMessage(hwnd, PRCM_GETREPORT, 0, 0);
  ip = SendMessage(hwnd, PRCM_GETPAGE, 0, 0);
  np = SendMessage(hwnd, PRCM_NPAGES, 0, 0);
  if (ip > 0) { sta |= APPS_PVP;}
  if (ip < np - 1) { sta |= APPS_NXP;}
  if (PageSetupInfo(rp)->adj & RSW_SKP) { sta |= APPS_SKP;}
  sprintfs(PRN_PAG, "%3d / %d", ip + 1, np);
  sc = SendMessage(hwnd, PRCM_GETSCALE, 0, 0);
  if (sc <= ZSCL[0]) { sta |= APPS_LSC;} else if (sc >= ZSCL[_countof(ZSCL) - 1]) { sta |= APPS_USC;}
  sprintfs(PRN_SCL, "%3d ％", sc);
  return sta;
}

HWND LOCALF CreatePrintPreviewX(HWND hwnd, pREPI ri, WNDPROC prc)
{ // 印刷プレビューウィンドウの作成
  HWND hprn;
  HREP rp;
  static CREI cri = { "BOLPRN", wsCHV, "印刷プレビュー", IDPRN, };

  if (IsWindow(hprn = GetDlgItem(hwnd, IDPRN))) { SendMessage(hwnd, FRMM_DESTROYCHILD, (uANY)hprn, 0);}
  rp = CreateReport(ri); if (!rp) return NULL;
  if (!ReportSetup(hwnd, rp)) goto _10;
  if (!PageSetup(hwnd, rp)) goto _10;
  cri.pa = (U4B)rp;
  hprn = (HWND)SendMessage(hwnd, FRMM_CREATECHILD, (uANY)&cri, (iANY)prc); if (!hprn) goto _10;
  return hprn;

 _10:
  DeleteReport(rp);
  return NULL;
}

HWND LOCALF CreatePrintPreview(HWND hwnd, pREPI ri)
{ // 印刷プレビューウィンドウの作成
  return CreatePrintPreviewX(hwnd, ri, PRN_WndProc);
}

//――――――――――――――――――――――――――――――――――――――//

static MNUI POPMI[] = {
  { mfP,  0,             ""                        }, // POP0
  { mf_,  IDM_CUT,       "切り取り(&T)\tCtrl+X"    },
  { mf_,  IDM_COPY,      "コピー(&C)\tCtrl+C"      },
  { mf_,  IDM_PASTE,     "貼り付け(&P)\tCtrl+V"    },
  { mf_,  0,             ""                        },
  { mf_,  IDM_CLEAR,     "クリア(&A)\tDel"         },
  { mf_,  0,             ""                        },
  { mfE,  IDM_OPENLINK,  "リンクを開く(&O)"        },
  { mfP,  0,             ""                        }, // POP1
  { mf_,  IDM_CUT,       "切り取り(&T)\tCtrl+X"    },
  { mf_,  IDM_COPY,      "コピー(&C)\tCtrl+C"      },
  { mf_,  IDM_PASTE,     "貼り付け(&P)\tCtrl+V"    },
  { mf_,  0,             ""                        },
  { mfE,  IDM_CLEAR,     "クリア(&A)\tDel"         },
  { mfP,  0,             ""                        }, // POP2
  { mf_,  IDM_CUT,       "切り取り(&T)\tCtrl+X"    },
  { mf_,  IDM_COPY,      "コピー(&C)\tCtrl+C"      },
  { mf_,  IDM_PASTE,     "貼り付け(&P)\tCtrl+V"    },
  { mf_,  0,             ""                        },
  { mf_,  IDM_INSERT,    "挿入(&I)\tCtrl+E"        },
  { mf_,  IDM_DELETE,    "削除(&D)\tCtrl+D"        },
  { mfE,  IDM_CLEAR,     "クリア(&A)\tDel"         },
  { mfPE, 0,             ""                        }, // POP3
  { mf_,  IDM_ARRANGE,   "表示項目(&A)..."         },
  { mf_,  IDM_FILTER,    "絞り込み(&F)..."         },
  { mfE,  IDM_SORT,      "並べ替え(&S)..."         },
};
static MNUS POPMS = { POPMI, _countof(POPMI) };

static HMENU hmnuPop, hmnuPo0, hmnuPo1, hmnuPo2, hmnuPo3;

void LOCALF InitPopupMenus(void)
{ // ポップアップメニュー設定
  hmnuPop = CreateMenuBar(&POPMS);
  hmnuPo0 = GetSubMenu(hmnuPop, 0);
  hmnuPo1 = GetSubMenu(hmnuPop, 1);
  hmnuPo2 = GetSubMenu(hmnuPop, 2);
  hmnuPo3 = GetSubMenu(hmnuPop, 3);
}

void LOCALF TermPopupMenus(void)
{ // ポップアップメニュー削除
  DestroyMenu(hmnuPop);
}

BOOL LOCALF LoadUserVIW(HTBL tvu)
{ // ユーザー定義表示形式セット読込
  I4B re = FALSE;
  STRBUF bak;

  get_working_path(bak, sizeof(bak));
  set_working_path(UserPath());
  if (file_exists(TableFileName(tvu))) {
    re = LoadBTB(tvu);
  }
  set_working_path(bak);
  return re;
}

BOOL LOCALF SaveUserVIW(HTBL tvu)
{ // ユーザー定義表示形式セット保存
  I4B sw, re = FALSE;
  STRBUF bak;

  get_working_path(bak, sizeof(bak));
  set_working_path(UserPath());
  sw = disable_error_reporting();
  if (file_exists(TableFileName(tvu)) || NTableRows(tvu) > 0) {
    re = SaveBTB(tvu);
  }
  if (sw) { enable_error_reporting();}
  set_working_path(bak);
  return re;
}

  // 表ウィンドウ設定情報
typedef struct {
  HOPN  op;
  I4B   flg;
  rSTR  tit;
  rVIWS vs;
  I4B   iv0;
} TBWI;
typedef TBWI *pTBWI;

  // 表ウィンドウ情報
typedef struct {
  HOPN  op;   // -+
  I4B   flg;  //  |
  rSTR  tit;  //  |
  rVIWS vs;   //  |= TBWI
  I4B   iv0;  // -+
  HWND  hprn;
  HWND  hsum;
  HTBL  tvs;
  HTBL  tvu;
  HTBL  tv;
  HBUF  bv;
  HTBL  tg;
  HTBL  tsm;
} TBWE;
typedef TBWE *pTBWE;

rSTR LOCALF defface(void);
I4B  LOCALF defptsz(void);
I4B  LOCALF optflg(void);
HWND LOCALF CreateTableWindow(HWND hwnd, I4B id, pTBWI wi);

iANY LOCALF TBL_settable(HWND hwnd, pTBWI wi)
{ // 表情報設定
  pTBWE we;
  I4B ir, re;
  HTBL tb;
  VIWI viw;

  we = halloc(sizeof(TBWE), TRUE); if (!we) { return (iANY)ierr(E_MEM);}
  SetWindowLong(hwnd, sizeof(iANY), (iANY)we);
  *(pTBWI)we = *wi;
  we->flg |= optflg();
  if (IsReadOnly(we->op)) { we->flg |= (TBCS_DED|TBCS_DSE);}
  SendMessage(hwnd, TBCM_SETMODE, we->flg, FALSE);
  tb = OHTBL(we->op);
  re = DefBOLTBLProc(hwnd, TBCM_SETTABLE, (uANY)tb, TRUE);
  if (!(we->flg & TBCS_DCV)) {
    if (we->vs) {
      we->tvs = CreateVIWTable(0, we->vs); if (!we->tvs) { return re;}
    }
    we->tvu = CreateVIWTable(32, NULL); if (!we->tvu) { return re;}
    SetTableFileName(we->tvu, addext(TableName(tb), ".VIW"));
    we->tv = CreateVIWTable(32, NULL); if (!we->tv) { return re;}
    we->bv = CreateRowBuf(we->tv); if (!we->bv) { return re;}
    LoadUserVIW(we->tvu);
    if (we->tvs) { CopyTable(we->tv, we->tvs);}
    MergeTable(we->tv, we->tvu);
    if (we->tvs && we->iv0 >= 0 && we->iv0 < NTableRows(we->tvs)) {
      GetTableRow(we->bv, we->tv, we->iv0);
    } else if ((ir = GetLastVIW(we->tv)) >= 0) {
      GetTableRow(we->bv, we->tv, ir);
    } else if (we->tvs && NTableRows(we->tvs) > 0) {
      GetTableRow(we->bv, we->tv, 0);
    }
    SendMessage(hwnd, TBCM_CHANGEVIEW, (uANY)SetVIWInfo(&viw, we->bv), TRUE);
  }
  return re;
}

iANY LOCALF TBL_trackpopup(HWND hwnd, rPOSI lc)
{ // ポップアップメニュー
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));
  I4B s0, s1, s2, sta;
  HVIW vw;
  HMENU hmnu;
  RBLK r;
  POINT pnt;
  STRBUF bf;

  s0 = SendMessage(hwnd, TBCM_GETSEL, (uANY)&r, 0);
  sta = AppStatus(hFRM(hwnd));
  if (lc->ir >= 0 && lc->ic >= 0 && !(s0 && lc->ir >= r.r1 && lc->ir <= r.r2)) {
    vw = (HVIW)SendMessage(hwnd, TBCM_GETVIEW, 0, 0);
    PrintViewCellValueF(bf, vw, lc->ir, lc->ic, 0);
    if ((ViewColumnFlagsF(vw, lc->ic) & CF_LNK) && bf[0] != '\0') {
      hmnu = hmnuPo0;
    } else {
      hmnu = hmnuPo1;
    }
    s1 = (!(we->flg & TBCS_DED));
    s2 = (s1 && (sta & APPS_CFT));
    EnableMenuItem(hmnu, IDM_CUT,    s1? mf_: mfG);
    EnableMenuItem(hmnu, IDM_PASTE,  s2? mf_: mfG);
    EnableMenuItem(hmnu, IDM_CLEAR,  s1? mf_: mfG);
  } else if (lc->ir >= 0) {
    hmnu = hmnuPo2;
    s1 = (!(we->flg & (TBCS_DED|TBCS_DSE)));
    s2 = (s1 && (sta & APPS_CFB));
    EnableMenuItem(hmnu, IDM_CUT,    s1? mf_: mfG);
    EnableMenuItem(hmnu, IDM_PASTE,  s2? mf_: mfG);
    EnableMenuItem(hmnu, IDM_INSERT, s1? mf_: mfG);
    EnableMenuItem(hmnu, IDM_DELETE, s1? mf_: mfG);
    EnableMenuItem(hmnu, IDM_CLEAR,  s1? mf_: mfG);
  } else { // lc->ic < 0
    hmnu = hmnuPo3;
  }
  GetCursorPos(&pnt);
  TrackPopupMenuEx(hmnu, TPM_RIGHTBUTTON, pnt.x, pnt.y, hwnd, NULL);
  return 0;
}

void LOCALF cmd_save(HWND hwnd)
{ // 上書保存
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));
  I4B re;

  if (SendMessage(hwnd, TBCM_GETMODE, 0, 0) & TBCS_DED) return;
  BusyMsg(hFRM(hwnd), "処理中...");
  reset_error();
  re = CheckTable(OHTBL(we->op));
  EndBusy(hFRM(hwnd));
  if (!re) {
    ErrorBox(hFRM(hwnd), "エラーが見つかりました。\n保存はキャンセルされます。", NULL); return;
  }
  BusyMsg(hFRM(hwnd), "保存中...");
  reset_error();
  re = SaveTable(we->op);
  if (re) { SendMessage(hwnd, TBCM_UPDATE, FALSE, TRUE);}
  EndBusy(hFRM(hwnd));
  if (!re) {
    ErrorBox(hFRM(hwnd), format("'%s'の保存に失敗しました。", WindowText(hwnd)), NULL);
  }
}

void LOCALF cmd_import(HWND hwnd)
{ // テキスト取込
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));
  I4B re;
  HVIW vw;
  STRBUF nam, bak;
  static OFNS ofn = { TFILTER, 1, NULL, OFN_HIDEREADONLY };
  static CSVI csi = { '\t', 0, TSW_NAM };

  if (SendMessage(hwnd, TBCM_GETMODE, 0, 0) & TBCS_DED) return;
  get_working_path(bak, sizeof(bak));
  (ofn.nam = nam)[0] = '\0';
  re = OpenFileDlgBox(hwnd, &ofn);
  set_working_path(bak);
  if (!re) return;
  if (!CSVInfoDlgBox(hwnd, &csi)) return;
  vw = (HVIW)SendMessage(hwnd, TBCM_GETVIEW, 0, 0);
  BusyMsg(hFRM(hwnd), "読込中...");
  reset_error();
  re = ReadCSV(vw, nam, &csi);
  if (re) { SendMessage(hwnd, TBCM_UPDATE, FALSE, TRUE);}
  EndBusy(hFRM(hwnd));
  if (!re) {
    ErrorBox(hFRM(hwnd), format("'%s'の読込に失敗しました。", WindowText(hwnd)), NULL);
  }
}

void LOCALF cmd_export(HWND hwnd)
{ // テキスト書出
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));
  I4B re;
  HVIW vw;
  STRBUF nam, bak;
  static OFNS ofn = { TFILTER, 1, NULL, OFN_HIDEREADONLY };
  static CSVI csi = { '\t', 0, TSW_NAM };

  get_working_path(bak, sizeof(bak));
  (ofn.nam = nam)[0] = '\0';
  re = SaveFileDlgBox(hwnd, &ofn);
  set_working_path(bak);
  if (!re) return;
  if (file_exists(nam)) {
    if (YesNoBox(hFRM(hwnd), format("'%s'は既に存在します。上書きしますか？", nam), NULL) != IDYES) return;
  }
  if (!CSVInfoDlgBox(hwnd, &csi)) return;
  vw = (HVIW)SendMessage(hwnd, TBCM_GETVIEW, 0, 0);
  BusyMsg(hFRM(hwnd), "書出中...");
  reset_error();
  re = WriteCSV(vw, nam, &csi);
  if (re) { SendMessage(hwnd, TBCM_UPDATE, FALSE, TRUE);}
  EndBusy(hFRM(hwnd));
  if (!re) {
    ErrorBox(hFRM(hwnd), format("'%s'の書出に失敗しました。", WindowText(hwnd)), NULL);
  }
}

  // 表描画情報
typedef struct {
  I4B   ns;
  F8B   *cw;
  F8B   dcw, drh, daw;
} TBLP;

BOOL LOCALF frepTBL(HREP rp, I4B msg, I4B pg, U4B pa)
{ // 表の描画
  I4B i, j, nr, nc, br, md, op, zr;
  HVIW vw;
  TBLP *px;
  CELL cel;
  GRID grd;
  WHSZ siz;
  STRBUF bf;

  vw = (HVIW)SendMessage((HWND)pa, TBCM_GETVIEW, 0, 0);
  if (msg == REPM_CREATE) {
    nc = NTableColumns(ViewHTBL(vw));
    px = halloc(sizeof(TBLP) + nc * 8, TRUE); if (!px) return FALSE;
    px->cw = (pANY)((pBIN)px + sizeof(TBLP));
    SetReportExtra(rp, (U4B)px);
    return TRUE;
  }
  px = (TBLP *)ReportExtra(rp);
  nr = NViewRows(vw);
  nc = NViewColumns(vw);
  md = SendMessage((HWND)pa, TBCM_GETMODE, 0, 0);
  op = ((md & TBCS_UCN)? PVF_NAM: (md & TBCS_UCI)? PVF_IDX: PVF_TIT) | ((md & TBCS_ZBI)? PVF_ZBI: PVF_OBI);
  zr = (md & TBCS_HCH)? 0: 1;
  if (msg == REPM_SETUP) {
    SetTextFont(rp, defface(), PT2MM(defptsz()), 0, 0);
    AveCharSize(&siz, rp);
    px->dcw = siz.w;
    px->drh = siz.h * 1.2;
    px->daw = 0.0;
    for (j = 0; j < nc; j++) {
      px->cw[j] = px->dcw * ((F8B)ViewColumnWidthF(vw, j) + 0.5);
      px->daw += px->cw[j];
    }
    SetReqBodyWidth(rp, px->daw, 0);
    SetCellMargin(rp, px->dcw / 3, 0.0);
  } else if (msg == REPM_PAGESETUP) {
    px->ns = (I4B)(BodyHeight(rp) / px->drh) - zr;
    SetNPages(rp, ((nr - 1) / px->ns) + 1);
  } else if (msg == REPM_DRAWPAGE) {
    SetTextFont(rp, defface(), PT2MM(defptsz()), 0, 0);
    HeaderCell(&cel, rp);
    CellText(rp, &cel, ReportTitle(rp), DT_LFT);
    CellText(rp, &cel, dformat("yyyy/mm/dd", ReportDate(rp)), DT_RGT);
    SetGrid1(&grd, BodyLeft(rp), BodyTop(rp), nc, px->ns + zr, px->cw, px->drh);
    br = (pg - 1) * px->ns;
    for (i = -zr; i < px->ns; i++) {
      for (j = 0; j < nc; j++) {
        if (px->cw[j] == 0.0) continue;
        PrintViewCellValueF(bf, vw, (i >= 0)? br + i: -1, j, op);
        GridCellText(rp, &grd, i + zr, j, RST_R_M, bf, DT_AUT|((i >= 0)? ViewColumnTextAlignF(vw, j): DT_CEN));
      }
    }
    FooterCell(&cel, rp);
    CellText(rp, &cel, format("- %d -", pg), DT_CEN);
  } else if (msg == REPM_DESTROY) {
    hfree(px);
  }
  return TRUE;
}

void LOCALF cmd_printtbl(HWND hwnd)
{ // 表印刷
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));
  static REPI rpi = { NULL, PSZ_A4P, frepTBL, 0 };

  if (IsWindow(we->hprn)) { SendMessage(we->hprn, WM_CLOSE, 0, 0);}
  rpi.tit = TableTitle(OHTBL(we->op));
  rpi.pa = (U4B)hwnd;
  we->hprn = CreatePrintPreview(hFRM(hwnd), &rpi);
}

void LOCALF cmd_search(HWND hwnd, BOOL sw)
{ // 文字列検索
  static STXP stx;

  if (sw == 0) {
    if (!SearchTextDlgBox(hwnd, &stx, 1)) return;
  }
  SendMessage(hwnd, TBCM_SEARCH + sw, (uANY)stx.str, stx.flg);
}

  // 表示形式名
typedef struct {
  I1B   nam[32];
} VNMP;

BOOL LOCALF VNM_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // 表示形式名ダイアログプロシージャ
  VNMP *pa;

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (VNMP *)lpa));
    SetESTR(hdlg, 101, pa->nam);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    pa = (VNMP *)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case IDOK:
      GetESTR(hdlg, 101, pa->nam, sizeof(pa->nam) - 1);
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

BOOL LOCALF GetVNM(HWND hwnd, VNMP *pa, BOOL sw)
{ // 表示形式名
  static DLGI dit[] = {
    { C_SLFT|wsG,   8, 10, 48,  8,  -1, "名　前(&N):" },
    { C_ESTR|wsT,  56,  8, 48, 12, 101, "" },
    { C_BDEF|wsGT, 54, 60, 48, 14, IDOK, "OK" },
    { C_BPSH|wsT, 106, 60, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 160, 80, NULL };

  dlg.tit = (sw == 0)? "表示形式登録": "名前変更";
  return DlgBoxInput(hwnd, &dlg, VNM_DlgProc, (iANY)pa) == IDOK;
}

  // 表示形式設定情報
typedef struct {
  HTBL  tv;
  HBUF  bv;
} VIWP;

#define  IDREG  22
#define  IDCHG  23
#define  IDDEL  24

BOOL LOCALF VIW_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // 表示形式設定ダイアログプロシージャ
  I4B ir, ix, sw, ud = 0;
  VIWP *pa;
  VNMP dat;
  static TBCI tci = { 10, "nam, ud", NULL, NULL, TTF_ADJ, 1, 0 };

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (VIWP *)lpa));
    if ((ir = SearchVIWTableRow(pa->tv, pa->bv)) < 0) {
      SetRowBufCellValue(pa->bv, 0, "未登録");
      SetRowBufCellValue(pa->bv, 1, I4BBox(1));
      SetNewTableRow(pa->tv, pa->bv);
      ir = NTableRows(pa->tv) - 1;
    }
    SetTSEL(hdlg, 101, ir, pa->tv, &tci);
    sw = (strcmp(TableRow(pa->tv, ir), "未登録") != 0);
    GetTableCellValue(&ud, pa->tv, ir, 1);
    EnableDlgItem(hdlg, IDREG, ud && !sw);
    EnableDlgItem(hdlg, IDCHG, ud && sw);
    EnableDlgItem(hdlg, IDDEL, ud && sw);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    pa = (VIWP *)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case MKU4B(101, APPN_SELCHANGE): _10:
      if (NTableRows(pa->tv) > 0) {
        ir = GetTSEL(hdlg, 101);
        sw = (strcmp(TableRow(pa->tv, ir), "未登録") != 0);
        GetTableCellValue(&ud, pa->tv, ir, 1);
      } else { ud = sw = 0;}
      EnableDlgItem(hdlg, IDREG, ud && !sw);
      EnableDlgItem(hdlg, IDCHG, ud && sw);
      EnableDlgItem(hdlg, IDDEL, ud && sw);
     _20: RedrawWindow(GetDlgItem(hdlg, 101), NULL, NULL, RDW_INVALIDATE|RDW_UPDATENOW);
      SendDlgItemMessage(hdlg, IDREG, BM_SETSTYLE, bsP, TRUE);
      SendDlgItemMessage(hdlg, IDCHG, BM_SETSTYLE, bsP, TRUE);
      SendDlgItemMessage(hdlg, IDDEL, BM_SETSTYLE, bsP, TRUE);
      SendDlgItemMessage(hdlg, IDOK,  BM_SETSTYLE, bsD, TRUE);
      SetFocus(GetDlgItem(hdlg, 101));
      break;
    case IDREG:
    case IDCHG:
      if (NTableRows(pa->tv) > 0) {
        ir = GetTSEL(hdlg, 101);
        sw = (strcmp(GetTableCellValue(dat.nam, pa->tv, ir, 0), "未登録") != 0);
        if (sw == 0) { dat.nam[0] = '\0';}
        if (GetVNM(hdlg, &dat, sw)) {
          if (dat.nam[0] == '\0') { ErrorBox(hdlg, "名前が必要です。", NULL); break;}
          ix = SearchVIWTableRowByName(pa->tv, dat.nam);
          if (ix >= 0) { ErrorBox(hdlg, "既にある名前です。", NULL); break;}
          SetTableCellValue(pa->tv, ir, 0, dat.nam);
          SetTSEL(hdlg, 101, ir, pa->tv, &tci); goto _10;
        }
      }
      goto _20;
    case IDDEL:
      if (NTableRows(pa->tv) > 0) {
        ir = GetTSEL(hdlg, 101);
        DeleteTableRow(pa->tv, ir);
        SetTSEL(hdlg, 101, ir, pa->tv, &tci); goto _10;
      }
      goto _20;
    case MKU4B(101, APPN_LBUTTONDBLCLK): wpa = IDOK;
    case IDOK:
      if (NTableRows(pa->tv) > 0) {
        ir = GetTSEL(hdlg, 101);
        GetTableRow(pa->bv, pa->tv, ir);
      }
      // fall through
    case IDCANCEL:
      if ((ir = SearchVIWTableRowByName(pa->tv, "未登録")) >= 0) {
        DeleteTableRow(pa->tv, ir);
      }
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

void LOCALF cmd_view(HWND hwnd)
{ // 表示形式
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));
  VIWI viw;
  VIWP par;
  static DLGI dit[] = {
    { C_SLFT|wsG,   8,  8, 32,  8,  -1, "表示形式" },
    { C_TSEL|wsT,   8, 18,156,116, 101, "" },
    { C_BPSH|wsGT,170, 18, 48, 14, IDREG, "登録(&R)" },
    { C_BPSH|wsT, 170, 36, 48, 14, IDCHG, "名前変更(&C)" },
    { C_BPSH|wsT, 170, 54, 48, 14, IDDEL, "削除(&D)" },
    { C_BDEF|wsGT,118,156, 48, 14, IDOK, "OK" },
    { C_BPSH|wsT, 170,156, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 224, 176, "表示形式" };

  if (we->flg & TBCS_DCV) return;
  par.tv = DuplicateTable(we->tv);  if (!par.tv) return;
  par.bv = DuplicateRowBuf(we->bv); if (!par.bv) goto _20;
  if (DlgBoxInput(hwnd, &dlg, VIW_DlgProc, (iANY)&par) == IDCANCEL) goto _10;
  CopyTable(we->tv, par.tv);
  CopyRowBuf(we->bv, par.bv);
  SendMessage(hwnd, TBCM_CHANGEVIEW, (uANY)SetVIWInfo(&viw, we->bv), TRUE);
 _10: DeleteRowBuf(par.bv);
 _20: DeleteTable(par.tv);
}

void LOCALF SetLTIT(HWND hdlg, I4B id, I4B ir, HTBL ta, BOOL sw)
{ // 項目名リスト作成
  I4B i, i0, ni;
  rI4B vi;
  HTBL tb;

  vi = (rI4B)TablePtr(ta);
  tb = (HTBL)TableColumnExtra(ta, 0);
  if (sw == 0) {
    i0 = 0; ni = NTableRows(ta);
  } else {
    i0 = NTableRows(ta); ni = TableCapacity(ta) - i0;
  }
  SendDlgItemMessage(hdlg, id, WM_SETREDRAW, FALSE, 0);
  SendDlgItemMessage(hdlg, id, LB_RESETCONTENT, 0, 0);
  for (i = 0; i < ni; i++) {
    SendDlgItemMessage(hdlg, id, LB_ADDSTRING, 0, (iANY)TableColumnTitle(tb, vi[i0 + i]));
  }
  SendDlgItemMessage(hdlg, id, LB_ADDSTRING, 0, (iANY)"");
  SendDlgItemMessage(hdlg, id, LB_SETCURSEL, ir, 0);
  SendDlgItemMessage(hdlg, id, WM_SETREDRAW, TRUE, 0);
  InvalidateRect(GetDlgItem(hdlg, id), NULL, TRUE);
}

  // 表示項目設定情報
typedef struct {
  HTBL  ta;
  pSTR  arr;
  pU2B  pf;
} VIW0P;

BOOL LOCALF VIW0_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // 表示項目ダイアログプロシージャ
  I4B i1, i2, fc;
  VIW0P *pa;

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (VIW0P *)lpa));
    ScanARRTable(pa->ta, pa->arr);
    SetLTIT(hdlg, 101, 0, pa->ta, 0);
    SetLTIT(hdlg, 102, 0, pa->ta, 1);
    SetEINT(hdlg, 105, *pa->pf);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    pa = (VIW0P *)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case 103:
      if ((i2 = GetLSEL(hdlg, 102)) < TableCapacity(pa->ta) - NTableRows(pa->ta)) {
        InsertARRTableRow(pa->ta, i1 = GetLSEL(hdlg, 101), i2);
        i1++;
        goto _10;
      }
      break;
    case 104:
      if ((i1 = GetLSEL(hdlg, 101)) < NTableRows(pa->ta)) {
        i2 = DeleteARRTableRow(pa->ta, i1);
       _10:
        SetLTIT(hdlg, 101, i1, pa->ta, 0);
        SetLTIT(hdlg, 102, i2, pa->ta, 1);
      }
      break;
    case IDPSH+1:
    case IDPSH+2:
    case IDOK:
      PrintARRTable(pa->arr, pa->ta);
      fc = GetEINT(hdlg, 105); if (fc >= 0 && fc < NTableRows(pa->ta)) { *pa->pf = (U2B)fc;}
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

iANY LOCALF VIW1_WndProc(HWND hwnd, U4B umsg, uANY wpa, iANY lpa)
{ // 絞り込み条件表のウィンドウプロシージャ
  switch (umsg) {
  case TBCM_ASKCELLFORMAT:
    if (((rPOSI)wpa)->ir == VIWCAP - 1 && ((rPOSI)wpa)->ic > 0) {
      return DC_INR|DC_DED|DC_RHD;
    }
    break;
  }
  return DefBOLTBLProc(hwnd, umsg, wpa, lpa);
}

  // 絞り込み条件設定情報
typedef struct {
  HTBL  tc;
  pSTR  cnd;
} VIW1P;

BOOL LOCALF VIW1_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // 絞り込みダイアログプロシージャ
  VIW1P *pa;
  POSI c;
  static TBCI tci = { 0, "lx, ic, cm, wk", NULL, NULL, TTF_ADJ, 3, 0 };

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (VIW1P *)lpa));
    SubclassWindow(GetDlgItem(hdlg, 101), VIW1_WndProc);
    ScanCNDTable(pa->tc, pa->cnd);
    SetTTBL(hdlg, 101, pa->tc, &tci);
    c.ir = 0; c.ic = 1;
    SendDlgItemMessage(hdlg, 101, TBCM_SETCUR, (uANY)&c, FALSE);
    PrintCNDTable(pa->cnd, pa->tc);
    SetSSTR(hdlg, 102, pa->cnd);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    pa = (VIW1P *)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case MKU4B(101, APPN_CHANGE):
      RedrawWindow(GetDlgItem(hdlg, 101), NULL, NULL, RDW_INVALIDATE|RDW_UPDATENOW);
      PrintCNDTable(pa->cnd, pa->tc);
      SetSSTR(hdlg, 102, pa->cnd);
      break;
    case IDPSH:
    case IDPSH+2:
    case IDOK:
      PrintCNDTable(pa->cnd, pa->tc);
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

iANY LOCALF VIW2_WndProc(HWND hwnd, U4B umsg, uANY wpa, iANY lpa)
{ // 並べ替え条件表のウィンドウプロシージャ
  switch (umsg) {
  case TBCM_ASKCELLFORMAT:
    if (((rPOSI)wpa)->ic < 0) {
      return (((rPOSI)wpa)->ir < 0)? DC_CHD|DC_RHD|DC_CEN: DC_INR|DC_RHD|DC_RGT;
    }
    break;
  case TBCM_GETCELLTEXT:
    if (((rPOSI)wpa)->ic < 0) {
      if (((rPOSI)wpa)->ir < 0) {
        sprintfs((pSTR)lpa, "優先順位");
      } else {
        sprintfs((pSTR)lpa, "%d ", ((rPOSI)wpa)->ir + 1);
      }
      return 0;
    }
    break;
  }
  return DefBOLTBLProc(hwnd, umsg, wpa, lpa);
}

  // 並べ替え条件設定情報
typedef struct {
  HTBL  ts;
  pSTR  srt;
} VIW2P;

BOOL LOCALF VIW2_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // 並べ替えダイアログプロシージャ
  VIW2P *pa;
  static TBCI tci = { 0, NULL, NULL, NULL, TTF_ADJ|TTF_HDW, 1, 8 };

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (VIW2P *)lpa));
    SubclassWindow(GetDlgItem(hdlg, 101), VIW2_WndProc);
    ScanSRTTable(pa->ts, pa->srt);
    SetTTBL(hdlg, 101, pa->ts, &tci);
    PrintSRTTable(pa->srt, pa->ts);
    SetSSTR(hdlg, 102, pa->srt);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    pa = (VIW2P *)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case MKU4B(101, APPN_CHANGE):
      RedrawWindow(GetDlgItem(hdlg, 101), NULL, NULL, RDW_INVALIDATE|RDW_UPDATENOW);
      PrintSRTTable(pa->srt, pa->ts);
      SetSSTR(hdlg, 102, pa->srt);
      break;
    case IDPSH:
    case IDPSH+1:
    case IDOK:
      PrintSRTTable(pa->srt, pa->ts);
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

void LOCALF cmd_arrange(HWND hwnd, I4B ip)
{ // 表示項目／絞り込み／並べ替え
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));
  HTBL tb;
  VIWI viw;
  VIWE dat;
  // 表示項目
  static VIW0P par0;
  static DLGI dit0[] = {
    { C_BGRP|wsG,   6,  0, 48, 18,  -1, "" },
    { C_SCEN|wsG,   8,  6, 44,  8,  -1, "表示項目" },
    { C_BPSH|wsG,  58,  3, 48, 14, IDPSH+1, "絞り込み" },
    { C_BPSH|wsG, 110,  3, 48, 14, IDPSH+2, "並べ替え" },
    { C_SLFT|wsG,   8, 30, 80,  8,  -1, "表示項目(列タイトル)" },
    { C_LSEL|wsT,   8, 40, 80,124, 101, "" },
    { C_SLFT|wsG, 152, 30, 80,  8,  -1, "非表示項目" },
    { C_LSEL|wsT, 152, 40, 80,124, 102, "" },
    { C_BPSH|wsGT, 96, 64, 48, 14, 103, "＜追加(&A)" },
    { C_BPSH|wsT,  96, 82, 48, 14, 104, "＞除外(&D)" },
    { C_SLFT|wsG,   8,169, 48,  8,  -1, "固定列数(&F):" },
    { C_ENUM|wsT,  56,167, 24, 12, 105, "" },
    { C_BDEF|wsGT,134,204, 48, 14, IDOK, "OK"   },
    { C_BPSH|wsT, 186,204, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg0 = { dit0, _countof(dit0), 0, 0, 240, 224, "表示形式" };
  // 絞り込み
  static VIW1P par1;
  static DLGI dit1[] = {
    { C_BPSH|wsG,   6,  3, 48, 14, IDPSH, "表示項目" },
    { C_BGRP|wsG,  58,  0, 48, 18,  -1, "" },
    { C_SCEN|wsG,  60,  6, 44,  8,  -1, "絞り込み" },
    { C_BPSH|wsG, 110,  3, 48, 14, IDPSH+2, "並べ替え" },
    { C_SLFT|wsG,   8, 30, 48,  8,  -1, "絞り込み条件" },
    { C_TTBX|wsGT,  8, 40,224,116, 101, "" },
    { C_BGRP|wsG,   6,156,228, 32,  -1, "絞り込み条件式" },
    { C_SMUL|wsG,  12,166,216, 18, 102, "" },
    { C_BDEF|wsGT,134,204, 48, 14, IDOK, "OK"   },
    { C_BPSH|wsT, 186,204, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg1 = { dit1, _countof(dit1), 0, 0, 240, 224, "表示形式" };
  // 並べ替え
  static VIW2P par2;
  static DLGI dit2[] = {
    { C_BPSH|wsG,   6,  3, 48, 14, IDPSH, "表示項目" },
    { C_BPSH|wsG,  58,  3, 48, 14, IDPSH+1, "絞り込み" },
    { C_BGRP|wsG, 110,  0, 48, 18,  -1, "" },
    { C_SCEN|wsG, 112,  6, 44,  8,  -1, "並べ替え" },
    { C_SLFT|wsG,   8, 30, 48,  8,  -1, "並べ替え条件" },
    { C_TTBF|wsGT,  8, 40,224,116, 101, "" },
    { C_BGRP|wsG,   6,156,228, 32,  -1, "並べ替え条件式" },
    { C_SMUL|wsG,  12,166,216, 18, 102, "" },
    { C_BDEF|wsGT,134,204, 48, 14, IDOK, "OK"   },
    { C_BPSH|wsT, 186,204, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg2 = { dit2, _countof(dit2), 0, 0, 240, 224, "表示形式" };
  // プロパティシート
  static PSHI psp[] = {
    { &dlg0, VIW0_DlgProc, (iANY)&par0, sizeof(par0) },
    { &dlg1, VIW1_DlgProc, (iANY)&par1, sizeof(par1) },
    { &dlg2, VIW2_DlgProc, (iANY)&par2, sizeof(par2) },
  };
  static PSHS psh = { psp, _countof(psp) };

  if (we->flg & TBCS_DCV) return;
  tb = OHTBL(we->op);
  dat = *(pVIWE)RowBufPtr(we->bv);
  par0.ta = CreateARRTable(0, tb); if (!par0.ta) return;
  par0.arr = dat.arr;
  par0.pf = &dat.fc;
  par1.tc = CreateCNDTable(VIWCAP, tb); if (!par1.tc) goto _30;
  par1.cnd = dat.cnd;
  par2.ts = CreateSRTTable(VIWCAP, tb); if (!par2.ts) goto _20;
  par2.srt = dat.srt;
  if (PropSheetInput(hwnd, &psh, ip) == IDCANCEL) goto _10;
  *(pVIWE)RowBufPtr(we->bv) = dat;
  SendMessage(hwnd, TBCM_CHANGEVIEW, (uANY)SetVIWInfo(&viw, we->bv), TRUE);
 _10: DeleteTable(par0.ta);
 _20: DeleteTable(par1.tc);
 _30: DeleteTable(par2.ts);
}

void LOCALF cmd_viewa(HWND hwnd, BOOL sw)
{ // 全項目表示／表示形式解除
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));
  VIWI viw;

  if (we->flg & TBCS_DCV) return;
  SetRowBufCellValue(we->bv, 3, I4BBox(0));
  SetRowBufCellValue(we->bv, 4, sw? "": "*");
  SetRowBufCellValue(we->bv, 5, "");
  if (sw) { SetRowBufCellValue(we->bv, 6, "");}
  SendMessage(hwnd, TBCM_CHANGEVIEW, (uANY)SetVIWInfo(&viw, we->bv), TRUE);
}

static CODI diHRH[] = {
  { TBCS_HRH, "表示しない" }, { 0, "行インデックス" }
};
static CODS dsHRH = { diHRH, _countof(diHRH) };

static CODI diHCH[] = {
  { TBCS_HCH, "表示しない" }, { TBCS_UCI, "列インデックス" }, { TBCS_UCN, "列名" }, { 0, "列タイトル" }
};
static CODS dsHCH = { diHCH, _countof(diHCH) };

  // 表示オプション情報
typedef struct {
  I1B   face[32];
  I4B   ptsz;
  I4B   tbcs;
} VOPP;

BOOL LOCALF setvopt_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // 表示オプション設定ダイアログプロシージャ
  VOPP *pa;

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (VOPP *)lpa));
    SetESTR(hdlg, 101, pa->face);
    SetEINT(hdlg, 102, pa->ptsz);
    SetCCOD(hdlg, 103, pa->tbcs & TBCS_HRH, &dsHRH);
    SetCCOD(hdlg, 104, pa->tbcs & (TBCS_HCH|TBCS_UCI|TBCS_UCN), &dsHCH);
    SetBFLG(hdlg, 105, pa->tbcs & TBCS_EMD);
    SetBFLG(hdlg, 106, pa->tbcs & TBCS_DDD);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    pa = (VOPP *)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case IDOK:
      GetESTR(hdlg, 101, pa->face, sizeof(pa->face) - 1);
      if (pa->face[0] == '\0') { strcpyx(pa->face, DEFFACE, sizeof(pa->face) - 1);}
      pa->ptsz = GetEINT(hdlg, 102); if (pa->ptsz < 6 || pa->ptsz > 30) { pa->ptsz = DEFPTSZ;}
      pa->tbcs = (pa->tbcs &~TBCS_HRH) | GetCCOD(hdlg, 103, &dsHRH);
      pa->tbcs = (pa->tbcs &~(TBCS_HCH|TBCS_UCI|TBCS_UCN)) | GetCCOD(hdlg, 104, &dsHCH);
      if (GetBFLG(hdlg, 105)) { pa->tbcs |= TBCS_EMD;} else { pa->tbcs &=~TBCS_EMD;}
      if (GetBFLG(hdlg, 106)) { pa->tbcs |= TBCS_DDD;} else { pa->tbcs &=~TBCS_DDD;}
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

void LOCALF cmd_setvopt(HWND hwnd)
{ // 表示オプション
  VOPP par;
  HFNT hfnt;
  LOGFONT lof;
  static DLGI dit[] = {
    { C_SLFT|wsG,   8, 10, 48,  8,  -1, "フォント(&F):" },
    { C_ESTR|wsT,  56,  8, 66, 12, 101, "" },
    { C_ENUM|wsT, 128,  8, 24, 12, 102, "" },
    { C_SLFT|wsG, 155, 10, 16,  8,  -1, "pt" },
    { C_SLFT|wsG,   8, 26, 48,  8,  -1, "行見出し(&R):" },
    { C_CSEL|wsT,  56, 24, 58,128, 103, "" },
    { C_SLFT|wsG,   8, 42, 48,  8,  -1, "列見出し(&C):" },
    { C_CSEL|wsT,  56, 40, 58,128, 104, "" },
    { C_BFLG|wsGT,  8, 60,120, 10, 105, "Enterキーで下に移動する(&D)" },
    { C_BFLG|wsGT,  8, 74,128, 10, 106, "ドロップダウンリストを使用しない(&L)" },
    { C_BDEF|wsGT, 86,124, 48, 14, IDOK, "OK" },
    { C_BPSH|wsT, 138,124, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 192, 144, "表示オプション" };

  hfnt = (HFNT)SendMessage(hwnd, WM_GETFONT, 0, 0);
  GetObject(hfnt, sizeof(LOGFONT), &lof);
  strcpyn(par.face, lof.lfFaceName, sizeof(par.face) - 1);
  par.ptsz = iround(abs(lof.lfHeight) * PTPI / DEFDPI);
  par.tbcs = SendMessage(hwnd, TBCM_GETMODE, 0, 0);
  if (DlgBoxInput(hwnd, &dlg, setvopt_DlgProc, (iANY)&par) == IDCANCEL) return;
  SendMessage(hwnd, TBCM_SETMODE, par.tbcs, FALSE);
  hfnt = CreateFontIndirect(SetLOGFONT(&lof, par.face, PT2DOTS(par.ptsz), 0));
  SendMessage(hwnd, WM_SETFONT, (uANY)hfnt, TRUE);
  DeleteObject(hfnt);
}

  // 集計表作成情報
typedef struct {
  HTBL  tg;
  I1B   grp[512];
} SUMP;

BOOL LOCALF SUM_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // 集計表作成ダイアログプロシージャ
  SUMP *pa;
  static TBCI tci = { 0, "ic, sw, tit, fmt", NULL, NULL, TTF_ADJ, 3, 0 };

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (SUMP *)lpa));
    ScanGRPTable(pa->tg, pa->grp);
    SetTTBL(hdlg, 101, pa->tg, &tci);
    PrintGRPTable(pa->grp, pa->tg);
    SetSSTR(hdlg, 102, pa->grp);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    pa = (SUMP *)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case MKU4B(101, APPN_CHANGE):
      RedrawWindow(GetDlgItem(hdlg, 101), NULL, NULL, RDW_INVALIDATE|RDW_UPDATENOW);
      PrintGRPTable(pa->grp, pa->tg);
      SetSSTR(hdlg, 102, pa->grp);
      break;
    case IDOK:
      PrintGRPTable(pa->grp, pa->tg);
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

void LOCALF cmd_summary(HWND hwnd)
{ // 集計
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));
  HWND hsum;
  HVIW vw;
  static TBWI tbw = { NULL, TBCS_DED|TBCS_DSE, NULL, NULL, 0 };
  static SUMP par;
  static DLGI dit[] = {
    { C_SLFT|wsG,   8,  8, 48,  8,  -1, "集計項目" },
    { C_TTBX|wsGT,  8, 18,272,116, 101, "" },
    { C_BGRP|wsG,   6,134,276, 32,  -1, "集計項目リスト" },
    { C_SMUL|wsG,  12,144,256, 18, 102, "" },
    { C_BDEF|wsGT,182,188, 48, 14, IDOK, "OK" },
    { C_BPSH|wsT, 234,188, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 288, 208, "集計表作成" };

  if (WindowID(hwnd) == IDSUM) {
    PostMessage(hFRM(hwnd), WM_COMMAND, IDM_SUMMARY, 0);
    SendMessage(hwnd, WM_CLOSE, 0, 0); return;
  }
  vw = (HVIW)SendMessage(hwnd, TBCM_GETVIEW, 0, 0);
  if (!we->tg) {
    we->tg = CreateGRPTable(VIWCAP, ViewHTBL(vw)); if (!we->tg) return;
    par.tg = we->tg;
    par.grp[0] = '\0';
  } else {
    par.tg = we->tg;
    PrintGRPTable(par.grp, par.tg);
  }
  if (DlgBoxInput(hwnd, &dlg, SUM_DlgProc, (iANY)&par) == IDCANCEL) return;
  if (IsWindow(hsum = GetDlgItem(hFRM(hwnd), IDSUM))) { SendMessage(hFRM(hwnd), FRMM_DESTROYCHILD, (uANY)hsum, 0);}
  if (we->tsm) { DeleteTable(we->tsm); we->tsm = NULL;}
  we->tsm = CreateSummary(vw, par.grp); if (!we->tsm) return;
  tbw.op = CreateHOPN(we->tsm, FALSE); if (!tbw.op) return;
  we->hsum = CreateTableWindow(hFRM(hwnd), IDSUM, &tbw);
}

void LOCALF cmd_check(HWND hwnd)
{ // チェック
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));
  I4B re;

  if (we->flg & TBCS_DED) return;
  BusyMsg(hFRM(hwnd), "処理中...");
  reset_error();
  re = CheckTable(OHTBL(we->op));
  EndBusy(hFRM(hwnd));
  OkBox(hFRM(hwnd), re? "エラーは見つかりませんでした。": "エラーが見つかりました。", NULL);
}

void LOCALF cmd_edit(HWND hwnd)
{ // 行編集
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));
  HBUF rb;

  if (we->flg & TBCS_DER) {
    SendMessage(hwnd, TBCM_MOVECUR, MVC_HOM, TRUE);
    SendMessage(hwnd, TBCM_EDITCELL, 0, 0); return;
  }
  rb = CreateRowBuf(OHTBL(we->op)); if (!rb) return;
  SendMessage(hwnd, TBCM_GETCURROW, (uANY)rb, 0);
  SendMessage(hwnd, TBCM_MOVECUR, MVC_SEL, TRUE);
  if (EditRowDlgBox(hwnd, rb, WindowText(hwnd))) {
    SendMessage(hwnd, TBCM_SETCURROW, (uANY)rb, 0);
  } else {
    SendMessage(hwnd, TBCM_SETSEL, FALSE, TRUE);
  }
  DeleteRowBuf(rb);
}

void LOCALF cmd_addedit(HWND hwnd)
{ // 行追加して編集
  SendMessage(hwnd, TBCM_MOVECUR, MVC_BTM, TRUE);
  SendMessage(hwnd, WM_COMMAND, IDM_EDIT, 0);
}

void LOCALF cmd_insertedit(HWND hwnd)
{ // 行挿入して編集
  if (!SendMessage(hwnd, TBCM_GETSEL, 0, 0)) {
    SendMessage(hwnd, TBCM_MOVECUR, MVC_SEL, TRUE);
  }
  SendMessage(hwnd, TBCM_INSERT, 0, 0);
  SendMessage(hwnd, WM_COMMAND, IDM_EDIT, 0);
}

void LOCALF cmd_seldelete(HWND hwnd)
{ // 行削除
  if (SendMessage(hwnd, TBCM_GETSEL, 0, 0)) {
    SendMessage(hwnd, TBCM_DELETE, 0, 0); return;
  }
  SendMessage(hwnd, TBCM_MOVECUR, MVC_SEL, TRUE);
  if (YesNoBox(hFRM(hwnd), "この行を削除しますか？", NULL) == IDYES) {
    SendMessage(hwnd, TBCM_DELETE, 0, 0);
  } else {
    SendMessage(hwnd, TBCM_SETSEL, FALSE, TRUE);
  }
}

void LOCALF cmd_psort(HWND hwnd)
{ // 物理的に並べ替え
  HVIW vw;

  if (YesNoBox(hFRM(hwnd), "現在の表示順でデータを物理的に並べ替えます。よろしいですか？", NULL) != IDYES) return;
  vw = (HVIW)SendMessage(hwnd, TBCM_GETVIEW, 0, 0);
  BusyMsg(hFRM(hwnd), "処理中...");
  SortTableRowsByView(vw);
  SendMessage(hwnd, TBCM_UPDATE, FALSE, TRUE);
  EndBusy(hFRM(hwnd));
}

iANY LOCALF TBL_command(HWND hwnd, uANY wpa, iANY lpa)
{ // コマンド
  switch (LOU2B(wpa)) {
  case IDM_SAVE:
    cmd_save(hwnd); break;
  case IDM_IMPORT:
    cmd_import(hwnd); break;
  case IDM_EXPORT:
    cmd_export(hwnd); break;
  case IDM_PRINT:
    cmd_printtbl(hwnd); break;
  case IDM_CUT:
    SendMessage(hwnd, TBCM_CUT, 0, 0); break;
  case IDM_COPY:
    SendMessage(hwnd, TBCM_COPY, 0, 0); break;
  case IDM_PASTE:
    SendMessage(hwnd, TBCM_PASTE, 0, 0); break;
  case IDM_CLEAR:
    SendMessage(hwnd, TBCM_CLEAR, 0, 0); break;
  case IDM_INSERT:
    SendMessage(hwnd, TBCM_INSERT, 0, 0); break;
  case IDM_DELETE:
    SendMessage(hwnd, TBCM_DELETE, 0, 0); break;
  case IDM_OPENLINK:
    SendMessage(hwnd, TBCM_OPENLINK, 0, 0); break;
  case IDM_SEARCH:
    cmd_search(hwnd, 0); break;
  case IDM_NSEARCH:
    cmd_search(hwnd, 1); break;
  case IDM_VIEW:
    cmd_view(hwnd); break;
  case IDM_ARRANGE:
    cmd_arrange(hwnd, 0); break;
  case IDM_FILTER:
    cmd_arrange(hwnd, 1); break;
  case IDM_SORT:
    cmd_arrange(hwnd, 2); break;
  case IDM_VIEWA:
    cmd_viewa(hwnd, 0); break;
  case IDM_VIEWB:
    cmd_viewa(hwnd, 1); break;
  case IDM_SETVOPT:
    cmd_setvopt(hwnd); break;
  case IDM_SUMMARY:
    cmd_summary(hwnd); break;
  case IDM_CHECK:
    cmd_check(hwnd); break;
  case IDM_EDIT:
    cmd_edit(hwnd); break;
  case IDM_ADDEDIT:
    cmd_addedit(hwnd); break;
  case IDM_INSERTEDIT:
    cmd_insertedit(hwnd); break;
  case IDM_SELDELETE:
    cmd_seldelete(hwnd); break;
  case IDM_PSORT:
    cmd_psort(hwnd); break;
  }
  return 0;
}

iANY LOCALF TBL_close(HWND hwnd)
{ // 表ウィンドウを閉じる
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));
  I4B sv, re;

  sv = !(SendMessage(hwnd, TBCM_GETMODE, 0, 0) & TBCS_DED) && IsModified(we->op);
  if (sv) {
    re = YesNoCanBox(hFRM(hwnd), format("'%s'は変更されています。保存しますか？", WindowText(hwnd)), NULL);
    switch (re) {
    case IDYES: sv = TRUE;  break;
    case IDNO:  sv = FALSE; break;
    default: return 0;
    }
  }
  if (sv) {
    BusyMsg(hFRM(hwnd), "処理中...");
    reset_error();
    re = CheckTable(OHTBL(we->op));
    EndBusy(hFRM(hwnd));
    if (!re) {
      ErrorBox(hFRM(hwnd), "エラーが見つかりました。\n保存はキャンセルされます。", NULL);
      return 0;
    }
  }
  BusyMsg(hFRM(hwnd), sv? "保存中...": "");
  reset_error();
  re = CloseTable(we->op, sv);
  EndBusy(hFRM(hwnd));
  if (sv && !re) {
    ErrorBox(hFRM(hwnd), format("'%s'の保存に失敗しました。", TableFileName(OHTBL(we->op))), NULL);
  }
  return DefBOLTBLProc(hwnd, WM_CLOSE, 0, 0);
}

iANY LOCALF TBL_destroy(HWND hwnd)
{ // 表ウィンドウ終了処理
  pTBWE we = (pTBWE)GetWindowLong(hwnd, sizeof(iANY));

  if (IsWindow(we->hprn)) { SendMessage(hFRM(hwnd), FRMM_DESTROYCHILD, (uANY)we->hprn, 0);}
  if (IsWindow(we->hsum)) { SendMessage(hFRM(hwnd), FRMM_DESTROYCHILD, (uANY)we->hsum, 0);}
  if (!(we->flg & TBCS_DCV)) {
    SetLastVIW(we->tv, we->bv);
    CopyUserVIWTable(we->tvu, we->tv);
    SaveUserVIW(we->tvu);
    DeleteRowBuf(we->bv);
    DeleteTable(we->tv);
    DeleteTable(we->tvu);
    DeleteTable(we->tvs);
  }
  DeleteTable(we->tg);
  DeleteTable(we->tsm);
  hfree(we);
  return DefBOLTBLProc(hwnd, WM_DESTROY, 0, 0);
}

iANY LOCALF TBL_WndProc(HWND hwnd, U4B umsg, uANY wpa, iANY lpa)
{ // ウィンドウプロシージャ
  switch (umsg) {
  case TBCM_SETTABLE:
    return TBL_settable(hwnd, (pTBWI)wpa);
  case TBCM_TRACKPOPUP:
    return TBL_trackpopup(hwnd, (rPOSI)wpa);
  case WM_COMMAND:
    return TBL_command(hwnd, wpa, lpa);
  case WM_CLOSE:
    return TBL_close(hwnd);
  case WM_DESTROY:
    return TBL_destroy(hwnd);
  }
  return DefBOLTBLProc(hwnd, umsg, wpa, lpa);
}

I4B  LOCALF TBL_appstatus(HWND hwnd)
{ // 表ウィンドウの状態の取得
  I4B fmt, sta = 0;
  POSI c;
  STRBUF bf;

  SendMessage(hwnd, TBCM_GETCUR, (uANY)&c, 0);
  fmt = SendMessage(hwnd, TBCM_GETCELLFORMAT, (uANY)&c, 0);
  if (fmt & DC_LNK) {
    SendMessage(hwnd, TBCM_GETCELLTEXT, (uANY)&c, (iANY)bf);
    if (bf[0] != '\0') { sta |= APPS_LNK;}
  }
  return sta;
}

HWND LOCALF CreateTableWindowX(HWND hwnd, I4B id, pTBWI wi, WNDPROC prc)
{ // 表ウィンドウの作成
  static CREI cri = { "BOLTBL", wsCHV, };

  cri.tit = wi->tit? wi->tit: TableTitle(OHTBL(wi->op));
  cri.id = id? id: SendMessage(hwnd, FRMM_GETNEWID, IDPRN + 1, 0);
  cri.pa = (U4B)wi;
  return (HWND)SendMessage(hwnd, FRMM_CREATECHILD, (uANY)&cri, (iANY)prc);
}

HWND LOCALF CreateTableWindow(HWND hwnd, I4B id, pTBWI wi)
{ // 表ウィンドウの作成
  return CreateTableWindowX(hwnd, id, wi, TBL_WndProc);
}

//――――――――――――――――――――――――――――――――――――――//

I4B  LOCALF INDcd(rSTR bf)
{ // 個人番号のチェックディジット
  I4B i, s = 0;

  for (i = 1; i < 12; i++) {
    s += (bf[11 - i] - '0') * ((i <= 6)? i + 1: i - 5);
  }
  return (s % 11 > 1)? 11 - (s % 11): 0;
}

BOOL LOCALF IsIND(F8B d)
{ // 個人番号
  I4B i;
  I1B bf[32];

  sprintfs(bf, "%012.0f", d);
  for (i = 0; i < 12; i++) {
    if (bf[i] < '0' || bf[i] > '9') return FALSE;
  }
  if (bf[12] != '\0') return FALSE;
  return (bf[11] - '0') == INDcd(bf);
}

I4B  LOCALF CORcd(rSTR bf)
{ // 法人番号のチェックディジット
  I4B i, s = 0;

  for (i = 0; i < 12; i++) {
    s += (bf[12 - i] - '0') * ((i % 2) + 1);
  }
  return 9 - (s % 9);
}

BOOL LOCALF IsCOR(F8B d)
{ // 法人番号
  I4B i;
  I1B bf[32];

  sprintfs(bf, "%012.0f", d);
  for (i = 0; i < 13; i++) {
    if (bf[i] < '0' || bf[i] > '9') return FALSE;
  }
  if (bf[13] != '\0') return FALSE;
  return (bf[0] - '0') == CORcd(bf);
}

  // データ入力表
typedef struct {
  U2B   ser;
  U1B   suf;
  U1B   atr;
  I1B   zip[12];
  I1B   adr[64];
  I1B   nam[64];
  I1B   kan[64];
  I1B   tel[16];
  F8B   pin;
  I1B   acc[32];
  I1B   det[48];
  I4B   fee;
  I4B   unp;
  I4B   tax;
  I4B   unc;
  I1B   rem[64];
  I1B   mem[64];
  I4B   reg;
  I1B   usr[32];
  U1B   rsv[20];
} HOUD;
typedef HOUD *pHOUD;
typedef const HOUD *rHOUD;

#define  idHOU  0
#define  tbHOU  DBTable(idHOU)

pANY LOCALF fdefSER(pANY pv, rANY pb, rCOLI co)
{ // 連番既定値
  U2B max = 0;
  rHOUD pc, pe;

  for (pc = TablePtr(tbHOU), pe = pc + NTableRows(tbHOU); pc < pe; pc++) {
    if (pc->ser > max) { max = pc->ser;}
  }
  *(pU2B)pv = (U2B)(max + 1);
  return pv;
}

pANY LOCALF fdefSUF(pANY pv, rANY pb, rCOLI co)
{ // 枝番既定値
  *(pU1B)pv = 1; return pv;
}

static rSTR SATR[] = { "", "個人", "法人", "追加" };
static ENMS esATR = { SATR, _countof(SATR) };

pANY LOCALF fdefREG(pANY pv, rANY pb, rCOLI co)
{ // 登録日既定値
  *(I4B *)pv = CurDate(); return pv;
}

pANY LOCALF fdefUSR(pANY pv, rANY pb, rCOLI co)
{ // 登録者既定値
  return ColumnScan(pv, CurUser(), co);
}

static COLI ciHOU[] = {
  { TID_U2B,  0, "ser", "連番", "連　番", CF_KEY, fdefSER, },
  { TID_U1B,  0, "suf", "枝番", "枝　番", CF_KEY|CF_PAC, fdefSUF, },
  { TID_E1B,  0, "atr", "属性", "属　性", CF_DDL, NULL, NULL, (uANY)&esATR, },
  { ASC(12),  8, "zip", "郵便番号",     },
  { STR(64),  0, "adr", "住所(所在地)", },
  { STR(64), 25, "nam", "氏名(名称)",   },
  { STR(64), 25, "kan", "ふりがな",     },
  { ASC(16), 12, "tel", "電話番号",     },
  { TID_F8B, 13, "pin", "個人(法人)番号", NULL, CF_HZR|CF_GR2, NULL, NULL, (uANY)"%012.0f", },
  { STR(32), 16, "acc", "区分", "区　分", },
  { STR(48), 20, "det", "細目", "細　目", },
  { TID_M4B, 12, "fee", "支払金額",     NULL, CF_HZR, },
  { TID_M4B, 12, "unp", "未払金額",     NULL, CF_HZR|CF_GR2, },
  { TID_M4B, 12, "tax", "源泉徴収税額", NULL, CF_HZR, },
  { TID_M4B, 12, "unc", "未徴収税額",   NULL, CF_HZR|CF_GR2, },
  { STR(64),  0, "rem", "摘要", "摘　要", },
  { STR(64),  0, "mem", "メモ", "メ　モ", },
  { TID_YMD,  0, "reg", "登録日",       NULL, 0, fdefREG, },
  { STR(32), 16, "usr", "登録者",       NULL, CF_PAC, fdefUSR, },
  { BIN(20),  0, "rsv", "－",           },
};
static COLS csHOU = { ciHOU, _countof(ciHOU) };
static VIWI viHOU[] = {
  { "支払調書(簡略)", 0, "ser, suf, atr, adr, nam, pin, acc, det, fee, tax", "", "" },
  { "支払調書(詳細)", 0, "ser, suf, atr, adr, nam, pin, acc, det, fee, unp, tax, unc, rem", "", "" },
  { "宛名ラベル",     0, "ser, suf, atr, zip, adr, nam", "", "" },
  { "全項目",         0, "ser, suf, atr, zip, adr, nam, kan, tel, pin, acc, det, fee, unp, tax, unc, rem, mem, reg, usr", "", "" },
};
static VIWS vsHOU = { viHOU, _countof(viHOU) };

static rSTR SOPR[] = { "Open", "Update", "Insert", "Delete", "Print(1)", "Print(2)" };

#define  LFILE  "HOULOG.LOG"

void LOCALF oprlog(I4B opr, I4B ser, I4B suf)
{ // 操作ログ書出
  HANDLE hf;
  I1B *pt, bf[256];

  pt = bf;
  pt += sprintfs(pt, "%s %s %s", dtformat("yyyy/mm/dd hh:mm:ss", now()), SOPR[opr], CurUser());
  if (opr >= 1 && opr <= 3) { pt += sprintfs(pt, " %d-%d", ser, suf);}
  hf = open_file(LFILE, OP_APD); if (!hf) return;
  set_file_ptr(hf, 0, FILE_END);
  write_line(hf, bf);
  close_file(hf);
}

BOOL LOCALF ftrgHOU(HTBL tb, I4B msg, I4B ir, pHOUD pb)
{ // データ入力表操作履歴
  switch (msg) {
  case TRGM_UPDATERECORD:
  case TRGM_INSERTRECORD:
  case TRGM_DELETERECORD:
    oprlog(msg - TRGM_UPDATERECORD + 1, pb->ser, pb->suf);
    break;
  }
  return DefaultTableTrigger(tb, msg, ir, pb);
}

static rSTR EDSC[] = {
  "",
  "住所無し",         // 1
  "所在地無し",       // 2
  "氏名無し",         // 3
  "名称無し",         // 4
  "個人番号無し",     // 5
  "法人番号無し",     // 6
  "個人番号不正",     // 7
  "法人番号不正",     // 8
  "区分無し",         // 9
  "支払金額無し",     //10
};

I4B  LOCALF echkHOU(rHOUD pc)
{ // エラーコード
  if (pc->atr == 1 || pc->atr == 2) {
    if (pc->adr[0] == '\0') { return 1 + (pc->atr == 2);}
    if (pc->nam[0] == '\0') { return 3 + (pc->atr == 2);}
    if (pc->pin == 0.0)     { return 5 + (pc->atr == 2);}
  }
  if (pc->atr == 1 && !IsIND(pc->pin)) return 7;
  if (pc->atr == 2 && !IsCOR(pc->pin)) return 8;
  if (pc->atr != 0) {
    if (pc->acc[0] == '\0' && (pc->fee != 0 || pc->tax != 0)) return 9;
    if (pc->acc[0] != '\0' && pc->fee == 0) return 10;
  }
  return 0;
}

static TBLI tiHOU[] = {
  { &csHOU, &vsHOU, "HOUDAT", "データ入力表", NULL, TF_CYP, (FTRG)ftrgHOU, NULL, 0, sizeof(HOUD) },
};
static TBLS tsHOU = { tiHOU, _countof(tiHOU) };

static BDBI dbHOU = { &tsHOU, PRGNAM, PRGTIT, PRGDSC };

//――――――――――――――――――――――――――――――――――――――//

  // メインメニュー
static MNUI BOLMI[] = {
  { mfP,  0,             "ファイル(&F)"            }, // 0 ファイル
  { mf_,  IDM_OMENU1,    "メニュー(&M)"            },
  { mf_,  0,             ""                        },
  { mf_,  IDM_OPEN,      "データ入力表(&O)\tCtrl+O" },
  { mf_,  IDM_CLOSE,     "閉じる(&C)\tCtrl+F4"     },
  { mf_,  IDM_SAVE,      "上書保存(&S)\tCtrl+S"    },
  { mf_,  IDM_SAVEAS,    "名前を付けて保存(&A)...\tF12" },
  { mf_,  0,             ""                        },
  { mf_,  IDM_IMPORT,    "テキスト取込(&I)..."     },
  { mf_,  IDM_EXPORT,    "テキスト書出(&E)..."     },
  { mf_,  0,             ""                        },
  { mf_,  IDM_PRINT,     "印刷(&P)...\tCtrl+P"     },
  { mf_,  0,             ""                        },
  { mfE,  IDM_EXIT,      "終了(&X)"                },
  { mfP,  0,             "編集(&E)"                }, // 1 編集
  { mf_,  IDM_CUT,       "切り取り(&T)\tCtrl+X"    },
  { mf_,  IDM_COPY,      "コピー(&C)\tCtrl+C"      },
  { mf_,  IDM_PASTE,     "貼り付け(&P)\tCtrl+V"    },
  { mf_,  0,             ""                        },
  { mf_,  IDM_CLEAR,     "クリア(&A)"              },
  { mf_,  IDM_INSERT,    "挿入(&I)\tCtrl+E"        },
  { mf_,  IDM_DELETE,    "削除(&D)\tCtrl+D"        },
  { mf_,  0,             ""                        },
  { mf_,  IDM_SEARCH,    "検索(&F)...\tCtrl+F"     },
  { mfE,  IDM_NSEARCH,   "次を検索(&N)\tF3"        },
  { mfP,  0,             "表示(&V)"                }, // 2 表示  VIW_POS
  { mf_,  IDM_VIEW,      "表示形式(&V)...\tF5"     },
  { mf_,  0,             ""                        },
  { mf_,  IDM_ARRANGE,   "表示項目(&A)..."         },
  { mf_,  IDM_FILTER,    "絞り込み(&F)..."         },
  { mf_,  IDM_SORT,      "並べ替え(&S)..."         },
  { mf_,  0,             ""                        },
  { mf_,  IDM_VIEWA,     "全項目表示(&W)\tF10"     },
  { mf_,  IDM_VIEWB,     "表示形式解除(&B)\tShift+F10" },
  { mf_,  0,             ""                        },
  { mfE,  IDM_SETVOPT,   "表示オプション(&O)..."   },
  { mfP,  0,             "データ(&D)"              }, // 3 データ
  { mf_,  IDM_SUMMARY,   "集計(&S)..."             },
  { mf_,  0,             ""                        },
  { mf_,  IDM_CHECK,     "チェック(&C)\tShift+F5"  },
  { mf_,  0,             ""                        },
  { mf_,  IDM_EDIT,      "行編集(&E)\tF8"          },
  { mf_,  IDM_ADDEDIT,   "行追加(&A)\tShift+F7"    },
  { mf_,  IDM_INSERTEDIT,"行挿入(&I)\tF9"          },
  { mf_,  IDM_SELDELETE, "行削除(&D)\tShift+F9"    },
  { mf_,  0,             ""                        },
  { mfE,  IDM_PSORT,     "物理的に並べ替え(&P)"    },
  { mfP,  0,             "処理(&P)"                }, // 4 処理
  { mf_,  IDM_CHECK1,    "データチェック(&C)"      },
  { mf_,  IDM_REPORT1,   "支払調書印刷(&R)..."     },
  { mf_,  IDM_REPORT2,   "宛名ラベル印刷(&E)..."   },
  { mf_,  0,             ""                        },
  { mf_,  IDM_INFO1,     "操作履歴確認(&I)"        },
  { mf_,  0,             ""                        },
  { mf_,  IDM_SITEWHO,   "ログイン状況(&S)"        },
  { mf_,  0,             ""                        },
  { mf_,  IDM_SETUSR,    "ユーザー管理(&U)"        },
  { mf_,  IDM_UNLOCK,    "ロック解除(&L)..."       },
  { mfE,  IDM_BACKUP,    "バックアップ(&B)..."     },
  { mfP,  0,             "ツール(&T)"              }, // 5 ツール
  { mf_,  IDM_SETENV,    "基本設定(&E)..."         },
  { mf_,  0,             ""                        },
  { mf_,  IDM_CSVOUT,    "提出用ＣＳＶ書出(&C)..." },
  { mf_,  0,             ""                        },
  { mf_,  IDM_TEDIT,     "テキスト編集(&T)..."     },
  { mf_,  0,             ""                        },
  { mfE,  IDM_SETOPT,    "オプション設定(&O)..."   },
  { mfP,  0,             "ウィンドウ(&W)"          }, // 6 ウィンドウ  WIN_POS
  { mf_,  IDM_PREVWINDOW,"前のウィンドウ(&P)\tShift+Ctrl+F6" },
  { mf_,  IDM_NEXTWINDOW,"次のウィンドウ(&N)\tCtrl+F6" },
  { mf_,  0,             ""                        },
  { mf_,  IDM_MAXIMIZE,  "単独で表示(&M)"          },
  { mf_,  IDM_TILE,      "左右に並べて表示(&T)"    },
  { mfE,  IDM_HTILE,     "上下に並べて表示(&H)"    },
  { mfPE, 0,             "ヘルプ(&H)"              }, // 7 ヘルプ
  { mf_,  IDM_MANUAL,    "マニュアル(&M)\tF1"      },
  { mf_,  0,             ""                        },
  { mfE,  IDM_ABOUT,     "バージョン情報(&A)"      },
};
static MNUS BOLMS = { BOLMI, _countof(BOLMI) };

#define  VIW_POS  2
#define  WIN_POS  6

static MNUI PRNMI[] = {
  { mfP,  0,             "表示(&V)"                }, // 表示(PRN)
  { mf_,  IDM_PREVPAGE,  "前ページ(&P)\tCtrl+Alt+P" },
  { mf_,  IDM_NEXTPAGE,  "次ページ(&N)\tCtrl+Alt+N" },
  { mf_,  0,             ""                        },
  { mf_,  IDM_SETPRN,    "用紙設定(&S)\tCtrl+Alt+S" },
  { mf_,  0,             ""                        },
  { mf_,  IDM_ZOOMIN,    "拡大(&Z)\tCtrl+;"        },
  { mf_,  IDM_ZOOMOUT,   "縮小(&O)\tCtrl+-"        },
  { mf_,  0,             ""                        },
  { mf_,  IDM_SAMESCALE, "等倍表示(&S)\tCtrl+1"    },
  { mfE,  IDM_AUTOSCALE, "全体表示(&A)\tCtrl+0"    },
};
static MNUS PRNMS = { PRNMI, _countof(PRNMI) };

  // ツールバー
static TOBI BOLTI[] = {
  { tbfB,      IDM_PREVWINDOW, "前のウィンドウ" }, //  0
  { tbfB,      IDM_NEXTWINDOW, "次のウィンドウ" },
  { tbfV,      0,              ""               },
  { tbfL(192), IDBAS,          ""               },
  { tbfB,      IDM_CLOSE,      "閉じる"         },
  { tbfV,      0,              ""               },
  { tbfB,      IDM_SETENV,     "基本設定"       },
  { tbfB,      IDM_OPEN,       "データ入力表"   },
  { tbfB,      IDM_SAVE,       "上書保存"       },
  { tbfB,      IDM_PRINT,      "印刷"           },
  { tbfV,      0,              ""               },
  { tbfB,      IDM_CUT,        "切り取り"       }, // 11  CUT_IDX
  { tbfB,      IDM_COPY,       "コピー"         },
  { tbfB,      IDM_PASTE,      "貼り付け"       },
  { tbfB,      IDM_SEARCH,     "検索"           },
  { tbfV,      0,              ""               },
  { tbfB,      IDM_VIEW,       "表示形式"       },
  { tbfB,      IDM_ARRANGE,    "表示項目"       },
  { tbfB,      IDM_FILTER,     "絞り込み"       },
  { tbfB,      IDM_SORT,       "並べ替え"       },
  { tbfV,      0,              ""               },
  { tbfB,      IDM_SUMMARY,    "集計"           },
  { tbfB,      IDM_CHECK,      "チェック"       },
  { tbfB,      IDM_EDIT,       "行編集"         },
  { tbfB,      IDM_ADDEDIT,    "行追加"         },
  { tbfB,      IDM_INSERTEDIT, "行挿入"         },
  { tbfB,      IDM_SELDELETE,  "行削除"         },
  { tbfV,      0,              ""               },
};
static TOBS BOLTS = { BOLTI, _countof(BOLTI) };

#define  CUT_IDX 11
#define  VIW_IDX 16

static TOBI TXTVW[] = {
  { tbfB,      IDM_SAVEAS,     "名前を付けて保存" },
  { tbfL(90),  IDPOS,          ""               },
  { tbfV,      0,              ""               },
};
static TOBI TXTTI[VIW_IDX + _countof(TXTVW)];
static TOBS TXTTS = { TXTTI, _countof(TXTTI) };

static TOBI PRNVW[] = {
  { tbfB,      IDM_PREVPAGE,   "前ページ"       },
  { tbfB,      IDM_NEXTPAGE,   "次ページ"       },
  { tbfL(54),  IDPAG,          ""               },
  { tbfB,      IDM_SETPRN,     "用紙設定"       },
  { tbfV,      0,              ""               },
  { tbfB,      IDM_ZOOMIN,     "拡大"           },
  { tbfB,      IDM_ZOOMOUT,    "縮小"           },
  { tbfL(42),  IDSCL,          ""               },
  { tbfB,      IDM_SAMESCALE,  "等倍表示"       },
  { tbfB,      IDM_AUTOSCALE,  "全体表示"       },
  { tbfV,      0,              ""               },
};
static TOBI PRNTI[CUT_IDX + _countof(PRNVW)];
static TOBS PRNTS = { PRNTI, _countof(PRNTI) };

void LOCALF PrepToolBar(void)
{ // ツールバー設定
  I4B i;

  for (i = 0; i < _countof(TXTTI); i++) {
    TXTTI[i] = (i < VIW_IDX)? BOLTI[i]: TXTVW[i - VIW_IDX];
  }
  for (i = 0; i < _countof(PRNTI); i++) {
    PRNTI[i] = (i < CUT_IDX)? BOLTI[i]: PRNVW[i - CUT_IDX];
  }
}

static TOBI BOLSI[] = {
  { tbfL(512), IDBAS, "" },
};
static TOBS BOLSS = { BOLSI, _countof(BOLSI) };

  // 開始メニュー
static MNDI BOLDI[] = {
  { mf_,  IDM_OPEN,      "データ入力表",    BID_TABLE   }, //  0
  { mf_,  IDM_CHECK1,    "データチェック",  BID_TEXT    },
  { mf_,  IDM_REPORT1,   "支払調書印刷",    BID_REPORT  },
  { mf_,  IDM_REPORT2,   "宛名ラベル印刷",  BID_REPORT  },
  { mf_,  IDM_SETENV,    "基本設定",        BID_INPUT   },
  { mf_,  IDM_SETOPT,    "オプション設定",  BID_SYSTEM  },
  { mf_,  IDJMP + 1,     "システム管理",    BID_SYSTEM  },
  { mf_,  IDM_EXIT,      "終　　了",        BID_EXIT    },
  { mf_,  IDM_SETUSR,    "ユーザー管理",    BID_TABLE   }, //  1
  { mf_,  IDM_INFO1,     "操作履歴確認",    BID_TEXT    },
  { mf_,  IDM_SITEWHO,   "ログイン状況",    BID_SYSTEM  },
  { mf_,  IDM_UNLOCK,    "ロック解除",      BID_SYSTEM  },
  { mf_,  IDM_BACKUP,    "バックアップ",    BID_SYSTEM  },
  { mf_,  0,             "",                0           },
  { mf_,  IDM_TEDIT,     "テキスト編集",    BID_TEXT    },
  { mf_,  IDJMP,         "戻　　る",        BID_BACK    },
};
static MNDS BOLDS = { BOLDI, _countof(BOLDI), 0, 0, NULL, NULL };

  // 環境設定情報
typedef struct {
  I1B   stit[64];
  I1B   padr[64];
  I1B   pnam[64];
  I1B   ptel[16];
  F8B   ppin;
  I1B   num1[16];
  I1B   num2[16];
  I1B   snum[8];
  I1B   udir[512];
} ENVD;

static COLI ciENV[] = {
  { STR(64),  0, "SlipTit", "[HOUENV]", }, // 0
  { STR(64),  0, "PayrAdr", },
  { STR(64),  0, "PayrNam", },
  { STR(16),  0, "PayrTel", },
  { TID_F8B,  0, "PayrPIN", NULL, NULL, CF_HZR, NULL, NULL, (uANY)"%012.0f" }, // 4
  { STR(16),  0, "RefNum1", },
  { STR(16),  0, "RefNum2", },
  { STR(8),   0, "SlipNum", },
  { STR(512), 0, "UserDir", },
};
#define  ciPPIN  ciENV[4]
static COLS csENV = { ciENV, _countof(ciENV) };
static TBLI tiENV = { &csENV, NULL, "HOUENV", NULL, NULL, 0, NULL, "HOUENV.INI", 0, sizeof(ENVD) };

ENVD EnvDat;

void LOCALF setenv(void)
{ // 環境設定
  SITI var;

  var.nam = PRGNAM;
  var.tit = PRGTIT;
  var.dsc = PRGDSC;
  var.tax = 0.0;
  var.fym = 0;
  var.nmf = 0;
  var.dir = EnvDat.udir;
  SetSiteVariables(&var);
}

void LOCALF LoadENV(void)
{ // 環境設定読込
  strcpyx(EnvDat.stit, "令和８年分　報酬、料金、契約金及び賞金の支払調書", sizeof(EnvDat.stit) - 1);
  strcpyx(EnvDat.snum, "309", sizeof(EnvDat.snum) - 1);
  strcpyx(EnvDat.udir, UserPath(), sizeof(EnvDat.udir) - 1);
  if (file_exists(tiENV.imp)) {
    ReadINI(&EnvDat, &tiENV);
  }
  setenv();
}

void LOCALF SaveENV(void)
{ // 環境設定書出
  setenv();
  WriteINI(&EnvDat, &tiENV);
}

#define  UOPT_ALI  0x0001
#define  UOPT_UMD  0x0002
#define  UOPT_ACN  0x0004
#define  UOPT_AOP  0x0008
#define  UOPT_ELG  0x0010

#define  BGRC_PTR  RGB(208,255,224)
#define  BGRC_IDX  RGB(208,208,208)
#define  BGRC_OLD  RGB(255,224,208)

  // 初期設定情報
typedef struct {
  WPOS  wpo;
  I1B   face[32]; // -+
  I4B   ptsz;     //  |
  I4B   tbcs;     //  |
  I4B   cptr;     //  |
  I4B   cidx;     //  |
  I4B   uopt;     //  |
  I1B   unam[32]; //  |= OPTP
  I1B   upwd[32]; // -+
  I1B   bdir[512];
} INID;

static COLI ciINI[] = {
  { TID_I4B,  0, "WinPosX", "["PRGNAM"]", }, // 0
  { TID_I4B,  0, "WinPosY", },
  { TID_I4B,  0, "WinSizW", },
  { TID_I4B,  0, "WinSizH", },
  { TID_I4B,  0, "WinMaxm", },
  { STR(32),  0, "TblFont", },
  { TID_I4B,  0, "TblFSiz", },
  { TID_I4B,  0, "TblStyl", },
  { TID_HEX,  0, "CPtrCol", }, // 8
  { TID_HEX,  0, "CIdxCol", }, // 9
  { TID_I4B,  0, "UserOpt", },
  { STR(32),  0, "UserNam", },
  { STR(32),  0, "UserPwd", NULL, NULL, CF_HCH, },
  { STR(512), 0, "BackDir", },
};
#define  ciCPTR  ciINI[8]
#define  ciCIDX  ciINI[9]
static COLS csINI = { ciINI, _countof(ciINI) };
static TBLI tiINI = { &csINI, NULL, "HOUINI", NULL, NULL, 0, NULL, PRGNAM".INI", 0, sizeof(INID) };

static INID IniDat = {
  { 0, 0, 1200, 730, FALSE },
  DEFFACE, DEFPTSZ,
  0,
  BGRC_PTR, BGRC_IDX,
  UOPT_UMD,
  "", "",
  "",
};

rSTR LOCALF defface(void) { return IniDat.face;}
I4B  LOCALF defptsz(void) { return IniDat.ptsz;}
I4B  LOCALF optflg(void)  { return IniDat.tbcs;}

void LOCALF LoadINI(void)
{ // 初期化ファイル読込
  LOGFONT lof;
  I1B bak[512];

  get_working_path(bak, sizeof(bak));
  set_working_path(UserPath());
  if (file_exists(tiINI.imp)) {
    ReadINI(&IniDat, &tiINI);
  }
  set_working_path(bak);
  SetAppFont(FID_DEF, CreateFontIndirect(SetLOGFONT(&lof, IniDat.face, PT2DOTS(IniDat.ptsz), 0)));
  SetAppColor(CID_PTR, IniDat.cptr); if (IniDat.cidx == BGRC_OLD) { IniDat.cidx = BGRC_IDX;}
  SetAppColor(CID_IDX, IniDat.cidx);
  if (IniDat.uopt & UOPT_ELG) { enable_error_logging();} else { disable_error_logging();}
}

void LOCALF SaveINI(void)
{ // 初期化ファイル書出
  I4B sw;
  I1B bak[512];

  if (!(IniDat.uopt & UOPT_ALI)) { IniDat.upwd[0] = '\0';}
  get_working_path(bak, sizeof(bak));
  set_working_path(UserPath());
  sw = disable_error_reporting();
  WriteINI(&IniDat, &tiINI);
  if (sw) { enable_error_reporting();}
  set_working_path(bak);
}

I4B LOCALF AppStatusX(HWND hwnd)
{ // 状態の取得
  I4B sta;

  sta = AppStatus(hwnd);
  switch (sta & APPS_ACT) {
  case APPS_TXT: sta |= TXT_appstatus((HWND)SendMessage(hwnd, FRMM_GETACTIVE, 0, 0)); break;
  case APPS_PRN: sta |= PRN_appstatus((HWND)SendMessage(hwnd, FRMM_GETACTIVE, 0, 0)); break;
  case APPS_TBL: sta |= TBL_appstatus((HWND)SendMessage(hwnd, FRMM_GETACTIVE, 0, 0)); break;
  }
  return sta;
}

#define  sta_ACT  ((sta & APPS_ACT) != 0)
#define  sta_NON  ((sta & APPS_ACT) == 0)
#define  sta_TXT  ((sta & APPS_ACT) == APPS_TXT)
#define  sta_PRN  ((sta & APPS_ACT) == APPS_PRN)
#define  sta_TBL  ((sta & APPS_ACT) == APPS_TBL)
#define  sta_EDI  ((sta & APPS_DED) == 0)
#define  sta_SEL  ((sta & APPS_SEL) != 0)
#define  sta_MUL  ((sta & APPS_MUL) != 0)
#define  sta_CFT  ((sta & APPS_CFT) != 0)
#define  sta_CFB  ((sta & APPS_CFB) != 0)
#define  sta_PVP  ((sta & APPS_PVP) != 0)
#define  sta_NXP  ((sta & APPS_NXP) != 0)
#define  sta_SKP  ((sta & APPS_SKP) != 0)
#define  sta_LSC  ((sta & APPS_LSC) != 0)
#define  sta_USC  ((sta & APPS_USC) != 0)
#define  sta_LNK  ((sta & APPS_LNK) != 0)

BOOL LOCALF Enabled(I4B id, I4B sta)
{ // コマンドが使用可能かどうかの判定
  switch (id) {
  case IDM_OMENU:
  case IDM_OMENU1:
  case IDM_OPEN:
  case IDM_CHECK1:
  case IDM_REPORT1:
  case IDM_REPORT2:
  case IDM_INFO1:
  case IDM_CSVOUT:
  case IDM_SETOPT:
    return sta_NON;
  case IDM_CLOSE:
  case IDM_PRINT:
    return sta_ACT;
  case IDM_SAVE:
  case IDM_CHECK:
    return sta_TBL && sta_EDI && CurAuth() >= 2;
  case IDM_PASTE:
    return ((sta_TXT && sta_CFT) || (sta_TBL && ((sta_CFB && sta_SEL) || sta_CFT))) && sta_EDI && CurAuth() >= 2;
  case IDM_SAVEAS:
    return sta_TXT && sta_EDI && CurAuth() >= 2;
  case IDM_IMPORT:
  case IDM_CLEAR:
  case IDM_EDIT:
  case IDM_ADDEDIT:
  case IDM_INSERTEDIT:
  case IDM_SELDELETE:
  case IDM_PSORT:
    return sta_TBL && sta_EDI && CurAuth() >= 2;
  case IDM_EXPORT:
  case IDM_VIEW:
  case IDM_ARRANGE:
  case IDM_FILTER:
  case IDM_SORT:
  case IDM_VIEWA:
  case IDM_VIEWB:
  case IDM_SETVOPT:
  case IDM_SUMMARY:
    return sta_TBL;
  case IDM_CUT:
    return ((sta_TXT && sta_SEL) || sta_TBL) && sta_EDI && CurAuth() >= 2;
  case IDM_COPY:
    return (sta_TXT && sta_SEL) || sta_TBL;
  case IDM_INSERT:
    return sta_TBL && sta_SEL && sta_EDI && CurAuth() >= 2;
  case IDM_DELETE:
    return (sta_TXT || sta_TBL) && sta_SEL && sta_EDI && CurAuth() >= 2;
  case IDM_OPENLINK:
    return sta_TBL && sta_LNK;
  case IDM_SEARCH:
  case IDM_NSEARCH:
    return sta_TXT || (sta_TBL && !sta_SEL);
  case IDM_UNLOCK:
  case IDM_BACKUP:
  case IDM_SETENV:
    return sta_NON && CurAuth() >= 3;
  case IDM_TEDIT:
    return CurAuth() >= 3;
  case IDM_SETUSR:
    return sta_NON && CurAuth() == 4;
  case IDM_PREVWINDOW:
  case IDM_NEXTWINDOW:
  case IDM_MAXIMIZE:
  case IDM_TILE:
  case IDM_HTILE:
    return sta_MUL;
  case IDM_PREVPAGE:
    return sta_PRN && sta_PVP;
  case IDM_NEXTPAGE:
    return sta_PRN && sta_NXP;
  case IDM_SETPRN:
    return sta_PRN && !sta_SKP;
  case IDM_ZOOMIN:
    return sta_PRN && !sta_USC;
  case IDM_ZOOMOUT:
    return sta_PRN && !sta_LSC;
  case IDM_SAMESCALE:
  case IDM_AUTOSCALE:
  case IDM_INVERT:
    return sta_PRN;
  }
  return TRUE;
}

pMNUS LOCALF viwpop(I4B sta)
{ // 表示メニュー
  I4B i, i1, i2, j;
  static MNUI VMI[24];
  static MNUS VMS = { VMI, 0 };

  switch (sta & APPS_ACT) {
  case APPS_PRN: return &PRNMS;
  }
  MenuSlice(&i1, &i2, &BOLMS, VIW_POS);
  for (i = i1, j = 0; i < i2 && j < _countof(VMI); i++, j++) {
    VMI[j] = BOLMI[i];
  }
  VMI[j-1].flg |= mfE;
  VMS.ni = j;
  return &VMS;
}

pMNUS LOCALF winpop(HWND hwnd)
{ // ウィンドウメニュー
  I4B i, i1, i2, j, j0, nc;
  HWND hchi;
  static MNUI WMI[24], WM0 = { mf_, 0, "" };
  static MNUS WMS = { WMI, 0 };

  MenuSlice(&i1, &i2, &BOLMS, WIN_POS);
  for (i = i1, j = 0; i < i2 && j < _countof(WMI); i++, j++) {
    WMI[j] = BOLMI[i];
  }
  WMI[j-1].flg |= mfE;
  nc = SendMessage(hwnd, FRMM_GETNCHILDREN, 0, 0);
  if (nc > 0) {
    WMI[j-1].flg &=~mfE;
    WMI[j++] = WM0;
    j0 = j;
    for (i = nc - 1; i >= 0 && j < _countof(WMI); i--, j++) {
      WMI[j].flg = (i == nc - 1)? mfC: mf_;
      hchi = (HWND)SendMessage(hwnd, FRMM_GETCHILD, i, 0);
      WMI[j].idm = IDM_WINDOW0 + j - j0;
      WMI[j].txt = basnam(WindowText(hchi));
    }
    WMI[j-1].flg |= mfE;
  }
  WMS.ni = j;
  return &WMS;
}

void LOCALF enummenuitems(HMENU hmnu, I4B sta)
{ // メニュー項目設定
  I4B i, n, id;
  HMENU hsub;

  n = GetMenuItemCount(hmnu);
  for (i = 0; i < n; i++) {
    id = GetMenuItemID(hmnu, i);
    if (id == -1 && !!(hsub = GetSubMenu(hmnu, i))) enummenuitems(hsub, sta);
    else if (id > 0) EnableMenuItem(hmnu, id, Enabled(id, sta)? mf_: mfG);
  }
}

iANY LOCALF FRM_initmenu(HWND hwnd, HMENU hmnu)
{ // メニュー設定
  I4B sta;

  sta = AppStatusX(hwnd);
  ChangeMenuBar(hmnu, VIW_POS, viwpop(sta));
  ChangeMenuBar(hmnu, WIN_POS, winpop(hwnd));
  DrawMenuBar(hwnd);
  enummenuitems(hmnu, sta);
  return 0;
}

iANY LOCALF FRM_inittool(HWND hwnd)
{ // ツールバー設定
  I4B i, id, ni, sta;
  HWND hact, htob;
  pTOBS ts;
  static pTOBS bk;

  htob = GetDlgItem(hwnd, IDTOB); if (!htob) return 0;
  hact = (HWND)SendMessage(hwnd, FRMM_GETACTIVE, 0, 0);
  sta = AppStatusX(hwnd);
  switch (sta & APPS_ACT) {
  case APPS_TXT: ts = &TXTTS; break;
  case APPS_PRN: ts = &PRNTS; break;
  default:       ts = &BOLTS; break;
  }
  if (ts != bk) {
    SendMessage(htob, TOBM_RESETITEMS, (uANY)ts, TRUE);
    bk = ts;
  }
  ni = SendMessage(htob, TOBM_GETNITEMS, 0, 0);
  for (i = 0; i < ni; i++) {
    if ((id = ((pTOBI)SendMessage(htob, TOBM_GETITEMINFO, i, 0))->idm) > 0) {
      EnableWindow(GetDlgItem(htob, id), Enabled(id, sta));
    }
  }
  SetDlgItemText(htob, IDBAS, hact? basnam(WindowText(hact)): dformat("yyyy 年 m 月 d 日", CurDate()));
  switch (sta & APPS_ACT) {
  case APPS_TXT:
    SetDlgItemText(htob, IDPOS, TXT_POS);
    break;
  case APPS_PRN:
    SetDlgItemText(htob, IDPAG, PRN_PAG);
    SetDlgItemText(htob, IDSCL, PRN_SCL);
    break;
  }
  for (i = 0; i < BOLDS.ni; i++) {
    if ((id = BOLDS.mi[i].idm) > 0) {
      BOLDS.mi[i].flg = Enabled(id, sta)? mf_: mfG;
    }
  }
  return 0;
}

  // ユーザー名とパスワード
typedef struct {
  I1B   usr[32];
  I1B   pwd[32];
} PWDP;

BOOL LOCALF getpwd_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // ユーザー確認ダイアログプロシージャ
  PWDP *pa;

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (PWDP *)lpa));
    SetESTR(hdlg, 101, pa->usr);
    SetFocus(GetDlgItem(hdlg, (pa->usr[0] == '\0')? 101: 102));
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return FALSE;
  case WM_COMMAND:
    pa = (PWDP *)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case IDOK:
      GetESTR(hdlg, 101, pa->usr, sizeof(pa->usr) - 1);
      GetESTR(hdlg, 102, pa->pwd, sizeof(pa->pwd) - 1);
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

BOOL LOCALF GetPwd(HWND hwnd, PWDP *pa)
{ // ユーザー名とパスワード取得
  static DLGI dit[] = {
    { C_SLFT|wsG,   8, 10, 40,  8,  -1, "ユーザー名:" },
    { C_ESTR|wsT,  48,  8,100, 12, 101, "" },
    { C_SLFT|wsG,   8, 26, 40,  8,  -1, "パスワード:" },
    { C_EPWD|wsT,  48, 24,100, 12, 102, "" },
    { C_BDEF|wsGT, 54, 60, 48, 14, IDOK, "OK" },
    { C_BPSH|wsT, 106, 60, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 160, 80, "ユーザー確認" };

  return DlgBoxInput(hwnd, &dlg, getpwd_DlgProc, (iANY)pa) == IDOK;
}

BOOL LOCALF alreadyloggedin(rSTR usr)
{ // 既にログインしている
  I4B i, ni, re = FALSE;
  HTBL tb;

  if (file_size(PRGNAM".BSU") == 0) return FALSE;
  tb = CreateBSUTable(32, TRUE); if (!tb) return FALSE;
  ni = NTableRows(tb);
  for (i = 0; i < ni; i++) {
    if (_stricmp(usr, TableRow(tb, i)) == 0) { re = TRUE; break;}
  }
  DeleteTable(tb);
  return re;
}

void LOCALF cmd_login(HWND hwnd)
{ // ログイン
  I4B re;
  static rSTR alertstr =
    "同名のユーザーが既にログインしています。\n"
    "プログラムを二重に起動していないか確認してください。\n"
    "二重起動している場合、「中止」でこのプログラムを終了するか\n"
    "先に起動した方を終了してから「再試行」で続行します。\n"
    "二重起動していない場合、「無視」で続行して構いません。";

  if (!(IniDat.uopt & UOPT_ALI)) {
   _00: IniDat.upwd[0] = '\0';
    re = GetPwd(hwnd, (PWDP *)&IniDat.unam);
    if (!re) { SendMessage(hwnd, WM_CLOSE, 0, 0); return;}
  }
 _10:
  reset_error();
  if (alreadyloggedin(IniDat.unam)) {
    re = AlertBox(hwnd, alertstr, NULL);
    switch (re) {
    default: SendMessage(hwnd, WM_CLOSE, 0, 0); return;
    case IDRETRY: goto _10;
    case IDIGNORE: break;
    }
  }
  re = LoginSite(IniDat.unam, IniDat.upwd);
  if (!re) {
    ErrorBox(hwnd, "ログインに失敗しました。", NULL);
    IniDat.uopt &=~UOPT_ALI;
    goto _00;
  }
  re = ConnectDBX(&dbHOU);
  if (!re) {
    ErrorBox(hwnd, "データベースへの接続に失敗しました。", NULL);
    SendMessage(hwnd, WM_CLOSE, 0, 0); return;
  }
  PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0);
}

rSTR LOCALF sitewhostr(void)
{ // サイトへのログイン状況、ファイルのロック状況
  I4B i, ni;
  pSTR pt;
  PLCKI pc;
  HTBL tb;
  static I1B msg[512];

  *(pt = msg) = '\0';
  if (file_size(PRGNAM".BSU") == 0) goto _10;
  tb = CreateBSUTable(32, TRUE); if (!tb) goto _10;
  ni = NTableRows(tb);
  for (i = 0; i < ni; i++) {
    if (pt - msg < 450) {
      pt += sprintfs(pt, (i == 0)? "'%s'": "、'%s'", TableRow(tb, i));
    }
  }
  if (ni > 0) { pt += sprintfs(pt, "がログイン中です。");}
  DeleteTable(tb);
  if (file_size(PRGNAM".LCK") == 0) goto _10;
  tb = CreateLCKTable(32, TRUE); if (!tb) goto _10;
  ni = NTableRows(tb);
  for (i = 0; i < ni; i++) {
    if (pt - msg < 450) {
      pc = TableRow(tb, i);
      pt += sprintfs(pt, (i == 0)? "'%s'を'%s'が": "、'%s'を'%s'が", DBTableFileName(pc->id), pc->usr);
    }
  }
  if (ni > 0) { pt += sprintfs(pt, "ロックしています。");}
  DeleteTable(tb); _10:
  return msg;
}

void LOCALF cmd_omenu(HWND hwnd, BOOL sw)
{ // 開始メニュー
  I4B re;

  if (AppStatus(hwnd) & APPS_ACT) return;
  if (!sw && !(IniDat.uopt & UOPT_UMD)) return;
 _00:
  BOLDS.msg2 = sitewhostr();
  re = MenuDlgBox(hwnd, &BOLDS, PRGTIT);
  if (re == IDREFRESH) { LoadENV(); goto _00;}  else if (re == IDCANCEL) return;
  if (re == IDMANUAL) { re = IDM_MANUAL;}
  PostMessage(hwnd, WM_COMMAND, re, 0);
}

void LOCALF cmd_open(HWND hwnd)
{ // データ入力表を開く
  HWND hchi;
  static TBWI tbw;

  tbw.tit = tiHOU[idHOU].tit; tbw.vs = tiHOU[idHOU].vs; tbw.iv0 = -1;
  if (!!(hchi = (HWND)SendMessage(hwnd, FRMM_GETCHILDBYNAME, (uANY)tbw.tit, 0))) {
    ErrorBox(hwnd, "同名のファイルを既に開いています。", NULL);
    SendMessage(hwnd, FRMM_ACTIVATE, (uANY)hchi, 0);
    return;
  }
  BusyMsg(hwnd, "読込中...");
  reset_error();
  tbw.op = OpenTable(tiHOU[idHOU].nam, (CurAuth() >= 2)? OM_RDW: OM_RDO);
  EndBusy(hwnd);
  if (!tbw.op) {
    ErrorBox(hwnd, format("'%s'の読込に失敗しました。", tbw.tit), NULL);
    PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0);
    return;
  }
  hchi = CreateTableWindow(hwnd, 0, &tbw); if (!hchi) return;
  oprlog(0, 0, 0);
}

BOOL LOCALF errout(rSTR nam)
{ // エラーレポート書出
  I4B ec, cn = 0;
  HANDLE hf;
  rHOUD pb, pe;

  hf = open_file(nam, OP_WRI); if (!hf) { return (BOOL)ferr(E_FIW, nam);}
  for (pb = TablePtr(tbHOU), pe = pb + NTableRows(tbHOU); pb < pe; pb++) {
    if ((ec = echkHOU(pb)) != 0) {
      hfprintf(hf, "%s %3d-%d %s\n", tiHOU[idHOU].tit, pb->ser, pb->suf, EDSC[ec]);
      cn++;
    }
  }
  close_file(hf);
  return cn == 0;
}

void LOCALF cmd_tedit(HWND hwnd, rSTR src);

void LOCALF cmd_check1(HWND hwnd)
{ // データチェック
  I4B re = FALSE;
  HOPN op;
  rSTR nam;

  if (YesNoBox(hwnd, "データチェックを行います。よろしいですか？", NULL) != IDYES) goto _20;
  nam = adddir(UserPath(), "HOUERR.TXT");
  BusyMsg(hwnd, "処理中...");
  reset_error();
  op = OpenTable(tiHOU[idHOU].nam, OM_RDO); if (!op) goto _10;
  re = errout(nam);
  CloseTable(op, FALSE);
 _10: EndBusy(hwnd);
  if (re) {
    OkBox(hwnd, "エラーは見つかりませんでした。", NULL);
  } else if (YesNoBox(hwnd, "エラーが見つかりました。エラーレポートを表示しますか？", NULL) == IDYES) {
    cmd_tedit(hwnd, nam); return;
  }
 _20:
  PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0);
}

BOOL LOCALF getD01_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // 支払調書印刷設定ダイアログプロシージャ
  PINT pa;

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (PINT)lpa));
    SetEINT(hdlg, 101,  *pa & 0x000f);
    SetEINT(hdlg, 102, (*pa & 0x00f0) >> 4);
    SetEINT(hdlg, 103, (*pa & 0x0f00) >> 8);
    SetEINT(hdlg, 104, (*pa & 0xf000) >>12);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    pa = (PINT)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case IDOK:
      *pa = ((GetEINT(hdlg, 104) & 0x0f) <<12) | ((GetEINT(hdlg, 103) & 0x0f) << 8)
          | ((GetEINT(hdlg, 102) & 0x0f) << 4) |  (GetEINT(hdlg, 101) & 0x0f);
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

BOOL LOCALF GetD01(HWND hwnd, PINT pa)
{ // 支払調書印刷設定
  static DLGI dit[] = {
    { C_SLFT|wsG,   8, 10, 88,  8,  -1, "個人／提出用(番号有)(&I):" },
    { C_ENUM|wsT,  96,  8, 24, 12, 101, "" },
    { C_SLFT|wsG, 121, 10,  8,  8,  -1, "部" },
    { C_SLFT|wsG,   8, 26, 88,  8,  -1, "個人／交付用(番号無)(&N):" },
    { C_ENUM|wsT,  96, 24, 24, 12, 102, "" },
    { C_SLFT|wsG, 121, 26,  8,  8,  -1, "部" },
    { C_SLFT|wsG,   8, 42, 88,  8,  -1, "法人／提出用(番号有)(&C):" },
    { C_ENUM|wsT,  96, 40, 24, 12, 103, "" },
    { C_SLFT|wsG, 121, 42,  8,  8,  -1, "部" },
    { C_SLFT|wsG,   8, 58, 88,  8,  -1, "法人／交付用(番号無)(&O):" },
    { C_ENUM|wsT,  96, 56, 24, 12, 104, "" },
    { C_SLFT|wsG, 121, 58,  8,  8,  -1, "部" },
    { C_BDEF|wsGT,102,124, 48, 14, IDOK, "OK" },
    { C_BPSH|wsT, 154,124, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 208, 144, "支払調書" };

  return DlgBoxInput(hwnd, &dlg, getD01_DlgProc, (uANY)pa) == IDOK;
}

void LOCALF GridText(HREP rp, PCGRID gr, I4B ir, I4B ic, rSTR st, I4B fmt)
{ // 文字列の描画
  CELL cel; CellText(rp, GridCell(&cel, gr, ir, ic), st, fmt);
}

void LOCALF PaddText(HREP rp, PCGRID gr, I4B ir, I4B ic, F8B lm, F8B tm, F8B rm, F8B bm, rSTR st, I4B fmt)
{ // 文字列の描画
  CELL cel; CellText(rp, ReshapeCell(GridCell(&cel, gr, ir, ic), lm, tm, rm, bm), st, fmt);
}

rSTR LOCALF PINdig(F8B d, I4B n)
{ // n桁目数字
  I4B m;
  I1B bf[32];
  static I1B dg[2];

  sprintfs(bf, "%012.0f", d);
  m = strlen(bf);
  dg[0] = (n >= 0 && n <= 12 && n >= 13 - m)? bf[n - (13 - m)]: '\0';
  return dg;
}

void LOCALF PrintSlip(HREP rp, rHOUD ph, I4B nk, BOOL sw)
{ // 調書印刷
  I4B i, j, k, rst;
  CELL cel;
  GRID grd;
  static F8B cw0[] = { 10.0, 14.0, 102.0 };
  static F8B cw1[] = { 26.0, 35.0, 32.5, 32.5 };
  static F8B cw3[] = { 33.0, 46.5, 46.5 };

  SetCell(&cel, 12.0, 4.5, 138.0, 11.0);
  SetTextFont(rp, "ＭＳ 明朝", 3.7, 0, 0);
  CellText(rp, &cel, EnvDat.stit, DT_CEN);

  SetGrid1(&grd, 12.0, 11.0, _countof(cw0), 2, cw0, 7.7);
  for (i = 0; i <  grd.nr; i++) {
    for (j = 0; j < grd.nc; j++) {
      rst = RST((j == 0)? lstB: 0, (i == 0)? lstB: 0, (j == grd.nc-1)? lstB: lstR, (i == grd.nr-1)? lstB: (i == 0 && j == 0)? 0: lstR);
      DrawGridCell(rp, &grd, i, j, rst);
    }
  }
  SetTextFont(rp, NULL, 2.4, 0, 0);
  PaddText(rp, &grd, 0, 0, 0.0, 5.0, 0.0, 0.5, "支 払 を", DT_CEN);
  PaddText(rp, &grd, 1, 0, 0.0, 0.5, 0.0, 5.0, "受ける者", DT_CEN);
  SetTextFont(rp, NULL, 2.4, 0, 0);
  PaddText(rp, &grd, 0, 1, 0.0, 1.3, 0.0, 4.2, "住所(居所)", DT_CEN);
  PaddText(rp, &grd, 0, 1, 0.0, 4.2, 0.0, 1.3, "又は所在地", DT_CEN);
  PaddText(rp, &grd, 1, 1, 0.0, 1.3, 0.0, 4.2, "氏 名 又は", DT_CEN);
  PaddText(rp, &grd, 1, 1, 0.0, 4.2, 0.0, 1.3, "名　　　称", DT_CEN);
  SetTextFont(rp, NULL, 3.7, 0, 0);
  PaddText(rp, &grd, 0, 2, 2.0, 0.0, 2.0, 0.0, ph->adr, DT_AUT);
  PaddText(rp, &grd, 1, 2, 2.0, 0.0,47.0, 0.0, ph->nam, DT_AUT);
  SetLineStyle(rp, lstR);
  DrawLine(rp, 93.0, 18.7, 93.0, 26.4);
  DrawLine(rp, 93.0, 21.7, 138.0, 21.7);
  SetLineStyle(rp, lstD);
  for (j = 1; j < 13; j++) {
    DrawLine(rp, 93.0 + j * 45.0/13, 21.7, 93.0 + j * 45.0/13, 26.4);
  }
  SetCell(&cel, 93.0, 18.7, 138.0, 21.7);
  SetTextFont(rp, NULL, 2.4, 0, 0);
  CellText(rp, &cel, "個人番号又は法人番号", DT_CEN);
  if (sw) {
    SetTextStyle(rp, 2.6, 2.6, 0.0, NULL);
    for (j = 0; j < 13; j++) {
      SetCell(&cel, 93.0 + j * 45.0/13, 21.7, 93.0 + (j + 1) * 45.0/13, 26.4);
      CellText(rp, &cel, PINdig(ph->pin, j), DT_CEN);
    }
  }

  SetGrid1(&grd, 12.0, 26.4, _countof(cw1), 7, cw1, 5.7);
  for (i = 0; i < grd.nr; i++) {
    for (j = 0; j < grd.nc; j++) {
      rst = RST((j == 0)? lstB: 0, 0, (j == grd.nc-1)? lstB: lstR, (i == grd.nr-1)? lstB: lstR);
      DrawGridCell(rp, &grd, i, j, rst);
    }
  }
  SetLineStyle(rp, lstD);
  DrawLine(rp, 84.0, 32.1, 84.0, 66.3);
  DrawLine(rp, 94.5, 32.1, 94.5, 66.3);
  DrawLine(rp,116.5, 32.1,116.5, 66.3);
  DrawLine(rp,127.0, 32.1,127.0, 66.3);
  SetTextFont(rp, NULL, 2.6, 0, 0);
  GridText(rp, &grd, 0, 0, "区　　　分", DT_CEN);
  GridText(rp, &grd, 0, 1, "細　　　　目", DT_CEN);
  GridText(rp, &grd, 0, 2, "支　払　金　額", DT_CEN);
  GridText(rp, &grd, 0, 3, "源 泉 徴 収 税 額", DT_CEN);
  for (k = 0; k < nk; k++) {
    SetTextFont(rp, NULL, 2.6, 0, 0);
    PaddText(rp, &grd, 1 + k, 0, 1.0, 0.0, 1.0, 0.0, (ph + k)->acc, DT_AUT);
    PaddText(rp, &grd, 1 + k, 1, 1.0, 0.0, 1.0, 0.0, (ph + k)->det, DT_AUT);
    if ((ph + k)->unp == 0) {
      SetTextStyle(rp, 2.6, 2.6, 0.0, NULL);
      PaddText(rp, &grd, 1 + k, 2, 1.0, 0.0, 2.0, 0.0, cformat("%d", (ph + k)->fee), DT_RGT);
    } else {
      SetTextFont(rp, NULL, 2.2, 0, 0);
      PaddText(rp, &grd, 1 + k, 2, 1.0, 0.0, 2.0, 2.8, "内", DT_LFT);
      SetTextStyle(rp, 2.6, 2.6, 0.0, NULL);
      PaddText(rp, &grd, 1 + k, 2, 1.0, 0.0, 2.0, 2.8, cformat("%d", (ph + k)->unp), DT_RGT);
      PaddText(rp, &grd, 1 + k, 2, 1.0, 2.8, 2.0, 0.0, cformat("%d", (ph + k)->fee), DT_RGT);
    }
    if ((ph + k)->unc == 0) {
      SetTextStyle(rp, 2.6, 2.6, 0.0, NULL);
      PaddText(rp, &grd, 1 + k, 3, 1.0, 0.0, 2.0, 0.0, cformat("%d", (ph + k)->tax), DT_RGT);
    } else {
      SetTextFont(rp, NULL, 2.2, 0, 0);
      PaddText(rp, &grd, 1 + k, 3, 1.0, 0.0, 2.0, 2.8, "内", DT_LFT);
      SetTextStyle(rp, 2.6, 2.6, 0.0, NULL);
      PaddText(rp, &grd, 1 + k, 3, 1.0, 0.0, 2.0, 2.8, cformat("%d", (ph + k)->unc), DT_RGT);
      PaddText(rp, &grd, 1 + k, 3, 1.0, 2.8, 2.0, 0.0, cformat("%d", (ph + k)->tax), DT_RGT);
    }
  }

  SetGrid0(&grd, 12.0, 66.3, 1, 1, 126.0, 9.5);
  rst = RST(lstB, 0, lstB, lstB);
  DrawGridCell(rp, &grd, 0, 0, rst);
  SetTextFont(rp, NULL, 2.4, 0, 0);
  PaddText(rp, &grd, 0, 0, 0.0, 0.0, 118.0, 6.5, "(摘要)", DT_CEN);
  SetTextFont(rp, NULL, 2.6, 0, 0);
  PaddText(rp, &grd, 0, 0, 8.0, 0.0, 1.0, 0.0, ph->rem, DT_AUT);

  SetGrid1(&grd, 12.0, 75.8, _countof(cw0), 2, cw0, 7.7);
  for (i = 0; i < grd.nr; i++) {
    for (j = 0; j < grd.nc; j++) {
      rst = RST((j == 0)? lstB: 0, 0, (j == grd.nc-1)? lstB: lstR, (i == grd.nr-1)? lstB: (i == 0 && j == 0)? 0: lstR);
      DrawGridCell(rp, &grd, i, j, rst);
    }
  }
  SetTextFont(rp, NULL, 2.4, 0, 0);
  PaddText(rp, &grd, 0, 0, 0.0, 6.6, 0.0,-1.1, "支 払 者", DT_CEN);
  SetTextFont(rp, NULL, 2.4, 0, 0);
  PaddText(rp, &grd, 0, 1, 0.0, 1.3, 0.0, 4.2, "住所(居所)", DT_CEN);
  PaddText(rp, &grd, 0, 1, 0.0, 4.2, 0.0, 1.3, "又は所在地", DT_CEN);
  PaddText(rp, &grd, 1, 1, 0.0, 1.3, 0.0, 4.2, "氏 名 又は", DT_CEN);
  PaddText(rp, &grd, 1, 1, 0.0, 4.2, 0.0, 1.3, "名　　　称", DT_CEN);
  SetTextFont(rp, NULL, 2.4, 0, 0);
  PaddText(rp, &grd, 1, 2, 27.0, 4.7, 67.0, 0.0, "(電話)", DT_CEN);
  SetTextFont(rp, NULL, 3.7, 0, 0);
  PaddText(rp, &grd, 0, 2, 2.0, 0.0, 2.0, 0.0, EnvDat.padr, DT_AUT);
  PaddText(rp, &grd, 1, 2, 2.0, 0.0,47.0, 2.0, EnvDat.pnam, DT_AUT);
  SetTextFont(rp, NULL, 2.6, 0, 0);
  PaddText(rp, &grd, 1, 2, 36.0, 4.7, 0.0, 0.0, EnvDat.ptel, DT_LFT);
  SetLineStyle(rp, lstR);
  DrawLine(rp, 93.0, 83.5, 93.0, 91.2);
  DrawLine(rp, 93.0, 86.5, 138.0, 86.5);
  SetLineStyle(rp, lstD);
  for (j = 1; j < 13; j++) {
    DrawLine(rp, 93.0 + j * 45.0/13, 86.5, 93.0 + j * 45.0/13, 91.2);
  }
  SetCell(&cel, 93.0, 83.5, 138.0, 86.5);
  SetTextFont(rp, NULL, 2.4, 0, 0);
  CellText(rp, &cel, "個人番号又は法人番号", DT_CEN);
  if (sw) {
    SetTextStyle(rp, 2.6, 2.6, 0.0, NULL);
    for (j = 0; j < 13; j++) {
      SetCell(&cel, 93.0 + j * 45.0/13, 86.5, 93.0 + (j + 1) * 45.0/13, 91.2);
      CellText(rp, &cel, PINdig(EnvDat.ppin, j), DT_CEN);
    }
  }

  SetGrid1(&grd, 12.0, 92.0, _countof(cw3), 1, cw3, 4.6);
  for (i = 0; i < grd.nr; i++) {
    for (j = 0; j < grd.nc; j++) {
      rst = RST((j == 0)? lstB: 0, (i == 0)? lstB: 0, (j == grd.nc-1)? lstB: lstR, (i == grd.nr-1)? lstB: lstR);
      DrawGridCell(rp, &grd, i, j, rst);
    }
  }
  SetTextFont(rp, NULL, 2.6, 0, 0);
  GridText(rp, &grd, 0, 0, "整　　理　　欄", DT_CEN);
  GridText(rp, &grd, 0, 1, " ①", DT_LFT);
  GridText(rp, &grd, 0, 2, " ②", DT_LFT);
  SetTextFont(rp, NULL, 3.7, 0, 0);
  PaddText(rp, &grd, 0, 1,10.0, 0.0, 0.0, 0.0, EnvDat.num1, DT_LFT);
  PaddText(rp, &grd, 0, 2,10.0, 0.0, 0.0, 0.0, EnvDat.num2, DT_LFT);

  SetCell(&cel, 12.0, 96.6, 138.0, 99.8);
  SetTextFont(rp, NULL, 2.6, 0, 0);
  CellText(rp, &cel, EnvDat.snum, DT_RGT);

  SetLineStyle(rp, lstD);
  DrawLine(rp, 0.0, 105.0, 148.5, 105.0);
  DrawLine(rp, 148.5, 0.0, 148.5, 105.0);
}

BOOL LOCALF frepD01(HREP rp, I4B msg, I4B pg, PINT pa)
{ // 支払調書
  I4B j, k;
  static I4B i, n, n1a, n1b, n2a, n2b, nb, nr;
  static HOPN op;
  static rHOUD dh;

  if (msg == REPM_CREATE) {
    op = OpenTable(tiHOU[idHOU].nam, OM_RDO); if (!op) return FALSE;
    dh = TablePtr(OHTBL(op)); nr = NTableRows(OHTBL(op));
  } else if (msg == REPM_SETUP) {
    if (!GetD01(hWND(rp), pa)) return FALSE;
    n1a =  *pa & 0x000f;
    n1b = (*pa & 0x00f0) >> 4;
    n2a = (*pa & 0x0f00) >> 8;
    n2b = (*pa & 0xf000) >>12;
    n = 0;
    for (i = 0; i < nr; i++) {
      switch (dh[i].atr) {
      case 1: n += (n1a + n1b); break;
      case 2: n += (n2a + n2b); break;
      }
    }
    SetReqBodyWidth(rp, 0.0, RSW_SKP);
    SetNPages(rp, (n + 3) / 4);
  } else if (msg == REPM_STARTPRINT) {
    i = n = 0;
  } else if (msg == REPM_DRAWPAGE || msg == REPM_SKIPPAGE) {
    for (j = 0; j < 4; j++) {
      if (n == 0) { _00:
      while (i < nr && dh[i].atr != 1 && dh[i].atr != 2) { i++;}
        if (i >= nr) break;
        switch (dh[i].atr) {
        case 1: n = n1a + n1b; nb = n1b; break;
        case 2: n = n2a + n2b; nb = n2b; break;
        }
        if (n == 0) { i++; goto _00;}
      }
      SetOrigin(rp, (j % 2) * 148.5, (j / 2) * 105.0);
      k = 1;
      while (i + k < nr && dh[i + k].atr == 3) { k++;}
      PrintSlip(rp, &dh[i], k, n > nb);
      n--;
      if (n == 0) { i++;}
    }
  } else if (msg == REPM_DESTROY) {
    CloseTable(op, FALSE);
  }
  return TRUE;
}

static rSTR SHON[] = { "なし", "様", "御中", "殿" };
static ENMS esHON = { SHON, _countof(SHON) };

BOOL LOCALF getD02_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // 宛名ラベル印刷設定ダイアログプロシージャ
  PINT pa;

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (PINT)lpa));
    SetEINT(hdlg, 101,  *pa & 0x000f);
    SetCSEL(hdlg, 102, (*pa & 0x00f0) >> 4, &esHON);
    SetEINT(hdlg, 103, (*pa & 0x0f00) >> 8);
    SetCSEL(hdlg, 104, (*pa & 0xf000) >>12, &esHON);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    pa = (PINT)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case IDOK:
      *pa = ((GetCSEL(hdlg, 104) & 0x0f) <<12) | ((GetEINT(hdlg, 103) & 0x0f) << 8)
          | ((GetCSEL(hdlg, 102) & 0x0f) << 4) |  (GetEINT(hdlg, 101) & 0x0f);
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

BOOL LOCALF GetD02(HWND hwnd, PINT pa)
{ // 宛名ラベル印刷設定
  static DLGI dit[] = {
    { C_SLFT|wsG,   8, 10, 32,  8,  -1, "個人(&I):" },
    { C_ENUM|wsT,  40,  8, 24, 12, 101, "" },
    { C_SLFT|wsG,  65, 10,  8,  8,  -1, "部" },
    { C_SLFT|wsG,  88, 10, 32,  8,  -1, "敬称(&N):" },
    { C_CSEL|wsT, 120,  8, 32, 64, 102, "" },
    { C_SLFT|wsG,   8, 26, 32,  8,  -1, "法人(&C):" },
    { C_ENUM|wsT,  40, 24, 24, 12, 103, "" },
    { C_SLFT|wsG,  65, 26,  8,  8,  -1, "部" },
    { C_SLFT|wsG,  88, 26, 32,  8,  -1, "敬称(&O):" },
    { C_CSEL|wsT, 120, 24, 32, 64, 104, "" },
    { C_BDEF|wsGT,102,124, 48, 14, IDOK, "OK" },
    { C_BPSH|wsT, 154,124, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 208, 144, "宛名ラベル" };

  return DlgBoxInput(hwnd, &dlg, getD02_DlgProc, (uANY)pa) == IDOK;
}

void LOCALF PrintLabel(HREP rp, rHOUD ph, I4B h)
{ // 宛名ラベル印刷
  pSTR pt;
  CELL cel;
  I1B bf[256];

  SetTextFont(rp, NULL, 4.0, 0, 0);
  SetCell(&cel, 6.0, 6.0, 80.0, 11.0);
  CellText(rp, &cel, ph->zip, DT_LFT);
  SetTextFont(rp, NULL, 4.0, 0, 0);
  sprintfs(bf, "%s", strrepl(ph->adr, "　", " "));
  if (!!(pt = strfind(bf, " "))) *pt = '\0';
  SetCell(&cel, 6.0, 12.0, 80.0, 17.0);
  CellText(rp, &cel, bf, DT_AUT);
  SetCell(&cel, 10.0, 18.0, 80.0, 23.0);
  if (pt) { CellText(rp, &cel, pt + 1, DT_AUT);}
  SetCell(&cel, 14.0, 28.0, 80.0, 33.0);
  sprintfs(bf, "%s　%s", ph->nam,  (h > 0 && h < _countof(SHON))? SHON[h]: "");
  CellText(rp, &cel, bf, DT_AUT);
}

BOOL LOCALF frepD02(HREP rp, I4B msg, I4B pg, PINT pa)
{ // 宛名ラベル
  I4B j;
  HTBL tb;
  static I4B i, n, n1, h1, n2, h2, h, nr;
  static HOPN op;
  static rHOUD dh;

  if (msg == REPM_CREATE) {
    op = OpenTable(tiHOU[idHOU].nam, OM_RDO); if (!op) return FALSE;
    tb = OHTBL(op);
    dh = TablePtr(tb); nr = NTableRows(tb);
  } else if (msg == REPM_SETUP) {
    if (!GetD02(hWND(rp), pa)) return FALSE;
    n1 =  *pa & 0x000f;
    h1 = (*pa & 0x00f0) >> 4;
    n2 = (*pa & 0x0f00) >> 8;
    h2 = (*pa & 0xf000) >>12;
    n = 0;
    for (i = 0; i < nr; i++) {
      switch (dh[i].atr) {
      case 1: n += n1; break;
      case 2: n += n2; break;
      }
    }
    SetReqBodyWidth(rp, 0.0, RSW_SKP);
    SetNPages(rp, (n + 11) / 12);
  } else if (msg == REPM_STARTPRINT) {
    i = n = 0;
  } else if (msg == REPM_DRAWPAGE || msg == REPM_SKIPPAGE) {
    for (j = 0; j < 12; j++) {
      if (n == 0) { _00:
      while (i < nr && dh[i].atr != 1 && dh[i].atr != 2) { i++;}
        if (i >= nr) break;
        switch (dh[i].atr) {
        case 1: n = n1; h = h1; break;
        case 2: n = n2; h = h2; break;
        }
        if (n == 0) { i++; goto _00;}
      }
      SetOrigin(rp, 19.0 + (j % 2) * 87.0, 21.0 + (j / 2) * 42.3);
      PrintLabel(rp, &dh[i], h);
      n--;
      if (n == 0) { i++;}
    }
  } else if (msg == REPM_DESTROY) {
    CloseTable(op, FALSE);
  }
  return TRUE;
}

void LOCALF cmd_report(HWND hwnd, I4B id)
{ // 帳票類印刷
  HWND hprn;
  static I4B par01 = 0x1111;
  static I4B par02 = 0x2111;
  static REPI rpi[] = {                                           // id
    { "支払調書",         PSZ_A4L, (FREP)frepD01, (uANY)&par01 }, //  1
    { "宛名ラベル",       PSZ_A4P, (FREP)frepD02, (uANY)&par02 }, //  2
  };

  oprlog(4 + id - 1, 0, 0);
  hprn = CreatePrintPreview(hwnd, &rpi[id - 1]);
  if (!hprn) PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0);
}

BOOL LOCALF who_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // ログイン状況等ダイアログプロシージャ
  switch (umsg) {
  case WM_INITDIALOG:
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    switch (wpa) {
    case IDOK:
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

void LOCALF cmd_who(HWND hwnd)
{ // サイトへのログイン状況、ファイルのロック状況を表示
  static DLGI dit[] = {
    { C_SMUL|wsG,   8,  8,192, 80,  -1, NULL },
    { C_BDEF|wsGT,154,108, 48, 14, IDOK, "OK" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 208, 128, "ログイン状況" };

  dit[0].txt = sitewhostr();
  DlgBoxInput(hwnd, &dlg, who_DlgProc, (iANY)NULL);
  PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0);
}

void LOCALF cmd_setusr(HWND hwnd)
{ // ユーザー管理表を開く
  static TBWI tbw = { NULL, 0, "ユーザー管理表", NULL, 0 };

  BusyMsg(hwnd, "読込中...");
  reset_error();
  tbw.op = OpenBSATable(OM_RDW);
  EndBusy(hwnd);
  if (!tbw.op) {
    ErrorBox(hwnd, format("'%s'の読込に失敗しました。", tbw.tit), NULL);
    PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0); return;
  }
  CreateTableWindow(hwnd, 0, &tbw);
}

HBLK LOCALF CreateEnmBlk(HTBL tb, I4B ic)
{ // 文字列リストの作成
  I4B i, ni, ofs;
  pENMS ls;

  ni = NTableRows(tb) + 1;
  ls = (pENMS)CreateMemBlk(ni, sizeof(rSTR)); if (!ls) return NULL;
  ls->ps[0] = "";
  ofs = TableColumnOffset(tb, ic);
  for (i = 1; i < ni; i++) {
    ls->ps[i] = (rSTR)((PBYTE)TableRow(tb, i - 1) + ofs);
  }
  ls->ni = ni;
  return (HBLK)ls;
}

  // ロック解除情報
typedef struct {
  HBLK  ls;
  I4B   va;
} UNLP;

BOOL LOCALF unlock_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // ロック解除ダイアログプロシージャ
  UNLP *pa;

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (UNLP *)lpa));
    SetCSEL(hdlg, 101, pa->va, (rENMS)pa->ls);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    pa = (UNLP *)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case IDOK:
      pa->va = GetCSEL(hdlg, 101);
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

void LOCALF cmd_unlock(HWND hwnd)
{ // ファイルのロック解除(ユーザーの強制ログアウト)
  I4B re;
  HTBL tb;
  UNLP par;
  I1B usr[32];
  static DLGI dit[] = {
    { C_SMUL|wsG,   8,  8,144, 24,  -1, "ユーザーを強制的にログアウトさせて\nファイルのロック状態を解除します。" },
    { C_SLFT|wsG,   8, 42, 40,  8,  -1, "ユーザー名:" },
    { C_CSEL|wsT,  48, 40,100,128, 101, "" },
    { C_BDEF|wsGT, 54,108, 48, 14, IDOK, "OK" },
    { C_BPSH|wsT, 106,108, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 160, 128, "ロック解除" };

  if (file_size(PRGNAM".BSU") == 0) goto _10;
  tb = CreateBSUTable(32, TRUE); if (!tb) return;
  par.ls = CreateEnmBlk(tb, 0); if (!par.ls) { DeleteTable(tb); return;}
  par.va = 0;
  re = (DlgBoxInput(hwnd, &dlg, unlock_DlgProc, (iANY)&par) == IDOK);
  strcpyn(usr, enmstr((pENMS)par.ls, par.va), sizeof(usr) - 1); if (usr[0] == '\0') { re = FALSE;}
  DeleteBinBlk(par.ls);
  DeleteTable(tb);
  if (!re) goto _10;
  if (strcmp(usr, CurUser()) == 0) {
    if (YesNoBox(hwnd, "自分自身を強制ログアウト（プログラムを終了）しますか？", NULL) == IDYES) {
      ForceLogoutSite(usr);
      PostMessage(hwnd, WM_COMMAND, IDM_EXIT, 0); return;
    }
    goto _10;
  }
  ForceLogoutSite(usr);
  OkBox(hwnd, format("'%s'を強制ログアウトさせました。", usr), NULL);
 _10:
  PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0);
}

BOOL LOCALF backup_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // バックアップダイアログプロシージャ
  switch (umsg) {
  case WM_INITDIALOG:
    SetESTR(hdlg, 101, IniDat.bdir);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    switch (wpa) {
    case IDOK:
      GetESTR(hdlg, 101, IniDat.bdir, sizeof(IniDat.bdir) - 1);
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

void LOCALF cmd_backup(HWND hwnd)
{ // バックアップ
  I4B re;
  static DLGI dit[] = {
    { C_SLFT|wsG,   8,  8,100,  8,  -1, "バックアップ先フォルダ(&B):" },
    { C_ESTR|wsT,   8, 18,226, 12, 101, "" },
    { C_BDEF|wsGT,134,108, 48, 14, IDOK, "OK" },
    { C_BPSH|wsT, 186,108, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 240, 128, "バックアップ" };

 _00:
  re = (DlgBoxInput(hwnd, &dlg, backup_DlgProc, 0) == IDOK);
  if (re && IniDat.bdir[0] == '\0') {
    ErrorBox(hwnd, "バックアップ先フォルダが必要です。", NULL); goto _00;
  }
  if (!re) goto _10;
  BusyMsg(hwnd, "処理中...");
  reset_error();
  re = copy_file_to(IniDat.bdir, DBTableFileName(idHOU));
  EndBusy(hwnd);
  ResultBox(hwnd, "バックアップ", NULL, re);
 _10:
  PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0);
}

#define  EXT_TXT  2

I4B  LOCALF ext_type(rSTR nam)
{ // ファイル名の拡張子でファイル形式を判別
  I4B i;
  static rSTR ex2[] = { ".txt", ".c", ".cpp", ".h", ".ini", ".log" };

  for (i = 0; i < _countof(ex2); i++) {
    if (strirfind(nam, ex2[i])) { return EXT_TXT;}
  }
  return 0;
}

void LOCALF cmd_tedit(HWND hwnd, rSTR src)
{ // テキスト編集
  I4B re;
  HWND hchi;
  WNDPROC prc;
  STRBUF nam, bak;
  static CREI cri = { NULL, wsCHV, };
  static OFNS ofn = { TFILTER, 1, NULL, OFN_HIDEREADONLY };

  if (CurAuth() < 2) { cri.sty |= TXCS_DED;}
  if (src) {
    strcpys(nam, src);
  } else {
    get_working_path(bak, sizeof(bak));
    (ofn.nam = nam)[0] = '\0';
    re = OpenFileDlgBox(hwnd, &ofn);
    set_working_path(bak);
    if (!re) {
      if (!(AppStatus(hwnd) & APPS_ACT)) { PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0);}
      return;
    }
  }
  if (!!(hchi = (HWND)SendMessage(hwnd, FRMM_GETCHILDBYNAME, (uANY)basnam(nam), 0))) {
    ErrorBox(hwnd, "同名のファイルを既に開いています。", NULL);
    SendMessage(hwnd, FRMM_ACTIVATE, (uANY)hchi, 0); return;
  }
  cri.tit = nam;
  cri.id = SendMessage(hwnd, FRMM_GETNEWID, IDPRN + 1, 0);
  switch (ext_type(nam)) {
  case EXT_TXT:
    BusyMsg(hwnd, "読込中...");
    reset_error();
    cri.pa = (U4B)LoadTXT(NULL, nam);
    EndBusy(hwnd);
    if (cri.pa == 0) return;
    cri.cls = "BOLTXT"; prc = TXT_WndProc;
    break;
  default:
    ErrorBox(hwnd, "このファイル形式はサポートしていません。", NULL); return;
  }
  SendMessage(hwnd, FRMM_CREATECHILD, (uANY)&cri, (iANY)prc);
}

BOOL LOCALF setenv_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // 基本設定ダイアログプロシージャ
  ENVD *pa;

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (ENVD *)lpa));
    SetESTR(hdlg, 101, pa->stit);
    SetESTR(hdlg, 102, pa->padr);
    SetESTR(hdlg, 103, pa->pnam);
    SetESTR(hdlg, 104, pa->ptel);
    SetEITM(hdlg, 105, &pa->ppin, &ciPPIN);
    SetESTR(hdlg, 106, pa->num1);
    SetESTR(hdlg, 107, pa->num2);
    SetESTR(hdlg, 108, pa->snum);
    SetESTR(hdlg, 109, pa->udir);
    SetFocus(GetDlgItem(hdlg, 102));
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return FALSE;
  case WM_COMMAND:
    pa = (ENVD *)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case IDOK:
      GetESTR(hdlg, 101, pa->stit, sizeof(pa->stit) - 1);
      GetESTR(hdlg, 102, pa->padr, sizeof(pa->padr) - 1);
      GetESTR(hdlg, 103, pa->pnam, sizeof(pa->pnam) - 1);
      GetESTR(hdlg, 104, pa->ptel, sizeof(pa->ptel) - 1);
      GetEITM(hdlg, 105, &pa->ppin, &ciPPIN);
      GetESTR(hdlg, 106, pa->num1, sizeof(pa->num1) - 1);
      GetESTR(hdlg, 107, pa->num2, sizeof(pa->num2) - 1);
      GetESTR(hdlg, 108, pa->snum, sizeof(pa->snum) - 1);
      GetESTR(hdlg, 109, pa->udir, sizeof(pa->udir) - 2);
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

void LOCALF cmd_setenv(HWND hwnd)
{ // 基本設定
  ENVD par;
  static DLGI dit[] = {
    { C_SLFT|wsG,   8, 10, 60,  8,  -1, "帳票タイトル(&T):" },
    { C_ESTR|wsT,  56,  8,178, 12, 101, "" },
    { C_SLFT|wsG,   8, 26, 60,  8,  -1, "支払者住所(&A):" },
    { C_ESTR|wsT,  56, 24,178, 12, 102, "" },
    { C_SLFT|wsG,   8, 42, 60,  8,  -1, "支払者名称(&N):" },
    { C_ESTR|wsT,  56, 40,140, 12, 103, "" },
    { C_SLFT|wsG,   8, 58, 60,  8,  -1, "支払者電話(&P):" },
    { C_ESTR|wsT,  56, 56, 56, 12, 104, "" },
    { C_SLFT|wsG,   8, 74, 60,  8,  -1, "支払者番号(&I):" },
    { C_ENUM|wsT,  56, 72, 64, 12, 105, "" },
    { C_SLFT|wsG,   8, 90, 60,  8,  -1, "整理欄(１)(&1):" },
    { C_ESTR|wsT,  56, 88, 56, 12, 106, "" },
    { C_SLFT|wsG,   8,106, 60,  8,  -1, "整理欄(２)(&2):" },
    { C_ESTR|wsT,  56,104, 56, 12, 107, "" },
    { C_SLFT|wsG,   8,122, 60,  8,  -1, "帳票番号(&S):" },
    { C_ESTR|wsT,  56,120, 32, 12, 108, "" },
    { C_SLFT|wsG,   8,138, 60,  8,  -1, "ユーザーフォルダ(&U):" },
    { C_ESTR|wsT,   8,148,226, 12, 109, "" },
    { C_BDEF|wsGT,134,172, 48, 14, IDOK, "OK" },
    { C_BPSH|wsT, 186,172, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 240, 192, "基本設定" };

  par = EnvDat;
  if (DlgBoxInput(hwnd, &dlg, setenv_DlgProc, (iANY)&par) == IDCANCEL) goto _10;
  EnvDat = par;
  SaveENV();
 _10:
  PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0);
}

BOOL LOCALF csvout(rSTR nam)
{ // 提出用ＣＳＶ書出
  I4B i, yr, re = FALSE;
  HTBL tb;
  HOPN op;
  HANDLE hf;
  rHOUD pc, pe, pr;
  I1B *pt, bf[1024];

  op = OpenTable(tiHOU[idHOU].nam, OM_RDO); if (!op) return FALSE;
  hf = open_file(nam, OP_WRI); if (!hf) { ferr(E_FIW, nam); goto _10;}
  tb = OHTBL(op);
  yr = era_year(ztod(EnvDat.stit)); if (yr == 0) { yr = era_year(CurDate()) - (month(CurDate()) < 6);}
  for (pc = TablePtr(tb), pe = pc + NTableRows(tb); pc < pe; pc++) {
    if (pc->atr != 1 && pc->atr != 2) continue;
    pt = bf;
    pt += sprintfs(pt, "%s,", EnvDat.snum); //  1
    pt += sprintfs(pt, "%s,", EnvDat.num1); //  2
    pt += sprintfs(pt, ",");                //  3
    pt += sprintfs(pt, "%s,", EnvDat.padr); //  4
    pt += sprintfs(pt, "%s,", EnvDat.pnam); //  5
    pt += sprintfs(pt, "%s,", EnvDat.ptel); //  6
    pt += sprintfs(pt, "%s,", EnvDat.num2); //  7
    pt += sprintfs(pt, ",");                //  8
    pt += sprintfs(pt, ",");                //  9
    pt += sprintfs(pt, ",");                // 10
    pt += sprintfs(pt, "%02d,", yr);        // 11
    pt += sprintfs(pt, "%s,", pc->adr);     // 12
    pt += sprintfs(pt, "%d,", 0);           // 13
    pt += sprintfs(pt, "%s,", pc->nam);     // 14
    pt += sprintfs(pt, "%s,", pc->acc);     // 15
    pt += sprintfs(pt, "%s,", pc->det);     // 16
    pt += sprintfs(pt, "%d,", pc->fee);     // 17
    pt += sprintfs(pt, "%d,", pc->unp);     // 18
    pt += sprintfs(pt, "%d,", pc->tax);     // 19
    pt += sprintfs(pt, "%d,", pc->unc);     // 20
    for (i = 1; i < 6 && (pr = &pc[i])->atr == 3; i++) {
      pt += sprintfs(pt, "%s,", pr->acc);
      pt += sprintfs(pt, "%s,", pr->det);
      pt += sprintfs(pt, "%d,", pr->fee);
      pt += sprintfs(pt, "%d,", pr->unp);
      pt += sprintfs(pt, "%d,", pr->tax);
      pt += sprintfs(pt, "%d,", pr->unc);
    }
    for ( ; i < 6; i++) {
      pt += sprintfs(pt, ",,,,,,");
    }
    pt += sprintfs(pt, "%s,", pc->rem);
    pt += sprintfs(pt, "%013.0f,", EnvDat.ppin);
    pt += sprintfs(pt, "%013.0f", pc->pin);
    write_line(hf, bf);
  }
  re = close_file(hf);
 _10:
  CloseTable(op, FALSE);
  return re;
}

typedef struct {
  I1B   nam[512];
} CSVP;

BOOL LOCALF csvout_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // 提出用ＣＳＶ書出ダイアログプロシージャ
  CSVP *pa;

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (CSVP *)lpa));
    SetESTR(hdlg, 101, pa->nam);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    pa = (CSVP *)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case IDOK:
      GetESTR(hdlg, 101, pa->nam, sizeof(pa->nam) - 1);
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

void LOCALF cmd_csvout(HWND hwnd)
{ // 提出用ＣＳＶ書出
  I4B re;
  static CSVP dat;
  static DLGI dit[] = {
    { C_SLFT|wsG,   8,  8,100,  8,  -1, "出力ファイル名(&O):" },
    { C_ESTR|wsT,   8, 18,226, 12, 101, "" },
    { C_BDEF|wsGT,134,108, 48, 14, IDOK, "OK" },
    { C_BPSH|wsT, 186,108, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 240, 128, "提出用ＣＳＶ書出" };

  if (dat.nam[0] == '\0') { strcpys(dat.nam, "309dat01.txt");}
  if (DlgBoxInput(hwnd, &dlg, csvout_DlgProc, (iANY)&dat) == IDCANCEL) goto _10;
  if (file_exists(dat.nam)) {
    if (YesNoBox(hwnd, format("'%s'は既に存在します。上書きしますか？", dat.nam), NULL) != IDYES) goto _10;
  }
  BusyMsg(hwnd, "書出中...");
  reset_error();
  re = csvout(dat.nam);
  EndBusy(hwnd);
  if (!re) {
    ErrorBox(hwnd, format("'%s'の書出に失敗しました。", dat.nam), NULL);
  }
 _10:
  PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0);
}

  // オプション設定情報
typedef struct {
  I1B   face[32];
  I4B   ptsz;
  I4B   tbcs;
  I4B   cptr;
  I4B   cidx;
  I4B   uopt;
  I1B   unam[32];
  I1B   upwd[32];
} OPTP;

BOOL LOCALF setopt_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // オプション設定ダイアログプロシージャ
  OPTP *pa;

  switch (umsg) {
  case WM_INITDIALOG:
    SetWindowLong(hdlg, DWL_USER, (iANY)(pa = (OPTP *)lpa));
    SetESTR(hdlg, 101, pa->face);
    SetEINT(hdlg, 102, pa->ptsz);
    SetCCOD(hdlg, 103, pa->tbcs & TBCS_HRH, &dsHRH);
    SetCCOD(hdlg, 104, pa->tbcs & (TBCS_HCH|TBCS_UCI|TBCS_UCN), &dsHCH);
    SetEITM(hdlg, 105, &pa->cptr, &ciCPTR);
    SetEITM(hdlg, 106, &pa->cidx, &ciCIDX);
    SetBFLG(hdlg, 107, pa->tbcs & TBCS_EMD);
    SetBFLG(hdlg, 108, pa->tbcs & TBCS_ZBI);
    SetBFLG(hdlg, 109, pa->tbcs & TBCS_DDD);
    SetBFLG(hdlg, 110, pa->tbcs & TBCS_DER);
    SetBFLG(hdlg, 111, pa->uopt & UOPT_ALI);
    SetSSTR(hdlg, 113, pa->unam);
    SetBFLG(hdlg, 114, pa->uopt & UOPT_UMD);
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    pa = (OPTP *)GetWindowLong(hdlg, DWL_USER);
    switch (wpa) {
    case IDOK:
      GetESTR(hdlg, 101, pa->face, sizeof(pa->face) - 1);
      if (pa->face[0] == '\0') { strcpyx(pa->face, DEFFACE, sizeof(pa->face) - 1);}
      pa->ptsz = GetEINT(hdlg, 102); if (pa->ptsz < 6 || pa->ptsz > 30) { pa->ptsz = DEFPTSZ;}
      pa->tbcs = (pa->tbcs &~TBCS_HRH) | GetCCOD(hdlg, 103, &dsHRH);
      pa->tbcs = (pa->tbcs &~(TBCS_HCH|TBCS_UCI|TBCS_UCN)) | GetCCOD(hdlg, 104, &dsHCH);
      GetEITM(hdlg, 105, &pa->cptr, &ciCPTR); if (pa->cptr == 0) { pa->cptr = BGRC_PTR;}
      GetEITM(hdlg, 106, &pa->cidx, &ciCIDX); if (pa->cidx == 0) { pa->cidx = BGRC_IDX;}
      if (GetBFLG(hdlg, 107)) { pa->tbcs |= TBCS_EMD;} else { pa->tbcs &=~TBCS_EMD;}
      if (GetBFLG(hdlg, 108)) { pa->tbcs |= TBCS_ZBI;} else { pa->tbcs &=~TBCS_ZBI;}
      if (GetBFLG(hdlg, 109)) { pa->tbcs |= TBCS_DDD;} else { pa->tbcs &=~TBCS_DDD;}
      if (GetBFLG(hdlg, 110)) { pa->tbcs |= TBCS_DER;} else { pa->tbcs &=~TBCS_DER;}
      if (GetBFLG(hdlg, 111)) { pa->uopt |= UOPT_ALI;} else { pa->uopt &=~UOPT_ALI;}
      if (GetBFLG(hdlg, 114)) { pa->uopt |= UOPT_UMD;} else { pa->uopt &=~UOPT_UMD;}
      // fall through
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

void LOCALF cmd_setopt(HWND hwnd)
{ // オプション設定
  OPTP par;
  LOGFONT lof;
  static DLGI dit[] = {
    { C_SLFT|wsG,   8, 10, 48,  8,  -1, "フォント(&F):" },
    { C_ESTR|wsT,  56,  8, 66, 12, 101, "" },
    { C_ENUM|wsT, 128,  8, 24, 12, 102, "" },
    { C_SLFT|wsG, 155, 10, 16,  8,  -1, "pt" },
    { C_SLFT|wsG,   8, 26, 48,  8,  -1, "行見出し(&R):" },
    { C_CSEL|wsT,  56, 24, 58,128, 103, "" },
    { C_SLFT|wsG,   8, 42, 48,  8,  -1, "列見出し(&C):" },
    { C_CSEL|wsT,  56, 40, 58,128, 104, "" },
    { C_SLFT|wsG, 128, 26, 60,  8,  -1, "セルポインタ色(&P):" },
    { C_ENUM|wsT, 188, 24, 40, 12, 105, "" },
    { C_SLFT|wsG, 128, 42, 60,  8,  -1, "見出しハイライト(&H):" },
    { C_ENUM|wsT, 188, 40, 40, 12, 106, "" },
    { C_BFLG|wsGT,  8, 60,120, 10, 107, "Enterキーで下に移動する(&D)" },
    { C_BFLG|wsGT,  8, 74,120, 10, 108, "インデックスを0から始める(&Z)" },
    { C_BFLG|wsGT,128, 60,128, 10, 109, "ドロップダウンリストを使用しない(&L)" },
    { C_BFLG|wsGT,128, 74,128, 10, 110, "ダイアログボックスを使用しない(&E)" },
    { C_BFLG|wsGT,  8, 92,120, 10, 111, "自動的にログインする(&A)" },
    { C_SLFT|wsG, 128, 93, 44,  8, 112, "ユーザー名:" },
    { C_SLFT|wsG, 172, 93, 84,  8, 113, "" },
    { C_BFLG|wsGT,  8,106,120, 10, 114, "開始メニューを使用する(&M)" },
    { C_BDEF|wsGT,150,172, 48, 14, IDOK, "OK" },
    { C_BPSH|wsT, 202,172, 48, 14, IDCANCEL, "キャンセル" },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 256, 192, "オプション設定" };

  par = *(OPTP *)&IniDat.face;
  if (DlgBoxInput(hwnd, &dlg, setopt_DlgProc, (iANY)&par) == IDCANCEL) goto _10;
  reset_error();
  if (par.ptsz != IniDat.ptsz || strcmp(par.face, IniDat.face) != 0) {
    SetAppFont(FID_DEF, CreateFontIndirect(SetLOGFONT(&lof, par.face, PT2DOTS(par.ptsz), 0)));
  }
  set_error_message_index_base((par.tbcs & TBCS_ZBI) == 0);
  SetAppColor(CID_PTR, par.cptr);
  SetAppColor(CID_IDX, par.cidx);
  if (par.uopt & UOPT_ELG) enable_error_logging();  else disable_error_logging();
  *(OPTP *)&IniDat.face = par;
 _10:
  PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0);
}

void LOCALF cmd_tile(HWND hwnd, I4B sw)
{ // 左右(上下)に並べて表示
  I4B flg;

  flg = SendMessage(hwnd, FRMM_GETFLAGS, 0, 0);
  flg &=~(FRMS_TIL|FRMS_HRZ|FRMS_RGT);
  switch (sw) {
  case 0: flg |= (FRMS_TIL|FRMS_VRT); break;
  case 1: flg |= (FRMS_TIL|FRMS_HRZ); break;
  }
  SendMessage(hwnd, FRMM_SETFLAGS, flg, 0);
  SendMessage(hwnd, FRMM_ARRANGE, OPR_TILE, 0);
}

void LOCALF cmd_window(HWND hwnd, I4B id)
{ // アクティブウィンドウ選択
  I4B nc;
  HWND hchi;

  nc = SendMessage(hwnd, FRMM_GETNCHILDREN, 0, 0);
  hchi = (HWND)SendMessage(hwnd, FRMM_GETCHILD, nc - 1 - id, 0);
  SendMessage(hwnd, FRMM_ARRANGE, OPR_ACTIVATE, (iANY)hchi);
}

void LOCALF cmd_manual(HWND hwnd)
{ // マニュアルを開く
  ShellExecute(NULL, "open", PRGNAM".PDF", NULL, NULL, SW_SHOWDEFAULT);
}

static HBMP hbmpMain;

rSTR LOCALF verstr(U4B ver)
{ // バージョン文字列
  rBIN vr = (rBIN)&ver; return format("Version %d.%d.%d", vr[0], vr[1], vr[2]);
}

BOOL LOCALF about_DlgProc(HWND hdlg, U4B umsg, uANY wpa, iANY lpa)
{ // バージョン情報ダイアログプロシージャ
  switch (umsg) {
  case WM_INITDIALOG:
    SendDlgItemMessage(hdlg, 101, STM_SETIMAGE, IMAGE_BITMAP, (iANY)hbmpMain);
    SetSSTR(hdlg, 102, verstr(BOLALGVer()));
    SetSSTR(hdlg, 103, verstr(BOLBASVer()));
    SetSSTR(hdlg, 104, verstr(BOLDBEVer()));
    SetSSTR(hdlg, 106, verstr(BOLFROVer()));
    SetSSTR(hdlg, 107, verstr(BOLGUIVer()));
    SetSSTR(hdlg, 110, verstr(MODVER));
    CenterWindow(hdlg, GetWindow(hdlg, GW_OWNER));
    return TRUE;
  case WM_COMMAND:
    switch (wpa) {
    case IDOK:
    case IDCANCEL:
      EndDialog(hdlg, wpa);
      return TRUE;
    }
    break;
  }
  return FALSE;
}

void LOCALF cmd_about(HWND hwnd)
{ // バージョン情報
  static DLGI dit[] = {
    { C_SBMP|wsG,   8, 10, 80, 80,101, ""       },
    { C_SLFT|wsG, 112, 12, 32,  8, -1, "BOLALG" },
    { C_SLFT|wsG, 144, 12, 56,  8,102, ""       },
    { C_SLFT|wsG, 112, 24, 32,  8, -1, "BOLBAS" },
    { C_SLFT|wsG, 144, 24, 56,  8,103, ""       },
    { C_SLFT|wsG, 112, 36, 32,  8, -1, "BOLDBE" },
    { C_SLFT|wsG, 144, 36, 56,  8,104, ""       },
    { C_SLFT|wsG, 112, 48, 32,  8, -1, "BOLFRO" },
    { C_SLFT|wsG, 144, 48, 56,  8,106, ""       },
    { C_SLFT|wsG, 112, 60, 32,  8, -1, "BOLGUI" },
    { C_SLFT|wsG, 144, 60, 56,  8,107, ""       },
    { C_SLFT|wsG,   8, 96, 96,  8, -1, PRGDSC   },
    { C_SLFT|wsG, 112, 96, 32,  8, -1, PRGNAM   },
    { C_SLFT|wsG, 144, 96, 56,  8,110, ""       },
    { C_SLFT|wsG,   8,126,144,  8, -1, VI_CPY   },
    { C_BDEF|wsGT,154,124, 48, 14, IDOK, "OK"   },
  };
  static DLGS dlg = { dit, _countof(dit), 0, 0, 208, 144, "バージョン情報" };

  DlgBoxInput(hwnd, &dlg, about_DlgProc, (iANY)NULL);
}

iANY LOCALF FRM_command(HWND hwnd, uANY wpa, iANY lpa)
{ // コマンド
  HWND hact, hchi;

  switch (LOU2B(wpa)) {
  case IDM_LOGIN:
    cmd_login(hwnd); break;
  case IDM_OMENU:
    cmd_omenu(hwnd, 0); break;
  case IDM_OMENU1:
    cmd_omenu(hwnd, 1); break;
  case IDM_OPEN:
    cmd_open(hwnd); break;
  case IDM_CLOSE:
    SendMessage((HWND)SendMessage(hwnd, FRMM_GETACTIVE, 0, 0), WM_CLOSE, 0, 0); break;
  case IDM_EXIT:
    SendMessage(hwnd, WM_CLOSE, 0, 0); return 0;
  case IDM_CHECK1:
    cmd_check1(hwnd); break;
  case IDM_REPORT1:
  case IDM_REPORT2:
    cmd_report(hwnd, LOU2B(wpa) - IDM_REPORT1 + 1); break;
  case IDM_INFO1:
    cmd_tedit(hwnd, LFILE); break;
  case IDM_SITEWHO:
    cmd_who(hwnd); break;
  case IDM_SETUSR:
    cmd_setusr(hwnd); break;
  case IDM_UNLOCK:
    cmd_unlock(hwnd); break;
  case IDM_BACKUP:
    cmd_backup(hwnd); break;
  case IDM_TEDIT:
    cmd_tedit(hwnd, NULL); break;
  case IDM_SETENV:
    cmd_setenv(hwnd); break;
  case IDM_CSVOUT:
    cmd_csvout(hwnd); break;
  case IDM_SETOPT:
    cmd_setopt(hwnd); break;
  case IDM_PREVWINDOW:
    SendMessage(hwnd, FRMM_ARRANGE, OPR_ACTIVATE, 0); break;
  case IDM_NEXTWINDOW:
    SendMessage(hwnd, FRMM_ARRANGE, OPR_ACTIVATE, SendMessage(hwnd, FRMM_GETCHILD, 0, 0)); break;
  case IDM_MAXIMIZE:
    cmd_tile(hwnd, -1); break;
  case IDM_TILE:
    cmd_tile(hwnd, 0); break;
  case IDM_HTILE:
    cmd_tile(hwnd, 1); break;
  case IDM_WINDOW0:
  case IDM_WINDOW1:
  case IDM_WINDOW2:
  case IDM_WINDOW3:
  case IDM_WINDOW4:
  case IDM_WINDOW5:
  case IDM_WINDOW6:
  case IDM_WINDOW7:
  case IDM_WINDOW8:
  case IDM_WINDOW9:
  case IDM_WINDOWA:
  case IDM_WINDOWB:
  case IDM_WINDOWC:
  case IDM_WINDOWD:
  case IDM_WINDOWE:
  case IDM_WINDOWF:
    cmd_window(hwnd, LOU2B(wpa) - IDM_WINDOW0); break;
  case IDM_MANUAL:
    cmd_manual(hwnd); break;
  case IDM_ABOUT:
    cmd_about(hwnd); break;
  default:
    hact = (HWND)SendMessage(hwnd, FRMM_GETACTIVE, 0, 0);
    if (LOU2B(wpa) >= IDPRN && LOU2B(wpa) < IDM_LOGIN) {
      switch (HIU2B(wpa)) {
      case APPN_SETFOCUS:
        break;
      case APPN_LBUTTONDOWN:
      case APPN_RBUTTONDOWN:
        if ((hchi = GetDlgItem(hwnd, LOU2B(wpa))) != hact) {
          SendMessage(hwnd, FRMM_ACTIVATE, (uANY)hchi, 0);
        }
        return 0;
      case APPN_SELCHANGE:
        break;
      default:
        return 0;
      }
    } else if (hact) {
      SendMessage(hact, WM_COMMAND, wpa, lpa);
    }
    break;
  }
  SendMessage(hwnd, FRMM_INITTOOL, 0, 0);
  return 0;
}

iANY LOCALF FRM_close(HWND hwnd)
{ // フレームウィンドウを閉じる
  HWND bact, hact;

  bact = NULL;
  while (!!(hact = (HWND)SendMessage(hwnd, FRMM_GETACTIVE, 0, 0))) {
    if (hact == bact) return 0;
    SendMessage(hact, WM_CLOSE, 0, 0);
    bact = hact;
  }
  SendMessage(hwnd, FRMM_GETPLACEMENT, (uANY)&IniDat.wpo, 0);
  return DefBOLFRMProc(hwnd, WM_CLOSE, 0, 0);
}

iANY LOCALF FRM_WndProc(HWND hwnd, U4B umsg, uANY wpa, iANY lpa)
{ // メインウィンドウプロシージャ
  switch (umsg) {
  case WM_INITMENU:
    return FRM_initmenu(hwnd, (HMENU)wpa);
  case FRMM_INITTOOL:
    return FRM_inittool(hwnd);
  case FRMM_DESTROYCHILD:
    DefBOLFRMProc(hwnd, umsg, wpa, lpa);
    if (!SendMessage(hwnd, FRMM_GETACTIVE, 0, 0)) PostMessage(hwnd, WM_COMMAND, IDM_OMENU, 0);
    return 0;
  case WM_COMMAND:
    return FRM_command(hwnd, wpa, lpa);
  case WM_CLOSE:
    return FRM_close(hwnd);
  }
  return DefBOLFRMProc(hwnd, umsg, wpa, lpa);
}

typedef struct {
  I4B   idm;
  I4B   bid;
} ICOI;
  // コマンドとビットマップの対応表
static ICOI ICI[] = {
  { IDM_OPEN,       BID_OPEN    },
  { IDM_CLOSE,      BID_CLOSE   },
  { IDM_SAVE,       BID_SAVE    },
  { IDM_SAVEAS,     BID_SAVEAS  },
  { IDM_PRINT,      BID_PRINT   },
  { IDM_CUT,        BID_CUT     },
  { IDM_COPY,       BID_COPY    },
  { IDM_PASTE,      BID_PASTE   },
  { IDM_SEARCH,     BID_SEARCH  },
  { IDM_VIEW,       BID_VIEW    },
  { IDM_ARRANGE,    BID_ARRANGE },
  { IDM_FILTER,     BID_FILTER  },
  { IDM_SORT,       BID_SORT    },
  { IDM_SUMMARY,    BID_SUMMARY },
  { IDM_CHECK,      BID_CHECK   },
  { IDM_EDIT,       BID_EDIT    },
  { IDM_ADDEDIT,    BID_ADD     },
  { IDM_INSERTEDIT, BID_INSERT  },
  { IDM_SELDELETE,  BID_DELETE  },
  { IDM_SETENV,     BID_NEW     },
  { IDM_PREVWINDOW, BID_PREVWIN },
  { IDM_NEXTWINDOW, BID_NEXTWIN },
  { IDM_PREVPAGE,   BID_PREV    },
  { IDM_NEXTPAGE,   BID_NEXT    },
  { IDM_SETPRN,     BID_SETUP   },
  { IDM_ZOOMIN,     BID_ZOOMIN  },
  { IDM_ZOOMOUT,    BID_ZOOMOUT },
  { IDM_SAMESCALE,  BID_SAME    },
  { IDM_AUTOSCALE,  BID_WHOLE   },
};

BOOL LOCALF InitHOUTEI(uANY pa)
{ // 開始処理
  I4B i;

  InitBOLALG(0);
  InitBOLBAS(0);
  InitBOLDBE(0);
  InitBOLFRO(0);
  InitBOLGUI(0);
  for (i = 0; i < _countof(ICI); i++) {
    SetMenuItemDDB(ICI[i].idm, AppDDB(ICI[i].bid));
  }
  InitPopupMenus();
  PrepToolBar();
  return TRUE;
}

void LOCALF TermHOUTEI(void)
{ // 終了処理
  LogoutSite();
  TermPopupMenus();
  TermBOLGUI();
  TermBOLFRO();
  TermBOLDBE();
  TermBOLBAS();
  TermBOLALG();
}

I4B  WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, pSTR lpszCmdLine, I4B nCmdShow)
{ // メイン関数
  HWND hwnd;
  MSG msg;
  HACCEL haccMain;
  HICON hicoMain;
  static FRMI fin = { PRGTIT, &IniDat.wpo, &BOLMS, IDTOB, &BOLTS, IDSTB, &BOLSS };

  InitHOUTEI(0);
  LoadENV();
  if (lpszCmdLine[0] != '\0') { SetUserPath(lpszCmdLine);}
  new_path_if_not_exists(UserPath());
  set_error_log_file(adddir(UserPath(), PRGNAM".LOG"));
//  start_debug(TRUE);
  LoadINI();

  haccMain = LoadAccelerators(hInstance, PRGNAM"ACC");
  hicoMain = LoadIcon(hInstance, PRGNAM"ICO");
  hbmpMain = LoadBitmap(hInstance, PRGNAM"BMP");
  hwnd = CreateBOLFRMWindow(hInstance, &fin); if (!hwnd) return 0;
  SubclassWindow(hwnd, FRM_WndProc);
  SendMessage(hwnd, WM_SETICON, ICON_BIG, (iANY)hicoMain);
  ShowWindow(hwnd, (IniDat.wpo.mx && nCmdShow == SW_SHOWNORMAL)? SW_SHOWMAXIMIZED: SW_SHOWDEFAULT);
  UpdateWindow(hwnd);
  PostMessage(hwnd, WM_COMMAND, IDM_LOGIN, 0);

  while (GetMessage(&msg, NULL, 0, 0)) {
    if (!TranslateAccelerator(hwnd, haccMain, &msg)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }

  SaveINI();
//  stop_debug();
  TermHOUTEI();

  return msg.wParam;
}

//――――――――――――――――――――――――――――――――――――――//
