home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
xwplascr.zip
/
XWPL0208.ZIP
/
tools
/
ximgview
/
ximgview.c
< prev
next >
Wrap
C/C++ Source or Header
|
2002-04-13
|
38KB
|
1,193 lines
/*
* ximgview.c:
* primitive image viewer. Uses MMPM/2 to understand all its
* image file formats.
*
* This is also a simple example of how to multithread a PM
* application. Loading the bitmap is passed to a second
* transient thread.
*
* Copyright (C) 2000 Ulrich Möller.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, in version 2 as it comes in the COPYING
* file of the XWorkplace main distribution.
* This program 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 General Public License for more details.
*/
#define INCL_WIN
#define INCL_WINWORKPLACE
#define INCL_DOS
#define INCL_DOSERRORS
#define INCL_GPI // required for INCL_MMIO_CODEC
#define INCL_GPIBITMAPS // required for INCL_MMIO_CODEC
#include <os2.h>
// multimedia includes
#define INCL_MCIOS2
#define INCL_MMIOOS2
#define INCL_MMIO_CODEC
#include <os2me.h>
#include <stdio.h>
#include <setjmp.h>
#include "setup.h"
#include "helpers\cnrh.h"
#include "helpers\eah.h"
#include "helpers\dosh.h"
#include "helpers\gpih.h"
#include "helpers\except.h"
#include "helpers\stringh.h"
#include "helpers\winh.h"
#include "helpers\threads.h"
#include "bldlevel.h"
#include "dlgids.h"
#include "ximgview.h"
/* ******************************************************************
*
* Global variables for all threads
*
********************************************************************/
CHAR G_szFilename[CCHMAXPATH] = "";
/* ******************************************************************
*
* Image handling
*
********************************************************************/
#define LOADBMP_SUCCESS 0
// #define LOADBMP_UNKNOWN_ERROR 1
#define LOADBMP_GOT_DOS_IOPROC 2
#define LOADBMP_NOT_IMAGE_FILE 3
#define LOADBMP_OPEN_FAILED 4
#define LOADBMP_INCOMPATIBLE_HEADER 5
#define LOADBMP_HEADER_UNAVAILABLE 6
#define LOADBMP_GPIGREATEPS_FAILED 7
#define LOADBMP_GPICREATEBITMAP_FAILED 8
#define LOADBMP_GPISETBITMAPBITS_FAILED 9
#define LOADBMP_OUT_OF_MEMORY 10
#define LOADBMP_CRASHED 11
#define LOADBMP_MMIOREAD_FAILED 12
/*
*@@ QueryErrorDescription:
*
*/
PSZ QueryErrorDescription(ULONG ulError)
{
switch (ulError)
{
// case LOADBMP_UNKNOWN_ERROR:
case LOADBMP_GOT_DOS_IOPROC:
return "Image file format not supported.";
case LOADBMP_NOT_IMAGE_FILE:
return "File is non-image multimedia file.";
case LOADBMP_OPEN_FAILED:
return "Open failed.";
case LOADBMP_INCOMPATIBLE_HEADER:
return "Incompatible bitmap header size.";
case LOADBMP_HEADER_UNAVAILABLE:
return "Bitmap header unavailable.";
case LOADBMP_GPIGREATEPS_FAILED:
return "Error creating memory presentation space (GpiCreatePS failed).";
case LOADBMP_GPICREATEBITMAP_FAILED:
return "Error creating PM bitmap (GpiCreateBitmap failed).";
case LOADBMP_GPISETBITMAPBITS_FAILED:
return "GpiSetBitmapBits failed.";
case LOADBMP_OUT_OF_MEMORY:
return "Out of memory.";
case LOADBMP_CRASHED:
return "Crashed reading bitmap.";
case LOADBMP_MMIOREAD_FAILED:
return "Error reading bitmap data.";
}
return ("Unknown error.");
}
/*
*@@ CreateBitmapFromFile:
* this actually loads the bitmap file
* specified by hmmio (which must be a
* bitmap file handle!) and creates a
* regular PM bitmap from it.
*
* The bitmap in stored *phbmOut. It
* is not selected into any HPS.
*
* Returns 0 (LOADBMP_SUCCESS) on success.
* On error, returns one of the following:
*
* -- LOADBMP_HEADER_UNAVAILABLE: mmioGetHeader failed.
* -- LOADBMP_OUT_OF_MEMORY: DosAllocMem failed.
* -- LOADBMP_GPIGREATEPS_FAILED: GpiCreatePS failed.
* -- LOADBMP_GPICREATEBITMAP_FAILED: GpiCreateBitmap failed.
* -- LOADBMP_MMIOREAD_FAILED: mmioRead failed.
* -- LOADBMP_GPISETBITMAPBITS_FAILED: GpiSetBitmapBits failed.
*/
ULONG CreateBitmapFromFile(HAB hab, // in: anchor block
HDC hdcMem, // in: memory device context
HMMIO hmmio, // in: MMIO file handle
HBITMAP *phbmOut) // out: bitmap created
{
ULONG ulReturn = LOADBMP_SUCCESS,
ulrc;
MMIMAGEHEADER mmImgHdr;
ULONG ulBytesRead;
ulrc = mmioGetHeader(hmmio,
&mmImgHdr,
sizeof(MMIMAGEHEADER),
(PLONG)&ulBytesRead,
0L,
0L);
if (ulrc != MMIO_SUCCESS)
// header unavailable
ulReturn = LOADBMP_HEADER_UNAVAILABLE;
else
{
/*
* Determine the number of bytes required, per row.
* PLANES MUST ALWAYS BE = 1
*/
ULONG dwHeight = mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cy;
ULONG dwWidth = mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cx;
SHORT wBitCount = mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cBitCount;
ULONG dwRowBits = dwWidth * mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cBitCount;
ULONG dwNumRowBytes = dwRowBits >> 3;
ULONG dwPadBytes;
SIZEL ImageSize;
PBYTE pRowBuffer;
/*
* Account for odd bits used in 1bpp or 4bpp images that are
* NOT on byte boundaries.
*/
if (dwRowBits % 8)
{
dwNumRowBytes++;
}
/*
* Ensure the row length in bytes accounts for byte padding.
* All bitmap data rows must are aligned on LONG/4-BYTE boundaries.
* The data FROM an IOProc should always appear in this form.
*/
dwPadBytes = (dwNumRowBytes % 4);
if (dwPadBytes)
dwNumRowBytes += 4 - dwPadBytes;
// allocate space for ONE row of pels
if (DosAllocMem((PPVOID)&pRowBuffer,
dwNumRowBytes,
fALLOC))
ulReturn = LOADBMP_OUT_OF_MEMORY;
else
{
// create a memory presentation space with the
// size of the bitmap
HPS hpsMem;
ImageSize.cx = dwWidth;
ImageSize.cy = dwHeight;
hpsMem = GpiCreatePS(hab,
hdcMem,
&ImageSize,
PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC);
if (!hpsMem)
ulReturn = LOADBMP_GPIGREATEPS_FAILED;
else
{
// create an uninitialized bitmap -- this is where we
// will put all of the bits once we read them in
*phbmOut = GpiCreateBitmap(hpsMem,
&mmImgHdr.mmXDIBHeader.BMPInfoHeader2,
0L,
NULL,
NULL);
if (!*phbmOut)
ulReturn = LOADBMP_GPICREATEBITMAP_FAILED;
else
{
// bitmap created:
if (GpiSetBitmap(hpsMem,
*phbmOut)
== HBM_ERROR)
ulReturn = LOADBMP_GPICREATEBITMAP_FAILED;
else
{
ULONG dwRowCount;
// load the bitmap from the file,
// one line at a time, starting from the BOTTOM
for (dwRowCount = 0;
dwRowCount < dwHeight;
dwRowCount++)
{
LONG lrc;
ulBytesRead = (ULONG)mmioRead(hmmio,
pRowBuffer,
dwNumRowBytes);
if (ulBytesRead == 0)
// done:
break;
else if (ulBytesRead == MMIO_ERROR)
{
ulReturn = LOADBMP_MMIOREAD_FAILED;
break;
}
// allow context switching while previewing.. Couldn't get
// it to work. Perhaps will get to it when time is available...
lrc = GpiSetBitmapBits(hpsMem,
(LONG)dwRowCount,
(LONG)1,
(PBYTE)pRowBuffer,
(PBITMAPINFO2)&mmImgHdr.mmXDIBHeader.BMPInfoHeader2);
if (lrc == GPI_ALTERROR)
{
ulReturn = LOADBMP_GPISETBITMAPBITS_FAILED;
break;
}
}
// unset bitmap im mem PS
GpiSetBitmap(hpsMem,
NULLHANDLE);
} // end if (GpiSetBitmap(hpsMem
if (ulReturn != LOADBMP_SUCCESS)
{
// error:
GpiDeleteBitmap(*phbmOut);
*phbmOut = NULLHANDLE;
}
}
GpiDestroyPS(hpsMem);
} // end if GpiCreatePS
DosFreeMem(pRowBuffer);
} // end if (DosAllocMem((PPVOID)&pRowBuffer,
}
return (ulReturn);
}
/*
*@@ LoadBitmap:
* one-shot function for loading an image
* from a file. Understands any image file
* format supported by MMPM/2.
*
* Returns 0 (LOADBMP_SUCCESS) on success.
* On error, returns one of the following:
*
* -- LOADBMP_GOT_DOS_IOPROC: file format not understood
* by MMPM/2.
* -- LOADBMP_NOT_IMAGE_FILE: file format understood, but
* is not image file or cannot be translated.
* -- LOADBMP_OPEN_FAILED: mmioOpen failed.
* -- LOADBMP_INCOMPATIBLE_HEADER: ioproc returned invalid
* header.
* -- LOADBMP_CRASHED: exception occured.
*
* plus the error codes from CreateBitmapFromFile().
*/
ULONG LoadBitmap(HAB hab, // in: anchor block
HDC hdcMem, // in: memory device context
HBITMAP *phbmOut, // out: bitmap created
PSZ pszFileName) // in: filename
{
ULONG ulReturn = LOADBMP_SUCCESS;
TRY_LOUD(excpt1)
{
ULONG ulrc = 0;
MMFORMATINFO mmFormatInfo;
FOURCC fccStorageSystem;
// find the IOProc which can understand this file
ulrc = mmioIdentifyFile(pszFileName,
NULL, // needed for RIFF only
&mmFormatInfo, // out: format info (FOURCC)
&fccStorageSystem, // out: FOURCC of storage IOProc
0L, // reserved
0L); // can be:
/* MMIO_FORCE_IDENTIFY_SS
Forces the identification of a storage
system by ignoring the file
name and actually checking the MMIO Manager's
I/O procedure list.
MMIO_FORCE_IDENTIFY_FF
Forces the identification of a file
format by ignoring the file name
and actually checking the MMIO Manager's
I/O procedure list.
*/
if (ulrc == MMIO_SUCCESS)
{
// if mmioIdentifyFile did not find a custom-written IO proc which
// can understand the image file, then it will return the DOS IO Proc
// info because the image file IS a DOS file.
if (mmFormatInfo.fccIOProc == FOURCC_DOS)
ulReturn = LOADBMP_GOT_DOS_IOPROC;
else
{
// ensure this is an IMAGE IOproc, and that it can read
// translated data
if ( (mmFormatInfo.ulMediaType != MMIO_MEDIATYPE_IMAGE)
|| ((mmFormatInfo.ulFlags & MMIO_CANREADTRANSLATED) == 0)
)
{
ulReturn = LOADBMP_NOT_IMAGE_FILE;
}
else
{
// remember fourcc of IOProc
FOURCC fccIOProc = mmFormatInfo.fccIOProc;
// load file
MMIOINFO mmioinfo;
HMMIO hmmio;
memset(&mmioinfo, 0, sizeof (MMIOINFO));
mmioinfo.fccIOProc = fccIOProc;
mmioinfo.ulTranslate = MMIO_TRANSLATEHEADER | MMIO_TRANSLATEDATA;
hmmio = mmioOpen((PSZ)pszFileName,
&mmioinfo,
MMIO_READ | MMIO_DENYWRITE | MMIO_NOIDENTIFY);
if (!hmmio)
{
ulReturn = LOADBMP_OPEN_FAILED;
}
else
{
ULONG ulImageHeaderLength;
ULONG dwReturnCode = mmioQueryHeaderLength(hmmio,
(PLONG)&ulImageHeaderLength,
0L,
0L);
if (ulImageHeaderLength != sizeof (MMIMAGEHEADER))
ulReturn = LOADBMP_INCOMPATIBLE_HEADER;
else
{
ulReturn = CreateBitmapFromFile(hab,
hdcMem,
hmmio,
phbmOut);
}
ulrc = mmioClose(hmmio, 0L);
} // end if hmmio
}
}
}
}
CATCH(excpt1)
{
ulReturn = LOADBMP_CRASHED;
} END_CATCH();
return (ulReturn);
}
/*
*@@ fntLoadBitmap:
* transient thread created by WMCommand_FileOpen
* to load the bitmap file.
*
* Posts WM_DONELOADINGBMP to the client when done.
*/
void _Optlink fntLoadBitmap(PTHREADINFO pti)
{
// create a memory PS
HWND hwndClient = (HWND)(pti->ulData);
HBITMAP hbmNew;
HDC hdcMem = DevOpenDC(pti->hab,
OD_MEMORY,
"*",
0L,
NULL,
0);
ULONG ulrc = LoadBitmap(pti->hab,
hdcMem,
&hbmNew,
G_szFilename);
WinPostMsg(hwndClient,
WM_DONELOADINGBMP,
(MPARAM)ulrc,
(MPARAM)hbmNew);
DevCloseDC(hdcMem);
}
/* ******************************************************************
*
* Global variables for main thread
*
********************************************************************/
HAB G_habMain = NULLHANDLE;
HMQ G_hmqMain = NULLHANDLE;
HWND G_hwndMain = NULLHANDLE;
HBITMAP G_hbmLoaded = NULLHANDLE;
PFNWP G_pfnwpFrameOrig = NULL;
BOOL G_fStartingUp = FALSE;
BOOL G_fUseLastOpenDir = FALSE;
// load-bitmap thread
BOOL G_fLoadingBitmap = FALSE;
THREADINFO G_tiLoadBitmap = {0};
// scroll bar data
BITMAPINFOHEADER G_bmihBitmapLoaded;
ULONG G_ulVertScrollOfs,
G_ulHorzScrollOfs;
#define VERT_SCROLL_UNIT 10
#define HORZ_SCROLL_UNIT 10
/*
*@@ G_GlobalSettings:
* global settings.
*/
struct
{
BOOL fResizeAfterLoad,
fConstrain2Screen,
fScale2WinSize;
} G_GlobalSettings;
/* ******************************************************************
*
* Command handler
*
********************************************************************/
VOID UpdateTitle(VOID)
{
CHAR szTitle[500] = "ximgview";
if (G_hbmLoaded)
{
CHAR szName[CCHMAXPATH] = "",
szExt[CCHMAXPATH] = "";
_splitpath(G_szFilename, NULL, NULL, szName, szExt);
sprintf(szTitle + strlen(szTitle),
" - %s%s (%dx%d, %d bpp)",
szName, szExt,
G_bmihBitmapLoaded.cx,
G_bmihBitmapLoaded.cy,
G_bmihBitmapLoaded.cBitCount);
}
WinSetWindowText(G_hwndMain, szTitle);
}
/*
*@@ CalcViewportSize:
*
*/
VOID CalcViewportSize(HWND hwndClient,
SIZEL *pszl)
{
SWP swpClient;
WinQueryWindowPos(hwndClient, &swpClient);
// this already has the scroll bars subtracted;
// apparently, these are frame controls...
pszl->cx = swpClient.cx; // - WinQuerySysValue(HWND_DESKTOP, SV_CXVSCROLL);
pszl->cy = swpClient.cy; // - WinQuerySysValue(HWND_DESKTOP, SV_CYHSCROLL);
}
/*
*@@ UpdateScrollBars:
*
*/
VOID UpdateScrollBars(HWND hwndClient)
{
HWND hwndVScroll = WinWindowFromID(G_hwndMain, FID_VERTSCROLL);
HWND hwndHScroll = WinWindowFromID(G_hwndMain, FID_HORZSCROLL);
if (G_GlobalSettings.fScale2WinSize)
{
WinEnableWindow(hwndVScroll, FALSE);
WinEnableWindow(hwndHScroll, FALSE);
}
else
{
SIZEL szlViewport;
CalcViewportSize(hwndClient, &szlViewport);
_Pmpf(("bitmap cx: %d", G_bmihBitmapLoaded.cx));
_Pmpf(("viewport cx: %d", szlViewport.cx));
// vertical (height)
winhUpdateScrollBar(hwndVScroll,
szlViewport.cy,
G_bmihBitmapLoaded.cy,
G_ulVertScrollOfs,
FALSE); // auto-hide
// horizontal (width)
winhUpdateScrollBar(hwndHScroll,
szlViewport.cx,
G_bmihBitmapLoaded.cx,
G_ulHorzScrollOfs,
FALSE); // auto-hide
}
}
/*
*@@ UpdateMenuItems:
*
*/
VOID UpdateMenuItems(VOID)
{
HWND hmenuMain = WinWindowFromID(G_hwndMain, FID_MENU);
// file menu
WinEnableMenuItem(hmenuMain, IDM_FILE,
// disable file menu if load-bitmap thread is running
!G_fLoadingBitmap);
// view menu
WinEnableMenuItem(hmenuMain, IDM_VIEW,
// disable view menu if load-bitmap thread is running
// or if we have no bitmap yet
( (!G_fLoadingBitmap)
&& (G_hbmLoaded != NULLHANDLE)
));
// options menu
WinCheckMenuItem(hmenuMain, IDMI_OPT_RESIZEAFTERLOAD,
G_GlobalSettings.fResizeAfterLoad);
WinCheckMenuItem(hmenuMain, IDMI_OPT_CONSTRAIN2SCREEEN,
G_GlobalSettings.fConstrain2Screen);
WinCheckMenuItem(hmenuMain, IDMI_OPT_SCALE2WINSIZE,
G_GlobalSettings.fScale2WinSize);
}
/*
*@@ SetWinSize2BmpSize:
*
*/
VOID SetWinSize2BmpSize(VOID)
{
SWP swpOld;
RECTL rcl;
LONG lNewCX,
lNewCY,
lNewY;
// calculate new frame size needed:
// WinCalcFrameRect really wants screen
// coordinates, but we don't care here
rcl.xLeft = 0;
rcl.xRight = rcl.xLeft + G_bmihBitmapLoaded.cx;
rcl.yBottom = 0;
rcl.yTop = rcl.yBottom + G_bmihBitmapLoaded.cy;
WinCalcFrameRect(G_hwndMain,
&rcl,
FALSE); // calc frame from client
// new frame window size:
WinQueryWindowPos(G_hwndMain,
&swpOld);
lNewCX = rcl.xRight - rcl.xLeft;
lNewCY = rcl.yTop - rcl.yBottom;
lNewY = swpOld.y
// calculate difference between old
// and new cy:
// if the new bitmap is larger, we
// need to move y DOWN
+ swpOld.cy
- lNewCY;
// limit window size if off screen
if (G_GlobalSettings.fConstrain2Screen)
{
if ((swpOld.x + lNewCX) > winhQueryScreenCX())
lNewCX = winhQueryScreenCX() - swpOld.x;
if (lNewY < 0)
{
lNewY = 0;
lNewCY = swpOld.y + swpOld.cy;
}
}
// now move/resize window:
// make the top left corner constant
WinSetWindowPos(G_hwndMain,
HWND_TOP,
// pos
swpOld.x, // don't change
lNewY,
// width
lNewCX,
lNewCY,
SWP_MOVE | SWP_SIZE
// in case the bitmap is loaded
// as a startup parameter:
| SWP_SHOW | SWP_ACTIVATE);
}
/*
*@@ WMCommand_FileOpen:
*
*/
BOOL WMCommand_FileOpen(HWND hwndClient)
{
BOOL brc = FALSE;
CHAR szFile[CCHMAXPATH] = "*";
ULONG flFlags = WINH_FOD_INISAVEDIR;
if (G_fUseLastOpenDir)
// this is FALSE if we were loaded with a
// image file on the cmd line; in that case,
// use the startup directory
flFlags |= WINH_FOD_INILOADDIR;
if (winhFileDlg(hwndClient,
szFile, // in: file mask; out: fully q'd filename
flFlags,
HINI_USER,
INIAPP,
INIKEY_LASTDIR))
{
strcpy(G_szFilename, szFile);
thrCreate(&G_tiLoadBitmap,
fntLoadBitmap,
&G_fLoadingBitmap, // running flag
"LoadBitmap",
THRF_PMMSGQUEUE | THRF_WAIT,
hwndClient); // user param
UpdateMenuItems();
// for subsequent opens, load from INI
G_fUseLastOpenDir = TRUE;
}
return (brc);
}
/*
*@@ WMDoneLoadingBitmap:
* handler for WM_DONELOADINGBITMAP when
* load-bitmap thread is done. ulError
* has the error code (0 if none).
*/
VOID WMDoneLoadingBitmap(HWND hwndClient,
ULONG ulError,
HBITMAP hbmNew)
{
if (ulError)
{
CHAR szMsg[1000];
sprintf(szMsg,
"Error %d loading \"%s\": %s",
ulError,
G_szFilename,
QueryErrorDescription(ulError));
if (G_fStartingUp)
// first call:
WinSetWindowPos(G_hwndMain, 0, 0,0,0,0, SWP_SHOW | SWP_ACTIVATE);
winhDebugBox(hwndClient, "Error", szMsg);
}
else
{
// free old bitmap
if (G_hbmLoaded)
GpiDeleteBitmap(G_hbmLoaded);
G_hbmLoaded = hbmNew;
GpiQueryBitmapParameters(G_hbmLoaded,
&G_bmihBitmapLoaded);
// reset scroller
G_ulVertScrollOfs = 0;
G_ulHorzScrollOfs = 0;
if (G_GlobalSettings.fResizeAfterLoad)
SetWinSize2BmpSize();
UpdateScrollBars(hwndClient);
WinInvalidateRect(hwndClient, NULL, FALSE);
}
// wait till load-bitmap thread has really exited
thrWait(&G_tiLoadBitmap);
UpdateTitle();
UpdateMenuItems();
G_fStartingUp = FALSE;
}
/*
*@@ WMClose_CanClose:
* if this returns TRUE, the main window is
* closed and the application is terminated.
* Prompt for saving data here.
*/
BOOL WMClose_CanClose(HWND hwndClient)
{
return (TRUE);
}
/* ******************************************************************
*
* Main window
*
********************************************************************/
/*
*@@ PaintClient:
*
*/
VOID PaintClient(HWND hwndClient,
HPS hps,
PRECTL prclPaint)
{
BOOL fFillBackground = TRUE;
if (G_hbmLoaded)
{
POINTL ptlDest = {0, 0};
RECTL rclDest;
SIZEL szlViewport;
CalcViewportSize(hwndClient, &szlViewport);
if (G_GlobalSettings.fScale2WinSize)
{
// "scale-to-window" mode: that's easy
RECTL rclClient;
WinQueryWindowRect(hwndClient, &rclClient);
WinDrawBitmap(hps,
G_hbmLoaded,
NULL, // all
(PPOINTL)&rclClient, // in stretch mode, this is
// used for the rectangle
CLR_BLACK, // in case we have a b/w bmp
CLR_WHITE, // in case we have a b/w bmp
DBM_NORMAL | DBM_STRETCH);
fFillBackground = FALSE;
}
else
{
// original size mode:
if (G_bmihBitmapLoaded.cy < szlViewport.cy)
// center vertically:
ptlDest.y = (szlViewport.cy - G_bmihBitmapLoaded.cy) / 2;
else
{
// use scroller offset:
// calc leftover space
ULONG ulClipped = G_bmihBitmapLoaded.cy - szlViewport.cy;
// this is a positive value if the scroller is
// down from the top or 0 if its at the top.
// So if it's zero, we must use -ulClipped.
ptlDest.y -= ulClipped;
// if it's above zero, we must add to y.
ptlDest.y += G_ulVertScrollOfs;
}
if (G_bmihBitmapLoaded.cx < szlViewport.cx)
// center horizontally:
ptlDest.x = (szlViewport.cx - G_bmihBitmapLoaded.cx) / 2;
else
{
// use scroller offset:
// calc leftover space
ULONG ulClipped = G_bmihBitmapLoaded.cx - szlViewport.cx;
// this is a positive value if the scroller is
// right from the left or 0 if its at the left.
// So if it's zero, we must use -ulClipped.
// ptlDest.x -= ulClipped;
// if it's above zero, we must further subtract from x.
ptlDest.x -= G_ulHorzScrollOfs;
}
WinDrawBitmap(hps,
G_hbmLoaded,
NULL, // subrectangle to be drawn
&ptlDest,
0, 0,
DBM_NORMAL);
// cut out the rectangle of the bitmap we just
// painted from the clipping region so that
// WinFillRect below cannot overwrite it
rclDest.xLeft = ptlDest.x;
rclDest.yBottom = ptlDest.y;
rclDest.xRight = ptlDest.x + G_bmihBitmapLoaded.cx;
rclDest.yTop = ptlDest.y + G_bmihBitmapLoaded.cy;
GpiExcludeClipRectangle(hps,
&rclDest);
}
}
if (fFillBackground)
// fill the remainder white
WinFillRect(hps, prclPaint, CLR_WHITE);
}
/*
*@@ fnwpMainClient:
*
*/
MRESULT EXPENTRY fnwpMainClient(HWND hwndClient, ULONG msg, MPARAM mp1, MPARAM mp2)
{
MRESULT mrc = 0;
switch (msg)
{
/*
* WM_COMMAND:
*
*/
case WM_COMMAND:
{
USHORT usCmd = SHORT1FROMMP(mp1);
switch (usCmd)
{
case IDMI_FILE_OPEN:
WMCommand_FileOpen(hwndClient);
break;
case IDMI_FILE_EXIT:
WinPostMsg(G_hwndMain, WM_CLOSE, 0, 0);
break;
case IDMI_VIEW_SIZEORIG:
SetWinSize2BmpSize();
break;
case IDMI_OPT_RESIZEAFTERLOAD:
G_GlobalSettings.fResizeAfterLoad = !G_GlobalSettings.fResizeAfterLoad;
UpdateMenuItems();
break;
case IDMI_OPT_CONSTRAIN2SCREEEN:
G_GlobalSettings.fConstrain2Screen = !G_GlobalSettings.fConstrain2Screen;
UpdateMenuItems();
break;
case IDMI_OPT_SCALE2WINSIZE:
G_GlobalSettings.fScale2WinSize = !G_GlobalSettings.fScale2WinSize;
UpdateMenuItems();
UpdateScrollBars(hwndClient);
WinInvalidateRect(hwndClient, NULL, FALSE);
break;
}
break; }
/*
*@@ WM_DONELOADINGBMP:
* load-bitmap thread is done.
* Parameters:
* -- ULONG mp1: error code.
* -- HBITMAP mp2: new bitmap handle.
*/
case WM_DONELOADINGBMP:
WMDoneLoadingBitmap(hwndClient,
(ULONG)mp1,
(HBITMAP)mp2);
break;
/*
* WM_SIZE:
*
*/
case WM_SIZE:
UpdateScrollBars(hwndClient);
WinInvalidateRect(hwndClient, NULL, FALSE);
break;
/*
* WM_PAINT:
*
*/
case WM_PAINT:
{
RECTL rclPaint;
HPS hps = WinBeginPaint(hwndClient,
NULLHANDLE,
&rclPaint);
PaintClient(hwndClient,
hps,
&rclPaint);
WinEndPaint(hps);
break; }
case WM_VSCROLL:
{
RECTL rcl;
WinQueryWindowRect(hwndClient, &rcl);
winhHandleScrollMsg(hwndClient,
WinWindowFromID(G_hwndMain, FID_VERTSCROLL),
&G_ulVertScrollOfs,
&rcl,
G_bmihBitmapLoaded.cy,
10,
msg,
mp2);
break; }
case WM_HSCROLL:
{
RECTL rcl;
WinQueryWindowRect(hwndClient, &rcl);
_Pmpf(("WM_HSCROLL"));
winhHandleScrollMsg(hwndClient,
WinWindowFromID(G_hwndMain, FID_HORZSCROLL),
&G_ulHorzScrollOfs,
&rcl,
G_bmihBitmapLoaded.cx,
10,
msg,
mp2);
_Pmpf(("End of WM_HSCROLL"));
break; }
case WM_CLOSE:
if (WMClose_CanClose(hwndClient))
winhSaveWindowPos(G_hwndMain,
HINI_USER,
INIAPP,
INIKEY_MAINWINPOS);
WinPostMsg(hwndClient, WM_QUIT, 0, 0);
break;
case WM_CHAR:
mrc = (MRESULT)winhProcessScrollChars(hwndClient,
WinWindowFromID(G_hwndMain, FID_VERTSCROLL),
WinWindowFromID(G_hwndMain, FID_HORZSCROLL),
mp1,
mp2,
G_bmihBitmapLoaded.cy,
G_bmihBitmapLoaded.cx);
break;
default:
mrc = WinDefWindowProc(hwndClient, msg, mp1, mp2);
}
return (mrc);
}
/*
*@@ fnwpMainFrame:
*
*/
MRESULT EXPENTRY fnwpMainFrame(HWND hwndFrame, ULONG msg, MPARAM mp1, MPARAM mp2)
{
MRESULT mrc = 0;
switch (msg)
{
case WM_QUERYTRACKINFO:
{
PTRACKINFO pti = (PTRACKINFO)mp2;
G_pfnwpFrameOrig(hwndFrame, msg, mp1, mp2);
if (pti->ptlMinTrackSize.x < 100)
pti->ptlMinTrackSize.x = 100;
if (pti->ptlMinTrackSize.y < 100)
pti->ptlMinTrackSize.y = 100;
mrc = (MRESULT)TRUE;
break; }
default:
mrc = G_pfnwpFrameOrig(hwndFrame, msg, mp1, mp2);
}
return (mrc);
}
/*
* main:
* program entry point.
*/
int main(int argc,
char *argv[])
{
QMSG qmsg;
HWND hwndClient;
ULONG cbGlobalSettings = sizeof(G_GlobalSettings);
ULONG flSwpFlags;
G_fStartingUp = TRUE;
if (!(G_habMain = WinInitialize(0)))
return FALSE;
if (!(G_hmqMain = WinCreateMsgQueue(G_habMain, 0)))
return FALSE;
// initialize global settings
memset(&G_GlobalSettings, 0, sizeof(G_GlobalSettings));
PrfQueryProfileData(HINI_USER,
INIAPP,
INIKEY_SETTINGS,
&G_GlobalSettings,
&cbGlobalSettings);
WinRegisterClass(G_habMain,
WC_MY_CLIENT_CLASS,
fnwpMainClient,
CS_SIZEREDRAW,
sizeof(ULONG));
G_hwndMain = winhCreateStdWindow(HWND_DESKTOP,
0,
FCF_TITLEBAR
| FCF_SYSMENU
| FCF_MINMAX
| FCF_VERTSCROLL
| FCF_HORZSCROLL
| FCF_SIZEBORDER
| FCF_ICON
| FCF_MENU
| FCF_TASKLIST,
0, // WS_VISIBLE,
"Title",
1, // icon resource
WC_MY_CLIENT_CLASS,
WS_VISIBLE,
0,
NULL,
&hwndClient);
G_pfnwpFrameOrig = WinSubclassWindow(G_hwndMain,
fnwpMainFrame);
// standard SWP flags
flSwpFlags = SWP_MOVE | SWP_SIZE;
// parse args
if (argc > 1)
{
HDC hdcMem;
HBITMAP hbmNew;
HWND hwndShape;
ULONG ulrc;
strcpy(G_szFilename, argv[1]);
thrCreate(&G_tiLoadBitmap,
fntLoadBitmap,
&G_fLoadingBitmap, // running flag
"LoadBitmap",
THRF_PMMSGQUEUE | THRF_WAIT,
hwndClient); // user param
}
else
{
// no parameter:
// show window initially
flSwpFlags |= SWP_SHOW | SWP_ACTIVATE;
// and retrieve last open dir from OS2.INI
G_fUseLastOpenDir = TRUE;
}
UpdateTitle();
UpdateMenuItems();
if (!winhRestoreWindowPos(G_hwndMain,
HINI_USER,
INIAPP,
INIKEY_MAINWINPOS,
flSwpFlags))
// standard pos
WinSetWindowPos(G_hwndMain,
HWND_TOP,
10, 10, 500, 500,
flSwpFlags);
// standard PM message loop
while (WinGetMsg(G_habMain, &qmsg, NULLHANDLE, 0, 0))
{
WinDispatchMsg(G_habMain, &qmsg);
}
// initialize global settings
PrfWriteProfileData(HINI_USER,
INIAPP,
INIKEY_SETTINGS,
&G_GlobalSettings,
sizeof(G_GlobalSettings));
// clean up on the way out
WinDestroyMsgQueue(G_hmqMain);
WinTerminate(G_habMain);
return TRUE;
}