/****************************************************************************
**
**  Name:  ldrprog.c
**
**  Description:
**     This is the source code to support the Loader Progress Indicator
**     and Load Information dialogs.  It uses "generic" progress indicator
**     code in gauge.c and g3d.c, which originally came from the Microsoft
**     Developer's Network CD under the name "zYzgauge".  
**
**  $Log:   S:/tbird/arcppc/cliulib/ldrprog.c_v  $
** 
**    Rev 1.0   17 Jan 1997 09:11:42   kevin
** Initial revision.
** 
**    Rev 1.0   03 Jun 1996 11:15:26   gene
** Initial revision.
** 
**    Rev 1.0   07 Sep 1995 10:08:28   gene
** Initial revision.
** 
**    Rev 1.4   17 Sep 1993 15:23:56   ron
** Needed to return TRUE after EndDialog
** 
**    Rev 1.3   10 Sep 1993 13:59:26   ron
** changed HANDLE to LONG for parameters passed by Actor
** 
**    Rev 1.2   31 Aug 1993 09:58:18   ron
** added SetToolBarHandle and GetToolBarHandle
** 
**    Rev 1.1   30 Aug 1993 10:47:32   ron
** changes for Load Progress dialog
** 
**  $Header:   S:/tbird/arcppc/cliulib/ldrprog.c_v   1.0   17 Jan 1997 09:11:42   kevin  $
**
**  Copyright (C) 1992 Microtek International.  All rights reserved.
**
*****************************************************************************/

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

#ifndef _BASEWIND_
#include "basewind.h"
#endif

#ifndef _VERSIONS_
#include "versions.h"
#endif

#ifndef _HLPENTRY_
#include "hlpentry.h"
#endif

#ifndef _CLIULIB_
#include "cliulib.h"
#endif

#ifndef _LDRSVR_
#include "ldrsvr.h"
#endif

#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif

#include <windows.h>
#include <stdio.h>
#include "gauge.h"

#ifndef _LDRPROG_
#include "ldrprog.h"
#endif

#define IDHELP  8

                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/



static BOOL inDialog = FALSE;
static HANDLE hProgressDlg = 0;
static LDRPROC callBackFn;
static BOOL loadComplete = 0; 
static WORD wCount = 0; 
static LONG lCount = 0L;
static char buf1[128];
static BOOL inThere = FALSE;
static BOOL cancelButtonPressed = FALSE;
static HANDLE hCancelButton; /* handle of cancel button subwindow */
static HANDLE hHelpButton; /* handle of cancel button subwindow */

LDRSTATBLOCK FAR *lstb;
HANDLE parentWindow;

extern HANDLE  hInstance; /* from LibMain */

                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/

BOOL FAR PASCAL tgLdrProgressDlgProc(HANDLE, WORD, WORD, LONG);
BOOL FAR PASCAL tgLdrInfoDlgProc(HANDLE, WORD, WORD, LONG);
int PASCAL MakeProgWindow(void);

                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/


/*--------------------------------------------------------------------------
**  tgSetControlParams
**
**  Purpose:
**     Set up the appearance of the "gauge" progress indicator.
**
**  Input:
**     hwnd:       The window handle.
**     wOrient:    The orientation (vertical/horizontal).
**     wFace:      The width of the bezel face.
**     wEdge:      The width of the bezel edge.
**     rgbBkColor: The background color of the gauge.
**     rgbFgColor: The foreground color of the gauge.
**
**  Output:
**     None.
----------------------------------------------------------------------------*/

void FAR PASCAL tgSetControlParams(HANDLE hwnd, WORD wOrient, WORD wRange,
                                    WORD wFace, WORD wEdge, LONG rgbBkColor,
                                    LONG rgbFgColor)
{
    /* set the orientation according to lParam value */
    SendMessage(hwnd, G_SETORIENTATION, wOrient, 0);

    /* set the range to some oddball value (demo) */
    SendMessage(hwnd, G_SETRANGE, wRange, 0);

    /* set the bezel face to some reasonable width */
    SendMessage(hwnd, G_SETBEZELFACE, wFace, 0);

    /* set the bezel edge to some width */
    SendMessage(hwnd, G_SETWIDTH3D, wEdge, 0);

    /* set the fore and back colors to cool values */
    SendMessage(hwnd, G_SETBKCOLOR, 0, rgbBkColor);
    SendMessage(hwnd, G_SETFGCOLOR, 0, rgbFgColor);
} /* tgSetControlParams() */


/** BOOL FAR PASCAL tgLdrProgressDlgProc(HANDLE, WORD, WORD, LONG)
 *
 *  DESCRIPTION: 
 *      This is the test dialog box for the Gauge control.  What it
 *      does is start a timer 
 *
 *  NOTES:
 *
 ** cjp */


/*--------------------------------------------------------------------------
**  tgLdrProgressDlgProc
**
**  Purpose:
**     Load Progress dialog procedure for PowerViews.  The tricky part is
**  that we can't let Windows do a task swap during the load, so we have
**  to set up the dialog, post a message to ourselves (PROGRESS_GO), handle
**  the initial painting (etc), and then in the processing of PROGRESS_GO
**  we call the loader, which uses SendMessage to increment the progress
**  indicator.  We change the Cancel button to an OK button when it is
**  done, and we enable Help only when it's done.  We use PeekMessage to
**  check for a message to the Cancel button during processing, and the
**  loader code will confirm the cancel operation with by calling the
**  ErrMessageBox() function.
**
**  Input:
**     hDlg:  The window handle.
**     uMsg:  The current message.
**     wParam:  The word message param.
**     lParam:  The long message param.
**
**  Output:
**     None.
----------------------------------------------------------------------------*/
BOOL FAR PASCAL tgLdrProgressDlgProc(HANDLE   hdlg,
                                     WORD   uMsg,
                                     WORD wParam,
                                     LONG lParam)
{
    MSG msg;
    switch (uMsg)
    {
        case WM_INITDIALOG:
            if (inThere) {
               break;
            }
            cancelButtonPressed = FALSE;
            hProgressDlg = hdlg;
            /* set up all of the gauges */
            tgSetControlParams(GetDlgItem(hdlg, IDD_GAUGE),
                    G_ORIENT_LEFTTORIGHT, TG_RANGE, 2, 2,
                    RGB(128, 128, 255), RGB(0, 0, 255));

            /* I want the focus on the cancel button */
            hCancelButton = GetDlgItem(hdlg, IDCANCEL);
            hHelpButton = GetDlgItem(hdlg, IDHELP);
            EnableWindow(hHelpButton, FALSE);
            SetFocus(hCancelButton);
            SetDlgItemText(hdlg, DLGMODULES, (LPSTR) "Modules:");
            SetDlgItemText(hdlg, DLGBYTES, (LPSTR) "Bytes:");
            SetDlgItemText(hdlg, DLGSYMBOLS, (LPSTR) "Symbols:");
            SetDlgItemText(hdlg, DLGLINES, (LPSTR) "Lines:");

            loadComplete = FALSE;
            /* call back (args saved in ldr.c) */
            SendMessage(hdlg, WM_COMMAND, ID_PROGRESS_INC, 0);
            InvalidateRect(hdlg, NULL, 0);
            UpdateWindow(hdlg);
            PostMessage(hdlg, WM_COMMAND, ID_PROGRESS_GO, 0);
            break;

        case WM_COMMAND:
            if ((wParam == IDOK) || (wParam == IDCANCEL))
            {
                EndDialog(hdlg, (wParam == IDOK));
                return (TRUE);
            }
            else if (wParam == IDHELP) {
               WinHelp(hdlg, "PWRVIEWS.HLP",
                  HELP_CONTEXT, HE_LOAD_PROGRESS);
               return (TRUE);
            } 
            else if ((wParam == ID_PROGRESS_INC) || 
                   (wParam == ID_PROGRESS_DONE)) {
               if (wParam == ID_PROGRESS_DONE) {
                  loadComplete = TRUE;
               }
               SetFocus(GetDlgItem(hdlg, IDCANCEL));
               lCount = 0L;
               if (lstb->loadFileSize > 0) {
                  lCount = (100L * lstb->curLocation) / lstb->loadFileSize;
               }
               if (lCount >= 100 || loadComplete) {
                  wCount = 100;
               }
               else {
                  wCount = (WORD) lCount;
               }
               /* set the position to +1 of the previous value */
               if (lstrlen(lstb->curLoadFile) > 0) {
                  sprintf(buf1, "%s", lstb->curLoadFile);
                  SetDlgItemText(hdlg, DLGLOADFILE, (LPSTR) buf1);
               }
               SendMessage(GetDlgItem(hdlg, IDD_GAUGE),
                                G_SETPOSITION, wCount, 0);
               if (lstrlen(lstb->curModule) > 0) {
                  sprintf(buf1, "%s", lstb->curModule);
                  SetDlgItemText(hdlg, DLGMODULE, (LPSTR) buf1);
               }
               else {
                  SetDlgItemText(hdlg, DLGMODULE, (LPSTR) " ");
               }
               sprintf(buf1, "%d", lstb->numModules);
               SetDlgItemText(hdlg, DLGMODULESLOADED, (LPSTR) buf1);
               sprintf(buf1, "%ld", lstb->numBytes);
               SetDlgItemText(hdlg, DLGBYTESLOADED, (LPSTR) buf1);
               sprintf(buf1, "%ld", lstb->numSymbols);
               SetDlgItemText(hdlg, DLGSYMBOLSLOADED, (LPSTR) buf1);
               sprintf(buf1, "%ld", lstb->numTypes);
               SetDlgItemText(hdlg, DLGTYPESLOADED, (LPSTR) buf1);
               sprintf(buf1, "%ld", lstb->numFunctions);
               SetDlgItemText(hdlg, DLGFUNCSLOADED, (LPSTR) buf1);
               sprintf(buf1, "%ld", lstb->numLines);
               SetDlgItemText(hdlg, DLGLINESLOADED, (LPSTR) buf1);

               if (wParam == ID_PROGRESS_DONE) {
                  SetWindowText(hdlg, (LPSTR) "Load Complete");
                  SetDlgItemText(hdlg, DLGSTARTPC, (LPSTR) "PC:");
                  if (lstb->startPC > 0) {
                     sprintf(buf1, "0x%lX", lstb->startPC);
                     SetDlgItemText(hdlg, DLGSTARTPCNUM, (LPSTR) buf1);
                  }
                  else {
                     SetDlgItemText(hdlg, DLGSTARTPCNUM, (LPSTR) "Unknown");
                  }
                  SetDlgItemText(hdlg, DLGSTACKBASE, (LPSTR) "Stack Base:");
                  if (lstb->stackBase > 0) {
                     sprintf(buf1, "0x%lX", lstb->stackBase);
                     SetDlgItemText(hdlg, DLGSTACKBASENUM, (LPSTR) buf1);
                  }
                  else {
                     SetDlgItemText(hdlg, DLGSTACKBASENUM, (LPSTR) "Unknown");
                  }
                  SetDlgItemText(hdlg, DLGSTACKSIZE, (LPSTR) "Stack Size:");
                  if (lstb->stackSize > 0) {
                     sprintf(buf1, "0x%lX", lstb->stackSize);
                     SetDlgItemText(hdlg, DLGSTACKSIZENUM, (LPSTR) buf1);
                  }
                  else {
                     SetDlgItemText(hdlg, DLGSTACKSIZENUM, (LPSTR) "Unknown");
                  }
                  SetDlgItemText(hdlg, DLGMODULE, (LPSTR) " ");
                  SetDlgItemText(hdlg, IDCANCEL, (LPSTR) "&OK");
                  EnableWindow(hHelpButton, TRUE);
               }
               else {
                  /* See if the Cancel button has been sent a buttondown */
                  if (PeekMessage((LPMSG) &msg, hCancelButton, WM_LBUTTONDOWN, 
                        WM_LBUTTONUP, PM_REMOVE | PM_NOYIELD)) {
                     cancelButtonPressed = TRUE;
                  }
                  else if (PeekMessage((LPMSG) &msg, hCancelButton, WM_SYSKEYDOWN, 
                        WM_SYSKEYDOWN, PM_REMOVE | PM_NOYIELD)) {
                     /*
                      * The reason for using WM_SYSKEYDOWN rather than
                      * WM_SYSCHAR is that I don't think WM_SYSCHAR is
                      * generated under these circumstances.  (Doesn't
                      * happen under tdw, at least.)
                      */
                     if(msg.wParam == VK_MENU) {
                        cancelButtonPressed = TRUE;
                     }
                  }
                  if (PeekMessage((LPMSG) &msg, hCancelButton, WM_KEYDOWN, 
                        WM_KEYDOWN, PM_REMOVE | PM_NOYIELD)) {
                     if (msg.wParam == VK_RETURN) {
                        cancelButtonPressed = TRUE;
                     }
                  }
               }
            }
            else if (wParam == ID_PROGRESS_GO) {
               /* prevent re-entrancy (firewall) */
               if (!inThere) {
                  inThere = TRUE;
                  callBackFn(0, 0, 0, 0L);
                  inThere = FALSE;
               }
            }
            break;
    }
    return (FALSE);
} /* tgLdrProgressDlgProc() */

/*--------------------------------------------------------------------------
**  tgLdrInfoDlgProc
**
**  Purpose:
**     Load Information dialog procedure for PowerViews.  This is a much
**  simplified version of the above -- the only difference in the dialogs
**  is that this one doesn't have a gauge control and doesn't have a
**  current module.
**
**  Input:
**     hDlg:  The window handle.
**     uMsg:  The current message.
**     wParam:  The word message param.
**     lParam:  The long message param.
**
**  Output:
**     None.
----------------------------------------------------------------------------*/
BOOL FAR PASCAL tgLdrInfoDlgProc(HANDLE   hdlg,
                                 WORD   uMsg,
                                 WORD wParam,
                                 LONG lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
            SetDlgItemText(hdlg, DLGMODULES, (LPSTR) "Modules:");
            SetDlgItemText(hdlg, DLGBYTES, (LPSTR) "Bytes:");
            SetDlgItemText(hdlg, DLGSYMBOLS, (LPSTR) "Symbols:");
            SetDlgItemText(hdlg, DLGLINES, (LPSTR) "Lines:");
            if (lstrlen(lstb->curLoadFile) > 0) {
               sprintf(buf1, "%s", lstb->curLoadFile);
               SetDlgItemText(hdlg, DLGLOADFILE, (LPSTR) buf1);
            }
            sprintf(buf1, "%d", lstb->numModules);
            SetDlgItemText(hdlg, DLGMODULESLOADED, (LPSTR) buf1);
            sprintf(buf1, "%ld", lstb->numBytes);
            SetDlgItemText(hdlg, DLGBYTESLOADED, (LPSTR) buf1);
            sprintf(buf1, "%ld", lstb->numSymbols);
            SetDlgItemText(hdlg, DLGSYMBOLSLOADED, (LPSTR) buf1);
            sprintf(buf1, "%ld", lstb->numTypes);
            SetDlgItemText(hdlg, DLGTYPESLOADED, (LPSTR) buf1);
            sprintf(buf1, "%ld", lstb->numFunctions);
            SetDlgItemText(hdlg, DLGFUNCSLOADED, (LPSTR) buf1);
            sprintf(buf1, "%ld", lstb->numLines);
            SetDlgItemText(hdlg, DLGLINESLOADED, (LPSTR) buf1);
            SetDlgItemText(hdlg, DLGSTARTPC, (LPSTR) "PC:");
            if (lstb->startPC > 0) {
               sprintf(buf1, "0x%lX", lstb->startPC);
               SetDlgItemText(hdlg, DLGSTARTPCNUM, (LPSTR) buf1);
            }
            else {
               SetDlgItemText(hdlg, DLGSTARTPCNUM, (LPSTR) "Unknown");
            }
            SetDlgItemText(hdlg, DLGSTACKBASE, (LPSTR) "Stack Base:");
            if (lstb->stackBase > 0) {
               sprintf(buf1, "0x%lX", lstb->stackBase);
               SetDlgItemText(hdlg, DLGSTACKBASENUM, (LPSTR) buf1);
            }
            else {
               SetDlgItemText(hdlg, DLGSTACKBASENUM, (LPSTR) "Unknown");
            }
            SetDlgItemText(hdlg, DLGSTACKSIZE, (LPSTR) "Stack Size:");
            if (lstb->stackSize > 0) {
               sprintf(buf1, "0x%lX", lstb->stackSize);
               SetDlgItemText(hdlg, DLGSTACKSIZENUM, (LPSTR) buf1);
            }
            else {
               SetDlgItemText(hdlg, DLGSTACKSIZENUM, (LPSTR) "Unknown");
            }
            SetDlgItemText(hdlg, DLGMODULE, (LPSTR) " ");
            SetDlgItemText(hdlg, IDCANCEL, (LPSTR) "&OK");

            break;

        case WM_COMMAND:
            if ((wParam == IDOK) || (wParam == IDCANCEL)) {
                EndDialog(hdlg, (wParam == IDOK));
                return (TRUE);
            }
            else if (wParam == IDHELP) {
               WinHelp(hdlg, "PWRVIEWS.HLP",
                  HELP_CONTEXT, HE_LOAD_INFO);
               return (TRUE);
            } 
            break;
    }
    return (FALSE);
} /* tgLdrInfoDlgProc() */


/*--------------------------------------------------------------------------
**  MakeProgWindow
**
**  Purpose:
**     Just a wrapper around the call to make the dialog box -- basically
**  just to make sure that the Gauge class initializes correctly.
**
**  Input:
**
**  Output:
**     None.
----------------------------------------------------------------------------*/
int PASCAL MakeProgWindow()
{
    FARPROC  lpfnDlgProc;

    /* initialize the Gauge class */
    if (!gaugeInit(hInstance))
        return (FALSE);

    lpfnDlgProc = MakeProcInstance(tgLdrProgressDlgProc, hInstance);
    inDialog = TRUE;
    DialogBox(hInstance, DLG_PROGRESS, parentWindow, lpfnDlgProc);
    inDialog = FALSE;
    FreeProcInstance(lpfnDlgProc);

    return (FALSE);
} /* MakeProgWindow() */

/*--------------------------------------------------------------------------
**  CliLdrProgressDialog
**
**  Purpose:
**     Create the Progress Dialog for PowerViews, and wait until
**     the Cancel/OK button is pressed.
**
**  Input:
**     parent:  The parent window handle.
**     callBack:  The function to call back, in the loader.  This is only
**                here because of load order dependencies in the DLLs.
**
**  Output:
**     None.  
----------------------------------------------------------------------------*/

RETCODE EXPORT CliLdrProgressDialog(HANDLE parent, LDRPROC callBack)
{

   parentWindow = parent;
   callBackFn = callBack;
   if (lstb == NULL) return(ER_INTERNAL); /* should never happen */
   
   /*
    * Clear out any info in the loader status block, now that we're
    * definately starting another load.
    */
   lstb->loadFileSize = 0L;
   lstb->curLocation = 0L;
   lstb->numSymbols = 0L;
   lstb->numBytes = 0L;
   lstb->numModules = 0;
   lstb->startPC = -1L;
   lstb->stackBase = -1L;
   lstb->stackSize = -1L;
   lstb->curModule[0] = '\0';
   lstb->curLoadFile[0] = '\0';
   lstb->numTypes = 0L;
   lstb->numFunctions = 0L;
   lstb->numLines = 0;

   MakeProgWindow();
   return(GOOD);
}

/*--------------------------------------------------------------------------
**  CliLdrProgressDialog
**
**  Purpose:
**     Create the Load Information Dialog for PowerViews, and wait until
**     the OK button is pressed.
**
**  Input:
**     parent:  The parent window handle.
**
**  Output:
----------------------------------------------------------------------------*/
RETCODE EXPORT CliLdrInfoDialog(LONG partmp)
{
   HANDLE parent;
   FARPROC  lpfnDlgProc;

   parent = (HANDLE) partmp; /* avoid problem with only signed ints in Actor */
   if (lstb == NULL) return(GOOD); /* ignore this until after a load */
   lpfnDlgProc = MakeProcInstance(tgLdrInfoDlgProc, hInstance);
   DialogBox(hInstance, DLG_LDRINFO, parent, lpfnDlgProc);
   return(GOOD);
}

/*--------------------------------------------------------------------------
**  CliLdrProgressActive
**
**  Purpose:
**     Tell the caller if the load progress dialog is active or not.
**
**  Input:
**
**  Output:
**     answer:  Return TRUE if we are doing the progress dialog.
----------------------------------------------------------------------------*/
RETCODE EXPORT CliLdrProgressActive(BOOL FAR *answer)
{
   *answer = inDialog;
   return (GOOD);
}

/*--------------------------------------------------------------------------
**  CliLdrProgressStep
**
**  Purpose:
**     Update the load progress dialog status based on the current
**  values in the loader status block.
**
**  Input:
**
**  Output:
----------------------------------------------------------------------------*/
RETCODE EXPORT CliLdrProgressStep()
{
   if (!inDialog) return (GOOD);
   SendMessage(hProgressDlg, WM_COMMAND, ID_PROGRESS_INC, 0);
   if (cancelButtonPressed) {
      cancelButtonPressed = FALSE;
      return (ER_LDR_ABORT);
   }
   return (GOOD);
}


/*--------------------------------------------------------------------------
**  CliLdrProgressDone
**
**  Purpose:
**     Update the load progress dialog status to final values, based on
**  values in the loader status block.
**
**  Input:
**     normal: if true, update and keep dialog up until OK is pressed.
**             Otherwise remove the dialog immediately.
**
**  Output:
----------------------------------------------------------------------------*/
RETCODE EXPORT CliLdrProgressDone(BOOL normal)
{
   if (!inDialog) return (GOOD);
   /* If this isn't a normal quit, they pressed the cancel button... */
   if (!normal) {
      SendMessage(hProgressDlg, WM_CLOSE, 0, 0);
      return (GOOD);
   }
   cancelButtonPressed = FALSE;
   SendMessage(hProgressDlg, WM_COMMAND, ID_PROGRESS_DONE, 0);
   return (GOOD);
}


/*--------------------------------------------------------------------------
**  CliLdrStatBlock
**
**  Purpose:
**     Save a pointer to the loader status block.  Needed in order to
**  prevent load order dependencies.  The status block is static data in
**  a DLL, so the pointer will not change during PowerViews operation.
**
**  Input:
**     lpstb: the pointer to the loader status block (see cliulib.h).
**
**  Output:
----------------------------------------------------------------------------*/
RETCODE EXPORT CliLdrStatBlock(LDRSTATBLOCK FAR *lpstb) 
{
   if (lpstb == NULL) return(ER_INTERNAL); /* should never happen */
   lstb = lpstb; /* save the pointer -- it's ok, it's a static DLL pointer */
   return (GOOD);
}

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