www.pudn.com > helpview.zip > Btnbar.c, change:1997-10-15,size:43854b


/* 
  
Copyright 1997 Willows Software, Inc.  
 
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 the file COPYING.LIB.  If 
not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
Cambridge, MA 02139, USA. 
 
*/ 
 
/************************************************************************* 
* 
*	BTNBAR.C 
*   Button bar routines.       
* 
*	Author: Paul E. Kissel 
* 
**************************************************************************/ 
/*********************************** 
** 
**  System Includes 
** 
***********************************/ 
 
#include <string.h> 
#include <ctype.h> 
 
 
/*********************************** 
** 
**  App. Includes 
** 
***********************************/ 
 
#include "accel.h" 
#include "btnbar.h" 
#include "winmem.h" 
#include "general.h" 
#include "hlpmacro.h" 
#include "msgbox.h" 
#include "twhlprc.h" 
#include "globals.h" 
#include "macroeng.h" 
 
 
/*********************************** 
** 
**  Private File Defines 
** 
***********************************/ 
 
/* Class names. */ 
#define WILL_BUTTON_CLASS  "WILL_HELP_BUTTON" 
 
 
/* Button bar window's extrabyte data positions. */                   
#define XBYTE_BUTTONLIST   0                                           /* Button list. */ 
#define XBYTE_BUTTONFONT   XBYTE_BUTTONLIST   + sizeof(HBUTTONLIST)    /* Button font. */  
#define XBYTE_BUTTONWIDTH  XBYTE_BUTTONFONT   + sizeof(HFONT)          /* Button width */  
#define XBYTE_BUTTONHEIGHT XBYTE_BUTTONWIDTH  + sizeof(WORD)           /* Button height.*/  
#define XBYTE_HDATAWND     XBYTE_BUTTONHEIGHT + sizeof(WORD)           /* Data Window. */  
 
/* Handle to the top of the button list. */ 
typedef HGLOBAL HBUTTONLIST; 
 
 
/* Custom font. */ 
#define BUTTON_FONT_SIZE  8 
#define BUTTON_FONT_FACE  "Helv" 
 
 
/* Accelerator character. */ 
#define ACCEL_CHAR '&' 
 
 
/*  
** Height and width of a button in dialog box units.  
*/ 
#define BUTTON_WIDTH_UNITS    32 
#define BUTTON_HEIGHT_UNITS   10 
 
 
/*  
** Macros to adjust height and width of  
** a button units to screen pixel sizes. 
*/ 
#define GetButtonWidth()  ( (BUTTON_WIDTH_UNITS * LOWORD(GetDialogBaseUnits())) / 4 ) 
#define GetButtonHeight() ( (BUTTON_HEIGHT_UNITS * HIWORD(GetDialogBaseUnits()) ) / 8 ) 
 
 
/* 
** Information for a button in the list. 
*/ 
typedef HGLOBAL HBUTTONINFO; 
typedef struct tagBUTTONINFO 
{ 
  HWND hButtonWnd; 
  ATOM ButtonID; 
  HGLOBAL hMacroString; 
  BOOL bEnabled;                      /* Is button active. */ 
  WORD wAccelKey;                    /* Button's accel. char. when active. */ 
  HBUTTONINFO Next; 
}  
BUTTONINFO; 
typedef BUTTONINFO * FPBUTTONINFO; 
 
 
/*  
** EnumButtonInfo() callback function.  
*/ 
typedef BOOL ( CALLBACK * BUTTONINFOENUMPROC )( FPBUTTONINFO fpButtonInfo, LPARAM lParam ); 
 
 
/*  
** Data used by the ChangeButtonsMacro() function.  
*/ 
typedef struct tagCHANGEBUTTONSMACROFUNCDATA 
{ 
  BOOL  bSuccess;                       /* Was the function successsful. */ 
  ATOM  ButtonID;                       /* Button's ID. */ 
  LPSTR MacroStringPtr;                 /* Replacement macro. */ 
  HWND  hButtonBarWnd;                  /* Handle to the button bar window. */ 
} 
CHANGEBUTTONSMACROFUNCDATA; 
typedef CHANGEBUTTONSMACROFUNCDATA * FPCHANGEBUTTONSMACROFUNCDATA; 
 
 
/*  
** Data used by the DisableButton() function.  
*/ 
typedef struct tagENABLEBUTTONFUNCDATA 
{ 
  HWND  hButtonBarWnd;                  /* Handle to the button bar window. */ 
  BOOL  bSuccess;                       /* Was the function successsful. */ 
  ATOM  ButtonID;                       /* Button's ID. */ 
  BOOL  bEnable;                        /* Enable it - FALSE means disable it. */ 
} 
ENABLEBUTTONFUNCDATA; 
typedef ENABLEBUTTONFUNCDATA * FPENABLEBUTTONFUNCDATA; 
 
 
/*  
** Data used by the GetButtonWindow() function.  
*/ 
typedef struct tagGETWINDOWFUNCDATA 
{ 
  HWND hWnd; 
  ATOM ButtonID; 
} 
GETWINDOWFUNCDATA; 
typedef GETWINDOWFUNCDATA * FPGETWINDOWFUNCDATA; 
 
 
/*  
** Data used by the ButtonIsPressed() function.  
*/ 
typedef struct tagBTNPRESSEDFUNCDATA 
{ 
  HWND hExecWnd; 
  HWND hDataWnd; 
  ATOM ButtonID; 
  HWND hButtonBarWnd; 
} 
BTNPRESSEDFUNCDATA; 
typedef BTNPRESSEDFUNCDATA * FPBTNPRESSEDFUNCDATA; 
 
 
/*  
** Data used by the RepositionButtons() function.  
*/ 
typedef struct tagREPOSITIONFUNCDATA 
{ 
  WORD wNextXPos; 
  WORD wNextYPos; 
 
  WORD wMaxYPos; 
 
  WORD wBtnBarWndHeight; 
   
  WORD wNumberButtons; 
 
  WORD wButtonWidth; 
  WORD wButtonHeight; 
} 
REPOSITIONFUNCDATA; 
typedef REPOSITIONFUNCDATA * FPREPOSITIONFUNCDATA; 
 
 
/*  
** Data used by the SetMaxButtonSize() function.  
*/ 
typedef struct tagCALCSIZEFUNCDATA 
{ 
  WORD wMaxButtonWidth; 
  WORD wMaxButtonHeight; 
} 
CALCSIZEFUNCDATA; 
typedef CALCSIZEFUNCDATA * FPCALCSIZEFUNCDATA; 
 
 
 
/*********************************** 
** 
**  Private Function Prototypes 
** 
***********************************/ 
 
static void  RepositionButtons( HWND hButtonBarWnd, WORD wMaxYPos, WORD * wBtnBarWndHeight ); 
static BOOL CALLBACK RepositionGivenButton( FPBUTTONINFO fpButtonInfo, LPARAM lParam ); 
static BOOL SaveButtonInfo( HWND hErrorWnd, HBUTTONLIST * hButtonList, FPBUTTONINFO fpButtonInfo ); 
static HBUTTONINFO NewButtonInfo( HWND hErrorWnd, FPBUTTONINFO fpButtonInfo ); 
static void FreeButtonInfo( HWND hButtonBarWnd, HBUTTONINFO hButtonInfo ); 
static void InsertButtonInfo( HBUTTONLIST * hButtonList, HBUTTONINFO hNewButtonInfo ); 
static void FreeButtonInfoList( HWND hButtonBarWnd, HBUTTONLIST * hButtonList ); 
static HBUTTONINFO GetNextButtonInfo( HBUTTONINFO hButtonInfo ); 
static void EnumButtonInfo( HBUTTONLIST hButtonList, BUTTONINFOENUMPROC ButtonInfoEnumProc, LPARAM lParam ); 
static BOOL CALLBACK ButtonIsPressed( FPBUTTONINFO fpButtonInfo, LPARAM lParam ); 
static WORD  GetNumberOfButtons( HWND hButtonBarWnd ); 
static BOOL CALLBACK CountButtons( FPBUTTONINFO fpButtonInfo, LPARAM lParam ); 
static BOOL CALLBACK FindButtonHandle( FPBUTTONINFO fpButtonInfo, LPARAM lParam ); 
static BOOL CALLBACK ChangeButtonsMacroProc( FPBUTTONINFO fpButtonInfo, LPARAM lParam ); 
static BOOL DeleteButtonWithID( HWND hButtonBarWnd, HBUTTONLIST * hButtonList, ATOM ButtonID  ); 
static BOOL CALLBACK GetMaxButtonSizesProc( FPBUTTONINFO fpButtonInfo, LPARAM lParam ); 
static void SetMaxButtonSize( HWND hButtonBarWnd, HBUTTONLIST hButtonList ); 
static BOOL CALLBACK EnableButtonProc( FPBUTTONINFO fpButtonInfo, LPARAM lParam ); 
 
 
/*********************************************************************************** 
** 
**                              Shared Functions 
** 
************************************************************************************/ 
 
 
/********************************************************************* 
** 
**  Register the button bar's window class.  
** 
**********************************************************************/ 
BOOL RegisterButtonBarClass( HINSTANCE hInstance ) 
{ 
  WNDCLASS  wcWndClass; 
 
  /*  
  ** Register the Button window class  
  */ 
  wcWndClass.lpszClassName = WILL_BUTTON_CLASS; 
  wcWndClass.lpfnWndProc = ButtonWndProc; 
  wcWndClass.cbClsExtra  = 0; 
  wcWndClass.cbWndExtra  = sizeof( HBUTTONLIST ) + sizeof( HFONT ) + sizeof( WORD ) + sizeof( WORD ) + sizeof(HWND); 
  wcWndClass.style = CS_HREDRAW | CS_VREDRAW; 
  wcWndClass.hInstance = hInstance; 
  wcWndClass.hIcon = (HICON)NULL; 
  wcWndClass.hCursor = (HCURSOR)NULL; 
  wcWndClass.hbrBackground = GetStockObject(GRAY_BRUSH); 
  wcWndClass.lpszMenuName = NULL; 
 
  /* Error? */ 
  if ( ! RegisterClass( &wcWndClass) )  
  { 
    /* Failure. */ 
    return (FALSE); 
  } 
 
  /* Success. */ 
  return( TRUE ); 
} 
 
 
/********************************************************************* 
** 
**  Create the button bar's window. 
** 
**********************************************************************/ 
HWND CreateButtonBarWnd( HWND hDataWnd, HWND hParentWnd, HINSTANCE hInstance ) 
{ 
  HWND hButtonBarWnd = 0; 
   
 
  /* Create button bar. */ 
  hButtonBarWnd = CreateWindow 
            (  
              WILL_BUTTON_CLASS, 
              NULL,  
              WS_CHILD | WS_VISIBLE, 
              0, 0,  
              CW_USEDEFAULT, CW_USEDEFAULT,  
              hParentWnd,  
              (HMENU) NULL, 
              hInstance, 
              NULL  
            ); 
             
  /* If we have a window. */ 
  if( hButtonBarWnd != NULL ) 
  { 
    /* Save handle to the macro engine. */ 
    SetWindowLong( hButtonBarWnd, XBYTE_HDATAWND, (LONG) hDataWnd ); 
  } 
 
  return( hButtonBarWnd ); 
} 
 
 
 
 
/********************************************************************* 
** 
**  Window procedure for the button bar's window. 
** 
**********************************************************************/ 
LRESULT CALLBACK ButtonWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) 
{ 
  /* Drawing variables. */ 
  HDC hDC; 
  PAINTSTRUCT ps; 
  RECT  Rect; 
   
  /* Handle to the button info. list. */ 
  HBUTTONLIST hButtonList; 
 
  /* Custom font for the button. */ 
  HFONT hButtonFont; 
 
  /* Button bar window size variables. */ 
  WORD wClientWidth, wClientHeight; 
 
  /* Button information. */ 
  WORD wBtnBarWndHeight, BtnCount; 
 
  /* Data sent we button is pressed. */ 
  BTNPRESSEDFUNCDATA ButtonPressedData; 
 
  switch ( message )  
  { 
    case WM_CTLCOLORBTN: 
    { 
      /* Get button bar list's handle. */ 
      return( (LONG) GetStockObject( GRAY_BRUSH ) ); 
    } 
 
    case WM_COMMAND: 
    { 
      /* Get button bar list's handle. */ 
      hButtonList = (HBUTTONLIST) GetWindowLong( hWnd, XBYTE_BUTTONLIST ); 
 
      /* Init. data struct. */ 
      ButtonPressedData.hExecWnd = GetParent( hWnd ); 
      ButtonPressedData.hDataWnd = (HWND) GetWindowLong( hWnd, XBYTE_HDATAWND ); 
      ButtonPressedData.ButtonID = (ATOM) wParam; 
      ButtonPressedData.hButtonBarWnd = hWnd; 
       
      /* Find button pressed and run its macro. */ 
      EnumButtonInfo( hButtonList, (BUTTONINFOENUMPROC) ButtonIsPressed, (LPARAM) (FPBTNPRESSEDFUNCDATA) &ButtonPressedData ); 
      return 0; 
    } 
 
    case WM_CREATE: 
    { 
      /* Initialize extrabytes data for button list. */ 
      SetWindowLong( hWnd, XBYTE_BUTTONLIST, 0 ); 
       
      /* Init. width and height of buttons. */ 
      SetWindowWord( hWnd, XBYTE_BUTTONWIDTH, 0 ); 
      SetWindowWord( hWnd, XBYTE_BUTTONHEIGHT, 0 ); 
 
      /******** 
      ** 
      ** Create our custom font. 
      ** 
      ********/ 
       
      hDC = GetDC( hWnd ); 
       
      /* Create button bar's custom font. */ 
      hButtonFont = CreateFont( -1 * MulDiv( BUTTON_FONT_SIZE, GetDeviceCaps( hDC, LOGPIXELSY ), 72 ), 
                                0, 0, 0, FW_BOLD, 0, 0, 0, 0, 0, 0, 0, 0, BUTTON_FONT_FACE );  
      ReleaseDC( hWnd, hDC ); 
       
      /* Got font? */ 
      if( hButtonFont == NULL ) return -1; 
         
      /* Save button bar window's old font to extrabytes data. */ 
      SetWindowLong( hWnd, XBYTE_BUTTONFONT, (LONG) hButtonFont ); 
 
      return 0; 
    } 
 
    case WM_DESTROY: 
    { 
      /******** 
      ** 
      ** Delete our button list. 
      ** 
      ********/ 
 
      /* Get button bar list's handle. */ 
      hButtonList = (HBUTTONLIST) GetWindowLong( hWnd, XBYTE_BUTTONLIST ); 
 
      /* Free button list. */ 
      FreeButtonInfoList( hWnd, &hButtonList ); 
 
 
      /******** 
      ** 
      ** Delete our custom font. 
      ** 
      ********/ 
       
      /* Get button bar's custom font. */ 
      hButtonFont = (HFONT) GetWindowLong( hWnd, XBYTE_BUTTONFONT ); 
       
      /* Delete our custom font. */ 
      DeleteObject( hButtonFont ); 
 
      return 0; 
    }   
 
    case PRIVMSG_RESIZE: 
    { 
      /*  
      ** Get client area of main window.  
      */ 
      GetClientRect( GetParent( hWnd ), &Rect ); 
      wClientWidth = Rect.right - Rect.left; 
      wClientHeight = Rect.bottom - Rect.top; 
 
      /* Get the number of buttons in the button list. */ 
      BtnCount = GetNumberOfButtons( hWnd ); 
 
      /* If we have buttons in the bar. */ 
      if( BtnCount != 0 ) 
      { 
        /*  
        ** Position the buttons and size the button bar window. 
        */ 
   
        /* Reposition buttons. */ 
        RepositionButtons( hWnd, wClientWidth, &wBtnBarWndHeight ); 
 
        /* Set button bar's window size. */ 
        MoveWindow( hWnd, 0, 0, wClientWidth, wBtnBarWndHeight, TRUE ); 
      } 
      return 0; 
    } 
 
    case WM_PAINT: 
    { 
      GetClientRect( hWnd, &Rect ); 
      hDC = BeginPaint(hWnd, &ps); 
       
      /* Paint border. */ 
      Rect.top -= 1; 
      Rect.right += 1; 
      FrameRect( hDC, &Rect, GetStockObject( BLACK_BRUSH ) ); 
       
      EndPaint(hWnd, &ps); 
      return 0; 
    } 
  } 
 
  return DefWindowProc(hWnd, message, wParam, lParam); 
} 
 
 
/********************************************************************* 
** 
**  Adds a new button to the button bar. 
** 
**********************************************************************/ 
BOOL AddBarButton 
(  
  HINSTANCE hInstance, 
  HWND hButtonBarWnd,  
  LPSTR lpButtonText,  
  LPSTR lpButtonID,  
  LPSTR lpMacro  
) 
{ 
  /* Pointer to macro string. */ 
  char * MacroStringPtr; 
   
  /* Handle to the button info. list. */ 
  HBUTTONLIST hButtonList; 
 
  /* Custom font for the button. */ 
  HFONT hButtonFont; 
 
  /* Button info. to store in button info. list. */ 
  BUTTONINFO ButtonInfo; 
   
  BOOL bReturn; 
     
  char * AccelCharPtr; 
   
   
  /* See if the button ID is already being used by an existing window. */ 
  if( GetButtonWindow( hButtonBarWnd, lpButtonID ) != 0 ) 
  { 
    /* Error. */  
    MsgBox( hInstance, hButtonBarWnd, IDS_ERRORTITLE, BTNALREADYEXISTS, MB_ICONHAND | MB_OK ); 
 
    /* Failure. */ 
    return( FALSE ); 
  } 
 
  /* Get button bar's button list handle. */ 
  hButtonList = (HBUTTONLIST) GetWindowLong( hButtonBarWnd, XBYTE_BUTTONLIST ); 
   
  /* Allocate memory for storage of macro string. */ 
  if( !GlobalAllocMem( hButtonBarWnd, &(ButtonInfo).hMacroString, strlen( lpMacro ) + sizeof(char) ) ) 
  { 
    /* Failure. */ 
    return( FALSE ); 
  } 
   
  /* Copy macro string storage area. */ 
  MacroStringPtr = (char *) GlobalLock( ButtonInfo.hMacroString ); 
  strcpy( MacroStringPtr, lpMacro ); 
  GlobalUnlock( ButtonInfo.hMacroString ); 
   
  /* Make atom for the button's ID. */ 
  ButtonInfo.ButtonID = AddAtom( lpButtonID );    
       
  /* Initially inactive. */ 
  ButtonInfo.bEnabled = FALSE; 
   
  /* Get the accelerator key for the button. */ 
  AccelCharPtr = strchr( lpButtonText, ACCEL_CHAR ); 
 
  /* No accelerator. */ 
  if( AccelCharPtr == NULL ) 
  { 
    /* No accelerator. */ 
    ButtonInfo.wAccelKey = 0; 
  } 
 
  /* There is an accelerator. */ 
  else 
  { 
    AccelCharPtr = AccelCharPtr + 1; 
    if( *AccelCharPtr != '\0' ) 
    { 
      /* Is accelerator a supported char.? */ 
      if( isalpha(*AccelCharPtr) ) 
      { 
        ButtonInfo.wAccelKey = (WORD) toupper( *AccelCharPtr ); 
      } 
      else 
      { 
        /* Accelerator is not a-z or A-Z. */ 
        ButtonInfo.wAccelKey = 0; 
      } 
    } 
  } 
   
  /* Create the button. */ 
  ButtonInfo.hButtonWnd = CreateWindow 
                          ( 
                            "BUTTON",  
                            lpButtonText,  
                            WS_CHILD | WS_DISABLED | BS_PUSHBUTTON,  
                            0, 0,  
                            CW_USEDEFAULT, CW_USEDEFAULT,  
                            hButtonBarWnd,  
                            (HMENU) ButtonInfo.ButtonID,  
                            hInstance,  
                            NULL 
                          ); 
     
  /* Error? */ 
  if( ! ButtonInfo.hButtonWnd ) 
  { 
    /* Delete allocated info. */ 
    DeleteAtom( ButtonInfo.ButtonID ); 
    MyGlobalFree( ButtonInfo.hMacroString );  
     
    return FALSE; 
  } 
 
  /* Set button's custom font. */ 
  hButtonFont = (HFONT) GetWindowLong( hButtonBarWnd, XBYTE_BUTTONFONT ); 
  SendMessage( ButtonInfo.hButtonWnd, WM_SETFONT, (WPARAM) hButtonFont, (LPARAM) MAKELONG((WORD) TRUE, 0) ); 
 
  /* Save button window. */ 
  bReturn = SaveButtonInfo( hButtonBarWnd, &hButtonList, (FPBUTTONINFO) &ButtonInfo ); 
  if( bReturn == FALSE ) 
  { 
    /* Delete allocated info. */ 
    DeleteAtom( ButtonInfo.ButtonID ); 
    MyGlobalFree( ButtonInfo.hMacroString );  
     
    /* Delete button. */ 
    DestroyWindow( ButtonInfo.hButtonWnd ); 
     
    return FALSE; 
  } 
       
  /* Set the button bar's button list handle in case it changed. */ 
  SetWindowLong( hButtonBarWnd, XBYTE_BUTTONLIST, (LONG) hButtonList ); 
   
  /* Set the maximum button size since maybe the new button is the largest. */ 
  SetMaxButtonSize( hButtonBarWnd, hButtonList ); 
       
  /* Resize window. */ 
  PostMessage( hButtonBarWnd, PRIVMSG_RESIZE, 0, 0L ); 
       
  /* Success. */ 
  return( TRUE ); 
} 
 
 
 
 
/********************************************************************* 
** 
**  Gets the window handle of a button in the button bar  
**  based on its ID. 
** 
**********************************************************************/ 
BOOL DeleteBarButton 
(  
  HWND hButtonBarWnd,  
  LPSTR lpButtonID 
) 
{ 
  HBUTTONLIST hButtonList; 
  ATOM ButtonID; 
   
  /* Get handle to button list. */ 
  hButtonList = (HBUTTONLIST) GetWindowLong( hButtonBarWnd, XBYTE_BUTTONLIST ); 
  if( hButtonList == 0 )  
  { 
    /* Failure. */ 
    return FALSE; 
  } 
   
  /* Make ID into an atom. */ 
  ButtonID = FindAtom( lpButtonID ); 
   
  /* No atom regisitered means no button exists. */ 
  if( ButtonID == 0 ) return FALSE; 
 
  /* Find the button with the I and delete it. */ 
  if( ! DeleteButtonWithID( hButtonBarWnd, &hButtonList, ButtonID ) ) 
  { 
    /* Failure. */ 
    return FALSE; 
  } 
 
  /* Set the button bar's button list handle in case it changed. */ 
  SetWindowLong( hButtonBarWnd, XBYTE_BUTTONLIST, (LONG) hButtonList ); 
 
  /* Set the maximum button size since maybe the deleted button was the largest. */ 
  SetMaxButtonSize( hButtonBarWnd, hButtonList ); 
 
  /* Success. */ 
  return( TRUE ); 
} 
 
 
 
/************************************* 
* 
*  Add info for a button to the list. 
*                   
**************************************/ 
static void SetMaxButtonSize 
(  
  HWND hButtonBarWnd,  
  HBUTTONLIST hButtonList 
) 
{ 
  CALCSIZEFUNCDATA FuncData; 
        
  /* Init. data. */ 
  FuncData.wMaxButtonWidth = 0; 
  FuncData.wMaxButtonHeight = 0; 
   
  /* Position each button. */ 
  EnumButtonInfo( hButtonList, (BUTTONINFOENUMPROC) GetMaxButtonSizesProc, (LPARAM) (FPCALCSIZEFUNCDATA) &FuncData ); 
 
  /* Set button width. */ 
  SetWindowWord( hButtonBarWnd, XBYTE_BUTTONWIDTH, FuncData.wMaxButtonWidth ); 
       
  /* Set button height. */ 
  SetWindowWord( hButtonBarWnd, XBYTE_BUTTONHEIGHT, FuncData.wMaxButtonHeight ); 
} 
 
 
 
/********************************************************************* 
** 
**  Change the macro of a button in the button bar given its ID. 
** 
**********************************************************************/ 
BOOL ChangeButtonsMacro 
(  
  HWND hButtonBarWnd,  
  LPSTR lpButtonID, 
  LPSTR lpMacro  
) 
{ 
  HBUTTONLIST hButtonList; 
  CHANGEBUTTONSMACROFUNCDATA FuncData; 
   
   
  /* Get handle to button list. */ 
  hButtonList = (HBUTTONLIST) GetWindowLong( hButtonBarWnd, XBYTE_BUTTONLIST ); 
  if( hButtonList == 0 ) return FALSE; 
   
  /* Initialize the data. */ 
  FuncData.bSuccess = FALSE; 
  FuncData.ButtonID = FindAtom( lpButtonID ); 
  FuncData.MacroStringPtr = lpMacro; 
  FuncData.hButtonBarWnd = hButtonBarWnd; 
   
  /* No atom regisitered means no button exists. */ 
  if( FuncData.ButtonID == 0 ) return FALSE; 
   
  /* Position each button. */ 
  EnumButtonInfo( hButtonList, (BUTTONINFOENUMPROC) ChangeButtonsMacroProc, (LPARAM) (FPCHANGEBUTTONSMACROFUNCDATA) &FuncData ); 
 
  /* Successful? */ 
  return( FuncData.bSuccess ); 
} 
 
 
 
/********************************************************************* 
** 
**  Enable/disable the button having the specified ID. 
** 
**********************************************************************/ 
BOOL EnableButton 
(  
  HWND  hButtonBarWnd,  
  LPSTR lpButtonID, 
  BOOL  bEnable  
) 
{ 
  HBUTTONLIST hButtonList; 
  ENABLEBUTTONFUNCDATA FuncData; 
   
   
  /* Get handle to button list. */ 
  hButtonList = (HBUTTONLIST) GetWindowLong( hButtonBarWnd, XBYTE_BUTTONLIST ); 
  if( hButtonList == 0 ) return FALSE; 
   
  /* Initialize the data. */ 
  FuncData.hButtonBarWnd = hButtonBarWnd; 
  FuncData.bSuccess      = FALSE; 
  FuncData.ButtonID      = FindAtom( lpButtonID ); 
  FuncData.bEnable       = bEnable; 
   
  /* No atom regisitered means no button exists. */ 
  if( FuncData.ButtonID == 0 ) return FALSE; 
   
  /* Position each button. */ 
  EnumButtonInfo( hButtonList, (BUTTONINFOENUMPROC) EnableButtonProc, (LPARAM) (FPENABLEBUTTONFUNCDATA) &FuncData ); 
 
  /* Successful? */ 
  return( FuncData.bSuccess ); 
} 
 
 
 
/********************************************************************* 
** 
**  Gets the window handle of a button in the button bar  
**  based on its ID. 
** 
**********************************************************************/ 
HWND GetButtonWindow( HWND hButtonBarWnd, LPSTR lpButtonID ) 
{ 
  HBUTTONLIST hButtonList; 
  GETWINDOWFUNCDATA FuncData; 
   
   
  /* Get handle to button list. */ 
  hButtonList = (HBUTTONLIST) GetWindowLong( hButtonBarWnd, XBYTE_BUTTONLIST ); 
  if( hButtonList == 0 ) return 0; 
   
  /* Initialize the data. */ 
  FuncData.hWnd = 0; 
  FuncData.ButtonID = FindAtom( lpButtonID ); 
 
  /* No atom regisitered means no button exists. */ 
  if( FuncData.ButtonID == 0 ) return 0; 
   
  /* Find the button's window handle. */ 
  EnumButtonInfo( hButtonList, (BUTTONINFOENUMPROC) FindButtonHandle, (LPARAM) (FPGETWINDOWFUNCDATA) &FuncData ); 
 
  /* Return button window found. */ 
  return( FuncData.hWnd ); 
} 
 
 
 
/********************************************************************* 
** 
**  Gets the number of buttons in the button bar. 
** 
**********************************************************************/ 
WORD GetNumberOfButtons( HWND hButtonBarWnd ) 
{ 
  WORD nButtons = 0; 
  HBUTTONLIST hButtonList; 
   
 
  /* Get handle to button list. */ 
  hButtonList = (HBUTTONLIST) GetWindowLong( hButtonBarWnd, XBYTE_BUTTONLIST ); 
  if( hButtonList == 0 ) return 0; 
   
  /* Position each button. */ 
  EnumButtonInfo( hButtonList, (BUTTONINFOENUMPROC) CountButtons, (LPARAM) (WORD *) &nButtons ); 
 
  /* Return number of buttons found. */ 
  return( nButtons ); 
} 
 
 
/********************************************************************* 
** 
**  Remove all of the buttons in the button bar. 
** 
**********************************************************************/ 
void RemoveAllButtons( HWND hButtonBarWnd ) 
{ 
  HBUTTONLIST hButtonList; 
 
  /* Get handle to button list. */ 
  hButtonList = (HBUTTONLIST) GetWindowLong( hButtonBarWnd, XBYTE_BUTTONLIST ); 
  if( hButtonList == 0 ) return; 
   
  /* Free button list. */ 
  FreeButtonInfoList( hButtonBarWnd, &hButtonList ); 
 
  /* Initialize extrabytes data for button list. */ 
  SetWindowLong( hButtonBarWnd, XBYTE_BUTTONLIST, 0 ); 
} 
 
 
/*********************************************************************************** 
** 
**                              Private Functions 
** 
************************************************************************************/ 
 
 
/********************************************************************* 
** 
**  Callback function for the ChangeButtonsMacro() function. 
**  Finds the button with the ID and changes its macro string. 
** 
**********************************************************************/ 
static BOOL CALLBACK ChangeButtonsMacroProc( FPBUTTONINFO fpButtonInfo, LPARAM lParam ) 
{ 
  FPCHANGEBUTTONSMACROFUNCDATA FuncData ; 
   
  /* Pointer to macro string. */ 
  HGLOBAL      hMacroString; 
  char * MacroStringPtr; 
   
 
  /* Get lParam data. */ 
  FuncData = (FPCHANGEBUTTONSMACROFUNCDATA) lParam; 
 
  /* Found correct button. */ 
  if( FuncData->ButtonID == fpButtonInfo->ButtonID ) 
  { 
    /* Allocate memory for storage of macro string. */ 
    if( !GlobalAllocMem( FuncData->hButtonBarWnd, &hMacroString,  
                        strlen( FuncData->MacroStringPtr ) + sizeof(char) ) ) 
    { 
      /* Stop enumerating. */ 
      return( FALSE ); 
    } 
     
    /* Copy macro string storage area. */ 
    MacroStringPtr = (char *) GlobalLock( hMacroString ); 
    strcpy( MacroStringPtr, FuncData->MacroStringPtr ); 
    GlobalUnlock( hMacroString ); 
 
    /* Free the current macro string. */ 
    MyGlobalFree( fpButtonInfo->hMacroString );  
 
    /* Assign new macro string. */ 
    fpButtonInfo->hMacroString = hMacroString; 
     
    /* Successful. */ 
    FuncData->bSuccess = TRUE; 
     
    /* Stop enumerating. */ 
    return FALSE; 
  } 
 
  /* Get next button's info. */ 
  return TRUE; 
} 
 
 
 
/********************************************************************* 
** 
**  Callback function for the EnableButton() function. 
**  Finds the button with the ID and enables/disables it. 
** 
**********************************************************************/ 
static BOOL CALLBACK EnableButtonProc( FPBUTTONINFO fpButtonInfo, LPARAM lParam ) 
{ 
  FPENABLEBUTTONFUNCDATA FuncData ; 
  char * MacroStringPtr; 
 
  /* Get lParam data. */ 
  FuncData = (FPENABLEBUTTONFUNCDATA) lParam; 
 
  /* Found correct button. */ 
  if( FuncData->ButtonID == fpButtonInfo->ButtonID ) 
  { 
    /* Disable/Enable the button. */ 
    EnableWindow( fpButtonInfo->hButtonWnd, FuncData->bEnable ); 
     
    /* If button enabled state is changing. */ 
    if( fpButtonInfo->bEnabled != FuncData->bEnable ) 
    { 
      /* Save enabled state. */ 
      fpButtonInfo->bEnabled = FuncData->bEnable; 
       
      /* If now disabled, delete accelerator. */ 
      if( fpButtonInfo->bEnabled == FALSE && fpButtonInfo->wAccelKey != 0 ) 
      { 
        RemoveAccelerator( GetParent(FuncData->hButtonBarWnd), fpButtonInfo->wAccelKey, ANY_KEYSTATESTATE ); 
      } 
   
      /* If now enabled, add accelerator. */ 
      else if( fpButtonInfo->bEnabled == TRUE && fpButtonInfo->wAccelKey != 0 ) 
      { 
        /* Lock macro string storage area. */ 
        MacroStringPtr = (char *) GlobalLock( fpButtonInfo->hMacroString ); 
     
        if( !AddAccelerator( GetParent(FuncData->hButtonBarWnd), fpButtonInfo->wAccelKey,  
                             ANY_KEYSTATESTATE, MacroStringPtr ) ) 
        { 
          /* Error. */  
          MsgBox( GetLibInst(), FuncData->hButtonBarWnd, IDS_ERRORTITLE, CANTADDBTNACCEL, MB_ICONHAND | MB_OK ); 
        } 
   
        /* Unlock macro string storage area. */ 
        GlobalUnlock( fpButtonInfo->hMacroString ); 
      } 
    } 
     
    /* Successful. */ 
    FuncData->bSuccess = TRUE; 
     
    /* Stop enumerating. */ 
    return FALSE; 
  } 
 
  /* Get next button's info. */ 
  return TRUE; 
} 
 
 
 
/********************************************************************* 
** 
**  Callback function for the GetButtonWindow() function. 
**  Finds the button with the ID and saves its window handle. 
** 
**********************************************************************/ 
static BOOL CALLBACK FindButtonHandle( FPBUTTONINFO fpButtonInfo, LPARAM lParam ) 
{ 
  FPGETWINDOWFUNCDATA FuncData ; 
   
  /* Get lParam data. */ 
  FuncData = (FPGETWINDOWFUNCDATA) lParam; 
 
  /* Found correct button. */ 
  if( FuncData->ButtonID == fpButtonInfo->ButtonID ) 
  { 
    /* Save button's window handle. */ 
    FuncData->hWnd = fpButtonInfo->hButtonWnd; 
 
    /* Stop enumerating. */ 
    return FALSE; 
  } 
 
  /* Get next button's info. */ 
  return TRUE; 
} 
 
 
/********************************************************************* 
** 
**  Callback function when the button bar is notified that a button  
*   is pressed. Performs the action associated with the pressed button. 
** 
**********************************************************************/ 
static BOOL CALLBACK ButtonIsPressed( FPBUTTONINFO fpButtonInfo, LPARAM lParam ) 
{ 
  char * MacroStringPtr; 
  FPBTNPRESSEDFUNCDATA ButtonPressedDataPtr; 
  char szMacro[MAX_MACRO_STRING]; 
 
  /* Get button pressed data. */ 
  ButtonPressedDataPtr = ( FPBTNPRESSEDFUNCDATA ) lParam; 
   
  /* If this is the correct button. */ 
  if( ButtonPressedDataPtr->ButtonID == fpButtonInfo->ButtonID ) 
  { 
    /* Get the macro string. */ 
    MacroStringPtr = (char *) GlobalLock( fpButtonInfo->hMacroString ); 
     
    /* Copy the macro string. */ 
    strcpy( szMacro, MacroStringPtr ); 
       
    /* Unlock macro string. */ 
    GlobalUnlock( fpButtonInfo->hMacroString ); 
   
    /* Run the macro. */ 
    RunHelpMacro( ButtonPressedDataPtr->hExecWnd, ButtonPressedDataPtr->hDataWnd, szMacro ); 
 
    /* Found it - stop enumerating. */ 
    return FALSE; 
  } 
 
  /* Get next button's info. */ 
  return TRUE; 
} 
 
 
 
/********************************************************************* 
** 
**  Adjusts current button positions. 
** 
**********************************************************************/ 
 
static void  RepositionButtons( HWND hButtonBarWnd, WORD wMaxYPos, WORD * wBtnBarWndHeight ) 
{ 
  REPOSITIONFUNCDATA RepositionFuncData; 
  HBUTTONLIST hButtonList; 
 
 
  /* Get handle to button list. */ 
  hButtonList = (HBUTTONLIST) GetWindowLong( hButtonBarWnd, XBYTE_BUTTONLIST ); 
  if( hButtonList == 0 ) return; 
 
  /* Init. data. */ 
  RepositionFuncData.wNextXPos = 0; 
  RepositionFuncData.wNextYPos = 0; 
  RepositionFuncData.wMaxYPos  = wMaxYPos; 
  RepositionFuncData.wBtnBarWndHeight = 0; 
  RepositionFuncData.wNumberButtons = 0; 
  RepositionFuncData.wButtonWidth = GetWindowWord( hButtonBarWnd, XBYTE_BUTTONWIDTH ); 
  RepositionFuncData.wButtonHeight = GetWindowWord( hButtonBarWnd, XBYTE_BUTTONHEIGHT ); 
   
  /* Position each button. */ 
  EnumButtonInfo( hButtonList, (BUTTONINFOENUMPROC) RepositionGivenButton, (LPARAM) (FPREPOSITIONFUNCDATA) &RepositionFuncData ); 
 
  /* Get the height of the button bar. */ 
  *wBtnBarWndHeight = RepositionFuncData.wBtnBarWndHeight; 
} 
 
 
 
 
 
/********************************************************************* 
** 
**  Callback function for the RepositionButtons() function. 
**  Repositions the button passed to the function. 
** 
**********************************************************************/ 
static BOOL CALLBACK RepositionGivenButton( FPBUTTONINFO fpButtonInfo, LPARAM lParam ) 
{ 
  FPREPOSITIONFUNCDATA fpRepositionFuncData; 
 
   
  /* Get data passed from the RepositionButtons() function. */ 
  fpRepositionFuncData = (FPREPOSITIONFUNCDATA) lParam; 
   
  /* Do we need to skip to the next row? */ 
  if( fpRepositionFuncData->wNextXPos + fpRepositionFuncData->wButtonWidth > fpRepositionFuncData->wMaxYPos ) 
  { 
    /* Reset x-position. */ 
    fpRepositionFuncData->wNextXPos = 0;     
 
    /* If this fpRepositionFuncData the first button, don't skip to next line. */ 
    if( fpRepositionFuncData->wNumberButtons != 0 ) 
    { 
      /*  
      ** Move to the next row. All buttons should be the same height so just use 
      ** current button's height to calc. position of next row.  
      */ 
      fpRepositionFuncData->wNextYPos = fpRepositionFuncData->wNextYPos + fpRepositionFuncData->wButtonHeight; 
    } 
  } 
 
  /* Move the button to next position. */ 
  MoveWindow( fpButtonInfo->hButtonWnd, fpRepositionFuncData->wNextXPos, fpRepositionFuncData->wNextYPos, fpRepositionFuncData->wButtonWidth, fpRepositionFuncData->wButtonHeight, TRUE ); 
 
  /* Move to the next x-position. */ 
  fpRepositionFuncData->wNextXPos  = fpRepositionFuncData->wNextXPos + fpRepositionFuncData->wButtonWidth; 
 
  /* Set last known height of button bar. */ 
  fpRepositionFuncData->wBtnBarWndHeight = fpRepositionFuncData->wNextYPos + fpRepositionFuncData->wButtonHeight; 
 
  /* Show button if it was just created and is not visible yet. */ 
  ShowWindow( fpButtonInfo->hButtonWnd, SW_SHOW );  
 
  /* Another button processed. */ 
  fpRepositionFuncData->wNumberButtons = fpRepositionFuncData->wNumberButtons + 1; 
 
  /* Get next button's info. */ 
  return TRUE; 
} 
 
 
/********************************************************************* 
** 
**  Callback function for the GetMaxButtonSizes() function. 
**  Finds the maximum width and height. 
** 
**********************************************************************/ 
static BOOL CALLBACK GetMaxButtonSizesProc( FPBUTTONINFO fpButtonInfo, LPARAM lParam ) 
{ 
  HDC hDC; 
  char szButtonText[ MAX_BUTTON_TEXT ]; 
  WORD wButtonWidth, wButtonHeight, wTextWidth, wTextHeight; 
  SIZE TextSize; 
   
  FPCALCSIZEFUNCDATA FuncData; 
   
   
  /* Get lParam data. */ 
  FuncData = (FPCALCSIZEFUNCDATA) lParam; 
 
  /* Get the button's text. */ 
  GetWindowText( fpButtonInfo->hButtonWnd, szButtonText, MAX_BUTTON_TEXT ); 
   
  /* Get a DC for the button. */ 
  hDC = GetDC( fpButtonInfo->hButtonWnd ); 
     
  /* Get button's text width in pixels. */ 
  GetTextExtentPoint32( hDC, szButtonText, strlen(szButtonText), &TextSize ); 
  wTextWidth = TextSize.cx;   
  wTextHeight = TextSize.cy;   
     
  /* Release DC. */ 
  ReleaseDC( fpButtonInfo->hButtonWnd, hDC ); 
 
  /* Each button has the same width - longest string defines that width. */ 
  wButtonWidth  = wTextWidth; 
  wButtonHeight = wTextHeight + ( wTextHeight * 1/4 ); 
       
  /* New max. button width. */ 
  if( FuncData->wMaxButtonWidth < wButtonWidth ) 
  { 
    FuncData->wMaxButtonWidth = wButtonWidth; 
  } 
       
  /* New max. button height. */ 
  if( FuncData->wMaxButtonHeight < wButtonHeight ) 
  { 
    FuncData->wMaxButtonHeight = wButtonHeight; 
  } 
       
  /* Get next button's info. */ 
  return TRUE; 
} 
 
 
 
/*************************************************************************************** 
** 
** 
**                              Button List Management Functions 
** 
** 
***************************************************************************************/ 
 
 
/************************************* 
* 
*  Add info for a button to the list. 
*                   
**************************************/ 
static BOOL SaveButtonInfo( HWND hErrorWnd, HBUTTONLIST * hButtonList, FPBUTTONINFO fpButtonInfo ) 
{ 
  HBUTTONINFO hNewButtonInfo; 
 
   
  /* Create new BUTTONINFO structure. */ 
  hNewButtonInfo = NewButtonInfo( hErrorWnd, fpButtonInfo ); 
  if( hNewButtonInfo == 0 ) return FALSE;  
   
  /* 
  ** Insert the BUTTONINFO structure into the button List. 
  */ 
  InsertButtonInfo( hButtonList, hNewButtonInfo ); 
   
  /* Return success. */ 
  return TRUE; 
} 
 
 
 
/************************************* 
* 
*  Create a new button info. structure. 
*                   
**************************************/ 
static HBUTTONINFO NewButtonInfo( HWND hErrorWnd, FPBUTTONINFO fpButtonInfo ) 
{ 
  HBUTTONINFO hNewButtonInfo; 
  FPBUTTONINFO fpNewButtonInfo; 
 
   
  /* Allocate the new button info. */ 
  if( ! GlobalAllocMem( hErrorWnd, &hNewButtonInfo, sizeof(BUTTONINFO) ) ) return NULL; 
   
  /* Lock it. */ 
  fpNewButtonInfo = (FPBUTTONINFO) GlobalLock( hNewButtonInfo ) ; 
 
  /* Initialize general elements in the structure. */ 
  fpNewButtonInfo->hButtonWnd   = fpButtonInfo->hButtonWnd; 
  fpNewButtonInfo->ButtonID     = fpButtonInfo->ButtonID; 
  fpNewButtonInfo->hMacroString = fpButtonInfo->hMacroString; 
  fpNewButtonInfo->bEnabled     = fpButtonInfo->bEnabled; 
  fpNewButtonInfo->wAccelKey    = fpButtonInfo->wAccelKey; 
  fpNewButtonInfo->Next         = 0; 
 
  /* Unlock it. */ 
  GlobalUnlock( hNewButtonInfo ) ; 
 
  /* Return it. */ 
  return hNewButtonInfo; 
} 
 
 
 
/************************************* 
* 
*  Free a button info. structure. 
*                   
*  If there is an accelerator for the 
*  button and the button is active, 
*  then there is an accelerator for the 
*  button in the accelerator table that 
*  needs to be removed.                 
*                   
**************************************/ 
static void FreeButtonInfo( HWND hButtonBarWnd, HBUTTONINFO hButtonInfo ) 
{ 
  FPBUTTONINFO fpButtonInfo; 
   
  /* Lock it. */ 
  fpButtonInfo = (FPBUTTONINFO) GlobalLock( hButtonInfo ) ; 
 
  /* Delete allocated info. */ 
  DeleteAtom( fpButtonInfo->ButtonID ); 
  MyGlobalFree( fpButtonInfo->hMacroString );  
     
  /* Delete button. */ 
  DestroyWindow( fpButtonInfo->hButtonWnd ); 
     
  /* Delete accelerator. */ 
  if( fpButtonInfo->wAccelKey != 0 && fpButtonInfo->bEnabled == TRUE ) 
  { 
    RemoveAccelerator( GetParent(hButtonBarWnd), fpButtonInfo->wAccelKey, ANY_KEYSTATESTATE ); 
  } 
 
  /* Unlock it. */ 
  GlobalUnlock( hButtonInfo ) ; 
 
  /* Free it. */ 
  MyGlobalFree( hButtonInfo ) ; 
} 
 
 
 
/************************************* 
* 
*  Insert a new button info. structure 
*  at the end of the button info. list. 
*                   
**************************************/ 
static void InsertButtonInfo( HBUTTONLIST * hButtonList, HBUTTONINFO hNewButtonInfo ) 
{ 
  FPBUTTONINFO fpCurrInfo; 
  HBUTTONINFO hCurrent, hNext; 
   
  /* No existing first node - insert it. */ 
  if( *hButtonList == 0 )  
  { 
    *hButtonList = hNewButtonInfo; 
    return; 
  } 
 
  /* Assign current handle the top handle to the list. */ 
  hCurrent = *hButtonList; 
 
  /* Move to end. */ 
  while( TRUE ) 
  { 
      /* Lock current node. */ 
    fpCurrInfo = (FPBUTTONINFO) GlobalLock( hCurrent ) ; 
     
    /* At the last node. */ 
    if( fpCurrInfo->Next == 0 ) 
    { 
      /* Point new node to existing top node. */ 
      fpCurrInfo->Next = hNewButtonInfo; 
     
      /* Unlock current node. */ 
      GlobalUnlock( hCurrent ) ; 
     
      return; 
    } 
 
    /* Go to next node. */ 
    else 
    { 
      /* Get handle to next node. */ 
      hNext = fpCurrInfo->Next; 
     
      /* Unlock current node. */ 
      GlobalUnlock( hCurrent ) ; 
     
      /* Assign current node to next node. */ 
      hCurrent = hNext; 
    } 
  } 
} 
 
 
/************************************* 
* 
*  Delete/Free button info. list.  
*                   
**************************************/ 
static void FreeButtonInfoList( HWND hButtonBarWnd, HBUTTONLIST * hButtonList ) 
{ 
  FPBUTTONINFO fpButtonInfo; 
  HBUTTONINFO  hTmpButtonInfo; 
 
 
  while( *hButtonList != 0 ) 
  { 
    /* Lock current node. */ 
    fpButtonInfo = (FPBUTTONINFO) GlobalLock( *hButtonList ) ; 
   
    /* Next node is to be new top node. */ 
    hTmpButtonInfo = fpButtonInfo->Next;  
     
    /* Unlock top handle. */ 
    GlobalUnlock( *hButtonList ); 
         
    /* Free top. */ 
    FreeButtonInfo( hButtonBarWnd, *hButtonList );  
 
    /* Assign new top. */ 
    *hButtonList = hTmpButtonInfo;  
  } 
} 
 
 
 
/************************************* 
* 
*  Get the next button info. structure 
*  after the given hButtonInfo. 
*                   
**************************************/ 
static HBUTTONINFO GetNextButtonInfo( HBUTTONINFO hButtonInfo ) 
{ 
  HBUTTONINFO hNextButtonInfo; 
  FPBUTTONINFO fpButtonInfo; 
   
  /* Handle bad. */ 
  if( hButtonInfo == 0 ) return 0; 
 
  /* Lock it. */ 
  fpButtonInfo = (FPBUTTONINFO) GlobalLock( hButtonInfo ) ; 
   
  /* Point new node to existing top node. */ 
  hNextButtonInfo = fpButtonInfo->Next; 
 
  /* Unlock it. */ 
  GlobalUnlock( hButtonInfo ) ; 
 
  /* Return next button info's handle. */ 
  return hNextButtonInfo; 
} 
 
 
 
/************************************* 
* 
*  Enumerates all BUTTONINFO structures 
*  contained in the button info. list. 
*                   
**************************************/ 
static void EnumButtonInfo( HBUTTONLIST hButtonList, BUTTONINFOENUMPROC ButtonInfoEnumProc, LPARAM lParam ) 
{ 
  FPBUTTONINFO fpButtonInfo; 
  HBUTTONINFO hButtonInfo; 
  BOOL bReturn; 
   
   
  /* Get first button info's handle. */ 
  hButtonInfo = hButtonList; 
   
  while( hButtonInfo != 0 ) 
  { 
    /* Lock it. */ 
    fpButtonInfo = (FPBUTTONINFO) GlobalLock( hButtonInfo ) ; 
   
    /* Call callback function. */ 
    bReturn = (*ButtonInfoEnumProc)( fpButtonInfo, lParam ); 
     
    /* Stop enumerating. */ 
    if( bReturn == FALSE ) 
    { 
      /* Unlock it. */ 
      GlobalUnlock( hButtonInfo ); 
 
      /* Stop enumerating. */ 
      return; 
    } 
     
    /* Unlock it. */ 
    GlobalUnlock( hButtonInfo ); 
 
    hButtonInfo = GetNextButtonInfo( hButtonInfo ); 
  } 
} 
 
 
 
/********************************************************************* 
** 
**  Callback function for the GetNumberOfButtons() function. 
**  Increments the button counter. 
** 
**********************************************************************/ 
static BOOL CALLBACK CountButtons( FPBUTTONINFO fpButtonInfo, LPARAM lParam ) 
{ 
  WORD * nButtons; 
   
  nButtons = (WORD *) lParam; 
  *nButtons = *nButtons + 1;  
   
  /* Get next button's info. */ 
  return TRUE; 
} 
 
 
/************************************* 
* 
*  Remove and free a button info. 
*  from the button info. list based on 
*  its wButtonID value. 
*                   
**************************************/ 
static BOOL DeleteButtonWithID( HWND hButtonBarWnd, HBUTTONLIST * hButtonList, ATOM ButtonID  ) 
{ 
  FPBUTTONINFO fpButtonInfo, fpTmpButtonInfo; 
  HBUTTONINFO hButtonInfo, hTmpButtonInfo; 
   
 
  /* Existing first node. */ 
  if( *hButtonList == 0 ) return FALSE; 
 
  /* Lock top handle. */ 
  fpButtonInfo = (FPBUTTONINFO) GlobalLock( *hButtonList ) ; 
   
  /* If deleting top node. */ 
  if( fpButtonInfo->ButtonID == ButtonID ) 
  { 
    /* Next node is to be new top node. */ 
    hTmpButtonInfo = fpButtonInfo->Next;  
 
    /* Unlock top handle. */ 
    GlobalUnlock( *hButtonList ); 
     
    /* Free top. */ 
    FreeButtonInfo( hButtonBarWnd, *hButtonList );  
   
    /* Assign new top. */ 
    *hButtonList = hTmpButtonInfo; 
 
    /* Success. */ 
    return TRUE; 
  } 
 
  /* Unlock top handle. */ 
  GlobalUnlock( *hButtonList ); 
     
  /* Find correct node and remove. */ 
  hTmpButtonInfo = *hButtonList; 
  hButtonInfo = GetNextButtonInfo( *hButtonList ); 
 
  while( hButtonInfo != 0 ) 
  { 
    /* Lock current node. */ 
    fpButtonInfo = (FPBUTTONINFO) GlobalLock( hButtonInfo ) ; 
   
    /* If deleting current node. */ 
    if( fpButtonInfo->ButtonID == ButtonID ) 
    { 
      /* Lock previous node. */ 
      fpTmpButtonInfo = (FPBUTTONINFO) GlobalLock( hTmpButtonInfo ) ; 
 
      /* Previous node's "next node" is deleted node's "next node". */ 
      fpTmpButtonInfo->Next = fpButtonInfo->Next;  
   
      /* Unlock previous node. */ 
      GlobalUnlock( hTmpButtonInfo ); 
 
      /* Unlock current node. */ 
      GlobalUnlock( hButtonInfo ); 
       
      /* Free top. */ 
      FreeButtonInfo( hButtonBarWnd, hButtonInfo );  
   
      /* Success. */ 
      return TRUE; 
    } 
     
    /* Unlock it. */ 
    GlobalUnlock( hButtonInfo ) ; 
 
    /* Get next node. */ 
    hTmpButtonInfo = hButtonInfo; 
    hButtonInfo = GetNextButtonInfo( hButtonInfo ); 
  } 
 
  /* Did not find button. */ 
  return FALSE; 
}