//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//                                                     VV         VV       //
//  VV       VV     V - A Portable C++ GUI Framework    VV       VV        //
//   VV     VV           designed and written by         VV     VV         //
//    VV   VV                                             VV   VV          //
//     VV VV              Bruce E. Wampler, Ph.D.          VV VV           //
//      VVV               e-mail: bruce@objectcentral.com   VVV            //
//       V                                                   V             //
//                 Ported to OS/2 by Jon B. Hacker, Ph.D.                  //
//                                                                         //
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//                                                                         //
// vapp.cxx - The vApp control object (For OS/2)                           //
//                                                                         //
// Copyright (C) 1995-1998  Bruce E. Wampler                               //
//                                                                         //
// This file is part of the V C++ GUI Framework.                           //
//                                                                         //
// This library is free software; you can redistribute it and/or           //
// modify it under the terms of the GNU Library General Public             //
// License as published by the Free Software Foundation; either            //
// version 2 of the License, or (at your option) any later version.        //
//                                                                         //
// This library is distributed in the hope that it will be useful,         //
// but WITHOUT ANY WARRANTY; without even the implied warranty of          //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       //
// Library General Public License for more details.                        //
//                                                                         //
// You should have received a copy of the GNU Library General Public       //
// License along with this library (see COPYING.LIB); if not, write to the //
// Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //
//                                                                         //
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
#include <v/vos2.h>            // for OS/2 stuff
#include <v/vapp.h>            // my header file
#include <v/vwindow.h>         // Win header
#include <v/vfont.h>           // for font stuff
#include <v/vcmdwin.h>
#include <v/vTimer.h>
#include <v/vcmdpane.h>
#include <v/vthislst.h>
#include <stdlib.h>

//    extern MRESULT EXPENTRY vTimerProc(HWND hwnd, UINT msg, UINT idTimer, ULONG mp2);

    // Globals available to the world
    vApp* theGlobalApp = NULL;	// to be filled in upon instantiation
    int (*pAppMain)(int, char**);
    DebugMask DebugState;
    static int _destroyed;         // for destructor

    // OS/2 bldlevel.exe signature
    static char bldlevel[] =  "@#Jon Hacker:1.21a100#@VGUI for OS/2";
    static char copyright[] =
      "****> Copyright (C) 1995-1999 Bruce E. Wampler; under terms of the\
 GNU Library General Public License, version 2 <****";

 //========================>>> vApp::vApp <<<=======================
  vApp::vApp(VCONST char* appName, int simSDI, int frameHeight, int frameWidth) :
       vBaseItem(appName)      // constructor
  {
    // First, set the global pointer to the main App. This happens
    // when the user declares the instance of the app, either from
    // a vApp object direct, or an object from a class derived
    // from the vApp class.
    theGlobalApp = this;        // this is our object

    // now the data members
    _curThis = 0;
    _running = 0;               // we are running
    _WindowList = 0;		// no windows registered
    _CmdPaneList = 0;		// no command panes registered
    _clipText = 0;
    _inExit = 0;
    _simSDI = simSDI;           // not used in OS/2
    _frameWidth = frameWidth;
    _frameHeight = frameHeight;
    _DefaultHeight = 300;       // default sizes for canvas window
    _DefaultWidth = 500;
    _destroyed = 0;             // Not destroyed yet
    _Frame = 0;                 // Not initiated yet.
    // Set which debug items to show
    DebugState.System = 0;                     // System debug messages
    DebugState.CmdEvents = 0;                  // Show command events (buttons, etc.)
    DebugState.MouseEvents = 0;                        // Show mouse events
    DebugState.WindowEvents = 0;               // Window events (resize, etc.)
    DebugState.Build = 0;                      // Define/Build window
    DebugState.BadVals= 0;                     // Error values
    DebugState.Misc = 0;                       // Misc stuff
    DebugState.Text = 0;                       // Text events
    DebugState.Constructor = 0;                        // Show constructors
    DebugState.Destructor = 0;                 // Show destructors
    DebugState.User = 0;                       // Debug user events
    DebugState.UserApp1 = 0;                   // Level 1 User App
    DebugState.UserApp2 = 0;                   // Level 2 User App
    DebugState.UserApp3 = 0;                   // Level 3 User App
    DebugState.OS2Dev = 0;                     // OS/2 Development Debug
  }
//========================>>> vApp::initialize <<<=======================
  void vApp::initialize(int& argc, char** argv)
  {
    // Main interface to the parent windowing system
    for (int argn = 1 ; argn < argc ; ++argn)  // look for vDebug switch
    {
      if (strcmp(argv[argn],"-vDebug") == 0)
      {
       // Turn them all off
       DebugState.System = 0;                  // System debug messages
       DebugState.CmdEvents = 0;               // Show command events (buttons, etc.)
       DebugState.MouseEvents = 0;             // Show mouse events
       DebugState.WindowEvents = 0;            // Window events (resize, etc.)
       DebugState.Build = 0;                   // Define/Build window
       DebugState.BadVals= 0;                  // Error values
       DebugState.Misc = 0;                    // Misc stuff
       DebugState.Text = 0;                    // Text events
       DebugState.Constructor = 0;             // Show constructors
       DebugState.Destructor = 0;              // Show destructors
       DebugState.User = 0;                    // Debug user events
       DebugState.UserApp1 = 0;                // Level 1 User App
       DebugState.UserApp2 = 0;                // Level 2 User App
       DebugState.UserApp3 = 0;                // Level 3 User App
       DebugState.OS2Dev = 0;                  // OS/2 Development Debug
       for (char* cp = argv[argn+1] ; *cp ; ++cp)
       {
         switch (*cp)
         {
           case 'S':
             DebugState.System = 1;            // System debug messages
             break;
           case 'c':
             DebugState.CmdEvents = 1; // Show command events (buttons, etc.)
             break;
           case 'm':
             DebugState.MouseEvents = 1;       // Show mouse events
             break;
           case 'w':
             DebugState.WindowEvents = 1;      // Window events (resize, etc.)
             break;
           case 'b':
             DebugState.Build = 1;             // Define/Build window
             break;
           case 'v':
             DebugState.BadVals= 1;            // Error values
             break;
           case 'o':
             DebugState.Misc = 1;              // (Other) Misc stuff
             break;
           case 't':
             DebugState.Text = 1;              // Text events
             break;
           case 'C':
             DebugState.Constructor = 1;       // Show constructors
             break;
           case 'D':
             DebugState.Destructor = 1;        // Show destructors
             break;
           case 'x':
             DebugState.OS2Dev = 1;            // OS/2 Development
             break;
           case 'U':
             DebugState.User = 1;              // Debug user events
             break;
           case '1':
             DebugState.UserApp1 = 1;  // Level 1 User App
             break;
           case '2':
             DebugState.UserApp2 = 1;  // Level 2 User App
             break;
           case '3':
             DebugState.UserApp3 = 1;        // Level 3 User App
             break;
         }
       }

       // Now fixup the argument list and break loop
       int ia;
       for (ia = argn ; ia < argc ; ++ia)
         argv[ia] = argv[ia+2];
       argv[ia] = 0;
       argc -= 2;      // eat the -vDebug args
       break;
      }
    }
    // This is pretty much the standard OS/2 startup code
    // required by all OS/2 applications.
    // We create an empty invisible frame to get
    // things going, then defer to vWindow to generate
    // useful visible windows.

    // register the client window class
    WinRegisterClass(_hab,
                    szvWindowClass,                    // window-class name
                    (PFNWP)&wpWindowProc,              // window-procedure ID
                    CS_SIZEREDRAW,                     // default window style
                    4L);                               // reserved storage

    // define frame window attributes
    ULONG flCtlData= FCF_SYSMENU        |        // system menu
                    FCF_TITLEBAR       |        // titlebar
                    FCF_SIZEBORDER     |        // resizeable border
//                  FCF_MENU           |        // application menu
                    FCF_ICON           |        // associate icon
//                  FCF_TASKLIST       |        // app name in tasklist
//                  FCF_ACCELTABLE     |        // use an accelerator table
//                  FCF_MINMAX         |        // min/max buttons
                    FCF_SHELLPOSITION;          // default window size & position

    // create the empty frame window
    _Frame = WinCreateStdWindow(HWND_DESKTOP,     // parent is desktop
                              0,                    // frame window style
                              &flCtlData,           // frame creation flags
//                            (PSZ)szWindowClass,   // client-window class name
                              NULL,                 // suppress client window creation
                              _name,                // title bar text
                              0,                    // client-window style
                              (HMODULE)NULL,        // resource ID
                              vID_FRAME,            // frame-window ID
                              0);                   // suppress client-window handle

    _vHandle = _Frame;
    SysDebug1(Build,"vApp::Initialize (dummy) _Frame = %u\n", _Frame)


/*
    // center and size the window
    // find the screen size for the system
    LONG lDisplayWidth = ::WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
    LONG lDisplayHeight = ::WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
    // now set the origin and width to center the window on the screen
    // if not using default values
    if (_frameHeight <=0 || _frameWidth <=0 )
    {
      WinSetWindowPos(_Frame,
                     HWND_TOP,
                     (lDisplayWidth - _frameWidth)/2,
                     (lDisplayHeight - _frameHeight)/2,
                     _frameWidth,
                     _frameHeight,
                     SWP_SIZE | SWP_MOVE     |
                     SWP_SHOW | SWP_ACTIVATE  );
    }
*/

    // we get the system codepage it will be needed for menu templates
    HPS hpsTemp = WinGetPS(_Frame);
    _codePage = GpiQueryCp(hpsTemp);
    WinReleasePS(hpsTemp);

    hAccel = 0;                                // No accelerators
    _workTimer = 0;

//    WinUpdateWindow(_Frame);
    _running = 1;
  }

//======================>>> vApp::~vApp <<<=======================
  vApp::~vApp()
  {
    // WARNING! This destructor never gets called automatically,
    // at least with Borland C++ 4.5.
    SysDebug(Destructor,"vApp::~vApp destructor\n")

    if (_clipText != 0)
      delete [] _clipText;

    _destroyed = 1;
  }

//======================>>> vApp::Exit <<<=======================
  void vApp::Exit(void)
  {
    // Close All registered windows and exit
    WindList *curWin;
    vWindow *tmp;

    SysDebug(Build,"vApp::Exit()\n")
/*
//  this code was moved to doEventLoop()
    if (_workTimer)		// Stop events first
    {
      _workTimer->TimerStop();
      delete _workTimer;
      _workTimer = 0;
    }
*/
    _inExit = 1;                       // Kludge - CloseAppWin needs this
    for (curWin = _WindowList ; curWin !=0 ; curWin = _WindowList)
    {
      int retv;
      tmp = curWin->window;

      if (IsHelpWin(tmp))
	retv = CloseHelpWin(tmp);
      else
      {
	retv = CloseAppWin(tmp);       // use local or derived close app
      }
      if (!retv)
      {
	_inExit = 0;
	return;
      }
    }
    _inExit = 0;                       // done now, so can exit
    AppExit(0);
  }

//======================>>> vApp::AppExit <<<=======================
  void vApp::AppExit(int exitVal)
  {
    if (!_inExit)              // Only ONE, please!
    {
       WinPostQueueMsg(_hmq, WM_QUIT, (void*) exitVal ,0);
    }
  }

//========================>>> vApp::CheckEvents <<<=======================
// This should probably be handled with threads in OS/2.
  void vApp::CheckEvents()
  {
    QMSG qmsg;

    while (WinPeekMsg(_hab, &qmsg, 0, 0, 0, PM_REMOVE))
    {
      WinDispatchMsg(_hab,&qmsg);
    }
  }

//==================>>> vApp::CloseLastCmdWindow <<<=======================
  void vApp::CloseLastCmdWindow(vWindow* win, int exitcode)
  {
    // This method, new in 1.21, allows an empty MDI frame for
    // Windows. Default behavior is to exit on last close. To
    // overide, the derived CloseLastCmdWindow should simply return.

    AppExit(0);
  }

//==================>>> vApp::ClipboardSetText <<<=======================
  int vApp::ClipboardSetText(VCONST char* text) VCONST
  {
    // set the system clipboard to the value in text

    int retval = 1;
    int lines = 0;  // how many lines in text

    // count number of lines and leave extra space for /n
    for (VCONST char *tp = text ; *tp ; ++tp)
      if (*tp == '\n')
	++lines;  // count up lines

    char* temp = new char[strlen(text)+lines+4]; // space for txt
    char* to = temp;
    char VCONST *from = text;
    // this routine does a unix2dos sort of thing
    // *to and *temp are the same thing
    // *from and *text are the same thing
    while (*from)
    {
      if (*from != '\r')	// all except CRs
      {
	if (*from == '\n')	// force all cr/lf or lf to cr/lf
	  *to++ = '\r';
	*to++ = *from;
      }
      ++from;
    }
    *to = 0;			// terminate
    int len = strlen(temp);	// how long string really is

    // Now, copy to system clipboard
    PVOID hMem;
    DosAllocSharedMem( &hMem, NULL, len+1, PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE );
    if (hMem != 0)
    {
      strcpy((char*) hMem, temp);	// copy our string
      if (WinOpenClipbrd(_hab))
      {
	if (WinEmptyClipbrd(_hab))
	{
	  WinSetClipbrdOwner (_hab, _Frame);
	  WinSetClipbrdData (_hab, (ULONG) hMem, CF_TEXT, CFI_POINTER);
	}
	WinCloseClipbrd(_hab);
      }
      else
      {
	// couldn't grab the clipboard so free up mem and punt
	DosFreeMem (hMem);
	retval = 0;
      }
    }
    else
      retval = 0;

    delete [] temp;
    return retval;
  }

//====================>>> vApp::ClipboardGetText <<<=====================
  char* vApp::ClipboardGetText() VCONST
  {
    // return a pointer to the clipboard text - we will allocate
    // space for it as needed

    if (!WinOpenClipbrd(_hab))
	return 0;

    if (_clipText != 0)			// had some previous data
	delete [] _clipText;
    _clipText = 0;			// make 0 for default return

    PVOID hMem = (PVOID) WinQueryClipbrdData(_hab, CF_TEXT);

    if (hMem != NULL)			// Got some text data
    {
      char* str = (char*) hMem;
      _clipText = new char[strlen(str)+1];
      strcpy(_clipText, str);
    }
    WinCloseClipbrd (_hab);
    return _clipText;
  }

//====================>>> vApp::ClipboardCheckText <<<===================
  int vApp::ClipboardCheckText() VCONST
  {
    ULONG format;
    // check if text available on system clipboard
    return WinQueryClipbrdFmtInfo (_hab, CF_TEXT, &format);
  }

//====================>>> vApp::ClipboardClear <<<========================
  void vApp::ClipboardClear() VCONST
  {
    // Clear out the clipboard
    if (WinOpenClipbrd(_hab))
    {
      WinEmptyClipbrd(_hab);
      WinCloseClipbrd(_hab);
    }
  }

//========================>>> vApp::CloseAppWin <<<=======================
  int vApp::CloseAppWin(vWindow* win)
  {
    SysDebug(Build,"vApp::CloseAppWin()\n");
    win->CloseWin();                   // let the window close itself

    unregisterWindow(win);             // take it off the list
    delete win;                        // free the window
    return 1;
  }

//========================>>> vApp::CloseHelpWin <<<=======================
  int vApp::CloseHelpWin(vWindow* win)
  {
    SysDebug(Build,"vApp::CloseHelpWin()\n");
    win->CloseWin();                   // let the window close itself
    unregisterWindow(win);             // take it off the list
    delete win;                        // free the window
    return 1;
  }

//========================>>> vApp::IsHelpWin <<<=======================
  int vApp::IsHelpWin(vWindow *Win)
  {
    WindList *curWin;
    for (curWin = _WindowList ; curWin !=0 ; curWin = curWin->nextWinList)
    {
      if (curWin->window == Win)
      {
        return (curWin->info == 0); // Help if 0
      }
    }
    return 0;
  }

//===========================>>> vApp::AppCommand <<<=========================
  void vApp::AppCommand(vWindow* win, ItemVal id, ItemVal retval, CmdType ctype)
  {
    // Do nothing by default.
    SysDebug1(CmdEvents,"vApp::AppCmd(id: %d)\n",id);

    if (id == M_Exit)
	Exit();
  }

//========================>>> vApp::GetDefaultFont <<<=======================

  vFont vApp::GetDefaultFont()
  {
    vFont sysF(vfDefaultSystem);       // construct a system font instance
    return sysF;                       // return it
  }

//========================>>> vApp::getAppWinInfo <<<=======================
  vAppWinInfo* vApp::getAppWinInfo(vWindow *Win)
  {
    // Search list to find associated vAppWinInfo.
    WindList *curWin;
    for (curWin = _WindowList ; curWin !=0 ; curWin = curWin->nextWinList)
      {
       if (curWin->window == Win)
         {
           return curWin->info;        // return assocated info ptr
         }
      }
    return 0;
  }

//===========================>>> vApp::KeyIn  <<<===========================
  void vApp::KeyIn(vWindow* win, vKey key, unsigned int shift)
  {
    // Do nothing by default.
    SysDebug(Misc,"vApp::KeyIn\n");
  }

//========================>>> vApp::NewAppWin <<<=======================
  vWindow* vApp::NewAppWin(vWindow* win, VCONST char* name, int w, int h,
       vAppWinInfo* winInfo)
  {
    // The derived vApp needs to call this.
    vWindow* thisWin = win;
    vAppWinInfo* awinfo = winInfo;
    SysDebug1(Build,"vApp::NewAppWin(%s)\n",name);
    if (!thisWin)              // Not created
       thisWin = new vCmdWindow(name, w, h);
    if (!winInfo)
       awinfo = new vAppWinInfo(name);
    registerWindow(thisWin, awinfo);   // register this window
    return thisWin;
  }
//========================>>> vApp::NewHelpWin <<<=======================
  vWindow* vApp::NewHelpWin(vWindow* win, VCONST char* name, int w, int h)
  {
    vWindow* thisWin = win;
    SysDebug1(Build,"vApp::NewHelpWin(%s)\n",name);
    if (!thisWin)              // Not created
       return 0;
    registerWindow(thisWin, 0);        // register this window
    return thisWin;
  }

//========================>>> vApp::registerWindow <<<=======================
  void vApp::registerWindow(vWindow *Win, vAppWinInfo *awinfo)
  {
    WindList* newList = new WindList;  // new cell to add to list
    SysDebug1(Misc,"vApp::registerWindow - %s\n",Win->name())
    newList->window = Win;                     // remember the window
    newList->info = awinfo;                    // and its info class
    newList->nextWinList = _WindowList;        // link in at front
    _WindowList = newList;
  }

//========================>>> vApp::unregisterWindow <<<=======================
  void vApp::unregisterWindow(vWindow *Win)
  {
    // Scan window list to unregister this window and free some space
    WindList *curWin, *tmp, *last, *next;
    last = 0;

    for (curWin = _WindowList ; curWin !=0 ; curWin = next)
    {
      next = curWin->nextWinList;
      if (curWin->window == Win)
      {
        SysDebug1(Misc,"vApp::unregisterWindow - %s\n",Win->name())
        tmp = curWin;
        if (curWin == _WindowList)
          _WindowList = curWin->nextWinList;
        else
          last->nextWinList = curWin->nextWinList;
        delete curWin->info;        // free the info space
        delete tmp;                 // free the list space
      }
      last = curWin;
    }
  }

//========================>>> vApp::registerCmdPane <<<======================
  void vApp::registerCmdPane(vCommandPane* cmdPane)
  {
    CmdPaneList* newList = new CmdPaneList;    // new cell to add to list
    SysDebug(Misc,"vApp::registerCmdPane\n")
    newList->commandPane = cmdPane;            // remember the cmd pane
    newList->nextCPList = _CmdPaneList;                // link in at front
    _CmdPaneList = newList;
  }

//========================>>> vApp::unregisterCmdPane <<<=======================
  void vApp::unregisterCmdPane(vCommandPane* cmdPane)
  {
    // Scan pane list to unregister this window and free some space
    CmdPaneList *curCP, *tmp, *last, *next;
    last = 0;
    for (curCP = _CmdPaneList ; curCP !=0 ; curCP = next)
    {
      next = curCP->nextCPList;
      if (curCP->commandPane == cmdPane)
      {
        SysDebug(Misc,"vApp::unregisterCmdPane\n")
        tmp = curCP;
        if (curCP == _CmdPaneList)
          _CmdPaneList = curCP->nextCPList;
        else
          last->nextCPList = curCP->nextCPList;
        delete tmp;                 // free the list space
      }
      last = curCP;
    }
  }

//========================>>> vApp::selectCmdPanes <<<=======================
/*
  void vApp::selectCmdPanes(vWindow* parent)
  {
    // This is needed by the MDI interface conventions.
    // This will turn off _isShown for panes that are children of other windows
    // and turn on all cmd panes that are in our window
    CmdPaneList *curCP;
    // First, turn off all command panes in other windows
    for (curCP = _CmdPaneList ; curCP !=0 ; curCP = curCP->nextCPList)
    {
      if ((curCP->commandPane)->_parentWin != parent)
      {
       (curCP->commandPane)->_isShown = 0;
       if (WinIsWindow(_hab, (curCP->commandPane)->_wDialog) &&
         WinIsWindowVisible((curCP->commandPane)->_wDialog))
       {
         WinShowWindow((curCP->commandPane)->_wDialog, FALSE);
       }
         }
      }
    // Now, turn ours on
    for (curCP = _CmdPaneList ; curCP !=0 ; curCP = curCP->nextCPList)
    {
      if ((curCP->commandPane)->_parentWin == parent)
      {
       (curCP->commandPane)->_isShown = 1;
       if (WinIsWindow(_hab, (curCP->commandPane)->_wDialog) &&
         !WinIsWindowVisible((curCP->commandPane)->_wDialog))
       {
         WinShowWindow((curCP->commandPane)->_wDialog, TRUE);
       }
      }
    }
  }
*/
//========================>>> vApp::SendWindowCommandAll <<<=======================
  void vApp::SendWindowCommandAll(ItemVal id, int val, CmdType ctype)
  {
    // send a command to all windows
    for (WindList* curWin = _WindowList ; curWin !=0 ; curWin = curWin->nextWinList)
      {
       (curWin->window)->WindowCommand(id, val, ctype);
      }
  }

//========================>>> vApp::UpdateAllViews <<<=======================
  void vApp::UpdateAllViews(vWindow* sender, int hint, void* pHint)
  {
    // Easy way to do MVC - call UpdateView in all windows
    // This function is called by the user whenever a change is made to
    // the model e.g the document. This causes vWindow::UpdateView to be
    // called for every open window. The parameters are used to both filter and
    // hint the windows on which actions to take in vWindow::UpdateView:
    //
    //     sender: If this is not zero, this window will not invoke UpdateView,
    //             because typically the change of model was a result of
    //             an interaction with this window.
    //     hint:   This should be an enum defined in your derived app class.
    //             Hints about which kind of change is made, so that only
    //             appropriate action is taken on appropriate windows.
    //     pHint:  This is normally a pointer to the object representing the
    //             document

    for (WindList* curWin = _WindowList ; curWin !=0 ; curWin = curWin->nextWinList)
    {
      if (curWin->window != sender)
        (curWin->window)->UpdateView(sender, hint, pHint);
    }
  }

//========================>>> vApp::SetValueAll <<<=======================
  void vApp::SetValueAll(ItemVal id, int val, ItemSetType setType)
  {
    // Set a Value in all windows
    for (WindList* curWin = _WindowList ; curWin !=0 ; curWin = curWin->nextWinList)
    {
      (curWin->window)->SetValue(id, val, setType);
    }
  }

//======================>>> vApp::SetAppTitle <<<==========================
  void vApp::SetAppTitle(VCONST char* title)
  {
    WinSetWindowText(_Frame, title);
  }

//========================>>> vApp::SetStringAll <<<=======================
  void vApp::SetStringAll(ItemVal id, VCONST char* str)
  {
    // Set a string in all windows
    for (WindList* curWin = _WindowList ; curWin !=0 ; curWin = curWin->nextWinList)
      {
       (curWin->window)->SetString(id, str);
      }
  }

//========================>>> vApp::ShowList <<<=======================
  int vApp::ShowList(void)
  {
    // This is a utility routine to show current information
//#ifdef HAS_PRINTF
    printf("Registered windows:\n");
    for (WindList* curWin = _WindowList ; curWin !=0 ; curWin = curWin->nextWinList)
    {
      printf("    %s\n",(curWin->window)->name());
    }
//#endif
    return 1;
  }

//====================>>> _appWorkTimer::TimerTick <<<====================
  void _appWorkTimer::TimerTick()
  {
   theApp->DispatchWork();
  }

//========================>>> vApp::EnableWorkSlice <<<====================
  int vApp::EnableWorkSlice(long slice)
  {
    if (slice > 0)
    {
      if (_workTimer == 0)            // First time to start timer
      {
        _workTimer = new _appWorkTimer;
      }
      return _workTimer->TimerSet(slice);
    }
    else
    {
      if (_workTimer)
        _workTimer->TimerStop();
    }
    return 1;
  }

//========================>>> vApp::DispatchWork <<<=======================
  void vApp::DispatchWork(void)
  {
    WorkSlice();               // Work Slice for App
    // Call WorkSlice for all windows
    for (WindList* curWin = _WindowList ; curWin !=0 ;
            curWin = curWin->nextWinList)
    {
      (curWin->window)->WorkSlice();
    }
  }

//========================>>> vApp::doEventLoop <<<=======================
  int vApp::doEventLoop(void)
  {
    // This is where we grab and handle events from the
    // parent windowing system
    QMSG qmsg;

    while (WinGetMsg(_hab, &qmsg, 0, 0, 0))
    {
      // timer events come thru here.  We look for those events not
      // assigned to a particular window and send them to vTimerProc
      // for handling.
      if (qmsg.msg == WM_TIMER)
      {
        if (qmsg.hwnd == 0)
        {
          SHORT idTimer = SHORT1FROMMP(qmsg.mp1);
          vTimerProc(qmsg.hwnd, qmsg.msg, idTimer, (ULONG) qmsg.mp2);
        }
      }
      WinDispatchMsg (_hab, &qmsg);
    }

    // Turns out that the destructor for the static
    // vApp object never gets called once PostQuitMessage is called. We do come
    // through here, however, so here is where we can return these
    // resources.
    if (_workTimer)
    {
      _workTimer->TimerStop();
      delete _workTimer;
    }

    WinDestroyWindow (_Frame);
    return (int) qmsg.mp1;
  }

//===========================>>> wpWindowProc <<<================================
  MRESULT EXPENTRY wpWindowProc(HWND hwnd, ULONG message,
                               MPARAM mp1, MPARAM mp2)
  {
    static vWindow* createThis = 0;
    vWindow* thisWin = (vWindow*) WinQueryWindowPtr(hwnd, 0);

    if (!thisWin && message == WM_CREATE)
    {
      // We need to intercept the message here to recover the vWindow
      // this of the window. It is sent in mp1
      createThis = thisWin = (vWindow*)mp1;   // remember the this!
    }
    else if (createThis != 0 && message == WM_ACTIVATE)
    {
      thisWin = createThis;
      createThis = 0;
 //     WinSetActiveWindow (HWND_DESKTOP, hwnd);
    }

    if (!thisWin)
    {
      return WinDefWindowProc(hwnd, message, mp1, mp2);
    }
    else
    {
      return (MRESULT) thisWin->vWindowProc(hwnd, message, mp1, mp2);
    }
  }

//#########################################################################
// The user function AppMain is accessed through a pointer. This function
// stores the user's AppMain in a pointer to function that is used inside
// the V library. This was added to enable V Win32 DLL. It works also
// with a static library.
//
V_EXPORT void vRegisterAppMain( int (*p)(int, char**) )
{
  pAppMain = p;
}

// Actually we should return vApp*, but VC++ complains that it
// can't find vGetApp when linking the application.
ULONG vGetApp()
{
  return (ULONG) theGlobalApp;
}

//=========================>>> CMain <<<====================================
// this is just chained from main in vstartup.cpp
  int CMain(int argc, char** argv)
  {
    int retcode;

    // initialize app anchor block and message queue
    theApp->_hab = WinInitialize( 0 );
    if( theApp->_hab )
      theApp->_hmq = WinCreateMsgQueue( theApp->_hab, 0 );

    if( theApp->_hmq )
    {
      theApp->initialize(argc, argv);              // Create top level widget
      if ((retcode = pAppMain(argc, argv)) == 0)   // call the app main program
        theApp->doEventLoop();                     // And enter the event loop
    }

    // cleanup and exit
    if( theApp->_hmq )
       WinDestroyMsgQueue( theApp->_hmq );
    if( theApp->_hab )
       WinTerminate( theApp->_hab );
    return (retcode);
  }

// Utilities
//=========================>>> vSysWarning <<<============================
  void vSysWarning(VCONST char* msg)
  {
    WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, msg,
                 "V", 1, MB_ICONEXCLAMATION | MB_OK);
  }

//=========================>>> vSys <<<============================
  void vSysError(VCONST char* msg)
  {
    WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, msg,
                 "V", 1, MB_ICONEXCLAMATION | MB_OK);
    theApp->AppExit(99);
  }

