CHILD.C

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1994-1997 Microsoft Corporation. All Rights Reserved.
//
// FILE:
// CHILD.C
//
// PURPOSE:
// Routines for child windows.
//
// PLATFORMS:
// Windows 95, Windows NT
//
// SPECIAL INSTRUCTIONS: N/A
//

// These pragmas allow for the maximum warning level to be set
// without getting bombarded by warnings from the Windows SDK
// include files.
#pragma warning(disable:4001) // Single-line comment warnings
#pragma warning(disable:4115) // Named type definition in parentheses
#pragma warning(disable:4201) // Nameless struct/union warning
#pragma warning(disable:4214) // Bit field types other than int warnings
#pragma warning(disable:4514) // Unreferenced inline function has been removed

// Windows Header Files:
#include <Windows.h>
#include <WindowsX.h>
#include <CommCtrl.h>
#include <STDLIB.H>
#include "icm.h"

// Restore the warnings--leave the single-line comment warning OFF
#pragma warning(default:4115) // Named type definition in parentheses
#pragma warning(default:4201) // Nameless struct/union warning
#pragma warning(default:4214) // Bit field types other than int warnings

// C RunTime Header Files

// Local Header Files
#include "icmview.h"
#include "resource.h"
#include "child.h"
#include "DibInfo.H"
#include "dialogs.h"
#include "Dibs.h"
#include "print.h"
#include "debug.h"

// Pre-processor definitions and macros

// Typedefs and structs

typedef struct TAG_PROGRESSPARAM
{
BOOL bCancel;
HWND hWnd;
HWND hDialog;
DWORD dwStartTick;
} PROGRESSPARAM, *PPROGRESSPARAM;


// External global data

// Private Global DataPrivate data

// Function prototypes
void ChildWndPaint (HWND hWnd);
BOOL ChildWndQueryNewPalette (HWND hWnd, HWND hWndFrame);
BOOL WINAPI TransformProgress(ULONG ulMax, ULONG ulCurrent, ULONG ulCallbackData);
BOOL CALLBACK TransformProgressProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
void SizeScrollBars(HWND hWnd, LPDIBINFO lpDIBInfo, UINT uiWindowHeight, UINT uiWindowWidth);
void InitializeScrollBars(HWND hWnd, LPDIBINFO lpDIBInfo);
BOOL ScrollChildWindow(HWND hWnd, int nScrollBar, WORD wScrollCode);



//////////////////////////////////////////////////////////////////////////
// Function: ChildWndProc
//
// Description:
// Child window procedure.
//
// Parameters:
// HWND handle of window
// UINT message identifier
// WPARAM first message parameter
// LPARAM second message parameter
//
// Returns:
//
// Comments:
//
//////////////////////////////////////////////////////////////////////////

LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
LPDIBINFO lpDIBInfo;
int wmId, wmEvent;

// Initialize variables
lpDIBInfo = GetDIBInfoPtr(hWnd);
switch (uiMsg)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);

switch (wmId)
{
case ID_FILE_SAVEAS:
SaveDIBToFileDialog(hWnd, lpDIBInfo);
break;

case IDM_FILE_PRINT_SETUP:
CreateDIBPropSheet(hWnd, ghInst, DIB_PROPSHEET_PRINT, lpDIBInfo->lpszImageFileName);
SetFocus(ghAppWnd);
break;

case IDM_FILE_PRINT:
PrintDialog(hWnd, ghInst, lpDIBInfo);
break;

case IDM_FILE_DISPLAY:
CreateDIBPropSheet(hWnd, ghInst, DIB_PROPSHEET_DISPLAY, lpDIBInfo->lpszImageFileName);
InvalidateRect(hWnd, NULL, FALSE);
break;

case IDM_FILE_CLOSE:
SendMessage(hWnd, WM_CLOSE, 0, 0);
break;

case IDM_FILE_ICM20:
case IDM_FILE_ICM10:
case IDM_FILE_CONFIGURE_ICM:
{
HGLOBAL hDIBInfo;
BOOL bFreed;

switch (wmId)
{
case IDM_FILE_ICM20:
if (!CHECK_DWFLAG(lpDIBInfo->dwICMFlags, ICMVFLAGS_ICM20)) // Don't redundantly transform DIB
{
SetDWFlags((LPDWORD)&(lpDIBInfo->dwICMFlags), ICMVFLAGS_CREATE_TRANSFORM, TRUE);
SetDWFlags((LPDWORD)&(lpDIBInfo->dwICMFlags), (ICMVFLAGS_ICM20), TRUE);
}
break;

case IDM_FILE_ICM10:
SetDWFlags((LPDWORD)&(lpDIBInfo->dwICMFlags), ICMVFLAGS_ICM20, FALSE);
break;

case IDM_FILE_CONFIGURE_ICM:
ColorMatchUI(hWnd, lpDIBInfo);
break;

default:
break;
}
hDIBInfo = GlobalHandle(lpDIBInfo);
bFreed = GlobalUnlock(hDIBInfo);
InvalidateRect(hWnd, NULL, FALSE);
}
break;

default:
return(DefMDIChildProc(hWnd, uiMsg, wParam, lParam));
break;
}
break;

case WM_CONTEXTMENU:
{
HMENU hMenu;
hMenu = InitImageMenu(hWnd);
if (hMenu)
{
TrackPopupMenu (hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, LOWORD(lParam), HIWORD(lParam), 0, hWnd, NULL);
DestroyMenu(hMenu);
}
return(0);
}
break;

case WM_PAINT:
ChildWndPaint(hWnd);
return(0); // We have processed the message
break;

case WM_CLOSE:
SendMessage(ghWndMDIClient, WM_MDIDESTROY, (WPARAM)(HWND)hWnd, 0);
DestroyWindow(hWnd);
SetFocus(ghAppWnd);
return(1L);

case WM_DESTROY:
fFreeDIBInfo(GetDIBInfoHandle(hWnd), TRUE);
SetWindowLong(hWnd, GWL_DIBINFO, (LONG)NULL);
break;

case WM_MDIACTIVATE:
if (0 != wParam)
{
SendMessage (hWnd, MYWM_QUERYNEWPALETTE, (WPARAM) GetParent(ghWndMDIClient), 0L);
}
break;

case WM_SIZE:
// May need to show or hide scroll bars.
SizeScrollBars(hWnd, lpDIBInfo, HIWORD(lParam), LOWORD(lParam));

// This makes sure that we redraw
// all the window if we are stretching to window
// and the size changes.
InvalidateRect(hWnd, NULL, FALSE);
break;

case WM_VSCROLL:
ScrollChildWindow(hWnd, SB_VERT, LOWORD(wParam));
return FALSE;

case WM_HSCROLL:
ScrollChildWindow(hWnd, SB_HORZ, LOWORD(wParam));
return FALSE;

case MYWM_QUERYNEWPALETTE:
return ChildWndQueryNewPalette (hWnd, (HWND) wParam);
}
return DefMDIChildProc(hWnd, uiMsg, wParam, lParam);
}

//////////////////////////////////////////////////////////////////////////
// Function: CreateNewImageWindow
//
// Description:
// Creates a new window for a new image. As this function is the
// startup function for a new thread, some administrative duties
// are required. The following actions are taken when the new
// thread is created:
// -A new window is created.
// -A DIBINFO structure is created and associated with the
// window via a SetWindowLong call.
// -The contents of the DIBINFO structure associated with the
// main application window are copied into the new window's
// DIBINFO structure.
//
// Parameters:
// DWORD Actually an LPTSTR to the file name of the image.
//
// Returns:
// DWORD to be returned by implicit CloseThread call
//
// Comments:
//
//////////////////////////////////////////////////////////////////////////

DWORD CreateNewImageWindow(HANDLE hDIBInfo)
{
// Local variables
int nWidth;
int nHeight;
HWND hwndImage;
HANDLE hAccel;
RECT rcWindow;
RECT rcClient;
LPDIBINFO lpDIBInfo;


// Initialize variables
ASSERT(ghWndMDIClient != NULL); // If it's NULL, won't get a window for thread
ASSERT(NULL != hDIBInfo);
if (NULL == hDIBInfo)
{
return(FALSE);
}
lpDIBInfo = GlobalLock(hDIBInfo);

// Determine the necessary window size to hold the DIB.
rcWindow.left = 0;
rcWindow.top = 0;
rcWindow.right = (int) lpDIBInfo->uiDIBWidth;
rcWindow.bottom = (int) lpDIBInfo->uiDIBHeight;

if (!AdjustWindowRectEx (&rcWindow,
MDIS_ALLCHILDSTYLES|WS_CHILD|WS_SYSMENU|WS_CAPTION|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX,
FALSE, WS_EX_MDICHILD))
{
DebugMsg(__TEXT("CreateNewImageWindow: AdjustWindowRectEx failed, LastError = %lu\r\n"), GetLastError());
return(ERROR_INVALID_PARAMETER);
}

// Make sure child window is not larger than parent's client area.
GetClientRect(ghWndMDIClient, &rcClient);
nWidth = __min(rcClient.right, rcWindow.right - rcWindow.left);
nHeight = __min(rcClient.bottom, rcWindow.bottom - rcWindow.top);

// Create child window; class declaration leaves room for pointer to DIBINFO.
hwndImage = CreateWindowEx(WS_EX_MDICHILD, CHILD_CLASSNAME,
lpDIBInfo->lpszImageFileName,
MDIS_ALLCHILDSTYLES | WS_CHILD|WS_SYSMENU|WS_CAPTION|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX,
0, 0, nWidth, nHeight, ghWndMDIClient, NULL, ghInst, 0);

if (hwndImage != NULL)
{
// Obtain handle to main window's ICMINFO
lpDIBInfo->hWndOwner = hwndImage; // Establish ownership of DIBINFO structure
SetWindowLong(hwndImage, GWL_DIBINFO, (LONG)hDIBInfo);

// Initial scroll information.
InitializeScrollBars(hwndImage, lpDIBInfo);

ShowWindow(hwndImage, SW_NORMAL);
UpdateWindow(hwndImage);

SendMessage(hwndImage, MYWM_QUERYNEWPALETTE, (WPARAM) ghAppWnd, 0L);
}
else
{
DebugMsg(__TEXT("CreateNewImageWindow: hwndImage = NULL, LastError = %lu\r\n"), GetLastError);
return(ERROR_INVALID_PARAMETER);
}

// Unlock handle
GlobalUnlock(hDIBInfo);
lpDIBInfo = NULL;

hAccel = LoadAccelerators(ghInst, __TEXT("ICMVIEW"));
ASSERT(hAccel);
return(ERROR_SUCCESS);
} // End of CreateNewImageWindow


///////////////////////////////////////////////////////////////////////
//
// Function: ChildWndPaint
//
// Purpose: Called by ChildWndProc() on WM_PAINT. Does all paints
// for this MDI child window.
//
// Reads position of scroll bars to find out what part
// of the DIB to display.
//
// Checks the stretching flag in the DIBINFO structure for
// this window to see if we are stretching to the window
// (if we're iconic, we always stretch to a tiny bitmap).
//
// Selects/Realizes the palette as a background palette.
// The palette was realized as the foreground palette in
// ChildWndQueryNewPalette() if this is the active window.
//
//
// Parms: hWnd == Handle to window being painted.
//
///////////////////////////////////////////////////////////////////////

void ChildWndPaint(HWND hWnd)
{
HDC hDC;
RECT rectClient;
RECT rectDDB;
BOOL bStretch;
HGLOBAL hDIBInfo;
LPDIBINFO lpDIBInfo;
HANDLE hDIB;
LPTSTR lpszTargetProfile;
DWORD dwLCSIntent;
HCURSOR hCur;
PAINTSTRUCT ps;
PROGRESSPARAM ProgressParam;
LPBITMAPINFOHEADER lpBi;

// Initialize variables
lpDIBInfo = NULL;
lpBi = NULL;
hDIBInfo = NULL;
hDIB = NULL;
lpszTargetProfile = NULL;

START_WAIT_CURSOR(hCur);
hDC = BeginPaint (hWnd, &ps);
hDIBInfo = GetDIBInfoHandle(hWnd);

if ( (NULL != hDC)
&&
(NULL != hDIBInfo)
&&
(RECTWIDTH(&ps.rcPaint) != 0)
&&
(RECTHEIGHT(&ps.rcPaint) !=0)
)
{
lpDIBInfo = (LPDIBINFO)GlobalLock(hDIBInfo);

//Check if we're using ICM outside of the DC. If so, it might be necessary to
// create a color transform.
if ( CHECK_DWFLAG(lpDIBInfo->dwICMFlags,ICMVFLAGS_ICM20) && CHECK_DWFLAG(lpDIBInfo->dwICMFlags, ICMVFLAGS_ENABLE_ICM))
{
// Outside DC selected
hDIB = lpDIBInfo->hDIBTransformed;
if (CHECK_DWFLAG(lpDIBInfo->dwICMFlags, ICMVFLAGS_CREATE_TRANSFORM) || (hDIB == NULL))
{
if (NULL != lpDIBInfo->hDIBTransformed) // Free old transformed bits
{
GlobalFree(lpDIBInfo->hDIBTransformed);
}
// Set up for transform
if (CHECK_DWFLAG(lpDIBInfo->dwICMFlags, ICMVFLAGS_PROOFING))
{
lpszTargetProfile = lpDIBInfo->lpszTargetProfile;
}
// Initialize Progress Param structure.
ProgressParam.bCancel = FALSE;
ProgressParam.hWnd = hWnd;
ProgressParam.hDialog = NULL;
ProgressParam.dwStartTick = GetTickCount();

ConvertIntent(lpDIBInfo->dwRenderIntent, ICC_TO_LCS, &dwLCSIntent);
lpDIBInfo->hDIBTransformed = TransformDIBOutsideDC(lpDIBInfo->hDIB,
lpDIBInfo->bmFormat,
lpDIBInfo->lpszMonitorProfile,
lpszTargetProfile,
USE_BITMAP_INTENT, NULL, //TransformProgress,
(ULONG) &ProgressParam);
// May need to destroy progress dialog.
if (IsWindow(ProgressParam.hDialog))
DestroyWindow(ProgressParam.hDialog);

if (NULL == lpDIBInfo->hDIBTransformed)
{
ErrMsg(hWnd, __TEXT("DIB Transform failed. Disabling ICM"));
hDIB = lpDIBInfo->hDIB;
SetDWFlags((LPDWORD)&(lpDIBInfo->dwICMFlags), ICMVFLAGS_ENABLE_ICM, FALSE);
}
else
{
SetDWFlags((LPDWORD)&(lpDIBInfo->dwICMFlags), ICMVFLAGS_CREATE_TRANSFORM, FALSE);
hDIB = lpDIBInfo->hDIBTransformed;
}
}
}
else // Inside DC selected
{
hDIB = lpDIBInfo->hDIB;
}

if (NULL != hDIB)
{
lpBi = GlobalLock(hDIB);
}
if (NULL == lpBi)
{
DebugMsg(__TEXT("CHILD.C : ChildWndPaint : NULL lpBi\r\n"));
goto ABORTPAINT;
}
bStretch = lpDIBInfo->bStretch;

// Set up the necessary rectangles -- i.e. the rectangle
// we're rendering into, and the rectangle in the DIB.
if (bStretch)
{
GetClientRect(hWnd, &rectClient);
rectDDB.left = 0;
rectDDB.top = 0;
rectDDB.right = BITMAPWIDTH(lpBi);
rectDDB.bottom = abs(BITMAPHEIGHT(lpBi));
}
else
{
int xScroll;
int yScroll;


xScroll = GetScrollPos(hWnd, SB_HORZ);
yScroll = GetScrollPos(hWnd, SB_VERT);
CopyRect(&rectClient, &ps.rcPaint);

rectDDB.left = xScroll + rectClient.left;
rectDDB.top = yScroll + rectClient.top;
rectDDB.right = rectDDB.left + RECTWIDTH(&rectClient);
rectDDB.bottom = rectDDB.top + RECTHEIGHT(&rectClient);

if (rectDDB.right > BITMAPWIDTH(lpBi))
{
int dx;

dx = BITMAPWIDTH(lpBi) - rectDDB.right;
rectDDB.right += dx;
rectClient.right += dx;
}

if (rectDDB.bottom > abs(BITMAPHEIGHT(lpBi)))
{
int dy;

dy = abs(BITMAPHEIGHT(lpBi)) - rectDDB.bottom;
rectDDB.bottom += dy;
rectClient.bottom += dy;
}
}
DIBPaint (hDC, &rectClient, hDIB, &rectDDB, lpDIBInfo);

// Draw the clipboard selection rubber-band.
SetWindowOrgEx (hDC, GetScrollPos(hWnd, SB_HORZ), GetScrollPos (hWnd, SB_VERT), NULL);
}
else
{
if (NULL == hDC)
{
END_WAIT_CURSOR(hCur);
DebugMsg(__TEXT("ChildWndPaint : NULL hDC\r\n"));
return;
}
}

ABORTPAINT:
EndPaint (hWnd, &ps);
END_WAIT_CURSOR(hCur);

if (hDIB && lpBi)
{
GlobalUnlock(hDIB);
}
if (lpDIBInfo)
{
GlobalUnlock(hDIBInfo);
}
}

///////////////////////////////////////////////////////////////////////
//
// Function: ChildWndQueryNewPalette
//
// Purpose: Called by ChildWndProc() on WM_QUERYNEWPALETTE.
//
// We get this message when an MDI child is getting
// focus (by hocus pocus in FRAME.C, and by passing
// this message when we get WM_MDIACTIVATE). Normally
// this message is passed only to the top level window(s)
// of an application.
//
// We want this window to have the foreground palette when this
// happens, so we select and realize the palette as
// a foreground palette (of the frame Window). Then make
// sure the window repaints, if necessary.
//
// Parms: hWnd == Handle to window getting WM_QUERYNEWPALETTE.
// hWndFrame == Handle to the frame window (i.e. the top-level
// window of this app.
//
///////////////////////////////////////////////////////////////////////

BOOL ChildWndQueryNewPalette (HWND hWnd, HWND hWndFrame)
{
HPALETTE hOldPal;
HDC hDC;
HGLOBAL hDIBInfo;
LPDIBINFO lpDIBInfo;
int nColorsChanged;

hDIBInfo = GetDIBInfoHandle (hWnd);

if (!hDIBInfo)
return FALSE;

lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);

if (!lpDIBInfo->hPal)
{
GlobalUnlock (hDIBInfo);
return FALSE;
}


// We're going to make our palette the foreground palette for
// this application. Window's palette manager expects the
// top-level window of the application to have the palette,
// so, we get a DC for the frame here!

hDC = GetDC (hWndFrame);
hOldPal = SelectPalette (hDC, lpDIBInfo->hPal, FALSE);

nColorsChanged = RealizePalette (hDC);
InvalidateRect (hWnd, NULL, FALSE);

if (hOldPal)
SelectPalette (hDC, hOldPal, FALSE);

ReleaseDC (hWndFrame, hDC);

GlobalUnlock (hDIBInfo);

return (nColorsChanged != 0);
}


//////////////////////////////////////////////////////////////////////////
// Function: TransformProgress
//
// Description:
//
//
// Parameters:
// @@@
//
// Returns:
// BOOL
//
// Comments:
//
//
//////////////////////////////////////////////////////////////////////////
BOOL WINAPI TransformProgress(ULONG ulMax, ULONG ulCurrent, ULONG ulCallbackData)
{
PPROGRESSPARAM pProgressParam = (PPROGRESSPARAM) ulCallbackData;


DebugMsg(__TEXT("TransformProgress: ulCurrent = %d, ulMax = %d\r\n"), ulCurrent, ulMax);

if (!IsWindow(pProgressParam->hDialog))
{
DWORD dwTick = GetTickCount();

// May need to create progress dialog for translate bitmap bits.
// Create dialog only if translation takes awhile to do.
if ( (dwTick - pProgressParam->dwStartTick > 1000L) && ((double)ulCurrent/ulMax < 0.33))
{
// Created dialog must be destroyed by TranslateBitmapBits caller.
pProgressParam->hDialog = CreateDialogParam(NULL, MAKEINTRESOURCE(IDD_TRANSLATE),
pProgressParam->hWnd, TransformProgressProc,
(LPARAM) ulCallbackData);
ShowWindow(pProgressParam->hDialog, SW_SHOWNORMAL);
UpdateWindow(pProgressParam->hDialog);
SendDlgItemMessage(pProgressParam->hDialog, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELONG(0, ulMax));
SendDlgItemMessage(pProgressParam->hDialog, IDC_PROGRESS, PBM_SETPOS, ulCurrent, 0);
}
}
else
{
// Update dialog's progress bar.
SendDlgItemMessage(pProgressParam->hDialog, IDC_PROGRESS, PBM_SETPOS, ulCurrent, 0);
}

return !pProgressParam->bCancel;
}



//////////////////////////////////////////////////////////////////////////
// Function: TransformProgressProc
//
// Description:
//
//
// Parameters:
// @@@
//
// Returns:
// BOOL
//
// Comments:
//
//
//////////////////////////////////////////////////////////////////////////
BOOL CALLBACK TransformProgressProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

switch (uMsg)
{
case WM_INITDIALOG:
ASSERT(lParam != 0);
SetWindowLong(hDlg, GWL_USERDATA, lParam);
return TRUE;

case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDCANCEL:
{
PPROGRESSPARAM pProgressParam = (PPROGRESSPARAM) GetWindowLong(hDlg, GWL_USERDATA);

pProgressParam->bCancel = TRUE;
}
break;
}
break;
}

return FALSE;
}


//////////////////////////////////////////////////////////////////////////
// Function: SizeScrollBars
//
// Description:
//
//
// Parameters:
// @@@
//
// Returns:
// BOOL
//
// Comments:
//
//
//////////////////////////////////////////////////////////////////////////
void SizeScrollBars(HWND hWnd, LPDIBINFO lpDIBInfo, UINT uiWindowHeight, UINT uiWindowWidth)
{
static BOOL bAdjusting = FALSE;


// Only size for validte windows and dib info.
// Don't SizeScrollBars if in process of adjusting them.
if ( IsWindow(hWnd)
&&
(NULL != lpDIBInfo)
&&
!bAdjusting
)
{
int nScrollHeight = GetSystemMetrics (SM_CXVSCROLL);
int nScrollWidth = GetSystemMetrics (SM_CYHSCROLL);
SCROLLINFO VertScrollInfo;
SCROLLINFO HorzScrollInfo;

// Make sure that we don't get into an infinite loop when updating scroll bars.
bAdjusting = TRUE;

// Get current vertical scroll info.
VertScrollInfo.cbSize = sizeof(SCROLLINFO);
VertScrollInfo.fMask = SIF_ALL;
GetScrollInfo(hWnd, SB_VERT, &VertScrollInfo);

// Get current horizontal scroll info.
HorzScrollInfo.cbSize = sizeof(SCROLLINFO);
HorzScrollInfo.fMask = SIF_ALL;
GetScrollInfo(hWnd, SB_HORZ, &HorzScrollInfo);

// Only adjust if not stretching to fit.
// Turn off scroll bars if stretching to fit.
if (!lpDIBInfo->bStretch)
{
// Adjust window width and height to account for current scroll bars.
if ((int) VertScrollInfo.nPage <= VertScrollInfo.nMax)
{
// INVARIANT: Vertical scroll bar exists.

// modify width to account for scroll bars width.
uiWindowWidth += nScrollWidth;
}
if ((int) HorzScrollInfo.nPage <= HorzScrollInfo.nMax)
{
// INVARIANT: Horizantal scroll bar exists.

// Modify height to account for scroll bars height.
uiWindowHeight += nScrollHeight;
}

// Adjust width and height based on what will happen.
if ((int) uiWindowHeight < VertScrollInfo.nMax)
{
uiWindowWidth -= nScrollWidth;
if ((int) uiWindowWidth < HorzScrollInfo.nMax)
{
uiWindowHeight -= nScrollHeight;
}
}
else if ((int) uiWindowWidth < HorzScrollInfo.nMax)
{
uiWindowHeight -= nScrollHeight;
if ((int) uiWindowHeight < VertScrollInfo.nMax)
{
uiWindowWidth -= nScrollWidth;
}
}

// If width or height equals bitmap, need to make it larger
// so that scroll bars will not appear.
if ((int) uiWindowWidth == HorzScrollInfo.nMax)
{
++uiWindowWidth;
}
if ((int) uiWindowHeight == VertScrollInfo.nMax)
{
++uiWindowHeight;
}
}
else
{
uiWindowHeight = VertScrollInfo.nMax + 1;
uiWindowWidth = HorzScrollInfo.nMax + 1;
}

// Update vertical scroll info.
VertScrollInfo.fMask = SIF_PAGE;
VertScrollInfo.nPage = uiWindowHeight;
SetScrollInfo(hWnd, SB_VERT, &VertScrollInfo, FALSE);

// Update horizontal scroll info.
HorzScrollInfo.fMask = SIF_PAGE;
HorzScrollInfo.nPage = uiWindowWidth;
SetScrollInfo(hWnd, SB_HORZ, &HorzScrollInfo, FALSE);

bAdjusting = FALSE;
}
}


//////////////////////////////////////////////////////////////////////////
// Function: InitializeScrollBars
//
// Description:
//
//
// Parameters:
// @@@
//
// Returns:
// BOOL
//
// Comments:
//
//
//////////////////////////////////////////////////////////////////////////
void InitializeScrollBars(HWND hWnd, LPDIBINFO lpDIBInfo)
{
RECT rClient;
SCROLLINFO ScrollInfo;


// Get windows client size.
GetClientRect(hWnd, &rClient);

// If client size is equel to dib size, then add one to client size
// so that we don't show scroll bars.
// However, if client size is less than dib size, subtract scroll bar
// size form client size so that page size will be correct when
// scroll bars are shown.
if ((UINT) rClient.bottom == lpDIBInfo->uiDIBHeight)
++rClient.bottom;
else if ((UINT) rClient.bottom < lpDIBInfo->uiDIBHeight)
rClient.bottom -= GetSystemMetrics (SM_CYHSCROLL);
if ((UINT) rClient.right == lpDIBInfo->uiDIBWidth)
++rClient.right;
else if ((UINT) rClient.right < lpDIBInfo->uiDIBWidth)
rClient.right -= GetSystemMetrics (SM_CXVSCROLL);

// Initialize vertical scroll bar.
ScrollInfo.cbSize = sizeof(SCROLLINFO);
ScrollInfo.fMask = SIF_ALL;
ScrollInfo.nMin = 0;
ScrollInfo.nMax = lpDIBInfo->uiDIBHeight;
ScrollInfo.nPage = rClient.bottom;
ScrollInfo.nPos = 0;
SetScrollInfo(hWnd, SB_VERT, &ScrollInfo, TRUE);

// Initialize vertical scroll bar.
ScrollInfo.nMax = lpDIBInfo->uiDIBWidth;
ScrollInfo.nPage = rClient.right;
SetScrollInfo(hWnd, SB_HORZ, &ScrollInfo, TRUE);
}


//////////////////////////////////////////////////////////////////////////
// Function: ScrollChildWindow
//
// Description:
//
//
// Parameters:
// @@@
//
// Returns:
// BOOL
//
// Comments:
//
//
//////////////////////////////////////////////////////////////////////////
BOOL ScrollChildWindow(HWND hWnd, int nScrollBar, WORD wScrollCode)
{
int nPosition;
int nHorzScroll = 0;
int nVertScroll = 0;
SCROLLINFO ScrollInfo;


// Get current scroll information.

ScrollInfo.cbSize = sizeof(SCROLLINFO); 
ScrollInfo.fMask = SIF_ALL;
GetScrollInfo(hWnd, nScrollBar, &ScrollInfo);
nPosition = ScrollInfo.nPos;

// Modify scroll information based on requested
// scroll action.
switch (wScrollCode)
{
case SB_LINEDOWN:
ScrollInfo.nPos++;
break;

case SB_LINEUP:
ScrollInfo.nPos--;
break;

case SB_PAGEDOWN:
ScrollInfo.nPos += ScrollInfo.nPage;
break;

case SB_PAGEUP:
ScrollInfo.nPos -= ScrollInfo.nPage;
break;

case SB_TOP:
ScrollInfo.nPos = ScrollInfo.nMin;
break;

case SB_BOTTOM:
ScrollInfo.nPos = ScrollInfo.nMax;
break;

// Don't do anything.
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
ScrollInfo.nPos = ScrollInfo.nTrackPos;
break;

case SB_ENDSCROLL:
default:
return FALSE;
}

// Make sure that scroll position is in range.
if (0 > ScrollInfo.nPos)
ScrollInfo.nPos = 0;
else if (ScrollInfo.nMax - (int) ScrollInfo.nPage + 1 < ScrollInfo.nPos)
ScrollInfo.nPos = ScrollInfo.nMax - ScrollInfo.nPage + 1;

// Set new scroll position.
ScrollInfo.fMask = SIF_POS;
SetScrollInfo(hWnd, nScrollBar, &ScrollInfo, TRUE);

// Scroll window.
if (SB_VERT == nScrollBar)
nVertScroll = nPosition - ScrollInfo.nPos;
else
nHorzScroll = nPosition - ScrollInfo.nPos;

ScrollWindowEx(hWnd, nHorzScroll, nVertScroll, NULL, NULL,
NULL, NULL, SW_INVALIDATE);

return TRUE;
}