/****************************************************************************
**
**  Name:  peripres.cpp
**
**  Description:
**     Source file for peripheral presenter.                
**
**  $Log:   S:/tbird/mt2_68k/peri/peripres.cpv  $
** 
**    Rev 1.1   18 Mar 1997 10:09:56   gene
** 
**    Rev 1.0   14 Feb 1997 14:38:28   gene
** Initial revision.
** 
**    Rev 1.0   07 Sep 1995 09:49:20   gene
** Initial revision.
** 
**    Rev 1.29   26 Apr 1994 11:17:54   nghia
** Revised to set disabledCmds with BOOLEAN value.
** 
**    Rev 1.28   22 Apr 1994 11:36:38   nghia
** Test the Left and width value of the window layout for non zero, else use
** the default window position.
** 
**    Rev 1.27   21 Apr 1994 11:38:02   nghia
** Revised to handle use default size when size is invalid.
** 
**    Rev 1.26   19 Apr 1994 16:52:34   nghia
** Fixed PPR 9256, 9257 - Do not close Peripheral window when error occurred.
** Fixed PPR 9289 - F5 and F6 does not function in Peripheral persenter.
** Revised to follow coding standard.
** Added check for loading MUSCROLL.DLL failed
** Revised persenter to check for server error to disable all editing commands.
** Handle error reading peripherals - display ? in place of value.
** Major cleanup for memory and address descriptor leakage.  It's clean now.
** 
**    Rev 1.25   23 Nov 1993 09:37:00   ron
** stuff to make sure messages are sent on to the vlist class appropriately
** 
**    Rev 1.24   22 Nov 1993 16:23:54   ron
** fixes for PPRs 9155,9173,9175
** 
**    Rev 1.23   22 Nov 1993 14:01:52   ron
** fix ppr 9111
** 
**    Rev 1.22   22 Nov 1993 11:03:16   ron
** fixes for pprs 9105, 9119, 9120
** 
**    Rev 1.21   17 Nov 1993 15:27:02   ron
** fix for ppr9100
** 
**    Rev 1.20   17 Nov 1993 14:11:20   ron
** work-arounds for brain-dead event handling fiasco
** 
**    Rev 1.19   16 Nov 1993 10:48:10   ron
** Fix for PPR 9090
** 
**    Rev 1.18   11 Nov 1993 10:36:08   ron
** bug fixes 90xx
** 
**    Rev 1.17   10 Nov 1993 16:06:30   ron
** fixes for 9066, 9063, 9069, 9070
** 
**    Rev 1.16   08 Nov 1993 16:00:08   ron
** bug fixes for pv2.1
** 
**    Rev 1.15   08 Nov 1993 09:20:16   ron
** fixed HOME/END button problems and refresh problems after event redisply.
** 
**    Rev 1.14   05 Nov 1993 10:02:12   ron
** fixed page up/down
** 
**    Rev 1.13   04 Nov 1993 10:35:14   ron
** misc bug fixes
** 
**    Rev 1.12   03 Nov 1993 13:43:00   ron
** ready for 2.1 initial build
** 
**    Rev 1.0   14 Jul 1993 16:06:30   marilyn
** Initial revision.
** 
**  $Header:   S:/tbird/mt2_68k/peri/peripres.cpv   1.1   18 Mar 1997 10:09:56   gene  $
**
**  Copyright (C) 1993 Microtek International.  All rights reserved.
**
*****************************************************************************/


                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/

#define WIN31
#define STRICT

#ifndef _PERIPRES
#include "peripres.h"
#endif

#ifndef _PERIDIAL_
#include "peridial.h"
#endif

#ifndef _PERIMENU_
#include "perimenu.h"
#endif

#ifndef _HEAP_
#include "heap.h"
#endif

#ifndef _ENLIB_
#include "enlib.h"
#endif

#ifndef _EVENTS_
#include "events.h"
#endif

#ifndef _ADDR_
#include "addr.h"
#endif

#ifndef __STDIO_H_
#include <stdio.h>
#endif

#ifndef _VLIST_
#include "vlist.h"
#endif



                       /****************************
                        *                          *
                        *        DEFINITIONS       *
                        *                          *
                        ****************************/

#define MENU_OFFSET        0x3F00
#define SOURCE_WINDOW      0
#define STACK_WINDOW       1
#define VARIABLE_WINDOW    2
#define STATUS_WINDOW      3
#define CPU_WINDOW         4
#define MEMORY_WINDOW      5
#define TRACE_WINDOW       6
#define TRIGGER_WINDOW     7
#define SHELL_WINDOW       8
//#define EVENT_WINDOW       9
#define BREAKPOINT_WINDOW  9
#define TOOLBAR_WINDOW     10
#define PERIPHERAL_WINDOW  11
#define SPA_WINDOW         12


#define MIN_CHARS_PER_LINE 30
#define MIN_LINES          2

// global window conditions
BOOLEAN hidden = FALSE;
BOOLEAN minimized = FALSE;
HINSTANCE hMLib; // spin control library
U32 PeriLastLineSelected = 0;

                       /****************************
                        *                          *
                        *       EXTERNALS          *
                        *                          *
                        ****************************/

extern PTPeriServer PeriServer;   // Peripheral server object ptr
extern HINSTANCE hLib;

#pragma warn -par

                       /****************************
                        *                          *
                        *        PROTOTYPES        *
                        *                          *
                        ****************************/

VOID ToolBarMsg(WPARAM WParam, LPARAM LParam);

// --------------------- C Interface ----------------------------------
//
CHAR szString[128];
CHAR szAppName[20];
HINSTANCE hInst;          
HWND hWndPeri = NULL;       
HWND hwndPeriList = NULL;
U32 CurRangeMax = 0L;

RETCODE CreatePeriphPresenter(HINSTANCE, S16, S16, S16, S16);
int nCwRegisterClasses(void);

PRIVATE BOOLEAN registeredClasses = FALSE;

/*****************************************************************************
**
**  ToolBarMsg
**
*****************************************************************************/
VOID ToolBarMsg(WPARAM WParam, LPARAM LParam)   {
   HWND hToolBar;
   // Pass a message off to the toolbar window
   if (GetToolBarHandle((HANDLE FAR *) &hToolBar) == GOOD) {
      SendMessage(hToolBar, WM_COMMAND, WParam, LParam);
   }
}

/*****************************************************************************
**
**  CreatePeriphPresenter
**
*****************************************************************************/
RETCODE CreatePeriphPresenter(HINSTANCE hInstance, S16 left, S16 top,
                              S16 width, S16 height) {
   MSG msg;  // MSG structure to store your messages        
   int nRc;  // return value from Register Classes          

   strcpy(szAppName, "Peripheral");
   hInst = hInstance;
   if(!registeredClasses) {
      /* register window classes if first instance of application */
      if ((nRc = nCwRegisterClasses()) == -1) {
         // TODO: Replace this with ErrDisplayFormattedError
         strcpy(szString, "Error registering virtual listbox or peripheral");
         MessageBox(NULL, szString, NULL, MB_ICONEXCLAMATION);
         return nRc;
      }
      registeredClasses = TRUE;
   }
   // 04/19/94 - Nghia
   // use Windows default size if no size specified
   if (!left && !width) {
      left = CW_USEDEFAULT;
      width = CW_USEDEFAULT;
   }
   
   /* create peripheral window */
   hWndPeri = CreateWindow((LPSTR) szAppName,    /* Window class name    */
                           (LPSTR)"Peripheral",  /* title                */
                           (WS_CAPTION     |     /* Title and Min/Max    */
                           WS_SYSMENU      |     /* Add system menu box  */
                           WS_MINIMIZEBOX  |     /* Add minimize box     */
                           WS_MAXIMIZEBOX  |     /* Add maximize box     */
                           WS_THICKFRAME   |     /* thick sizeable frame */
                           WS_CLIPCHILDREN |     /* don't draw in child ares */
                           WS_OVERLAPPED),
                           left, top,         
                           width, height,     
                           NULL,                 /* Parent window's handle */
                           NULL,                 /* Default to Class Menu */
                           hInst,                /* Instance of window */
                           NULL);       /* Create struct for WM_CREATE */                                     
   if (hWndPeri == NULL) 
      return ER_PP_WIN_CREATE_FAIL;

   // ShowWindow is done by Actor
   // ShowWindow(hWndPeri, SW_SHOWNORMAL);
   UpdateWindow(hWndPeri);
   return PeriServer->status;
}

PRIVATE BOOLEAN inSize = FALSE;
PRIVATE BOOLEAN inLBU = FALSE;
PRIVATE WORD cxChar = 0; // save this, for hit testing
PRIVATE HWND lasthWnd;
PRIVATE UINT lastMessage;
PRIVATE WPARAM lastwParam;
PRIVATE LPARAM lastlParam;
PRIVATE int tabs[5];

/*****************************************************************************
**
**  PeriWndProc
**
*****************************************************************************/
LONG FAR PASCAL EXPORT PeriWndProc(HWND hWnd, UINT Message, WPARAM wParam,
                            LPARAM lParam) {
   HMENU hMenu=0; /* handle for the menu */
   WORD cyChar, cxWant, cyWant, cxWidth, cyHeight, cyOverhead;
   TEXTMETRIC tm;
   HDC hdc;
   RECT wRect, cRect;
   PRIVATE char szText[256];
   LPVLBSTRUCT lpvlbInStruct;
   U16 nLines;
   WORD x;
   S32 which, other, topitem;
   HWND hToolBar;
   U16 helpIndex;
   U32 fieldNum, dbu;
   WORD helpEntry;
   PRIVATE HANDLE lastHandle = 0;
   PRIVATE S32 rVal;
   PTRegister reg;
   U8 i;
   BOOLEAN isPeriph;
   HWND whoHasFocus;
   S16 buttonID;
   DESCRIPTOR tmpDesc = NULL;
   RETCODE err;
 
   lasthWnd = hWnd;
   lastMessage = Message;
   lastwParam = wParam;
   lastlParam = lParam;

   switch (Message) {
      case WM_COMMAND:
         switch (wParam) {
         case CM_FSAVEAS:
            break;
            
         case CM_FEXIT:
            PostMessage(hWnd, WM_CLOSE, 0, 0L);
            break;
            
         case CM_EREG:
            // prevent any editing action from mouse click - keyboard.
            if (PeriServer->status != GOOD) {
               MessageBeep(0);
               break;
            }
            SetFocus(hWnd);
            which = SendMessage(hwndPeriList, VLB_GETCURSEL, 0, 0L);
            topitem = SendMessage(hwndPeriList, VLB_GETTOPINDEX, 0, 0L);
            
            if (which == 0 && topitem == 0) { 
               reg = PeriServer->LineToReg(which, (U32 FAR *) &fieldNum,
                        (BOOLEAN FAR *) &isPeriph);
               if (reg == NULL) break;
               if ((err = PeriServer->GetRegisterAddress(reg,&tmpDesc)) != GOOD)
                  return err;
               // Read memory for teh latest value of register
               err = PeriServer->ReadRegValue(reg, tmpDesc);
               AdrDestroyAddress(tmpDesc);
            }
            if (which >= 0) {
               reg = PeriServer->LineToReg(which, (U32 FAR *) &fieldNum,
                        (BOOLEAN FAR *) &isPeriph);
               if (reg != NULL) {
                  if (reg->IsBuffer()) {
                     // do the edit buffer dialog
                     EditBuffer(hInst, hWnd, reg);
                  }
                  else {
                     EditReg(hInst, hWnd, reg, fieldNum);
                     other = SendMessage(hwndPeriList, VLB_GETCURSEL, 0, 0L);
                     if (other >= 0) {
                        which = other; // handle Next/Prev from edit dialog
                     }
                  }
               }
               SendMessage(hwndPeriList, WM_SETREDRAW, 0, 0L);
               SendMessage(hwndPeriList, VLB_RESETCONTENT, 0, 0L);
               SendMessage(hwndPeriList, WM_SETREDRAW, 1, 0L);
               SendMessage(hwndPeriList, VLB_SETTOPINDEX, 0, topitem);
               SetFocus(hwndPeriList);
               // Force the virtual listbox to give the focus to it's own child
               InvalidateRect(hwndPeriList, NULL, FALSE);
               SendMessage(hwndPeriList, VLB_SETCURSEL, (WORD) VLB_FINDITEM,
                           (LONG) which);
               PeriLastLineSelected = which;
               SendMessage(hwndPeriList, WM_KEYDOWN, (WPARAM) VK_SPACE, 0L);
               PostMessage(hWnd, WM_PAINT, 0, 0L);
            }
            break;
            
         case CM_EGO2REG:
            EditGotoReg(hInst, hWnd);
            // Force virtual listbox to give the focus to the actual listbox
            SetFocus(hwndPeriList);
            SendMessage(hwndPeriList, WM_KEYDOWN, (WPARAM) VK_SPACE, 0L);
            SendMessage(hwndPeriList, VLB_SETCURSEL, (WORD) VLB_FINDITEM, 
               (LONG) PeriLastLineSelected);
            PostMessage(hWnd, WM_PAINT, 0, 0L);
            break;
            
         case CM_EGO2PERI:
            EditGotoPeri(hInst, hWnd);
            // Force virtual listbox to give the focus to the actual listbox
            SetFocus(hwndPeriList);
            SendMessage(hwndPeriList, WM_KEYDOWN, (WPARAM) VK_SPACE, 0L);
            SendMessage(hwndPeriList, VLB_SETCURSEL, (WORD) VLB_FINDITEM, 
               (LONG) PeriLastLineSelected);
            PostMessage(hWnd, WM_PAINT, 0, 0L);
            break;
            
         case CM_EGO2ADR:
            EditGotoAddr(hInst, hWnd);
            // Force virtual listbox to give the focus to the actual listbox
            SetFocus(hwndPeriList);
            SendMessage(hwndPeriList, WM_KEYDOWN, (WPARAM) VK_SPACE, 0L);
            SendMessage(hwndPeriList, VLB_SETCURSEL, (WORD) VLB_FINDITEM, 
               (LONG) PeriLastLineSelected);
            PostMessage(hWnd, WM_PAINT, 0, 0L);
            break;
            
         case CM_VALL:
         case CM_VWATCH:
         case CM_VEXREG:
            break;

         case CM_VEXALL:
            PeriServer->ExpandAll();
            SendMessage(hwndPeriList, WM_SETREDRAW, 0, 0L);
            SendMessage(hwndPeriList, VLB_RESETCONTENT, 0, 0L);
            SendMessage(hwndPeriList, WM_SETREDRAW, 1, 0L);
            InvalidateRect(hwndPeriList, NULL, FALSE);
            SendMessage(hwndPeriList, VLB_SETCURSEL, (WORD) VLB_FINDITEM, 0L);
            PeriLastLineSelected = 0;
            PostMessage(hWnd, WM_PAINT, 0, 0L);
            break;
         case CM_VCMREG:
            break;

         case CM_VCMALL:
            PeriServer->CompressAll();
            SendMessage(hwndPeriList, WM_SETREDRAW, 0, 0L);
            SendMessage(hwndPeriList, VLB_RESETCONTENT, 0, 0L);
            SendMessage(hwndPeriList, WM_SETREDRAW, 1, 0L);
            InvalidateRect(hwndPeriList, NULL, FALSE);
            SendMessage(hwndPeriList, VLB_SETCURSEL, (WORD) VLB_FINDITEM, 0L);
            PeriLastLineSelected = 0;
            PostMessage(hWnd, WM_PAINT, 0, 0L);
            break;

         case CM_VREF:
            if (PeriServer->HasMBAR()) {
               PeriServer->UpdatePeriBaseAddress();
               if (!PeriServer->periBaseValid) {
                  ErrDisplayFormattedError(ER_PP_INVALID_BASE, FORCE_POPUP,
                                           NULL,NULL,NULL,MB_OK,
                                           (S16 FAR *) &buttonID);
                  return (GOOD);
               }
            }
            whoHasFocus = GetFocus();
            PeriServer->SetRegistersDirty();
            SendMessage(hwndPeriList, WM_KILLFOCUS, (WORD) whoHasFocus, 0L);
            which = SendMessage(hwndPeriList, VLB_GETCURSEL, 0, 0L);
            SendMessage(hwndPeriList, WM_SETREDRAW, 0, 0L);
            SendMessage(hwndPeriList, VLB_RESETCONTENT, 0, 0L);
            SendMessage(hwndPeriList, WM_SETREDRAW, 1, 0L);
            InvalidateRect(hwndPeriList, NULL, FALSE);
            PostMessage(hwndPeriList, WM_PAINT, 0, 0L);
            if (which >= 0) {
               SendMessage(hwndPeriList, VLB_SETCURSEL, (WORD) VLB_FINDITEM,
                           (LONG) which);
               PeriLastLineSelected = which;
            }
            if (whoHasFocus != NULL) SetFocus(whoHasFocus);

            // 04/21/94 - Nghia
            // Check if there is any error
            if ((PeriServer->status != GOOD) && !PeriServer->disabledCmds) {
               // turn off window editing commands
               PeriSetCommands(hWnd, FALSE);
            }
            PostMessage(hWnd, WM_PAINT, 0, 0L);            
            break;
            
         case CM_VHADDR:
            break;
         case CM_VHDESC:
            break;
         case CM_VHHL:
            break;
         case CM_VHTB:
            break;
         case CM_VHSB:
            break;
         case CM_WADD:
            break;
         case CM_WDEL:
            break;
         case CM_WDELALL:
            break;
         case CM_WINBKP:
            ToolBarMsg(MENU_OFFSET + BREAKPOINT_WINDOW, lParam);
            break;
         case CM_WINCPU:
            ToolBarMsg(MENU_OFFSET + CPU_WINDOW, lParam);
            break;
//         case CM_WINEVT:
//            ToolBarMsg(MENU_OFFSET + EVENT_WINDOW, lParam);
//            break;
         case CM_WINSPA:
            ToolBarMsg(MENU_OFFSET + SPA_WINDOW, lParam);
            break;
         case CM_WINMEM:
            ToolBarMsg(MENU_OFFSET + MEMORY_WINDOW, lParam);
            break;
         case CM_WINSHL:
            ToolBarMsg(MENU_OFFSET + SHELL_WINDOW, lParam);
            break;
         case CM_WINSRC:
            ToolBarMsg(MENU_OFFSET + SOURCE_WINDOW, lParam);
            break;
         case CM_WINSTK:
            ToolBarMsg(MENU_OFFSET + STACK_WINDOW, lParam);
            break;
         case CM_WINSTAT:
            ToolBarMsg(MENU_OFFSET + STATUS_WINDOW, lParam);
            break;
         case CM_WINTB:
            ToolBarMsg(MENU_OFFSET + TOOLBAR_WINDOW, lParam);
            break;
         case CM_WINTRC:
            ToolBarMsg(MENU_OFFSET + TRACE_WINDOW, lParam);
            break;
         case CM_WINTRG:
            ToolBarMsg(MENU_OFFSET + TRIGGER_WINDOW, lParam);
            break;
         case CM_WINVAR:
            ToolBarMsg(MENU_OFFSET + VARIABLE_WINDOW, lParam);
            break;
         case CM_HLPIN:
            WinHelp(hWnd, "PWRVIEWS.HLP", HELP_CONTEXT, HE_CONTENTS);
            break;
         case CM_HLPHLP:
            WinHelp(hWnd, "PWRVIEWS.HLP", HELP_HELPONHELP, HE_CONTENTS);
            break;
         case CM_HLPPERI:
            WinHelp(hWnd, "PWRVIEWS.HLP", HELP_CONTEXT, HE_PERIPHERAL);
            break;
         default:
               return DefWindowProc(hWnd, Message, wParam, lParam);
         }
         break; /* End of WM_COMMAND */
      case WM_SETFOCUS:
         SetFocus(hwndPeriList);
         break;
         
      case WM_PAINT:        
         SendMessage(hwndPeriList, VLB_SETCURSEL, (WORD) VLB_FINDITEM, 
               (LONG) PeriLastLineSelected);
         return DefWindowProc(hWnd, Message, wParam, lParam);
      case WM_VKEYTOITEM:
         switch(wParam) {
            case VK_RETURN:  // return key
               lParam = 0L;
               SendMessage(hWnd, WM_COMMAND, CM_EREG, lParam);
               break;
            case VK_ADD:        // + key (from windows.h)
            case (VK_ADD + 80):        // + key (as seen by debugger)
            case VK_SUBTRACT:   // - key (from windows.h)
            case (VK_SUBTRACT + 80):   // - key (as seen by debugger)
               // prevent any editing action from mouse click - keyboard.
               if (PeriServer->status != GOOD) {
                  MessageBeep(0);
                  break;
               }
               // just toggle, whichever key is pressed (least surprising)
               topitem = SendMessage(hwndPeriList, VLB_GETTOPINDEX, 0, 0L);
               which = SendMessage(hwndPeriList, VLB_GETCURSEL, 0, 0L);
               if (which >= 0) {
                  PeriServer->ToggleDisplay(which);
                  SendMessage(hwndPeriList, WM_SETREDRAW, 0, 0L);
                  SendMessage(hwndPeriList, VLB_RESETCONTENT, 0, 0L);
                  SendMessage(hwndPeriList, WM_SETREDRAW, 1, 0L);
                  PeriLastLineSelected = which;
                  SendMessage(hwndPeriList, VLB_SETTOPINDEX, 0, topitem);
                  SendMessage(hwndPeriList, VLB_SETCURSEL,
                              (WORD)VLB_FINDITEM, (LONG) which);
                  SetFocus(hwndPeriList);
                  // Force the virtual listbox to give the focus to it's
                  // own child
                  SendMessage(hwndPeriList, WM_KEYDOWN, (WPARAM) VK_SPACE, 0L);
                  InvalidateRect(hwndPeriList, NULL, FALSE);
                  PostMessage(hWnd, WM_PAINT, 0, 0L);
               }
               break;
               
            case VK_F1: /* let the Hook Procedure handle it */
               break;
            case VK_F2:
            case VK_F3:
            case VK_F4:
            case VK_F5:
            case VK_F6:
            case VK_F7:
            case VK_F8:
            case VK_F9:
            case VK_F10:
            case VK_F11:
            case VK_F12:
               if ((GetToolBarHandle((HANDLE FAR *) &hToolBar) == GOOD) &&
                   (hToolBar != NULL)) {
                  SendMessage(hToolBar, WM_KEYDOWN, wParam, lParam);
               }
               break;
            default:
               lastwParam = wParam; // debug
               return DefWindowProc(hWnd, Message, wParam, lParam);
         }
         break;

      case WM_KEYDOWN:
      case WM_SYSKEYDOWN:           
         switch(wParam) {            
            case VK_END:     // End key
            case VK_HOME:    // Home key
            case VK_UP:      // up arrow key
            case VK_DOWN:    // down arrow key
               SendMessage(hwndPeriList, Message, wParam, lParam);
               break;
            default:
               lastwParam = wParam; // debug
               return DefWindowProc(hWnd, Message, wParam, lParam);
         }
         break; /* End of WM_KEYDOWN */
      
      case WM_CREATE:
         // Tell the F1 Hook procedure who we are, so it doesn't
         // get confused and pop up somebody else's help...
         if (lastHandle != hWnd) {
            lastHandle = hWnd;
            helpIndex = HI_PERIPHERAL;
            helpEntry = HE_PERIPHERAL;
            RegisterHelpEntry(helpIndex, (LONG) lastHandle, helpEntry);
         }
         hWndPeri = hWnd;
         if (PeriServer->HasMBAR()) {
            PeriServer->UpdatePeriBaseAddress();
            if (!PeriServer->periBaseValid) {
               ErrDisplayFormattedError(ER_PP_INVALID_BASE, FORCE_POPUP,NULL,
                  NULL,NULL,MB_OK, (S16 FAR *) &buttonID);
               return (0L);
            }
         }
         hMenu = LoadMenu(hInst, "peri_menu");
         hwndPeriList = CreateWindow ("VList", 
                       NULL, 
                       WS_CHILD | WS_VISIBLE | WS_BORDER | LBS_NOTIFY |
                       LBS_USETABSTOPS | VLBS_USETABSTOPS |
                       LBS_WANTKEYBOARDINPUT | VLBS_WANTKEYBOARDINPUT,
                       0, 0, 0, 0,
                       hWnd,
                       hMenu,
                       hInst,
                       NULL);
         // 04/18/94 _ Nghia
         // set server content to be reread
         PeriServer->SetRegistersDirty();
         
         /* initialize working variables   */
         SendMessage(hwndPeriList, VLB_INITIALIZE, 0, 0L);
         // TODO: Set this to something reasonable
         SendMessage(hwndPeriList, VLB_SETHORIZONTALEXTENT, 700, 0L);
         dbu = GetDialogBaseUnits();
         for (i = 0; i < 5; i++) {
            tabs[i] = ((i + 1) * 5 * (int) dbu) - (3 * dbu);
         }
         rVal = SendMessage(hwndPeriList, LB_SETTABSTOPS, 5,
                            (LONG)(int FAR *) &(tabs[0]));
         SendMessage(hwndPeriList, VLB_SETCURSEL, (WORD) VLB_FINDITEM,
                     (LONG) 0L);
         PeriLastLineSelected = 0;
         InvalidateRect(hwndPeriList, NULL, FALSE);
         SetFocus(hwndPeriList);
         // Force the virtual listbox to give the focus to it's own child
         SendMessage(hwndPeriList, WM_KEYDOWN, (WPARAM) VK_SPACE, 0L);
         break;    /*  End of WM_CREATE */

      case WM_SIZE:
         if (inSize || wParam == SIZEICONIC) {
            return (DefWindowProc(hWnd, Message, wParam, lParam));
         }
         cxWidth = LOWORD(lParam);
         cyHeight = HIWORD(lParam);
         hdc = GetDC(hWnd);
         GetTextMetrics(hdc, &tm);
         cyChar = tm.tmHeight;
         cxChar = tm.tmAveCharWidth;
         ReleaseDC(hWnd, hdc);
         nLines = cyHeight / cyChar;
         if (nLines > MIN_LINES) {
            // make it an even multiple
            cyHeight = (nLines * cyChar);
         }
         cxWant = cxChar * MIN_CHARS_PER_LINE + GetSystemMetrics(SM_CXVSCROLL);
         cyWant = cyChar * MIN_LINES;
         if (cxWidth < cxWant) {
            cxWidth = cxWant;
         }
         if (cyHeight < cyWant) {
            cyHeight = cyWant;
         }
         GetWindowRect(hWnd, (LPRECT) &wRect);
         cyOverhead = GetSystemMetrics(SM_CYCAPTION) + 
                      (2 * GetSystemMetrics(SM_CYFRAME)) +
                      GetSystemMetrics(SM_CYMENU);
         inSize = TRUE;
         MoveWindow(hWnd, 
            wRect.left, 
            wRect.top, 
            cxWidth + 2 * GetSystemMetrics(SM_CXFRAME), 
            cyHeight + cyOverhead,
               TRUE);
         inSize = FALSE;
         // List box takes up entire client area of window
         GetClientRect(hWnd, (LPRECT) &cRect);
         MoveWindow(hwndPeriList, 0, 0, cRect.right, cRect.bottom, TRUE);
         break;    /*  End of WM_SIZE */

    case WM_NCDESTROY:
        SendMessage(hwndPeriList, Message, wParam, lParam);
        break;

    case WM_DESTROY:
        // SendMessage(hwndPeriList, WM_DESTROY, 0, 0);
        PeriCloseWindow();
        break;

    case WM_CLOSE:  /* close the window */
        DestroyWindow(hwndPeriList);
        DestroyWindow(hWnd);
        hWndPeri = NULL;
        break;

    case WM_LBUTTONUP:
        // prevent any view changes action from mouse click - keyboard.
        if (PeriServer->status != GOOD) {
           break;
        }
        if (inLBU) break;
        inLBU = TRUE;
        x = LOWORD(lParam);
        if (x <= 4 * cxChar) { // temporary
           which = SendMessage(hwndPeriList, VLB_GETCURSEL, 0, 0L);
           topitem = SendMessage(hwndPeriList, VLB_GETTOPINDEX, 0, 0L);
           if (which >= 0) {
              PeriServer->ToggleDisplay(which);
              SendMessage(hwndPeriList, WM_SETREDRAW, 0, 0L);
              SendMessage(hwndPeriList, VLB_RESETCONTENT, 0, 0L);
              SendMessage(hwndPeriList, WM_SETREDRAW, 1, 0L);
              PeriLastLineSelected = which;
              SendMessage(hwndPeriList, VLB_SETTOPINDEX, 0, topitem);
              SendMessage(hwndPeriList, VLB_SETCURSEL,
                          (WORD)VLB_FINDITEM, (LONG) which);
              SetFocus(hwndPeriList);
              // Force the virtual listbox to give the focus to it's own child
              SendMessage(hwndPeriList, WM_KEYDOWN, (WPARAM) VK_SPACE, 0L);
              InvalidateRect(hwndPeriList, NULL, FALSE);
              PostMessage(hWnd, WM_PAINT, 0, 0L);
           }
        }
        inLBU = FALSE;
        break;

    case WM_LBUTTONDBLCLK:
        SendMessage(hWnd, WM_COMMAND, CM_EREG, 0L);
        break;

    case VLB_PREV:
       lpvlbInStruct = (LPVLBSTRUCT)lParam;

       if ( lpvlbInStruct->lIndex > 0 ) {
          lpvlbInStruct->nStatus = VLB_OK;
          lpvlbInStruct->lIndex--;
          lpvlbInStruct->lData = lpvlbInStruct->lIndex;
          _fstrcpy(szText, PeriServer->LineToText(lpvlbInStruct->lIndex));
          lpvlbInStruct->lpTextPointer = szText;
       }
       else {
          lpvlbInStruct->nStatus = VLB_ENDOFFILE;
       }
       break;

    case VLB_FINDPOS:
       lpvlbInStruct = (LPVLBSTRUCT)lParam;

       if ( lpvlbInStruct->lIndex == 0L ) {
          goto First;
       }
       else if ( lpvlbInStruct->lIndex == 100L ) {
          goto Last;
       }
       else {
          lpvlbInStruct->lIndex = lpvlbInStruct->lData*1000L;
          lpvlbInStruct->nStatus = VLB_OK;
          _fstrcpy(szText, PeriServer->LineToText(lpvlbInStruct->lIndex));
          lpvlbInStruct->lpTextPointer = szText;
       }
       break;

    case VLB_FINDITEM:
       lpvlbInStruct = (LPVLBSTRUCT)lParam;

       lpvlbInStruct->lIndex = lpvlbInStruct->lData;
       lpvlbInStruct->nStatus = VLB_OK;
       _fstrcpy(szText, PeriServer->LineToText(lpvlbInStruct->lIndex));
       lpvlbInStruct->lpTextPointer = szText;
       break;

    case VLB_FINDSTRING:
    case VLB_FINDSTRINGEXACT:
    case VLB_SELECTSTRING:
    case VLBR_FINDSTRING:
    case VLBR_FINDSTRINGEXACT:
    case VLBR_SELECTSTRING:
     {
       lpvlbInStruct = (LPVLBSTRUCT)lParam;

       _fstrcpy(szText,lpvlbInStruct->lpFindString);
       lpvlbInStruct->lIndex = atol(szText);
       _fstrcpy(szText, PeriServer->LineToText(lpvlbInStruct->lIndex));
       lpvlbInStruct->lpTextPointer = szText;
       lpvlbInStruct->lData = lpvlbInStruct->lIndex;
       lpvlbInStruct->nStatus = VLB_OK;
     }
     break;

    case VLB_RANGE:
       lpvlbInStruct = (LPVLBSTRUCT)lParam;
       if (PeriServer->HasMBAR()) {
          PeriServer->UpdatePeriBaseAddress();
          if (!PeriServer->periBaseValid) {
             ErrDisplayFormattedError(ER_PP_INVALID_BASE, FORCE_POPUP,NULL,
                NULL,NULL,MB_OK, (S16 FAR *) &buttonID);
             return (0L);
          }
       }

       lpvlbInStruct->lIndex = PeriServer->CountLines();
       CurRangeMax = lpvlbInStruct->lIndex - 1;
       lpvlbInStruct->nStatus = VLB_OK;
       break;

    case VLB_NEXT:
       lpvlbInStruct = (LPVLBSTRUCT)lParam;

       if ( lpvlbInStruct->lIndex < CurRangeMax ) {
          lpvlbInStruct->nStatus = VLB_OK;
          lpvlbInStruct->lIndex++;
          lpvlbInStruct->lData = lpvlbInStruct->lIndex;
          _fstrcpy(szText, PeriServer->LineToText(lpvlbInStruct->lIndex));
          lpvlbInStruct->lpTextPointer = szText;
       }
       else {
          lpvlbInStruct->nStatus = VLB_ENDOFFILE;
       }
       break;

    case VLB_FIRST:
First:
       lpvlbInStruct = (LPVLBSTRUCT)lParam;

       lpvlbInStruct->nStatus = VLB_OK;
       lpvlbInStruct->lIndex = 0L;
       _fstrcpy(szText, PeriServer->LineToText(0L));
       lpvlbInStruct->lpTextPointer = szText;
       lpvlbInStruct->lData = lpvlbInStruct->lIndex;
       break;

    case VLB_LAST:
Last:
       lpvlbInStruct = (LPVLBSTRUCT)lParam;

       lpvlbInStruct->nStatus = VLB_OK;
       lpvlbInStruct->lIndex = CurRangeMax;
       _fstrcpy(szText, PeriServer->LineToText(lpvlbInStruct->lIndex));
       lpvlbInStruct->lpTextPointer = szText;
       lpvlbInStruct->lData = lpvlbInStruct->lIndex;
       break;

    case VLB_GETITEMDATA:
    case VLBR_GETITEMDATA:
       lpvlbInStruct = (LPVLBSTRUCT)lParam;
       lpvlbInStruct->nStatus = VLB_OK;
       lpvlbInStruct->lData = lpvlbInStruct->lIndex;
       break;

    case VLB_GETTEXT:
    case VLBR_GETTEXT:
       lpvlbInStruct = (LPVLBSTRUCT)lParam;
       lpvlbInStruct->nStatus = VLB_OK;
       lpvlbInStruct->lData = lpvlbInStruct->lIndex;
       _fstrcpy(szText, PeriServer->LineToText(lpvlbInStruct->lIndex));
       lpvlbInStruct->lpTextPointer = szText;
     break;

    case VLBR_GETTEXTLEN:
       lpvlbInStruct = (LPVLBSTRUCT)lParam;
       lpvlbInStruct->nStatus = VLB_OK;
       lpvlbInStruct->lData = lpvlbInStruct->lIndex;
       _fstrcpy(szText, PeriServer->LineToText(lpvlbInStruct->lIndex));
       lpvlbInStruct->lpTextPointer = szText;
       return (lstrlen((LPSTR) szText));

    default:
         lastlParam = lParam;
         return (DefWindowProc(hWnd, Message, wParam, lParam));
   }
   return 0L;
}


/*****************************************************************************
**
**  nCwRegisterClasses
**
*****************************************************************************/
int nCwRegisterClasses(VOID) {
   WNDCLASS   wndclass;    /* struct to define a window class */
   HWND hToolBar;
   HINSTANCE hInstance;
   memset(&wndclass, 0x00, sizeof(WNDCLASS));

   if (GetToolBarHandle((HANDLE FAR *) &hToolBar) == GOOD) {
      hInstance = ((HINSTANCE) GetWindowWord(hToolBar, GWW_HINSTANCE));
      wndclass.hIcon = LoadIcon(hInstance, "PERIPHERAL");
   }
  
   /* load WNDCLASS with window's characteristics */
   wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW;
   wndclass.lpfnWndProc = PeriWndProc;

   /* Extra storage for Class and Window objects  */
   wndclass.cbClsExtra = 0;
   wndclass.cbWndExtra = 0;
   wndclass.hInstance = hInst;
   wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);

   /* Create brush for erasing background */
   wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
   wndclass.lpszMenuName = "peri_menu";   /* Menu Name is App Name */
   wndclass.lpszClassName = szAppName; /* Class Name is App Name */
   if(!RegisterClass(&wndclass))
      return -1;
  
   return(0);
}

/*****************************************************************************
**
**  PeriInvalidateDisplay
**
*****************************************************************************/
RETCODE EXPORT PeriInvalidateDisplay() {
   S16 buttonID;

   if (hWndPeri == NULL || hwndPeriList == NULL) {
      return (GOOD);
   }
   if (PeriServer->HasMBAR()) {
      PeriServer->UpdatePeriBaseAddress();
      if (!PeriServer->periBaseValid) {
         ErrDisplayFormattedError(ER_PP_INVALID_BASE, FORCE_POPUP,NULL,
            NULL,NULL,MB_OK, (S16 FAR *) &buttonID);
         return (ER_PP_INVALID_BASE);
      }
   }
   // Try This
   PostMessage(hWndPeri, WM_COMMAND, CM_VREF, 0L);
   // Enable editing commands
   PeriSetCommands(hWndPeri, TRUE);
   return (GOOD);
}

/*****************************************************************************
**
**  PeriRepaintDisplay
**
*****************************************************************************/
RETCODE EXPORT PeriRepaintDisplay() {
   return (GOOD);
}

/*****************************************************************************
**
**  PeriOpenWindow
**
*****************************************************************************/
RETCODE EXPORT PeriOpenWindow(HWND parent,int left,int top,int width,
      int height,DWORD dwStyle,DWORD dwExStyle,HWND *periWindow) {
   RETCODE err=GOOD;
   S16 buttonID;

   if (PeriServer) {
      if (hWndPeri == NULL) {
         hMLib = LoadLibrary((LPSTR)"muscroll.dll");
         if (hMLib < (HINSTANCE) 32) {
            err = ER_NO_LIBS; // failed to load the required library
            ErrDisplayFormattedError(err, FORCE_POPUP, (LPSTR) "Peripheral",
                                     NULL, NULL,MB_OK, (S16 FAR *) &buttonID);
            return err;
         }
         else {
            // create the presenter  
            if (((err = CreatePeriphPresenter(hLib, left, top,
                                              width, height)) != GOOD) &&
                                              (err != ER_PP_WIN_CREATE_FAIL)) {
               PeriSetCommands(hWndPeri, FALSE);
            }
            *periWindow = hWndPeri;
         }
      }
   }
   else {
      // Critical error - PeriServer is not valid
      err = ER_PP_WIN_CREATE_FAIL;
   }
   return err;
}

/*****************************************************************************
**
**  PeriCloseWindow
**
*****************************************************************************/
RETCODE EXPORT PeriCloseWindow() {
   if (hWndPeri != NULL) {
      hWndPeri = NULL;
      FreeLibrary(hMLib);
   }
   return GOOD;
}

/*****************************************************************************
**
**  PeriExistsWindow
**
*****************************************************************************/
RETCODE EXPORT PeriExistsWindow(HWND *periHandle) {
   *periHandle = hWndPeri;
   return (GOOD);
}

/*****************************************************************************
**
**  PeriSetCommands
**
*****************************************************************************/
RETCODE PeriSetCommands(HWND periHwnd, BOOLEAN commandState) {
   HMENU hMainMenu;
   RECT  periWndRect;
   
   U16 menuFlag = (commandState ? MF_ENABLED : MF_GRAYED);
   PeriServer->disabledCmds = (BOOLEAN)!commandState;
   
   if (periHwnd && ((hMainMenu = GetMenu(periHwnd)) != NULL))  {
      // Edit Menu
      EnableMenuItem(hMainMenu, PERI_EDITMENU, menuFlag | MF_BYPOSITION);
      // View Menu
      EnableMenuItem(hMainMenu, PERI_VIEWMENU, menuFlag | MF_BYPOSITION);  
      // Repaint the window menu bar
      DrawMenuBar(periHwnd);
   }            
   return (GOOD);
}

//************************** E O F ****************************************/
