// ************************************************************************
// MODULE    : ToolBar.C
// PURPOSE   :
// FUNCTIONS :
// ************************************************************************
#define NOCREATEFONT

#ifdef DEBUG
  #define DebugOut( str ) OutputDebugString ( (LPCTSTR) __FILE__ TEXT( ": " ) TEXT( str ) TEXT( "\n" ) )
#else
  #define DebugOut( str )
#endif

#ifndef WIN32
 #define INT int
#endif

#ifndef WIN32S
 #define   UNICODE             // make the application unicode complient
#endif
#define   STRICT
#include <windows.h>

#include <stdlib.h> // For 'abs'

#ifdef WIN32
 #define GetWindowInstance(hwnd)  (HANDLE) GetWindowLong( hwnd, GWL_HINSTANCE )
 #define GetWindowID(hwnd) GetWindowLong( hwnd, GWL_ID )
#else
 #define GetWindowInstance(hwnd)  (HANDLE) GetWindowWord( hwnd, GWW_HINSTANCE )
 #define GetWindowID(hwnd) GetWindowWord( hwnd, GWW_ID )
#endif

HWND    hwndMain;
HWND    hwndTools, hwndToolText, hwndToolCombo, hwndToolButton;
HWND    hwndStatus;

HFONT   hfontTools=0;
HFONT   hfontStatus;

TEXTMETRIC tmToolFont;
TEXTMETRIC tmStatusFont;

INT     dyTools = 0, dyCombo;
INT     cxToolBorder, cyToolBorder, cntToolCtrls = 0;
INT     xCurrent = 10;
INT     dxTools;

INT     cntStatusField = 0;
INT     dyStatus, cxStatusBorder, cyStatusBorder, cxFrame, cyFrame, dyField;

HBRUSH  hbrBtnFace=0;
HBRUSH  hbrToolBar=0;
HBRUSH  hbrStatus=0;

// HANDLE  hInst=0;

#define COLOR_BLACK     RGB(0, 0, 0)
#define COLOR_WHITE     RGB(255, 255, 255)
#define COLOR_HEAVYGRAY RGB(64, 64, 64)
#define COLOR_DARKGRAY  RGB(128, 128, 128)
#define COLOR_GRAY      RGB(192, 192, 192)

COLORREF rgbFrame       = COLOR_BLACK;
COLORREF rgbBtnFace     = COLOR_GRAY;
COLORREF rgbHilite      = COLOR_WHITE;
COLORREF rgbDirectLight = COLOR_WHITE;
COLORREF rgbShadow      = COLOR_DARKGRAY;
COLORREF rgbToolBar     = COLOR_GRAY;
COLORREF rgbStatic      = COLOR_BLACK;

#define TC_SPACE 0
#define TC_LABEL 1
#define TC_COMBO 2
#define TC_BUTTON 3

#define MAXCTRLS 25
typedef struct _tagTools {
  HWND    hwnd;
  WORD    wType;
  INT     iWidth, iHeight;
  HICON   hIcon;
} Tools;
Tools  toolCtrl[MAXCTRLS];

#define MAXSTATUS 10
typedef struct _tagStatus {
        HWND    hwnd;
        INT     iMaxWidth, iMinWidth, iGiveWidth;
} Status;
Status  statusField[ MAXSTATUS ];

//-- internal prototypes
LONG CALLBACK ToolsProc       (HWND, UINT, WPARAM, LPARAM);
LONG CALLBACK MyComboProc     (HWND, UINT, WPARAM, LPARAM);
VOID UpdatePositions (HWND);

LONG CALLBACK StatusProc      (HWND, UINT, WPARAM, LPARAM);
LONG CALLBACK StatusFieldProc (HWND, UINT, WPARAM, LPARAM);

// ************************************************************************
// FUNCTION : DllEntryPoint( HINSTANCE, DWORD, LPVOID )
// PURPOSE  : DllEntryPoint is called by Windows when
//            the DLL is initialized, Thread Attached, and other times.
// COMMENTS : No initialization is needed here.
// ************************************************************************
BOOL WINAPI
DllEntryPoint( HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved )
{
  UNREFERENCED_PARAMETER( hInstDLL );
  UNREFERENCED_PARAMETER( fdwReason );
  UNREFERENCED_PARAMETER( lpvReserved );

  return( TRUE );
}

// ========================================================================
//                        TOOL BAR FUNCTIONS
// ========================================================================


// ************************************************************************
// FUNCTION : InitToolBar (HANDLE hInstance)
// PURPOSE  :
// COMMENTS :
// ************************************************************************
BOOL
InitToolBar (HANDLE hInstance)
{
  WNDCLASS    wndclass;

  hbrBtnFace = CreateSolidBrush( rgbBtnFace );
  hbrToolBar = CreateSolidBrush( rgbToolBar );

  wndclass.style         = CS_HREDRAW | CS_VREDRAW;
  wndclass.lpfnWndProc   = (WNDPROC) ToolsProc;
  wndclass.cbClsExtra    = 0;
  wndclass.cbWndExtra    = 0;
  wndclass.hInstance     = hInstance;
  wndclass.hIcon         = NULL;
  wndclass.hbrBackground = hbrToolBar;
  wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
  wndclass.lpszMenuName  = NULL;
  wndclass.lpszClassName = TEXT( "ToolBar" );

  if( !RegisterClass(&wndclass) )
    return FALSE;
}


// ************************************************************************
// FUNCTION : CreateToolBar( HWND hwnd, HANDLE hInstance, INT iId )
// PURPOSE  :
// COMMENTS :
// ************************************************************************
BOOL
CreateToolBar( HWND hwnd, HANDLE hInstance, INT iId )
{
  HWND hwndTmp;
  RECT rect;

  DebugOut( "CreateToolBar" );
  if( hbrBtnFace==0 )
    hbrBtnFace = CreateSolidBrush( rgbBtnFace );
  if( hbrToolBar==0 )
    hbrToolBar = CreateSolidBrush( rgbToolBar );
  cxToolBorder = GetSystemMetrics( SM_CXBORDER );
  cyToolBorder = GetSystemMetrics( SM_CYBORDER );
  DebugOut( "Calling CreateWindow" );
  hwndTools = CreateWindow ( TEXT( "ToolBar" ), TEXT( "ToolBar" ),
                WS_CHILD | WS_CLIPSIBLINGS | WS_BORDER | WS_VISIBLE,
                0, 0, 0, 0,
                hwnd, (HMENU) iId, hInstance, NULL );
  if( !hwndTools ) {
    DebugOut( "CreateWindow Failed" );
    return FALSE;
  }
  DebugOut( "CreateWindow suceeded" );

  /* Lets find out how big a combo box is... */
  hwndTmp = CreateWindow ( TEXT( "COMBOBOX" ), TEXT( "Combo" ),
              WS_CHILD //| WS_CLIPSIBLINGS
              | WS_VISIBLE | CBS_DROPDOWNLIST,
                      0, 0, 0, 0,
              hwndTools, NULL, hInstance, NULL);
  if( hwndTmp ) {
    // SendMessage( hwndTmp, WM_SETFONT, (UINT) hfontTools, MAKELONG (TRUE, 0) );
    GetClientRect( hwndTmp, &rect );
    dyCombo = rect.bottom - rect.top;
    DestroyWindow( hwndTmp );
  }
  else {
    dyCombo = 30; // Just for a default value
  }

  hwndMain = hwnd; // So we can pass WM_CONTROL messages back to the master parent
  hInstance; // unreferenced

  return TRUE;
}


// ************************************************************************
// FUNCTION : ToolBarHeight( HWND hwnd )
// PURPOSE  :
// COMMENTS :
// ************************************************************************
int
ToolBarHeight( HWND hwnd )
{
  RECT rect;

  GetClientRect( hwndTools, &rect );

  return( rect.bottom-rect.top );

  hwnd; // unreferenced
}


// ************************************************************************
// FUNCTION : AdjustToolBar( HWND hwnd )
// PURPOSE  :
// COMMENTS :
// ************************************************************************
BOOL
AdjustToolBar( HWND hwnd )
{
  RECT rect;

  GetClientRect (hwnd, &rect);
  MoveWindow( hwndTools,
    rect.left - cxToolBorder,
    rect.top - cyToolBorder,
    rect.right - rect.left + (cxToolBorder*2),
    dyTools,
    TRUE );

  return TRUE;
}


// ************************************************************************
// FUNCTION : UpdatePositions( HWND hwnd )
// PURPOSE  :
// COMMENTS :
// ************************************************************************
VOID
UpdatePositions( HWND hwnd )
{
  INT i, x, y, dx, dy, cnt;

  x = 10;
  for( i=0; i<cntToolCtrls; i++ ) {

   switch( toolCtrl[i].wType ) {

     case TC_SPACE:
       dx = toolCtrl[i].iWidth;
       break;

     case TC_LABEL:
       dy = toolCtrl[i].iHeight;
       y = (dyTools/2) - (dy/2) - 1;
       dx = toolCtrl[i].iWidth;
       break;

     case TC_COMBO:
       dy = toolCtrl[i].iHeight;
       y = (dyTools/2) - (dy/2) - 1;
       dx = toolCtrl[i].iWidth;
       cnt = (INT) SendMessage( toolCtrl[i].hwnd, CB_GETCOUNT,
                     (WPARAM) 0, (LPARAM) 0 );
       if( cnt > 5 )
         cnt = 5;
       dy = dy * cnt;
       break;

     case TC_BUTTON:
       dy = toolCtrl[i].iHeight;
       y = (dyTools/2) - (dy/2) - 1;
       dx = toolCtrl[i].iWidth;
       break;

     default:
       dy = toolCtrl[i].iHeight;
       y = (dyTools/2) - (dy/2) - 1;
       dx = toolCtrl[i].iWidth;
       break;

    }

    if( toolCtrl[i].wType != TC_SPACE ) {
      MoveWindow( toolCtrl[i].hwnd, x, y, dx, dy, FALSE );
    }
    x += dx;
  }

  if( hwnd == NULL ) {
    UpdateWindow( hwndTools );
  }
  else{
    UpdateWindow( hwnd );
  }
}

// ************************************************************************
// FUNCTION : AddToolSpace( INT iWidth, INT iHeight )
// PURPOSE  :
// COMMENTS :
// ************************************************************************
BOOL
AddToolSpace( INT iWidth, INT iHeight )
{
  if( cntToolCtrls >= MAXCTRLS )
    return FALSE;
  toolCtrl[cntToolCtrls].hwnd    = 0;
  toolCtrl[cntToolCtrls].wType   = TC_SPACE;
  toolCtrl[cntToolCtrls].iWidth  = iWidth;
  toolCtrl[cntToolCtrls].iHeight = iHeight;

  if( (toolCtrl[cntToolCtrls].iHeight + (6*cyToolBorder)) > dyTools ) {
    dyTools = (toolCtrl[cntToolCtrls].iHeight + (6*cyToolBorder));
  }
  UpdatePositions( NULL );
  cntToolCtrls++;

  return( TRUE );
}

// ************************************************************************
// FUNCTION : AddToolLabel( HANDLE hInst, INT iId, LPTSTR szLabel, INT iWidth, DWORD dwStyle )
// PURPOSE  :
// COMMENTS :
// ************************************************************************
HWND
AddToolLabel( HANDLE hInstance, INT iId, LPTSTR szLabel, INT iWidth, DWORD dwStyle )
{
  HDC hdc;

  if( cntToolCtrls >= MAXCTRLS )
    return( (HWND) 0 ); // No room left in our fixed array

  toolCtrl[cntToolCtrls].hwnd = CreateWindow ( TEXT( "STATIC" ), szLabel,
                                  WS_CHILD //| WS_CLIPSIBLINGS
                                  | WS_VISIBLE | dwStyle,
                                  0, 0, 0, 0,
                                  hwndTools, (HMENU)iId, hInstance, NULL );

  if( !toolCtrl[cntToolCtrls].hwnd )
    return( (HWND) 0 ); // CreateWindow failed for some reason

  // SendMessage (toolCtrl[cntToolCtrls].hwnd, WM_SETFONT, (UINT) hfontTools, MAKELONG (TRUE, 0) );
  toolCtrl[cntToolCtrls].wType = TC_LABEL;

  hdc = GetDC (hwndTools);
  if( iWidth < 0 ) {
    toolCtrl[cntToolCtrls].iWidth = tmToolFont.tmAveCharWidth * abs(iWidth);
  }
  else if( iWidth == 0 ) {
#ifdef WIN32
    SIZE size;
    GetTextExtentPoint( hdc, szLabel, lstrlen(szLabel), &size );
    toolCtrl[cntToolCtrls].iWidth = size.cx;
#else
    toolCtrl[cntToolCtrls].iWidth = LOWORD( GetTextExtent( hdc, szLabel, lstrlen(szLabel) ) );
#endif
  }
  else {
    toolCtrl[cntToolCtrls].iWidth = iWidth;
  }

  toolCtrl[cntToolCtrls].iHeight = tmToolFont.tmHeight;
  if( (toolCtrl[cntToolCtrls].iHeight + (6*cyToolBorder)) > dyTools ) {
    dyTools = (toolCtrl[cntToolCtrls].iHeight + (6*cyToolBorder));
  }
  ReleaseDC( hwndTools, hdc );
  UpdatePositions( NULL );

  return( toolCtrl[cntToolCtrls++].hwnd );
}


// ************************************************************************
// FUNCTION : AddToolCombo (HANDLE hInst, INT iId, INT iWidth, DWORD dwStyle)
// PURPOSE  :
// COMMENTS :
// ************************************************************************
HWND
AddToolCombo (HANDLE hInstance, INT iId, INT iWidth, DWORD dwStyle)
{

  if( cntToolCtrls >= MAXCTRLS )
    return( (HWND) 0 ); // No room left in our fixed array

  if( dwStyle==0 )
    dwStyle = CBS_DROPDOWNLIST;
  toolCtrl[cntToolCtrls].hwnd = CreateWindow ( TEXT( "COMBOBOX" ), TEXT( "" ),
                                  WS_CHILD | // WS_CLIPSIBLINGS |
                                  WS_VISIBLE | dwStyle,
                                  0, 0, 0, 0,
                                  hwndTools, (HMENU) iId, hInstance, NULL );

  if( !toolCtrl[cntToolCtrls].hwnd )
    return( (HWND) 0 ); // CreateWindow failed for some reason

  // SendMessage (toolCtrl[cntToolCtrls].hwnd, WM_SETFONT, (UINT)hfontTools, MAKELONG (TRUE, 0));
  toolCtrl[cntToolCtrls].wType = TC_COMBO;

  if( iWidth < 0 ) {
    toolCtrl[cntToolCtrls].iWidth = tmToolFont.tmAveCharWidth * abs(iWidth);
  }
  else if( iWidth == 0 ) {
    toolCtrl[cntToolCtrls].iWidth = tmToolFont.tmAveCharWidth * 15; // just a default width
  }
  else {
    toolCtrl[cntToolCtrls].iWidth = iWidth;
  }

  toolCtrl[cntToolCtrls].iHeight = dyCombo;
  if( (toolCtrl[cntToolCtrls].iHeight + (6*cyToolBorder)) > dyTools ) {
    dyTools = (toolCtrl[cntToolCtrls].iHeight + (6*cyToolBorder));
  }
  UpdatePositions( NULL );

  return( toolCtrl[cntToolCtrls++].hwnd );
}

// ************************************************************************
// FUNCTION : AddToolButton( HANDLE hInst, INT iId, LPTSTR szLabel, INT iWidth, INT iHeight, DWORD dwStyle )
// PURPOSE  :
// COMMENTS :
// ************************************************************************
HWND
AddToolButton( HANDLE hInstance, INT iId, LPTSTR szLabel, INT iWidth, INT iHeight, DWORD dwStyle )
{
  HDC hdc;

  if( cntToolCtrls >= MAXCTRLS )
    return( (HWND) 0 ); // No room left in our fixed array

  if( dwStyle == 0 )
    dwStyle = BS_PUSHBUTTON | BS_OWNERDRAW;
  toolCtrl[cntToolCtrls].hwnd = CreateWindow ( TEXT( "BUTTON" ), szLabel,
                                  WS_CHILD | // WS_CLIPSIBLINGS |
                                  WS_VISIBLE | dwStyle,
                                  0, 0, 0, 0,
                                  hwndTools, (HMENU)iId, hInstance, NULL );

  if( !toolCtrl[cntToolCtrls].hwnd )
    return( (HWND) 0 ); // CreateWindow failed for some reason

  // SendMessage (toolCtrl[cntToolCtrls].hwnd, WM_SETFONT, (UINT)hfontTools, MAKELONG (TRUE, 0));
  toolCtrl[cntToolCtrls].wType = TC_BUTTON;

  hdc = GetDC (hwndTools);
  SelectObject (hdc, hfontTools);
  if( iWidth < 0 ) {
    toolCtrl[cntToolCtrls].iWidth = tmToolFont.tmAveCharWidth * abs(iWidth);
    toolCtrl[cntToolCtrls].iWidth += (6*cxToolBorder);
  }
  else if( iWidth == 0 ) {
 #ifdef WIN32
    SIZE size;
    GetTextExtentPoint (hdc, szLabel, lstrlen(szLabel), &size);
    toolCtrl[cntToolCtrls].iWidth = size.cx;
 #else
    toolCtrl[cntToolCtrls].iWidth = LOWORD(GetTextExtent (hdc, szLabel, lstrlen(szLabel)));
 #endif
    toolCtrl[cntToolCtrls].iWidth += (6*cxToolBorder);
  }
  else {
    toolCtrl[cntToolCtrls].iWidth = iWidth;
  }
  if( iHeight < 0 ) {
    toolCtrl[cntToolCtrls].iHeight = tmToolFont.tmHeight;
    toolCtrl[cntToolCtrls].iHeight += (6*cyToolBorder);
  }
  else if( iHeight==0 ) {
    toolCtrl[cntToolCtrls].iHeight = dyTools - (6*cyToolBorder);
  } else {
    toolCtrl[cntToolCtrls].iHeight = iHeight;
  }
  if( (toolCtrl[cntToolCtrls].iHeight + (6*cyToolBorder)) > dyTools ) {
    dyTools = (toolCtrl[cntToolCtrls].iHeight + (6*cyToolBorder));
  }
  if( dwStyle & BS_OWNERDRAW ) {
    toolCtrl[cntToolCtrls].hIcon = LoadIcon (hInstance, szLabel);
  }
  else {
    toolCtrl[cntToolCtrls].hIcon = NULL;
  }

  ReleaseDC( hwndTools, hdc );
  UpdatePositions( NULL );
  return toolCtrl[cntToolCtrls++].hwnd;
}

// ************************************************************************
// FUNCTION : DestroyToolBar (VOID)
// PURPOSE  :
// COMMENTS :
// ************************************************************************
BOOL
DestroyToolBar( VOID )
{
  return( DeleteObject(hbrBtnFace) );
}


// ************************************************************************
// FUNCTION : DrawButton (HDC hdc, RECT rect, BOOL bDown, HICON hIcon)
// PURPOSE  :
// COMMENTS :
// ************************************************************************
VOID
DrawButton (HDC hdc, RECT rect, BOOL bDown, HICON hIcon)
{
  HBRUSH  hBrush, hbrFrame, hbrBtnFace, hbrHilite, hbrShadow;
  RECT    border;
  INT     i;

  hbrFrame   = CreateSolidBrush( rgbFrame );
  hbrBtnFace = CreateSolidBrush( rgbBtnFace );
  hbrHilite  = CreateSolidBrush( rgbHilite );
  hbrShadow  = CreateSolidBrush( rgbShadow );

  FillRect (hdc, &rect, hbrBtnFace);

  if( hIcon ) {
    if( bDown ) {
      DrawIcon (hdc, rect.left + (4*cyToolBorder), rect.top + (4*cyToolBorder), hIcon);
    }
    else {
      DrawIcon (hdc, rect.left + (3*cyToolBorder), rect.top + (3*cyToolBorder), hIcon);
    }
  }

  hBrush = hbrFrame;
  border = rect; border.bottom = border.top + cyToolBorder;
  FillRect( hdc, &border, hBrush );
  border = rect; border.right = border.left + cxToolBorder;
  FillRect( hdc, &border, hBrush );
  border = rect; border.top = border.bottom - cyToolBorder;
  FillRect( hdc, &border, hBrush );
  border = rect; border.left = border.right - cxToolBorder;
  FillRect( hdc, &border, hBrush );

  for( i= 0; i<2; i++ ) {
    InflateRect( &rect, -cxToolBorder, -cyToolBorder );
    hBrush = ( bDown? hbrShadow:hbrHilite );
    border = rect; border.bottom = border.top + cyToolBorder;
    FillRect( hdc, &border, hBrush );
    border = rect; border.right = border.left + cxToolBorder;
    FillRect( hdc, &border, hBrush );
    if( !bDown ) {
      hBrush = hbrShadow;
      border = rect; border.top = border.bottom - cyToolBorder;
      FillRect( hdc, &border, hBrush );
      border = rect; border.left = border.right - cxToolBorder;
      FillRect( hdc, &border, hBrush );
    }
  }
  DeleteObject( hbrFrame );
  DeleteObject( hbrBtnFace );
  DeleteObject( hbrHilite );
  DeleteObject( hbrShadow );

}


// ************************************************************************
// FUNCTION : ToolsProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
// PURPOSE  :
// COMMENTS :
// ************************************************************************
LONG CALLBACK
ToolsProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  HDC              hdc;
  PAINTSTRUCT      ps;
  INT              iType, idCtrl, msgCtrl, i;
  RECT             rect, border;
  COLORREF         clrColor = rgbToolBar;
  HWND             hwndCtl;
  LONG             lStyle;
  HBRUSH           hBrush = hbrToolBar;
  LPDRAWITEMSTRUCT lpdi;
  HICON            hIcon;

  switch( msg ) {

    case WM_CREATE:
      //DebugOut( "[ToolsProc] WM_CREATE" );

     #ifdef NOCREATEFONT // CreateFont is failing in NT
      hfontTools = (HFONT)NULL;
     #else
      hfontTools = CreateFont(16, 0, 0, 0, 0, 0, 0, 0,
                      ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
                      DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, NULL);
      if( !hfontTools ) {
        MessageBox( GetFocus(), TEXT( "Failed To Create Font" ),
          TEXT( "StatusProc" ), MB_OK );
      }
     #endif
       hdc = GetDC( hwnd );
       SelectObject( hdc, hfontTools );
       GetTextMetrics( hdc, &tmToolFont );
       ReleaseDC( hwnd, hdc );
      //DebugOut( "[ToolsProc] WM_CREATE (exit)" );
       return( DefWindowProc( hwnd, msg, wParam, lParam ) );

    case WM_SIZE:
      UpdatePositions(hwnd);
      break;

   #ifdef WIN32
    case WM_CTLCOLORLISTBOX:
    case WM_CTLCOLOREDIT:
    case WM_CTLCOLORSTATIC:
    case WM_CTLCOLORBTN:
    case WM_CTLCOLORDLG:
    case WM_CTLCOLORMSGBOX:
    case WM_CTLCOLORSCROLLBAR:
      iType   = msg - WM_CTLCOLORMSGBOX;
      hdc     = (HDC)wParam;
      hwndCtl = (HWND)lParam;
   #else
    case WM_CTLCOLOR:
      hdc = wParam;
      hwndCtl = LOWORD( lParam );
      iType = HIWORD ( lParam );
   #endif

      switch (iType) {
        case CTLCOLOR_EDIT: //Edit control
          clrColor = rgbBtnFace;
          hBrush = hbrToolBar;
          break;

        case CTLCOLOR_LISTBOX: //List-box control
          lStyle = GetWindowLong (hwndCtl, GWL_STYLE);
          if( lStyle & CBS_SIMPLE ) {
            clrColor = rgbToolBar;
            hBrush = hbrToolBar;
          }
          else {
            clrColor = rgbBtnFace;
            hBrush = hbrToolBar;
          }
          break;

        case CTLCOLOR_STATIC:
          clrColor = rgbBtnFace;
          hBrush = hbrBtnFace;
          break;

        case CTLCOLOR_BTN:
          clrColor = rgbBtnFace;
          hBrush = hbrBtnFace;
          break;

        case CTLCOLOR_SCROLLBAR:
        case CTLCOLOR_DLG:
        case CTLCOLOR_MSGBOX:
        default:
          return FALSE;
      }
      SetBkColor(hdc, clrColor);
      return (LONG)hBrush;

    case WM_PAINT:
      hdc = BeginPaint (hwnd, &ps);
      GetClientRect (hwnd, &rect);
      /* Shade the top of the bar white */
      hBrush = CreateSolidBrush( rgbDirectLight );
      border = rect;
      border.bottom = border.top + cyToolBorder;
      FillRect (hdc, &border, hBrush);
      DeleteObject (hBrush);
      /* Shade the bottom of the bar dark gray */
      hBrush = CreateSolidBrush( rgbShadow );
      border = rect;
      border.top = border.bottom - cyToolBorder;
      FillRect (hdc, &border, hBrush);
      DeleteObject (hBrush);
      EndPaint (hwnd, &ps);
      return DefWindowProc (hwnd, msg, wParam, lParam);

    case WM_DRAWITEM: // Indicates that an owner-draw control needs to be redrawn.
      lpdi = (LPDRAWITEMSTRUCT)lParam;
      switch( lpdi->itemAction ) {
        // handle normal drawing of button, but check if its selected or focus
        case ODA_SELECT:
        case ODA_DRAWENTIRE:
          // handle button pressed down select state -- button down bitmap
          //   text is right & down 2 pixels
          hIcon = NULL;
          for( i=0; i< cntToolCtrls; i++ ) {
            if( toolCtrl[i].hwnd == lpdi->hwndItem ) {
              hIcon = toolCtrl[i].hIcon;
            }
          }
          if( lpdi->itemState & ODS_SELECTED ) {
            DrawButton( lpdi->hDC,lpdi->rcItem, TRUE, hIcon );
          }
          else { // not selected -- button up; text is in normal position
            DrawButton( lpdi->hDC,lpdi->rcItem, FALSE, hIcon );
          }
          return( TRUE );
      }
      break;

    case WM_COMMAND:
     #ifdef WIN32
      idCtrl  = LOWORD( wParam );
      msgCtrl = HIWORD( wParam );
      hwndCtl = (HWND) lParam;
     #else
      idCtrl  = wParam;
      msgCtrl = HIWORD( lParam );
      hwndCtl = LOWORD( lParam );
     #endif
      if( GetWindowLong( hwndCtl, GWL_STYLE ) & BS_OWNERDRAW ) {
        if( msgCtrl == BN_DOUBLECLICKED ) {
          PostMessage( hwndCtl, WM_LBUTTONDOWN, 0, 0 );
          return( TRUE );
        }
      }
      PostMessage (hwndMain, msg, wParam, lParam);
      return DefWindowProc (hwnd, msg, wParam, lParam);


    default:
      return( DefWindowProc( hwnd, msg, wParam, lParam ) );
  }
  return( 0L );
}


// ========================================================================
//                        STATUS BAR FUNCTIONS
// ========================================================================


// ************************************************************************
// FUNCTION :
// PURPOSE  :
// COMMENTS :
// ************************************************************************
BOOL
InitStatusBar( HANDLE hInstance )
{
  WNDCLASS    wndclass;

  hbrStatus = CreateSolidBrush( rgbBtnFace );

  wndclass.style         = CS_HREDRAW | CS_VREDRAW;
  wndclass.lpfnWndProc   = (WNDPROC)StatusProc;
  wndclass.cbClsExtra    = 0;
  wndclass.cbWndExtra    = 0;
  wndclass.hInstance     = hInstance;
  wndclass.hIcon         = NULL;
  wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
  wndclass.hbrBackground = hbrStatus;
  wndclass.lpszMenuName  = NULL;
  wndclass.lpszClassName = TEXT( "SamplerStatus" );

  if( !RegisterClass(&wndclass) )
    return( FALSE );

  wndclass.style         = CS_HREDRAW | CS_VREDRAW;
  wndclass.lpfnWndProc   = (WNDPROC)StatusFieldProc;
  wndclass.cbClsExtra    = 0;
  wndclass.cbWndExtra    = 0;
  wndclass.hInstance     = hInstance;
  wndclass.hIcon         = NULL;
  wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
  wndclass.hbrBackground = hbrStatus;
  wndclass.lpszMenuName  = NULL;
  wndclass.lpszClassName = TEXT( "StatusField" );

  if( !RegisterClass (&wndclass) )
    return( FALSE );

}

// ************************************************************************
// FUNCTION :
// PURPOSE  :
// COMMENTS :
// ************************************************************************
BOOL
CreateStatusBar (HWND hwnd, HANDLE hInstance, INT iId)
{
  cxStatusBorder = GetSystemMetrics (SM_CXBORDER);
  cyStatusBorder = GetSystemMetrics (SM_CYBORDER);

  //DebugOut( ": Calling CreateWindow" );
  hwndStatus = CreateWindow ( TEXT( "SamplerStatus" ), TEXT( "SamplerStatus" ),
    WS_CHILD //| WS_CLIPSIBLINGS
    | WS_BORDER | WS_VISIBLE,
    0, 0, 0, 0,
    hwnd, (HMENU)iId, hInstance, NULL);

  if (!hwndStatus) {
          return FALSE;
  }
  //DebugOut( "CreateWindow Succeeded" );
  return TRUE;
}

// ************************************************************************
// FUNCTION :
// PURPOSE  :
// COMMENTS :
// ************************************************************************
int
StatusBarHeight (HWND hwnd)
{
  RECT rect;

  GetClientRect( hwndStatus, &rect );

  return rect.bottom-rect.top;

  hwnd; // unreferenced
}

// ************************************************************************
// FUNCTION :
// PURPOSE  :
// COMMENTS :
// ************************************************************************
BOOL
AdjustStatusBar (HWND hwnd)
{
  RECT rect;
  GetClientRect (hwnd, &rect);
  MoveWindow (hwndStatus,
    rect.left-cxStatusBorder,
    rect.bottom - dyStatus + cyStatusBorder,
    rect.right - rect.left + (cxStatusBorder*2),
    dyStatus, TRUE);
  return TRUE;
}

// ************************************************************************
// FUNCTION :
// PURPOSE  :
// COMMENTS :
// ************************************************************************
HWND
AddStatusField (HANDLE hInstance, INT iId, INT iMin, INT iMax, BOOL bNewGroup)
{
  LONG lStyle;

  if( cntStatusField >= MAXSTATUS )
    return( (HWND) 0 ); // No room left in our fixed array
  statusField[cntStatusField].hwnd = CreateWindow( TEXT( "StatusField" ), TEXT( "" ),
                                       WS_CHILD | WS_VISIBLE,
                                       0, 0, 0, 0,
                                       hwndStatus, (HMENU)iId,
                                       hInstance, NULL );
  if( !statusField[cntStatusField].hwnd )
    return( (HWND) 0 ); // CreateWindow failed for some reason
  if( iMin < 0 ) {
    statusField[cntStatusField].iMinWidth = tmStatusFont.tmAveCharWidth*abs(iMin);
  }
  else {
    statusField[cntStatusField].iMinWidth = iMin;
  }
  if( iMax < 0 ) {
    statusField[cntStatusField].iMaxWidth = tmStatusFont.tmAveCharWidth*abs(iMax);
  }
  else {
    statusField[cntStatusField].iMaxWidth = iMax;
  }
  if( bNewGroup ) {
    lStyle = GetWindowLong( statusField[cntStatusField].hwnd, GWL_STYLE );
    lStyle |= WS_GROUP;
    SetWindowLong( statusField[cntStatusField].hwnd, GWL_STYLE, lStyle );
  }

  return( statusField[cntStatusField++].hwnd );
}

// ************************************************************************
// FUNCTION :
// PURPOSE  :
// COMMENTS :
// ************************************************************************
BOOL
DestroyStatusBar (VOID)
{
  return( DeleteObject( hbrStatus ) );
}

// ************************************************************************
// FUNCTION :
// PURPOSE  :
// COMMENTS :
// ************************************************************************
LONG CALLBACK
StatusProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  HDC         hdc;
  PAINTSTRUCT ps;
  INT         x, y, i;
  INT         wAvailWidth, wFlexWidth, cntFlexWidth, wNeedWidth, cntNeedWidth;
  RECT        rect, border;
  HBRUSH      hBrush;

  switch (msg) {
    case WM_CREATE:
     #ifdef NOCREATEFONT // CreateFont is failing in NT
      hfontStatus = (HFONT)NULL;
     #else
      hfontStatus = CreateFont(16, 0, 0, 0, 0, 0, 0, 0,
              ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
              DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, NULL);
      if( !hfontStatus ) {
        MessageBox( GetFocus(), TEXT( "Failed To Create Font" ),
          TEXT( "StatusProc" ), MB_OK );
      }
     #endif
      hdc = GetDC (hwnd);
      SelectObject (hdc, hfontStatus);
      GetTextMetrics (hdc, &tmStatusFont);
      cxStatusBorder = GetSystemMetrics (SM_CXBORDER);
      cyStatusBorder = GetSystemMetrics (SM_CYBORDER);
      cxFrame = 3*cxStatusBorder;
      cyFrame = 3*cyStatusBorder;
      dyField = tmStatusFont.tmHeight + (2*cyStatusBorder);
      dyStatus = dyField + (2*cyFrame);
      ReleaseDC (hwnd, hdc);
      return DefWindowProc (hwnd, msg, wParam, lParam);

    case WM_SIZE:
      if( cntStatusField ) {
        GetClientRect (hwnd, &rect);
        wAvailWidth = rect.right - rect.left - (cxStatusBorder*8);
        wNeedWidth = 0;
        cntNeedWidth = 0;
        cntFlexWidth = 0;

        /* First Pass: Dole out to fields that have a minimum need */
        for (i=0; i<cntStatusField; i++) {
          statusField[i].iGiveWidth = 0; // Make sure all are initialized to 0
          if( statusField[i].iMinWidth ) {
            /* (n, ?) */
            statusField[i].iGiveWidth = statusField[i].iMinWidth;
            wAvailWidth -= (statusField[i].iGiveWidth + cxStatusBorder*2);
            if( GetWindowLong(statusField[i].hwnd, GWL_STYLE) & WS_GROUP ) {
              wAvailWidth -= cxStatusBorder*4;
            }
          }
          else {
            /* They didn't specify a minimum... don't give them anything yet */
            /* (0, ?) */
            statusField[i].iGiveWidth = 0;
            //++cntFlexWidth;
          }
          /* For those that have a minimum, but can grow to be as large as possible...*/
          /* (n, 0) */
          if( (statusField[i].iMinWidth >0) && (statusField[i].iMaxWidth ==0) ) {
            ++cntFlexWidth;
          }
          /* For those that have a max that is greater then their min... */
          /* Includes (0,n) and (n,>n) */
          if( statusField[i].iMaxWidth > statusField[i].iGiveWidth ) {
            wNeedWidth += (statusField[i].iMaxWidth - statusField[i].iGiveWidth);
            ++cntNeedWidth;
          }
        }

        /* Second Pass: Dole out to fields that have a stated maximum need */
        /* This will also hit those who had no minimum, but did have a maximum */
        /* It will still not give anything to those with no min, no max */
        if( (cntNeedWidth > 0) && (wAvailWidth > 0) ) {
          if( wNeedWidth > wAvailWidth ) {
            wNeedWidth = wAvailWidth;
          }
          wNeedWidth = wNeedWidth / cntNeedWidth;
          for( i=0; i<cntStatusField; i++ ) {
            if( statusField[i].iMaxWidth > statusField[i].iGiveWidth ) {
              statusField[i].iGiveWidth += wNeedWidth;
              wAvailWidth -= (statusField[i].iGiveWidth + cxStatusBorder*2);
              if( GetWindowLong(statusField[i].hwnd, GWL_STYLE) & WS_GROUP ) {
                wAvailWidth -= cxStatusBorder*4;
              }
            }
          }
        }

        /* Third Pass: Dole out the remaining to fields that want all they can get */
        /* This includes those who had a minimum, but no maximum */
        if( (cntFlexWidth > 0) && (wAvailWidth > 0) ) {
          wFlexWidth = wAvailWidth / cntFlexWidth;
          for( i=0; i<cntStatusField; i++ ) {
            if( statusField[i].iMaxWidth==0 ) {
              statusField[i].iGiveWidth += wFlexWidth;
              wAvailWidth -= ((wFlexWidth - statusField[i].iMinWidth) + cxStatusBorder*2);
              if( GetWindowLong(statusField[i].hwnd, GWL_STYLE) & WS_GROUP ) {
               wAvailWidth -= cxStatusBorder*4;
              }
            }
          }
        }

        x = cxStatusBorder*4;
        y = rect.top + (2*cyStatusBorder);
        for( i=0; i<cntStatusField; i++ ) {
          if( GetWindowLong (statusField[i].hwnd, GWL_STYLE) & WS_GROUP ) {
            x += (cxStatusBorder*4);
          }
          MoveWindow( statusField[i].hwnd, x, y, statusField[i].iGiveWidth, dyField, TRUE );
          x += statusField[i].iGiveWidth + (cxStatusBorder*2);
        }
      }
      break;

    case WM_PAINT:
      hdc = BeginPaint( hwnd, &ps );
      GetClientRect( hwnd, &rect );
      hBrush = CreateSolidBrush( rgbToolBar );
      border = rect;
      border.bottom = border.top + cyStatusBorder;
      FillRect( hdc, &border, hBrush );
      DeleteObject (hBrush);
      hBrush = CreateSolidBrush( rgbShadow );
      border = rect;
      border.top = border.bottom - cyStatusBorder;
      FillRect( hdc, &border, hBrush );
      DeleteObject( hBrush );
      EndPaint( hwnd, &ps );
      return( DefWindowProc (hwnd, msg, wParam, lParam) );

    default:
      return( DefWindowProc( hwnd, msg, wParam, lParam ) );

  }

  return( 0L );
}


// ************************************************************************
// FUNCTION :
// PURPOSE  :
// COMMENTS :
// ************************************************************************
LONG CALLBACK
StatusFieldProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  static HFONT hFieldFont;

  HDC         hdc;
  PAINTSTRUCT ps;
  RECT        rect, border;
  HBRUSH      hBrush;
  WORD        edge = 1;
  HFONT       hTmp;
  TCHAR       szText[80];
  INT         len;

  switch( msg ) {
    case WM_CREATE:
      hFieldFont = CreateFont( 8, 0, 0, 0, 0, 0, 0, 0,
                     ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
                     DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, TEXT( "Helv" ) );
      return( DefWindowProc( hwnd, msg, wParam, lParam ) );

    case WM_PAINT:
      hdc = BeginPaint( hwnd, &ps );
      GetClientRect( hwnd, &rect );
      //-- fill top of the status windows
      hBrush = CreateSolidBrush( rgbShadow );
      border = rect;
      border.bottom = border.top + cyStatusBorder;
      FillRect( hdc, &border, hBrush );
      border = rect;
      border.right = border.left + cxStatusBorder;
      FillRect( hdc, &border, hBrush );
      DeleteObject( hBrush );
      //-- fill botton of the status windows
      hBrush = CreateSolidBrush( rgbDirectLight );
      border = rect;
      border.top = border.bottom - cyStatusBorder;
      FillRect( hdc, &border, hBrush );
      border = rect;
      border.left = border.right - cxStatusBorder;
      FillRect( hdc, &border, hBrush );
      DeleteObject( hBrush );

      if( len = GetWindowText( hwnd, szText, sizeof (szText) ) ) {
        hTmp = SelectObject( hdc, hfontStatus );
        SetTextColor( hdc, rgbStatic );
        SetBkColor( hdc, rgbToolBar );
        InflateRect( &rect, -(cxStatusBorder*2), -cyStatusBorder );
        ExtTextOut( hdc, rect.left, rect.top,
          ETO_OPAQUE | ETO_CLIPPED,
          &rect,
          (LPTSTR) szText,
          len, NULL);
        SelectObject( hdc, hTmp );
      }
      EndPaint( hwnd, &ps );
      break;

    case WM_SETTEXT:
      InvalidateRect( hwnd, NULL, TRUE );
      return( DefWindowProc( hwnd, msg, wParam, lParam ) );

    default:
      return( DefWindowProc( hwnd, msg, wParam, lParam ) );
  }

  return( 0L );
}
