/***************************************************************************
 *                                                                         *
 *   MAJORWIN.C                                                            *
 *                                                                         *
 *   Copyright (c) 1996-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Functions used for WGSERVER main GUI window.                          *
 *                                                                         *
 *                                        - I. Minkin 6/26/96              *
 *                                                                         *
 ***************************************************************************/

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include "gcomm.h"
#include "majorbbs.h"
#include "majorwin.h"
#include "wgservrc.h"
#include "elogapi.h"

#define WINSTA_ALLACCESS   (WINSTA_ACCESSCLIPBOARD|   \
                            WINSTA_ACCESSGLOBALATOMS| \
                            WINSTA_CREATEDESKTOP|     \
                            WINSTA_ENUMDESKTOPS|      \
                            WINSTA_ENUMERATE|         \
                            WINSTA_EXITWINDOWS|       \
                            WINSTA_READATTRIBUTES|    \
                            WINSTA_READSCREEN|        \
                            WINSTA_WRITEATTRIBUTES)

#define DESKTOP_ALLACCESS  (DESKTOP_CREATEMENU|       \
                            DESKTOP_CREATEWINDOW|     \
                            DESKTOP_ENUMERATE|        \
                            DESKTOP_HOOKCONTROL|      \
                            DESKTOP_JOURNALPLAYBACK|  \
                            DESKTOP_JOURNALRECORD|    \
                            DESKTOP_READOBJECTS|      \
                            DESKTOP_SWITCHDESKTOP|    \
                            DESKTOP_WRITEOBJECTS)

#define WGS_WINDOW_CLASS  "WGSMAINWINDOW_CLASS"

// The normal HANDLE_MSG macro in WINDOWSX.H does not work properly for dialog
// boxes because DlgProc's return a BOOL instead of an LRESULT (like
// WndProcs). This HANDLE_DLGMSG macro corrects the problem:
#define HANDLE_DLGMSG(hwnd, message, fn)                      \
     case (message): return(SetDlgMsgResult(hwnd, uMsg,       \
                            HANDLE_##message((hwnd), (wParam), (lParam), (fn))))


#define WMU_SYSOPPAGE (WM_USER+100)     // display "page" to sysop
// VOID Cls_OnSysopPage(HWND hwnd,const CHAR *pageText)
#define HANDLE_WMU_SYSOPPAGE(hwnd, wParam, lParam, fn) \
    ((fn)(hwnd,(const CHAR *)(lParam)),0L)

#define WMU_SETSYSLOAD (WM_USER+101)    // display # channels and response time
// VOID Cls_OnSetSysLoad(HWND hwnd,USHORT nChans,const CHAR *respTime)
#define HANDLE_WMU_SETSYSLOAD(hwnd, wParam, lParam, fn) \
    ((fn)(hwnd,(USHORT)(wParam),(const CHAR *)(lParam)),0L)

#define WMU_AUDITTRAIL (WM_USER+102)    // display audt trail
// VOID Cls_OnAuditTrail(HWND hwnd,const CHAR *auditText)
#define HANDLE_WMU_AUDITTRAIL(hwnd, wParam, lParam, fn) \
    ((fn)(hwnd,(const CHAR *)(lParam)),0L)

#define WMU_NEWSYSOPPAGE (WM_USER+103)  // display "new page" message
// VOID Cls_OnSysopPage(HWND hwnd,const CHAR *pageText)
#define HANDLE_WMU_NEWSYSOPPAGE(hwnd, wParam, lParam, fn) \
    ((fn)(hwnd,(GBOOL)(lParam)),0L)

#define WMU_STATUS (WM_USER+104)        // display status
// VOID Cls_OnStatus(HWND hwnd,const CHAR *statusText)
#define HANDLE_WMU_STATUS(hwnd, wParam, lParam, fn) \
    ((fn)(hwnd,(const CHAR *)(lParam)),0L)

#define WMU_REGINFO (WM_USER+105)       // set registration info
// VOID Cls_OnLicenseInfo(HWND hwnd,WGSREGINFO *regInfo)
#define HANDLE_WMU_REGINFO(hwnd, wParam, lParam, fn) \
    ((fn)(hwnd,(WGSREGINFO *)(lParam)),0L)

#define WMU_WGSREADY (WM_USER+106)      // WGS ready
// VOID Cls_OnWGSReady(HWND hwnd)
#define HANDLE_WMU_WGSREADY(hwnd, wParam, lParam, fn) \
    ((fn)(hwnd),0L)

#define WMU_CLOSEMAINWINDOW (WM_USER+107) // close main window
// VOID Cls_OnCloseMainWindow(HWND hwnd)
#define HANDLE_WMU_CLOSEMAINWINDOW(hwnd, wParam, lParam, fn) \
    ((fn)(hwnd),0L)

static GBOOL fUseConsoleWindow;    // use console window
static HWND hMainWin;              // main window handle
static HINSTANCE hInstance;        // module instance

// registry key and items for main window size and position
static const CHAR keyRoot[]="SOFTWARE\\Galacticomm\\" SVR_FULL;

static const CHAR keySettings[]="\\CurrentVersion";
static const CHAR useConName[]="UseConsoleWindow";
static const CHAR mainWinPosName[]="MainWinPosition";
static const CHAR mainWinSizeName[]="MainWinSize";

static const CHAR initCapt[]="Initializing ...";
static const CHAR mainCapt[]=SVR_FULL;

static GBOOL fNTShutdown=FALSE;

const INT margin=5;                // margins size

typedef struct tagMainWinData {    // main window instance data
     HWND hStatDlg;                //   dialog handle
     HWND hStatBar;                //   status bar handle
     INT statBarH;                 //   status bar height
     ULONG hour;                   //   hours online
     ULONG min;                    //   minutes online
     ULONG sec;                    //   seconds online
     CHAR timeSeparator;           //   separator for time display
     ULONG nChans;                 //   current number of channels in use
     ULONG version;                //   version number
     ULONG licenseCount;           //   WGS license count
     CHAR regNumber[16];           //   WGS registration number
     CHAR regName[64];             //   WGS owner name
     const CHAR *pIconCapt;        //   caption when iconized
     const CHAR *pNormalCapt;      //   caption othrwise
} MAINWINDATA;

typedef struct tagColorData {      // color data
     COLORREF textColor;           //   text color
     COLORREF bkColor;             //   background color
     HBRUSH hBkBrush;              //   backgroung brush
} COLORDATA;

typedef struct tagStatDlgData {    // status dialog instance data
     COLORDATA cd;                 //   color data
     ULONG margX;                  //   X margin
     ULONG margY;                  //   Y margin
} STATDLGDATA;

typedef struct tagAboutDlgData {   // about dialog instance data
     HBITMAP hBmp1;                // foreground bitmap
     HBITMAP hBmp2;                // background bitmap
#ifdef WEBCAST
     HBITMAP hBmp3;                // Vosaic Bitmap
#endif
} ABOUTDLGDATA;

typedef struct tagGUIThInfo {      // GUI thread creation info
     HANDLE heReady;               //   main window ready
} GUITHINFO;

static BOOL WINAPI consoleCtrlHandler(DWORD ctrlType);
static GBOOL isUseConsoleWindow(VOID);
static DWORD WINAPI runMainWindow(VOID *pInfo);
static GBOOL createWindow(VOID);

static LRESULT CALLBACK mainWinWndProc(HWND hWnd,UINT uMessage,WPARAM wParam,
                                       LPARAM lParam);
static VOID mainWin_OnClose(HWND hwnd);
static VOID mainWin_OnDestroy(HWND hwnd);
static VOID mainWin_OnSize(HWND hwnd, UINT state,INT cx,INT cy);
static VOID mainWin_OnTimer(HWND hWnd,UINT id);
static BOOL mainWin_OnCommand(HWND hWnd,INT id,HWND hWndCtl,UINT codeNotify);
static VOID mainWin_OnSysColorChange(HWND hwnd);
static BOOL mainWin_OnQueryEndSession(HWND hwnd);
static VOID mainWin_OnSetSysLoad(HWND hwnd,USHORT nChans,const CHAR *respTime);
static VOID mainWin_OnSysopPage(HWND hwnd,const CHAR *pageText);
static VOID mainWin_OnAuditTrail(HWND hwnd,const CHAR *auditText);
static VOID mainWin_OnNewSysopPage(HWND hWnd,GBOOL fNewPage);
static VOID mainWin_OnStatus(HWND hwnd,const CHAR *statusText);
static VOID mainWin_OnRegInfo(HWND hwnd,WGSREGINFO *regInfo);
static VOID mainWin_OnWGSReady(HWND hwnd);
static VOID mainWin_OnCloseMainWindow(HWND hwnd);

static BOOL confirmShutdown(HWND hWnd);
static VOID doShutdown(HWND hWnd);
static VOID getMainWinSizePos(INT *x,INT *y,INT *cx,INT *cy);
static VOID saveMainWinSizePos(INT x,INT y,INT cx,INT cy);
static VOID setStatBarItemsSize(HWND hStatBar,CHAR timeSeparator);
static VOID makeOnlineTimeStr(CHAR *textBuf,INT hours,INT min,INT sec,CHAR timeSeparator);
static VOID makeResponseTimeStr(CHAR *textBuf,const CHAR *responseTime);
static VOID makeLinesInUseStr(CHAR *textBuf,INT linesInUse);
static VOID makeNewSysopPageStr(CHAR *textBuf,GBOOL fNewPage);
static CHAR getTimeSeparator(VOID);

static BOOL CALLBACK wgsStatDlgProc(HWND  hWndDlg,UINT uMsg,WPARAM wParam,
                                    LPARAM  lParam);
static BOOL wgsStat_OnCommand(HWND hWnd,INT id,HWND hWndCtl,UINT codeNotify);
static BOOL wgsStat_OnInitDialog(HWND hWnd,HWND hWndFocus,LPARAM lParam);
static VOID wgsStat_OnSize(HWND hwnd, UINT state,INT cx,INT cy);
static HBRUSH wgsStat_OnCtlColorListBox(HWND hWnd,HDC hDC,HWND hWndChild,INT type);
static VOID wgsStat_OnSysColorChange(HWND hwnd);
static BOOL wgsStat_OnNotify(HWND hwnd,INT idFrom,LPNMHDR nmhdr);
static BOOL wgsStat_OnSysopPage(HWND hwnd,const CHAR *pageText);
static BOOL wgsStat_OnAuditTrail(HWND hwnd,const CHAR *auditText);
static VOID setLBTabStops(HWND hWnd);

static BOOL CALLBACK wgsAboutDlgProc(HWND hWndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam);
static BOOL wgsAbout_OnCommand(HWND hWnd,INT id,HWND hWndCtl,UINT codeNotify);
static BOOL wgsAbout_OnInitDialog(HWND hWnd,HWND hWndFocus,LPARAM lParam);
static VOID wgsAbout_OnClose(HWND hwnd);
static VOID wgsAbout_OnDrawItem(HWND hwnd,const DRAWITEMSTRUCT *lpDrawItem);
static VOID displayAboutInfo(HWND hWnd,ULONG licenseCount,CHAR *regNumber,
                             CHAR *regName,ULONG version,CHAR *subVer);
static VOID loadSubVersionInfo(CHAR *subVer,INT subVerSize);

static HBRUSH handleCtlColor(HDC hDC,COLORDATA *cd);
static VOID handleSysColorChange(HWND hWnd,COLORDATA *cd);

VOID                               // create console or GUI window based on
openVideoOutput(VOID)              // Registry setting
{
     HANDLE hTh;
     DWORD mainWinThId;
     GUITHINFO gi;

     fUseConsoleWindow=isUseConsoleWindow();
     if (fUseConsoleWindow) {
          AllocConsole();
          if (isWinNT()) {
               SetConsoleCtrlHandler(consoleCtrlHandler,TRUE);
          }
     }
     else {
          if (isRunAsService()) {
               SetConsoleCtrlHandler(consoleCtrlHandler,TRUE);
          }
          disableConsoleVideo();
          gi.heReady=CreateEvent(NULL,FALSE,FALSE,NULL);
          hTh=CreateThread(NULL,0,runMainWindow,(VOID *)&gi,0,&mainWinThId);
          CloseHandle(hTh);
          WaitForSingleObject(gi.heReady,5000); // make sure main window is ready
     }
}

VOID
closeVideoOutput(VOID)             // shutdown GUI window if present
{
     if (fUseConsoleWindow) {
          FreeConsole();
     }
     else {
          if (hMainWin) {
               SendMessage(hMainWin,WMU_CLOSEMAINWINDOW,0,0);
          }
     }
}

VOID
sendSysopPageToMainWin(            // sends sysop page to GUI window
const CHAR *pageText)              //   text to send
{
     if (hMainWin == NULL) {
          return;
     }
     SendMessage(hMainWin,WMU_SYSOPPAGE,0,(LPARAM)pageText);
}

VOID
sendAuditTrailToMainWin(           // sends audit trail to GUI window
const CHAR *auditText,             //   text to send
const CHAR *auditTextEx)           //   text to send
{
     CHAR tmpBuf[512];
     INT len;

     if (hMainWin == NULL) {
          return;
     }
     GetDateFormat(LOCALE_SYSTEM_DEFAULT,0,NULL,NULL,tmpBuf,sizeof(tmpBuf));
     len=lstrlen(tmpBuf);
     tmpBuf[len++]='\t';
     GetTimeFormat(LOCALE_SYSTEM_DEFAULT,0,NULL,NULL,&tmpBuf[len],sizeof(tmpBuf)-len);
     lstrcat(tmpBuf,"\t");
     lstrcat(tmpBuf,auditText);
     if (auditTextEx[0] != '\0') {
          lstrcat(tmpBuf," - ");
          lstrcat(tmpBuf,auditTextEx);
     }
     SendMessage(hMainWin,WMU_AUDITTRAIL,0,(LPARAM)tmpBuf);
}

VOID
sendStatusToMainWin(               // sends status to GUI window
const CHAR *statusText)            //   text to send
{
     if (hMainWin == NULL) {
          return;
     }
     SendMessage(hMainWin,WMU_STATUS,0,(LPARAM)statusText);
}

VOID
sendSysLoadToMainWin(              // sends response time to GUI window
const CHAR *respTime)              //   response time
{
     INT unum;
     USHORT nChans;
     struct user *uptr;

     if (hMainWin == NULL) {
          return;
     }
     for (unum=0,nChans=0 ; unum < nterms ; unum++) {
          uptr=usroff(unum);
          if (!(uptr->flags&NOHDWE) && uptr->usrcls != VACANT) {
               nChans++;
          }
     }
     SendMessage(hMainWin,WMU_SETSYSLOAD,(WPARAM)nChans,(LPARAM)respTime);
}

VOID
sendRegInfoToMainWin(              // sends registration info to GUI window
WGSREGINFO *regInfo)               //   registration info
{
     if (hMainWin == NULL) {
          return;
     }
     SendMessage(hMainWin,WMU_REGINFO,0,(LPARAM)regInfo);
}

VOID
sendWGSReadyToMainWin(VOID)        // inform GUI window that WGS is ready
{
     if (hMainWin == NULL) {
          return;
     }
     SendMessage(hMainWin,WMU_WGSREADY,0,0);
}

GBOOL                              //   TRUE - yes
isNTShutdown(VOID)                 // is NT currently shutting down
{
     return(fNTShutdown);
}

////////////////////////////////////////////////////////////////////////////////

static BOOL WINAPI
consoleCtrlHandler(                // routine to handle Windows events
DWORD ctrlType)                    //   event type
{
     switch (ctrlType) {
     case CTRL_C_EVENT:
          return(TRUE);
     case CTRL_BREAK_EVENT:
          MessageBox(NULL,"Please use F10 F9 to exit.","Error",
                     MB_ICONSTOP|MB_OK|MB_SETFOREGROUND|MB_TASKMODAL);
          return(TRUE);
     case CTRL_CLOSE_EVENT:
          shutdownMainThread();
          return(TRUE);
     case CTRL_LOGOFF_EVENT:
          if (!isRunAsService()) {
               shutdownMainThread();
          }
          return(TRUE);
     case CTRL_SHUTDOWN_EVENT:
          fNTShutdown=TRUE;
          shutdownMainThread();
          return(TRUE);
     default:
          return(FALSE);
     }
}

static GBOOL                       //   TRUE - console window
isUseConsoleWindow(VOID)           // display console or GUI window
{
     GBOOL retVal=FALSE;
     HKEY hKey;
     DWORD valType,valSize;
     DWORD valBuffer;
     DWORD disposition;
     CHAR keyName[256];

     lstrcpy(keyName,keyRoot);
     lstrcat(keyName,keySettings);
     if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,keyName,0,"",0,KEY_ALL_ACCESS ,NULL,
                                         &hKey,&disposition) != ERROR_SUCCESS) {
          return(retVal);
     }
     valSize=sizeof(valBuffer);
     if (RegQueryValueEx(hKey,useConName,NULL,&valType,
                         (LPBYTE)&valBuffer,&valSize) == ERROR_SUCCESS
      && valType == REG_DWORD
      && valSize == sizeof(valBuffer)) {
          if (valBuffer != 0) {
               retVal=TRUE;
          }
     }
     else {                        // if not there try to create
          valBuffer=0;
          RegSetValueEx(hKey,useConName,0,REG_DWORD,(LPBYTE)&valBuffer,
                        sizeof(valBuffer));
     }
     RegCloseKey(hKey);
     return(retVal);
}

static DWORD WINAPI
runMainWindow(                     // GUI window main thread
VOID *pInfo)                       //   argument
{
     MSG  msg;
     HACCEL hAccel;
     GUITHINFO gi;

     gi=*((GUITHINFO *)pInfo);
     hInstance=GetModuleHandle(NULL);
     InitCommonControls();
     if (!createWindow()) {
          SetEvent(gi.heReady);
          return(0);
     }
     SetEvent(gi.heReady);
     hAccel=LoadAccelerators(hInstance,MAKEINTRESOURCE(ACCELERATORS_1));
     while (GetMessage(&msg, NULL, 0, 0) ) {
          if (!TranslateAccelerator(hMainWin,hAccel,&msg)) {
               TranslateMessage(&msg);
               DispatchMessage(&msg);
          }
     }
     return(1);
}

static GBOOL
createWindow(VOID)                 // creates main GUI window
{
     WNDCLASS wndClass;
     INT x,y,cx,cy;
     RECT rect;
     MAINWINDATA *pMWData;
     HMENU hMenu;

     wndClass.lpszClassName=WGS_WINDOW_CLASS;
     wndClass.lpfnWndProc=mainWinWndProc;
     wndClass.hInstance=hInstance;
     wndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
     wndClass.hIcon=LoadIcon(hInstance,MAKEINTRESOURCE(MAIN_ICON));
     wndClass.hbrBackground=NULL;
     wndClass.lpszMenuName=MAKEINTRESOURCE(MAIN_MENU);
     wndClass.style=CS_NOCLOSE;
     wndClass.cbClsExtra=0;
     wndClass.cbWndExtra=0;

     if (!RegisterClass(&wndClass)) {
          return(FALSE);
     }
     getMainWinSizePos(&x,&y,&cx,&cy);
     hMainWin=CreateWindow(WGS_WINDOW_CLASS,
                           initCapt,
                            WS_OVERLAPPEDWINDOW,
                            x,y,cx,cy,
                            NULL,NULL,hInstance,NULL);
     if (hMainWin == NULL) {
          return(FALSE);
     }
     hMenu=GetMenu(hMainWin);
     EnableMenuItem(hMenu,0,MF_BYPOSITION|MF_GRAYED);
     EnableMenuItem(hMenu,1,MF_BYPOSITION|MF_GRAYED);
     if (isRunAsService()) {
          EnableMenuItem(hMenu,CM_SHUTDOWN,MF_BYCOMMAND|MF_GRAYED);
     }
     DrawMenuBar(hMainWin);
     pMWData=LocalAlloc(LPTR,sizeof(MAINWINDATA));
     SetWindowLong(hMainWin,GWL_USERDATA,(LONG)pMWData);
     pMWData->pIconCapt=initCapt;
     pMWData->pNormalCapt=mainCapt;
     // status bar
     pMWData->hStatBar=CreateStatusWindow(WS_CHILD|WS_VISIBLE,"",hMainWin,101);
     pMWData->timeSeparator=getTimeSeparator();
     setStatBarItemsSize(pMWData->hStatBar,pMWData->timeSeparator);
     GetWindowRect(pMWData->hStatBar,&rect);
     pMWData->statBarH=rect.bottom-rect.top;
     // status dialog
     pMWData->hStatDlg=CreateDialog(hInstance,MAKEINTRESOURCE(STAT_DIALOG),
                                         hMainWin,wgsStatDlgProc);
     ShowWindow(pMWData->hStatDlg,SW_SHOW);
     ShowWindow(hMainWin,SW_SHOWMINNOACTIVE);
     return(TRUE);
}

static LRESULT CALLBACK
mainWinWndProc(                    // main window window process
HWND hWnd,
UINT uMessage,
WPARAM wParam,
LPARAM lParam)
{
     switch(uMessage) {
          HANDLE_MSG(hWnd,WM_CLOSE,mainWin_OnClose);
          HANDLE_MSG(hWnd,WM_DESTROY,mainWin_OnDestroy);
          HANDLE_MSG(hWnd,WM_SIZE,mainWin_OnSize);
          HANDLE_MSG(hWnd,WM_TIMER,mainWin_OnTimer);
          HANDLE_MSG(hWnd,WM_COMMAND,mainWin_OnCommand);
          HANDLE_MSG(hWnd,WM_SYSCOLORCHANGE,mainWin_OnSysColorChange);
          HANDLE_MSG(hWnd,WM_QUERYENDSESSION,mainWin_OnQueryEndSession);
          HANDLE_MSG(hWnd,WMU_SETSYSLOAD,mainWin_OnSetSysLoad);
          HANDLE_MSG(hWnd,WMU_SYSOPPAGE,mainWin_OnSysopPage);
          HANDLE_MSG(hWnd,WMU_AUDITTRAIL,mainWin_OnAuditTrail);
          HANDLE_MSG(hWnd,WMU_NEWSYSOPPAGE,mainWin_OnNewSysopPage);
          HANDLE_MSG(hWnd,WMU_STATUS,mainWin_OnStatus);
          HANDLE_MSG(hWnd,WMU_REGINFO,mainWin_OnRegInfo);
          HANDLE_MSG(hWnd,WMU_WGSREADY,mainWin_OnWGSReady);
          HANDLE_MSG(hWnd,WMU_CLOSEMAINWINDOW,mainWin_OnCloseMainWindow);
     default:
          return(DefWindowProc(hWnd,uMessage,wParam,lParam));
     }
}

static VOID
mainWin_OnClose(                   // handle WM_CLOSE
HWND hWnd)
{
     if (confirmShutdown(hWnd)) {
          doShutdown(hWnd);
     }
}

static VOID
mainWin_OnDestroy(                 // handle WM_DESTROY
HWND hWnd)
{
     WINDOWPLACEMENT wp;

     wp.length=sizeof(WINDOWPLACEMENT);
     GetWindowPlacement(hWnd,&wp);
     saveMainWinSizePos(wp.rcNormalPosition.left,
                        wp.rcNormalPosition.top,
                        wp.rcNormalPosition.right-wp.rcNormalPosition.left,
                        wp.rcNormalPosition.bottom-wp.rcNormalPosition.top);
     PostQuitMessage(0);
     FORWARD_WM_DESTROY(hWnd,DefWindowProc);
}

static VOID
mainWin_OnSize(                    // handle WM_SIZE
HWND hWnd,
UINT state,
INT cx,
INT cy)
{
     MAINWINDATA *pMWData;

     pMWData=(MAINWINDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     SendMessage(pMWData->hStatBar,WM_SIZE,state,(LPARAM)MAKELONG(cx,cy));
     SetWindowPos(pMWData->hStatDlg,NULL,0,0,cx,cy-pMWData->statBarH,SWP_NOZORDER|SWP_NOMOVE|SWP_NOREDRAW);
     if (state == SIZE_MINIMIZED) {
          SetWindowText(hWnd,pMWData->pIconCapt);
     }
     else if (state == SIZE_MAXIMIZED || state == SIZE_RESTORED) {
          SetWindowText(hWnd,pMWData->pNormalCapt);
     }
     FORWARD_WM_SIZE(hWnd,state,cx,cy,DefWindowProc);
}

static VOID
mainWin_OnTimer(                   // handle WM_TIMER
HWND hWnd,
UINT id)
{
     CHAR textBuf[64];
     MAINWINDATA *pMWData;

     (VOID)hWnd;
     (VOID)id;
     pMWData=(MAINWINDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     makeOnlineTimeStr(textBuf,pMWData->hour,pMWData->min,pMWData->sec,
                       pMWData->timeSeparator);
     SendMessage(pMWData->hStatBar,SB_SETTEXT,0,(LPARAM)textBuf);
     if (++pMWData->sec >= 60) {
          pMWData->min++;
          pMWData->sec=0;
     }
     if (pMWData->min >= 60) {
          pMWData->hour++;
          pMWData->min=0;
     }
}

static BOOL
mainWin_OnCommand(                 // handle WM_COMMAND
HWND hWnd,
INT id,
HWND hWndCtl,
UINT codeNotify)
{
     MAINWINDATA *pMWData;

     (VOID)hWndCtl;
     pMWData=(MAINWINDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     if (codeNotify == 0) {        // from menu
          switch(id) {
          case CM_SHUTDOWN:
               if (confirmShutdown(hWnd)) {
                    doShutdown(hWnd);
               }
               break;
          case CM_HELPCONTENTS:
#ifdef ACTIBASE
               WinHelp(hWnd,"HELP\\ACTIBASE.HLP",HELP_FINDER,0);
#else
               WinHelp(hWnd,"HELP\\WGSERVER.HLP",HELP_FINDER,0);
#endif
               break;
          case CM_HELPABOUT:
               DialogBoxParam(hInstance,MAKEINTRESOURCE(ABOUT_DIALOG),hWnd,
                              wgsAboutDlgProc,(LPARAM)pMWData);
               break;
          }
     }
     else if (codeNotify == 1) {        // from accelerators
          switch(id) {
          case ACC_HELP:
#ifdef ACTIBASE
               WinHelp(hWnd,"HELP\\ACTIBASE.HLP",HELP_CONTEXT,5);
#else
               WinHelp(hWnd,"HELP\\WGSERVER.HLP",HELP_CONTEXT,5);
#endif
               break;
          }
     }
     return(TRUE);
}

static VOID
mainWin_OnSysColorChange(          // handle WM_SYSCOLORCHANGE
HWND hWnd)
{
     MAINWINDATA *pMWData;

     pMWData=(MAINWINDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     SendMessage(pMWData->hStatDlg,WM_SYSCOLORCHANGE,0,0);
     SendMessage(pMWData->hStatBar,WM_SYSCOLORCHANGE,0,0);
     InvalidateRect(pMWData->hStatBar,NULL,TRUE);
}

static BOOL
mainWin_OnQueryEndSession(         // handle WM_QUERYENDSESSION
HWND hWnd)
{
     static const CHAR mbCapt[]=SVR_FULL;
     static const CHAR mbText[]="Please shut down " SVR_FULL " before "
                                "proceeding with log off/shutdown!";

     if (!isRunAsService()) {
          MessageBox(hWnd,mbText,mbCapt,MB_ICONEXCLAMATION|MB_OK);
          return(FALSE);
     }
     else {
          return(TRUE);
     }
}

static VOID
mainWin_OnSetSysLoad(              // handle WMU_SYSLOAD
HWND hWnd,
USHORT nChans,
const CHAR *respTime)
{
     CHAR textBuf[64];
     MAINWINDATA *pMWData;

     pMWData=(MAINWINDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     makeResponseTimeStr(textBuf,respTime);
     SendMessage(pMWData->hStatBar,SB_SETTEXT,1,(LPARAM)textBuf);
     makeLinesInUseStr(textBuf,nChans);
     SendMessage(pMWData->hStatBar,SB_SETTEXT,2,(LPARAM)textBuf);
     pMWData->nChans=nChans;
}

static VOID
mainWin_OnSysopPage(               // handle WMU_SYSOPPAGE
HWND hWnd,
const CHAR *pageText)
{
     MAINWINDATA *pMWData;

     pMWData=(MAINWINDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     SendMessage(pMWData->hStatDlg,WMU_SYSOPPAGE,0,(LPARAM)pageText);
}

static VOID
mainWin_OnAuditTrail(              // handle WMU_AUDITTRAIL
HWND hWnd,
const CHAR *auditText)
{
     MAINWINDATA *pMWData;

     pMWData=(MAINWINDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     SendMessage(pMWData->hStatDlg,WMU_AUDITTRAIL,0,(LPARAM)auditText);
}

static VOID
mainWin_OnNewSysopPage(            // handle WMU_NEWSYSOPPAGE
HWND hWnd,
GBOOL fNewPage)
{
     CHAR textBuf[64];
     MAINWINDATA *pMWData;

     pMWData=(MAINWINDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     makeNewSysopPageStr(textBuf,fNewPage);
     SendMessage(pMWData->hStatBar,SB_SETTEXT,3,(LPARAM)textBuf);
}

static VOID
mainWin_OnStatus(                  // handle WMU_STATUS
HWND hWnd,
const CHAR *statusText)
{
     MAINWINDATA *pMWData;

     pMWData=(MAINWINDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     SendMessage(pMWData->hStatBar,SB_SETTEXT,4,(LPARAM)statusText);
}

static VOID
mainWin_OnRegInfo(
HWND hWnd,
WGSREGINFO *regInfo)
{
     MAINWINDATA *pMWData;

     pMWData=(MAINWINDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     pMWData->version=regInfo->version;
     pMWData->licenseCount=regInfo->licenseCount;
     stlcpy(pMWData->regNumber,regInfo->regNumber,sizeof(pMWData->regNumber));
     stlcpy(pMWData->regName,regInfo->regName,sizeof(pMWData->regName));
}

static VOID
mainWin_OnWGSReady(                // handle WMU_WGSREADY
HWND hWnd)
{
     MAINWINDATA *pMWData;
     HMENU hMenu;

     pMWData=(MAINWINDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     hMenu=GetMenu(hWnd);
     EnableMenuItem(hMenu,0,MF_BYPOSITION|MF_ENABLED);
     EnableMenuItem(hMenu,1,MF_BYPOSITION|MF_ENABLED);
     DrawMenuBar(hWnd);
     pMWData->pIconCapt=mainCapt;
     SetWindowText(hWnd,mainCapt);
     SetTimer(hMainWin,1,1000,NULL);
}

static VOID
mainWin_OnCloseMainWindow(         // handle WMU_CLOSEMAINWINDOW
HWND hWnd)
{
     DestroyWindow(hWnd);
}

static BOOL
confirmShutdown(                   // confirms shutdown
HWND hWnd)                         //   man window handle
{
     GBOOL fSDOk=FALSE;
     MAINWINDATA *pMWData;
     static const CHAR mbCapt[]="Shut down " SVR_FULL;
     static const CHAR mbText[]="Warning!!!  Selecting OK will disconnect all "
      "users from your system and take your system offline.\nSelect CANCEL "
      "to leave your system online.";

     pMWData=(MAINWINDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     if (pMWData->nChans > 0) {
          if (MessageBox(hWnd,mbText,mbCapt,MB_ICONEXCLAMATION|MB_OKCANCEL) == IDOK) {
               fSDOk=TRUE;
          }
     }
     else {
          fSDOk=TRUE;
     }
     return(fSDOk);
}

static VOID
doShutdown(                        // terminate main thread
HWND hWnd)                         //   man window handle
{
     HMENU hMenu;

     hMenu=GetMenu(hWnd);
     EnableMenuItem(hMenu,0,MF_BYPOSITION|MF_GRAYED);
     EnableMenuItem(hMenu,1,MF_BYPOSITION|MF_GRAYED);
     DrawMenuBar(hWnd);
     shutdownMainThread();
}

static VOID
getMainWinSizePos(                 // get window pos. from Registry
INT *x,                            //   X - coordinate
INT *y,                            //   Y - coordinate
INT *cx,                           //   X - size
INT *cy)                           //   Y - size
{
     HKEY hKey;
     DWORD valType,valSize;
     DWORD valBuffer;
     DWORD disposition;
     INT screenX;
     INT screenY;
     CHAR keyName[256];

     *x=*y=10;                     // default position & size
     *cx=200;
     *cy=150;
     lstrcpy(keyName,keyRoot);
     lstrcat(keyName,keySettings);
     if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,keyName,0,"",0,KEY_QUERY_VALUE,NULL,
                                         &hKey,&disposition) != ERROR_SUCCESS) {
          return;
     }
     valSize=sizeof(valBuffer);
     if (RegQueryValueEx(hKey,mainWinPosName,NULL,&valType,
                         (LPBYTE)&valBuffer,&valSize) == ERROR_SUCCESS
      && valType == REG_DWORD
      && valSize == sizeof(valBuffer)) {
          *x=LOWORD(valBuffer);
          *y=HIWORD(valBuffer);
     }
     if (RegQueryValueEx(hKey,mainWinSizeName,NULL,&valType,
                         (LPBYTE)&valBuffer,&valSize) == ERROR_SUCCESS
      && valType == REG_DWORD
      && valSize == sizeof(valBuffer)) {
          *cx=LOWORD(valBuffer);
          *cy=HIWORD(valBuffer);
     }
     RegCloseKey(hKey);
     screenX=GetSystemMetrics(SM_CXSCREEN);
     screenY=GetSystemMetrics(SM_CYSCREEN);
     if (*cx < 0) {
          *cx=0;
     }
     else if (*cx > screenX) {
          *cx=screenX;
     }
     if (*cy < 0) {
          *cy=0;
     }
     else if (*cy > screenY) {
          *cy=screenY;
     }
     screenX-=*cx;
     screenY-=*cy;
     if (*x < 0) {
          *x=0;
     }
     else if (*x > screenX) {
          *x=screenX;
     }
     if (*y < 0) {
          *y=0;
     }
     else if (*y > screenY) {
          *y=screenY;
     }
}

static VOID
saveMainWinSizePos(                // save window pos. to Registry
INT x,                             //   X - coordinate
INT y,                             //   Y - coordinate
INT cx,                            //   X - size
INT cy)                            //   Y - size
{
     HKEY hKey;
     DWORD valBuffer;
     DWORD disposition;
     CHAR keyName[256];

     lstrcpy(keyName,keyRoot);
     lstrcat(keyName,keySettings);
     if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,keyName,0,"",0,KEY_ALL_ACCESS ,NULL,
                                         &hKey,&disposition) != ERROR_SUCCESS) {
          return;
     }
     valBuffer=MAKELONG(x,y);
     RegSetValueEx(hKey,mainWinPosName,0,REG_DWORD,(LPBYTE)&valBuffer,
                   sizeof(valBuffer));
     valBuffer=MAKELONG(cx,cy);
     RegSetValueEx(hKey,mainWinSizeName,0,REG_DWORD,(LPBYTE)&valBuffer,
                   sizeof(valBuffer));
     RegCloseKey(hKey);
}

static VOID
setStatBarItemsSize(               // set sizes if status bar items
HWND hStatBar,                     //   status bar HWND
CHAR timeSeparator)                //   time sepatator (':')
{
     INT width[5];
     CHAR textBuf[128];
     HFONT hfCurrent,hfOld;
     HDC hDC;
     SIZE size;

     hfCurrent=(HFONT)SendMessage(hStatBar,WM_GETFONT,0,0);
     hDC=GetDC(hStatBar);
     hfOld=(HFONT)SelectObject(hDC,hfCurrent);
     makeOnlineTimeStr(textBuf,99,99,99,timeSeparator);
     GetTextExtentPoint32(hDC,textBuf,lstrlen(textBuf),&size);
     width[0]=size.cx+10;
     makeResponseTimeStr(textBuf,"999 sec");
     GetTextExtentPoint32(hDC,textBuf,lstrlen(textBuf),&size);
     width[1]=width[0]+size.cx+10;
     makeLinesInUseStr(textBuf,999);
     GetTextExtentPoint32(hDC,textBuf,lstrlen(textBuf),&size);
     width[2]=width[1]+size.cx+10;
     makeNewSysopPageStr(textBuf,TRUE);
     GetTextExtentPoint32(hDC,textBuf,lstrlen(textBuf),&size);
     width[3]=width[2]+size.cx+10;
     width[4]=-1;
     SelectObject(hDC,hfOld);
     ReleaseDC(hStatBar,hDC);
     SendMessage(hStatBar,SB_SETPARTS,sizeof(width)/sizeof(width[0]),(LPARAM)width);
}

static VOID
makeOnlineTimeStr(                 // makes "Online for" line
CHAR *textBuf,                     //   output buffer
INT hours,                         //   hours online
INT min,                           //   minutes online
INT sec,                           //   seconds online
CHAR timeSeparator)                //   time separator to use (':')
{
     wsprintf(textBuf,"Online for: %d%c%2.2d%c%2.2d",hours,
                                                     timeSeparator,
                                                     min,
                                                     timeSeparator,
                                                     sec);
}

static VOID
makeResponseTimeStr(               // makes "Response time:" line
CHAR *textBuf,                     //   output buffer
const CHAR *responseTime)          //   response time
{
     wsprintf(textBuf,"Response time: %s",responseTime);
}

static VOID
makeLinesInUseStr(                 // makes "Lines in use:" line
CHAR *textBuf,                     //   output buffer
INT linesInUse)                    //   number of line in use
{
     wsprintf(textBuf,"Lines in use: %d",linesInUse);
}

static VOID
makeNewSysopPageStr(               // makes "New Sysop Pages" line
CHAR *textBuf,                     //   output buffer
GBOOL fNewPage)                    //   new pages?
{
     if (fNewPage) {
          lstrcpy(textBuf,"New Sysop Pages");
     }
     else {
          lstrcpy(textBuf,"");
     }
}

static CHAR                        //   returns time separator
getTimeSeparator(VOID)             // get time separator for Windows settings
{
     CHAR buf[16];

     if (GetLocaleInfo(LOCALE_SYSTEM_DEFAULT,LOCALE_STIME,buf,sizeof(buf)) > 0) {
          return(buf[0]);
     }
     else {
          return(':');
     }
}

////////////////////////////////////////////////////////////////////////////////

static BOOL CALLBACK
wgsStatDlgProc(                    // system status dialog process
HWND hWndDlg,                      //   handle of dialog box
UINT uMsg,                         //   message
WPARAM wParam,                     //   first message parameter
LPARAM lParam)                     //   second message parameter
{
     switch (uMsg) {
          HANDLE_DLGMSG(hWndDlg,WM_NOTIFY,wgsStat_OnNotify);
          HANDLE_DLGMSG(hWndDlg,WM_COMMAND,wgsStat_OnCommand);
          HANDLE_DLGMSG(hWndDlg,WM_INITDIALOG,wgsStat_OnInitDialog);
          HANDLE_DLGMSG(hWndDlg,WM_SIZE,wgsStat_OnSize);
          HANDLE_DLGMSG(hWndDlg,WM_CTLCOLORLISTBOX,wgsStat_OnCtlColorListBox);
          HANDLE_DLGMSG(hWndDlg,WM_SYSCOLORCHANGE,wgsStat_OnSysColorChange);
          HANDLE_DLGMSG(hWndDlg,WMU_SYSOPPAGE,wgsStat_OnSysopPage);
          HANDLE_DLGMSG(hWndDlg,WMU_AUDITTRAIL,wgsStat_OnAuditTrail);
     default:
          return(FALSE);
     }
}

static BOOL
wgsStat_OnInitDialog(              // handle WM_INITDIALOG
HWND hWnd,
HWND hWndFocus,
LPARAM lParam)
{
     HWND hTab;
     TC_ITEM tie;
     STATDLGDATA *pSDData;
     RECT tabR,dispR;
     INT lbX,lbY;

     (VOID)hWndFocus;
     (VOID)lParam;
     pSDData=(STATDLGDATA *)LocalAlloc(LPTR,sizeof(STATDLGDATA));
     SetWindowLong(hWnd,GWL_USERDATA,(LONG)pSDData);
     pSDData->cd.textColor=GetSysColor(COLOR_WINDOWTEXT);
     pSDData->cd.bkColor=GetSysColor(COLOR_BTNFACE);
     pSDData->cd.hBkBrush=CreateSolidBrush(pSDData->cd.bkColor);
     // setup tab control
     hTab=GetDlgItem(hWnd,TAB_CONTROL);
     tie.mask=TCIF_TEXT|TCIF_IMAGE;
     tie.iImage=-1;
     tie.pszText="Audit Trail";
     TabCtrl_InsertItem(hTab,0,&tie);
     tie.pszText="Sysop Page";
     TabCtrl_InsertItem(hTab,1,&tie);
     ShowWindow(GetDlgItem(hWnd,LBOX_AUDITTRAIL),SW_SHOW);
     // adjust all positions
     GetWindowRect(hTab,&dispR);
     OffsetRect(&dispR,-dispR.left,-dispR.top);
     tabR=dispR;
     TabCtrl_AdjustRect(hTab,FALSE,&dispR);
     lbX=margin*2+dispR.left;
     lbY=margin*2+dispR.top;
     OffsetRect(&dispR,-dispR.left,-dispR.top);
     pSDData->margX=margin*4+tabR.right-dispR.right;
     pSDData->margY=margin*4+tabR.bottom-dispR.bottom;
     SetWindowPos(GetDlgItem(hWnd,TAB_CONTROL),NULL,margin,margin,0,0,SWP_NOZORDER|SWP_NOSIZE);
     SetWindowPos(GetDlgItem(hWnd,LBOX_AUDITTRAIL),NULL,lbX,lbY,0,0,SWP_NOZORDER|SWP_NOSIZE);
     SetWindowPos(GetDlgItem(hWnd,LBOX_SYSOPPAGE),NULL,lbX,lbY,0,0,SWP_NOZORDER|SWP_NOSIZE);
     setLBTabStops(hWnd);
     return(TRUE);
}


static BOOL
wgsStat_OnCommand(                 // handle WM_COMMAND
HWND hWnd,
INT id,
HWND hWndCtl,
UINT codeNotify)
{
     INT curSel;
     INT i;
     INT len;
     CHAR selText[256];
     const CHAR *pCapt;
     const CHAR *caption[2]={"Audit Trail Details","Sysop Page"};

     if (codeNotify == LBN_DBLCLK
      && (id == LBOX_AUDITTRAIL || id == LBOX_SYSOPPAGE)) {
          curSel=SendMessage(hWndCtl,LB_GETCURSEL,0,0);
          if (SendMessage(hWndCtl,LB_GETTEXT,(WPARAM)curSel,(LPARAM)selText)
                                                                    != LB_ERR) {
               for (len=lstrlen(selText),i=0 ; i < len ; i++) {
                    if (selText[i] == '\t') {
                         selText[i]=' ';
                    }
               }
               if (id == LBOX_AUDITTRAIL) {
                    pCapt=caption[0];
               }
               else if (id == LBOX_SYSOPPAGE) {
                    pCapt=caption[1];
               }
               else {
                    pCapt="";
               }
               MessageBox(hWnd,selText,pCapt,0);
          }
     }
     return(TRUE);
}

static BOOL
wgsStat_OnNotify(                  // handle WM_NOTIFY
HWND hWnd,
INT idFrom,
LPNMHDR nmhdr)
{
     INT sel;

     (VOID)idFrom;
     if (nmhdr->idFrom == TAB_CONTROL && nmhdr->code == TCN_SELCHANGE) {
          sel=TabCtrl_GetCurSel(nmhdr->hwndFrom);
          if (sel == 0) {
               ShowWindow(GetDlgItem(hWnd,LBOX_SYSOPPAGE),SW_HIDE);
               ShowWindow(GetDlgItem(hWnd,LBOX_AUDITTRAIL),SW_SHOW);
          }
          else {
               ShowWindow(GetDlgItem(hWnd,LBOX_AUDITTRAIL),SW_HIDE);
               ShowWindow(GetDlgItem(hWnd,LBOX_SYSOPPAGE),SW_SHOW);
               // clear status bar "new page" message
               SendMessage(GetParent(hWnd),WMU_NEWSYSOPPAGE,0,(LPARAM)FALSE);
          }
     }
     return(FALSE);
}

static VOID
wgsStat_OnSize(                    // handle WM_SIZE
HWND hWnd,
UINT state,
INT cx,
INT cy)
{
     STATDLGDATA *pSDData;

     (VOID)state;
     pSDData=(STATDLGDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     SetWindowPos(GetDlgItem(hWnd,LBOX_AUDITTRAIL),NULL,0,0,cx-pSDData->margX,
                  cy-pSDData->margY,SWP_NOZORDER|SWP_NOMOVE|SWP_NOREDRAW);
     SetWindowPos(GetDlgItem(hWnd,LBOX_SYSOPPAGE),NULL,0,0,cx-pSDData->margX,
                  cy-pSDData->margY,SWP_NOZORDER|SWP_NOMOVE|SWP_NOREDRAW);
     SetWindowPos(GetDlgItem(hWnd,TAB_CONTROL),NULL,0,0,cx-margin*2,cy-margin*2,
                  SWP_NOZORDER|SWP_NOMOVE|SWP_NOREDRAW);
     InvalidateRect(hWnd,NULL,TRUE);
}

static VOID
wgsStat_OnSysColorChange(          // handle WM_SYSCOLORCHANGE
HWND hWnd)
{
     STATDLGDATA *pSDData;

     pSDData=(STATDLGDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     handleSysColorChange(hWnd,&pSDData->cd);
     SendMessage(GetDlgItem(hWnd,TAB_CONTROL),WM_SYSCOLORCHANGE,0,0);
}

static HBRUSH
wgsStat_OnCtlColorListBox(         // handle WM_CTLCOLORLISTBOX
HWND hWnd,
HDC hDC,
HWND hWndChild,
INT type)
{
     STATDLGDATA *pSDData;

     (VOID)hWndChild;
     (VOID)type;
     pSDData=(STATDLGDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     return(handleCtlColor(hDC,&pSDData->cd));
}

static BOOL
wgsStat_OnSysopPage(               // handle WMU_SYSOPPAGE
HWND hWnd,
const CHAR *pageText)
{
     INT listCount;
     HWND hCtrl;
     CHAR tmpBuf[256];

     GetTimeFormat(LOCALE_SYSTEM_DEFAULT,0,NULL,NULL,tmpBuf,sizeof(tmpBuf));
     stlcat(tmpBuf,"\t",sizeof(tmpBuf));
     stlcat(tmpBuf,pageText,sizeof(tmpBuf));
     hCtrl=GetDlgItem(hWnd,LBOX_SYSOPPAGE);
     (VOID)ListBox_AddString(hCtrl,tmpBuf);
     listCount=ListBox_GetCount(hCtrl);
     if (listCount > 32) {
          (VOID)ListBox_DeleteString(hCtrl,0);
     }
     if (!IsWindowVisible(GetDlgItem(hWnd,LBOX_SYSOPPAGE))) {
          // display status bar "new page" message
          SendMessage(GetParent(hWnd),WMU_NEWSYSOPPAGE,0,(LPARAM)TRUE);
     }
     return(TRUE);
}

static BOOL
wgsStat_OnAuditTrail(              // handle WMU_AUDITTRAIL
HWND hWnd,
const CHAR *auditText)
{
     INT listCount;
     INT curSel;
     INT topIndex;
     HWND hCtrl;

     hCtrl=GetDlgItem(hWnd,LBOX_AUDITTRAIL);
     SendMessage(hCtrl,WM_SETREDRAW,(WPARAM)FALSE,0);
     (VOID)ListBox_AddString(hCtrl,auditText);
     listCount=ListBox_GetCount(hCtrl);
     curSel=ListBox_GetCurSel(hCtrl);
     if (curSel == (listCount-2)) { // was on the last line
          (VOID)ListBox_SetCurSel(hCtrl,listCount-1);
     }
     if (listCount > 1024) {
          topIndex=ListBox_GetTopIndex(hCtrl)-1;
          (VOID)ListBox_DeleteString(hCtrl,0);
          if (topIndex >= 0) {
               (VOID)ListBox_SetTopIndex(hCtrl,topIndex);
          }
          if (curSel == 0) {
               (VOID)ListBox_SetCurSel(hCtrl,0);
          }
          else if (curSel != (listCount-2)) {
               (VOID)ListBox_SetCurSel(hCtrl,curSel-1);
          }
          else {
               (VOID)ListBox_SetCurSel(hCtrl,curSel);
          }
     }
     SendMessage(hCtrl,WM_SETREDRAW,(WPARAM)TRUE,0);
     return(TRUE);
}

static VOID
setLBTabStops(
HWND hWnd)
{
     INT tab[2];
     INT i;
     CHAR ch;
     CHAR tmpBuf[256];
     LONG dlgBaseUnit;
     HDC hDC;
     HWND hCtlWnd;
     HFONT hfCurrent,hfOld;
     SIZE size;
     SYSTEMTIME st;

     hCtlWnd=GetDlgItem(hWnd,LBOX_AUDITTRAIL);
     hfCurrent=(HFONT)SendMessage(hCtlWnd,WM_GETFONT,0,0);
     hDC=GetDC(hCtlWnd);
     hfOld=(HFONT)SelectObject(hDC,hfCurrent);
     for (i=0,ch=' ' ; ch <= 'z' ; ch++,i++) {
          tmpBuf[i]=ch;
     }
     GetTextExtentPoint32(hDC,tmpBuf,i,&size);
     dlgBaseUnit=size.cx/i;
     // get longest string for date/time
     st.wYear=2000;
     st.wMonth=12;
     st.wDayOfWeek=0;
     st.wDay=30;
     st.wHour=12;
     st.wMinute=59;
     st.wSecond=59;
     st.wMilliseconds=0;
     GetDateFormat(LOCALE_SYSTEM_DEFAULT,0,&st,NULL,tmpBuf,sizeof(tmpBuf));
     GetTextExtentPoint32(hDC,tmpBuf,lstrlen(tmpBuf),&size);
     tab[0]=(size.cx*4/dlgBaseUnit)+4;
     GetTimeFormat(LOCALE_SYSTEM_DEFAULT,0,&st,NULL,tmpBuf,sizeof(tmpBuf));
     GetTextExtentPoint32(hDC,tmpBuf,lstrlen(tmpBuf),&size);
     tab[1]=(size.cx*4/dlgBaseUnit)+tab[0]+4;
     SelectObject(hDC,hfOld);
     ReleaseDC(hCtlWnd,hDC);
     SendMessage(hCtlWnd,LB_SETTABSTOPS,(WPARAM)(sizeof(tab)/sizeof(tab[0])),(LPARAM)tab);
     hCtlWnd=GetDlgItem(hWnd,LBOX_SYSOPPAGE);
     tab[1]-=tab[0]; // sysop page list has time only
     SendMessage(hCtlWnd,LB_SETTABSTOPS,(WPARAM)1,(LPARAM)&tab[1]);
}

////////////////////////////////////////////////////////////////////////////////

static BOOL CALLBACK
wgsAboutDlgProc(                   // about dialog process
HWND hWndDlg,                      //   handle of dialog box
UINT uMsg,                         //   message
WPARAM wParam,                     //   first message parameter
LPARAM lParam)                     //   second message parameter
{
     switch (uMsg) {
          HANDLE_DLGMSG(hWndDlg,WM_COMMAND,wgsAbout_OnCommand);
          HANDLE_DLGMSG(hWndDlg,WM_INITDIALOG,wgsAbout_OnInitDialog);
          HANDLE_DLGMSG(hWndDlg,WM_CLOSE,wgsAbout_OnClose);
          HANDLE_DLGMSG(hWndDlg,WM_DRAWITEM,wgsAbout_OnDrawItem);
     default:
          return(FALSE);
     }
}

static BOOL
wgsAbout_OnInitDialog(             // handle WM_INITDIALOG
HWND hWnd,
HWND hWndFocus,
LPARAM lParam)
{
     ABOUTDLGDATA *pABData;
     RECT rect;
     INT dlgX,dlgY,dlgCX,dlgCY;
     INT screenX;
     INT screenY;
     BITMAP bm;
#ifdef WEBCAST
     BITMAP vosbm;
#endif
     MAINWINDATA *pMWData;
     CHAR subVer[64];

     (VOID)hWndFocus;
     pMWData=(MAINWINDATA *)lParam;
     pABData=(ABOUTDLGDATA *)LocalAlloc(LPTR,sizeof(ABOUTDLGDATA));
     SetWindowLong(hWnd,GWL_USERDATA,(LONG)pABData);
     // move to the middle of main window
     screenX=GetSystemMetrics(SM_CXSCREEN);
     screenY=GetSystemMetrics(SM_CYSCREEN);
     GetWindowRect(hWnd,&rect);
     dlgCX=rect.right-rect.left;
     dlgCY=rect.bottom-rect.top;
     GetWindowRect(GetParent(hWnd),&rect);
     dlgX=(rect.right-rect.left)/2+rect.left-dlgCX/2;
     dlgY=(rect.bottom-rect.top)/2+rect.top-dlgCY/2;
     if ((dlgX+dlgCX) > screenX) {
          dlgX=screenX-dlgCX;
     }
     if ((dlgY+dlgCY) > screenY) {
          dlgY=screenY-dlgCY;
     }
     if (dlgX < 0) {
          dlgX=0;
     }
     if (dlgY < 0) {
          dlgY=0;
     }
     SetWindowPos(hWnd,NULL,dlgX,dlgY,0,0,SWP_NOZORDER|SWP_NOSIZE|SWP_NOREDRAW);
     // get bitmaps
     pABData->hBmp1=LoadBitmap(hInstance,MAKEINTRESOURCE(BMP_LOGO1));
     pABData->hBmp2=LoadBitmap(hInstance,MAKEINTRESOURCE(BMP_LOGO2));
     GetObject(pABData->hBmp1,sizeof(BITMAP),&bm);
     CreateWindowEx(0,"BUTTON",(LPCTSTR) NULL,
                    WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
                    30,5,bm.bmWidth, bm.bmHeight,
                    hWnd,(HMENU)301,hInstance,NULL);
#ifdef WEBCAST
     pABData->hBmp3=LoadBitmap(hInstance,MAKEINTRESOURCE(BMP_VOSAIC));
     GetObject(pABData->hBmp3,sizeof(BITMAP),&vosbm);
     CreateWindowEx(0,"BUTTON",(LPCTSTR) NULL,
      WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,
      15,60,vosbm.bmWidth,vosbm.bmHeight,
      hWnd,(HMENU)302,hInstance,NULL);
#endif
     loadSubVersionInfo(subVer,sizeof(subVer));
     displayAboutInfo(hWnd,pMWData->licenseCount,pMWData->regNumber,
                      pMWData->regName,pMWData->version,subVer);
     return(TRUE);
}


static BOOL
wgsAbout_OnCommand(                // handle WM_COMMAND
HWND hWnd,
INT id,
HWND hWndCtl,
UINT codeNotify)
{
     (VOID)hWndCtl;
     (VOID)codeNotify;
     if (id == IDOK) {
          SendMessage(hWnd,WM_CLOSE,0,0);
     }
     return(TRUE);
}

static VOID
wgsAbout_OnClose(                  // handle WM_CLOSE
HWND hWnd)
{
     ABOUTDLGDATA *pABData;

     pABData=(ABOUTDLGDATA *)GetWindowLong(hWnd,GWL_USERDATA);
     if (pABData->hBmp1) {
          DeleteObject(pABData->hBmp1);
     }
     if (pABData->hBmp2) {
          DeleteObject(pABData->hBmp2);
     }
#ifdef WEBCAST
     if (pABData->hBmp3) {
          DeleteObject(pABData->hBmp3);
     }
#endif
     LocalFree(pABData);
     EndDialog(hWnd,0);
}

static VOID
wgsAbout_OnDrawItem(               // handle WM_DRAWITEM
HWND hWnd,
const DRAWITEMSTRUCT *lpDrawItem)
{
     HDC memDC;
     HANDLE memObj;
     ABOUTDLGDATA *pABData;

#ifdef WEBCAST
     if (lpDrawItem->CtlType == ODT_BUTTON && lpDrawItem->CtlID == 302) {
          pABData=(ABOUTDLGDATA *)GetWindowLong(hWnd,GWL_USERDATA);
          memDC=CreateCompatibleDC(lpDrawItem->hDC);
          memObj=SelectObject(memDC,pABData->hBmp3);
          BitBlt(lpDrawItem->hDC,
                 lpDrawItem->rcItem.left,
                 lpDrawItem->rcItem.top,
                 lpDrawItem->rcItem.right,
                 lpDrawItem->rcItem.bottom,
                 memDC,
                 lpDrawItem->rcItem.left,
                 lpDrawItem->rcItem.top,
                 SRCCOPY);
          SelectObject(memDC,memObj);
          DeleteDC(memDC);
     }
#endif
     if (lpDrawItem->CtlType == ODT_BUTTON && lpDrawItem->CtlID == 301) {
          pABData=(ABOUTDLGDATA *)GetWindowLong(hWnd,GWL_USERDATA);
          memDC=CreateCompatibleDC(lpDrawItem->hDC);
          memObj=SelectObject(memDC,pABData->hBmp2);
          BitBlt(lpDrawItem->hDC,
                 lpDrawItem->rcItem.left,
                 lpDrawItem->rcItem.top,
                 lpDrawItem->rcItem.right,
                 lpDrawItem->rcItem.bottom,
                 memDC,
                 lpDrawItem->rcItem.left,
                 lpDrawItem->rcItem.top,
                 SRCAND);
          SelectObject(memDC,pABData->hBmp1);
          BitBlt(lpDrawItem->hDC,
                 lpDrawItem->rcItem.left,
                 lpDrawItem->rcItem.top,
                 lpDrawItem->rcItem.right,
                 lpDrawItem->rcItem.bottom,
                 memDC,
                 lpDrawItem->rcItem.left,
                 lpDrawItem->rcItem.top,
                 SRCPAINT);
          SelectObject(memDC,memObj);
          DeleteDC(memDC);
     }
}

static VOID
displayAboutInfo(
HWND hWnd,
ULONG licenseCount,
CHAR *regNumber,
CHAR *regName,
ULONG version,
CHAR *subVer)
{
     CHAR textBuf[128];

     if (subVer[0] != '\0') {
          wsprintf(textBuf,"Version %lu.%02lu (%s)",version/100,version%100,subVer);
     }
     else {
          wsprintf(textBuf,"Version %lu.%02lu",version/100,version%100);
     }
     SetWindowText(GetDlgItem(hWnd,SC_ABOUTVERSION),textBuf);
     SetWindowText(GetDlgItem(hWnd,SC_ABOUTREGNAME),regName);
     wsprintf(textBuf,"Registration number %s - %u",regNumber,licenseCount);
     SetWindowText(GetDlgItem(hWnd,SC_ABOUTREGNUM),textBuf);
}

static VOID
loadSubVersionInfo(
CHAR *subVer,
INT subVerSize)
{
     HKEY hKey;
     DWORD valType;
     DWORD valSize;

     subVer[0]='\0';
     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,keyRoot,0,KEY_READ,&hKey)
                                                             != ERROR_SUCCESS) {
          return;
     }
     valSize=subVerSize;
     if (RegQueryValueEx(hKey,"SubVersion",NULL,&valType,
                         (LPBYTE)subVer,&valSize) != ERROR_SUCCESS
      || valType != REG_SZ) {
          subVer[0]='\0';
     }
     RegCloseKey(hKey);
}

static HBRUSH
handleCtlColor(
HDC hDC,
COLORDATA *cd)
{
     SelectObject(hDC,cd->hBkBrush);
     SetTextColor(hDC,cd->textColor);
     SetBkColor(hDC,cd->bkColor);
     return(cd->hBkBrush);
}

static VOID
handleSysColorChange(
HWND hWnd,
COLORDATA *cd)
{
     cd->textColor=GetSysColor(COLOR_WINDOWTEXT);
     cd->bkColor=GetSysColor(COLOR_BTNFACE);
     if (cd->hBkBrush) {
          DeleteObject(cd->hBkBrush);
     }
     cd->hBkBrush=CreateSolidBrush(cd->bkColor);
     InvalidateRect(hWnd,NULL,TRUE);
}
