1179 lines
31 KiB
C
1179 lines
31 KiB
C
/*
|
|
* Windows Calendar
|
|
* Copyright (c) 1985 by Microsoft Corporation, all rights reserved.
|
|
* Written by Mark L. Chamberlin, consultant to Microsoft.
|
|
*
|
|
*/
|
|
|
|
|
|
/*
|
|
*****
|
|
***** calprint.c
|
|
*****
|
|
*/
|
|
|
|
#include "cal.h"
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
|
|
int TranslateString(CHAR *);
|
|
CHAR * APIENTRY PFileInPath(CHAR *sz);
|
|
VOID APIENTRY DestroyAbortWnd(VOID);
|
|
|
|
/* We'll dynamically allocate this */
|
|
HANDLE hHeadFoot=NULL;
|
|
LPSTR szHeadFoot;
|
|
SHORT xCharPage, dyHeadFoot;
|
|
int dyTop,dyBottom,dxLeft ,dxRight;
|
|
int CharPrintWidth, CharPrintHeight;
|
|
HDC vhDCPrint;
|
|
BOOL vfAbortPrint;
|
|
HWND vhwndAbortPrint;
|
|
|
|
INT vcyPrintLineToLine;
|
|
INT xPrintRes, yPrintRes, xPixInch, yPixInch;
|
|
INT hChars;
|
|
INT vclnPage;
|
|
INT nSpace;
|
|
INT vclnPrinted;
|
|
INT vclnDate;
|
|
INT vlnFooter; /* footer line */
|
|
INT vlnBottom; /* last line of calendar text */
|
|
INT vlnTop; /* first line of calendar text */
|
|
BOOL vfPrint;
|
|
CHAR szCurDateBuf[9]; /* buffer containing date string for header/footer */
|
|
CHAR *szcurDptr = szCurDateBuf;
|
|
INT iCurDateLength ;
|
|
DOSDATE CurDD;
|
|
|
|
CHAR *vpchPrintBuf;
|
|
|
|
#define CLNHEADING 1
|
|
#define CLNAFTERHEADING 1
|
|
#define CLNAFTERAPPOINTMENTS 1
|
|
#define CLNBETWEENDATES 2
|
|
|
|
/* 1 - blank or asterisk for alarm
|
|
1 - blank
|
|
CCHTIMESZ - appointment time (includes 0 terminator, which is used
|
|
to hold a blank here)
|
|
CCHQDMAX - room for a maximum length appointment description
|
|
1 - room for the 0 terminator
|
|
|
|
The print buffer is also used for outputting the heading, so it
|
|
must be long enough for that too. To make sure this is the case,
|
|
add in CCHDATEDISP.
|
|
*/
|
|
|
|
#define CCHPRINTBUF (1 + 1 + CCHTIMESZ + CCHQDMAX + 1 + CCHDATEDISP)
|
|
|
|
|
|
|
|
/* Format of a printed date:
|
|
if not at top of page, CLNBETWEENDATES blank lines
|
|
Heading (e.g., Thursday, July 11, 1985)
|
|
CLNAFTERHEADING blank lines
|
|
Appointments (e.g., * 10:00 Call Tandy to report progress (asterisk
|
|
indicates alarm set)
|
|
CLNAFTERAPPOINTMENTS blank lines
|
|
notes
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**** FnPrint ****/
|
|
|
|
INT_PTR CALLBACK FnPrint (
|
|
HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
CHAR szFromDate [CCHDASHDATE];
|
|
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
/* Remember the window handle of the dialog for AlertBox. */
|
|
vhwndDialog = hwnd;
|
|
|
|
GetDashDateSel (szFromDate);
|
|
SetDlgItemText (hwnd, IDCN_FROMDATE, szFromDate);
|
|
return (TRUE);
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
|
|
case IDOK:
|
|
GetRangeOfDates (hwnd);
|
|
/* line added to fix keyboard hanging when Calendar
|
|
is run under ver3.0 rel 1.11 */
|
|
CalSetFocus (GetDlgItem(hwnd, IDCN_FROMDATE));
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
EndDialog (hwnd, FALSE);
|
|
break;
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/* Tell Windows we did not process the message. */
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** Print */
|
|
|
|
VOID FAR APIENTRY Print ()
|
|
{
|
|
TEXTMETRIC Metrics;
|
|
WORD idrFree;
|
|
INT itdd;
|
|
|
|
DD *pdd;
|
|
DT dt;
|
|
DL dl;
|
|
WORD idr;
|
|
CHAR rgchPrintBuf [CCHPRINTBUF];
|
|
INT iDateLen;
|
|
INT iHeight;
|
|
INT iWidth;
|
|
|
|
/* Note - there is no need to force edits to be recorded here.
|
|
We got here after the Print dialog, which took over the focus
|
|
and therefore has recorded the edits. We are not going to
|
|
modify the current DR, so we do not need to set the focus
|
|
to NULL (as we sometimes do to prevent recording data into
|
|
the wrong DR).
|
|
*/
|
|
|
|
|
|
if (BeginPrint () < 0)
|
|
{
|
|
/* Unable to get print DC - display error and give up. */
|
|
CalPrintAlert(SP_ERROR);
|
|
return;
|
|
}
|
|
|
|
/* Determine the number of lines per page. */
|
|
GetTextMetrics (vhDCPrint, &Metrics);
|
|
CharPrintHeight = Metrics.tmHeight + Metrics.tmExternalLeading;
|
|
CharPrintWidth = (Metrics.tmAveCharWidth + Metrics.tmMaxCharWidth)/2; /* character width */
|
|
|
|
xPrintRes = GetDeviceCaps(vhDCPrint, HORZRES);
|
|
yPrintRes = GetDeviceCaps(vhDCPrint, VERTRES);
|
|
xPixInch = GetDeviceCaps(vhDCPrint, LOGPIXELSX);
|
|
yPixInch = GetDeviceCaps(vhDCPrint, LOGPIXELSY);
|
|
|
|
dyHeadFoot = yPixInch / 2; /* 1/2 an inch */
|
|
|
|
dyTop = atopix(chPageText[4], yPixInch);
|
|
dyBottom = atopix(chPageText[5], yPixInch);
|
|
dxLeft = atopix(chPageText[2], xPixInch);
|
|
dxRight = atopix(chPageText[3], xPixInch);
|
|
|
|
/* There's some recalculating here entirely unneeded. The call to
|
|
GetDeviceCaps for hChars isn't needed since xPrintRes already has
|
|
that value. CharPrintHeight already has the value desired for
|
|
vcyPrintLineToLine. (I'd pull one of them out entirely, but that
|
|
requres a lot of code review without the time.) The call to
|
|
GetDeviceCaps for vlnFooter isn't needed since yPrintRes already
|
|
has that value. 21 Sept 1989 Clark Cyr */
|
|
#if 0
|
|
hChars=GetDeviceCaps(vhDCPrint, HORZRES)/Metrics.tmAveCharWidth;
|
|
vcyPrintLineToLine = Metrics.tmHeight + Metrics.tmExternalLeading;
|
|
vlnFooter = GetDeviceCaps(vhDCPrint, VERTRES) / vcyPrintLineToLine - 1;
|
|
#endif
|
|
hChars = xPrintRes / Metrics.tmAveCharWidth;
|
|
vlnFooter = yPrintRes / (vcyPrintLineToLine = CharPrintHeight) - 1;
|
|
|
|
MGetTextExtent(vhDCPrint, " ", 1, &iHeight, &iWidth);
|
|
nSpace = iWidth;
|
|
|
|
viLeftMarginLen =dxLeft/nSpace;
|
|
viRightMarginLen=dxRight/nSpace;
|
|
viTopMarginLen =dyTop/vcyPrintLineToLine;
|
|
viBotMarginLen =dyBottom/vcyPrintLineToLine;
|
|
|
|
/* Number of characters between margins */
|
|
xCharPage=(xPrintRes/CharPrintWidth)-viLeftMarginLen-viRightMarginLen;
|
|
|
|
|
|
/* Allocate memory for the header.footer string. Will allow any size
|
|
* of paper and still have enough for the string.
|
|
*/
|
|
hHeadFoot=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (DWORD)xCharPage+2);
|
|
|
|
if (!hHeadFoot)
|
|
{
|
|
/* Tell user that there's not memory to do this... */
|
|
CalPrintAlert(SP_OUTOFMEMORY);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/* Change 2 to 3. Leave room for footer itself. 21 Sept 1989 Clark Cyr */
|
|
vclnPage = vlnFooter - 3;
|
|
vlnTop = viTopMarginLen;
|
|
vlnBottom = vclnPage - viBotMarginLen;
|
|
|
|
CurDD.dayofweek = 0xff;
|
|
CurDD.year = vd3Cur.wYear + 1980;
|
|
CurDD.month = vd3Cur.wMonth + 1;
|
|
CurDD.day = vd3Cur.wDay + 1;
|
|
iDateLen = GetDateString(&CurDD, szCurDateBuf, GDS_LONG|GDS_DAYOFWEEK);
|
|
*(szcurDptr + iDateLen) = '\0';
|
|
|
|
/* Find a free DR in case we need to read from the disk. */
|
|
idrFree = IdrFree ();
|
|
|
|
/* Say we are at the top of the page. */
|
|
/* vclnPrinted = 0; */
|
|
/* print out header string */
|
|
|
|
viCurrentPage = 1;
|
|
vpchPrintBuf = rgchPrintBuf;
|
|
PrintHeaderFooter(TRUE);
|
|
|
|
vclnPrinted = vlnTop;
|
|
|
|
for (itdd = vitddFirst; !vfAbortPrint && itdd < vitddMax; itdd++)
|
|
{
|
|
pdd = TddLock () + itdd;
|
|
dt = pdd -> dt;
|
|
dl = pdd -> dl;
|
|
idr = pdd -> idr;
|
|
TddUnlock ();
|
|
|
|
if (idr == IDRNIL)
|
|
{
|
|
/* The date is not in memory, see if it's on disk. */
|
|
if (dl == DLNIL)
|
|
{
|
|
/*
|
|
Not on disk either - this is an empty DD. Skip
|
|
over this date.
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
/* Read the date from disk into the free DR. */
|
|
ReadTempDr (idr = idrFree, dl);
|
|
}
|
|
|
|
/* Calculate how many lines are needed to print this date. */
|
|
if (!PrintDate (idr, dt, FALSE))
|
|
return;
|
|
if (vclnDate == 0)
|
|
{
|
|
/* There's nothing to print in this date (must just
|
|
be special times without alarms or text and no notes).
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
/* Change 0 to vlnTop + 2. 21 Sept 1989 Clark Cyr */
|
|
if (vclnPrinted > vlnTop + 2)
|
|
{
|
|
/* Not at top of page - see if this entire date will fit
|
|
on the remainder of this page. If it won't fit, start
|
|
a new page.
|
|
*/
|
|
/* print out the footer if bottom of page is reached */
|
|
|
|
if (vclnPrinted + vclnDate > vlnBottom ){
|
|
|
|
PrintHeaderFooter( FALSE );
|
|
if (!NewPage ())
|
|
return;
|
|
viCurrentPage++;
|
|
|
|
/* print out the header on top of new page */
|
|
PrintHeaderFooter( TRUE );
|
|
vclnPrinted = vlnTop;
|
|
}
|
|
}
|
|
/* Print the schedule for the date. */
|
|
if (!PrintDate (idr, dt, TRUE))
|
|
return;
|
|
}
|
|
/* print out the footer if bottom of page is reached */
|
|
|
|
if (vclnPrinted < vlnFooter - 3)
|
|
PrintHeaderFooter(FALSE);
|
|
|
|
if (NewPage ())
|
|
EndPrint ();
|
|
|
|
|
|
GlobalFree(hHeadFoot);
|
|
hHeadFoot=NULL;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* convert floating point strings (like 2.75 1.5 2) into number of pixels
|
|
* given the number of pixels per inch
|
|
*/
|
|
|
|
INT atopix(CHAR *ptr, INT pix_per_in)
|
|
{
|
|
CHAR *dot_ptr;
|
|
CHAR sz[20];
|
|
INT decimal;
|
|
|
|
lstrcpy(sz, ptr);
|
|
|
|
dot_ptr = strchr(sz, szDec[0]);
|
|
|
|
if (dot_ptr)
|
|
{
|
|
*dot_ptr++ = 0; /* terminate the inches */
|
|
if (*(dot_ptr + 1) == 0)
|
|
{
|
|
*(dot_ptr + 1) = '0'; /* convert decimal part to hundredths */
|
|
*(dot_ptr + 2) = 0;
|
|
}
|
|
decimal = ((INT)atol(dot_ptr) * pix_per_in) / 100; /* first part */
|
|
}
|
|
else
|
|
decimal = 0; /* there is not fraction part */
|
|
|
|
return ((INT)atol(sz) * pix_per_in) + decimal; /* second part */
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** PrintDate - Print the specified date. If fPrint == FALSE then
|
|
don't actually print - just set up vclnDate with the number of
|
|
lines required to print the date.
|
|
*/
|
|
|
|
BOOL APIENTRY PrintDate (
|
|
INT idr,
|
|
DT dt,
|
|
BOOL fPrint)
|
|
{
|
|
CHAR rgchPrintBuf [CCHPRINTBUF];
|
|
CHAR szTemp[CCHPRINTBUF];
|
|
DR *pdr;
|
|
register PQR pqr;
|
|
PQR pqrMax;
|
|
CHAR *pchTemp;
|
|
register CHAR *pchSrc;
|
|
CHAR *pchDest;
|
|
CHAR c;
|
|
BOOL fSameLine;
|
|
INT cchTime, i, nx;
|
|
BOOL fFirstLine;
|
|
BOOL fFirstPM;
|
|
|
|
/* Set up global print flag for routines to be called. */
|
|
vfPrint = fPrint;
|
|
|
|
/* Set up a global pointer to the print buffer. */
|
|
vpchPrintBuf = rgchPrintBuf;
|
|
|
|
/* Initialize the count of lines required to print this date. */
|
|
vclnDate = 0;
|
|
|
|
pdr = PdrLock (idr);
|
|
fFirstLine = TRUE;
|
|
fFirstPM = TRUE;
|
|
for (pqrMax = (PQR)((BYTE UNALIGNED*)(pqr = (PQR)PbTqrFromPdr(pdr)) +
|
|
pdr->cbTqr); pqr<pqrMax; pqr = (PQR)((BYTE UNALIGNED*)pqr + pqr->cb))
|
|
{
|
|
/* Don't print special times that don't have an alarm
|
|
or an appointment description.
|
|
*/
|
|
if (pqr -> fAlarm || pqr -> cb != CBQRHEAD)
|
|
{
|
|
if (!PrintHeading (dt))
|
|
goto error0;
|
|
|
|
FillBuf (vpchPrintBuf, CCHPRINTBUF, ' ');
|
|
if (pqr -> fAlarm)
|
|
*(vpchPrintBuf+viLeftMarginLen) = '*';
|
|
|
|
cchTime = GetTimeSz (pqr -> tm, vpchPrintBuf + viLeftMarginLen + 2);
|
|
|
|
/* Print am/pm strings only for first line printed and noon */
|
|
if (!(fFirstLine || ((fFirstPM && pqr->tm >= TMNOON))))
|
|
FillBuf (vpchPrintBuf+viLeftMarginLen+5+2, cchTime-5, ' ');
|
|
|
|
FillBuf (vpchPrintBuf, viLeftMarginLen, ' '); /* pad margin space
|
|
with blanks */
|
|
pchTemp = vpchPrintBuf + viLeftMarginLen + cchTime+2;
|
|
*pchTemp++ = ' ';
|
|
*pchTemp = '\0';
|
|
if (pqr -> cb > CBQRHEAD)
|
|
lstrcpy (pchTemp, (CHAR *)((BYTE *)pqr + CBQRHEAD));
|
|
|
|
|
|
CheckMarg:
|
|
if (lstrlen(vpchPrintBuf) > (hChars-viRightMarginLen))
|
|
{
|
|
lstrcpy(szTemp, vpchPrintBuf+hChars-viRightMarginLen);
|
|
vpchPrintBuf[hChars-viRightMarginLen]=0;
|
|
|
|
if (!PrintLine ())
|
|
goto error0;
|
|
|
|
/* CHeck for one space at the beginning and strip if needed */
|
|
nx=0;
|
|
if (szTemp[0]==' ' && szTemp[1]!=' ')
|
|
nx=1;
|
|
|
|
/* Note that 11 = strlen[* + Time + AM/PM + Space] */
|
|
|
|
FillBuf (vpchPrintBuf, viLeftMarginLen+11-nx, ' ');
|
|
vpchPrintBuf[viLeftMarginLen+11-nx]=0;
|
|
lstrcat(vpchPrintBuf, szTemp);
|
|
goto CheckMarg;
|
|
}
|
|
|
|
if (!PrintLine ())
|
|
goto error0;
|
|
fFirstLine = FALSE;
|
|
if (pqr->tm >= TMNOON)
|
|
fFirstPM = FALSE;
|
|
}
|
|
}
|
|
|
|
if (pdr -> cbNotes != 0)
|
|
{
|
|
if (vclnDate != 0)
|
|
{
|
|
if (!PrintBlankLn (CLNAFTERAPPOINTMENTS))
|
|
goto error0;
|
|
}
|
|
else
|
|
if (!PrintHeading (dt))
|
|
goto error0;
|
|
|
|
/* The notes are split into lines as follows:
|
|
'\0' terminates a line and terminates the notes.
|
|
<CR,LF> is a hard line break (user typed Enter key).
|
|
<CR,CR,LF> is a soft line break (caused by word wrap).
|
|
In order to do something reasonable no matter what is seen,
|
|
this code skips an abitrary number of CRs followed by an
|
|
arbitrary number of LFs.
|
|
*/
|
|
pchSrc = (CHAR *)((BYTE *)pdr + CBDRHEAD);
|
|
while (*pchSrc != '\0')
|
|
{
|
|
pchDest = rgchPrintBuf;
|
|
fSameLine = TRUE;
|
|
|
|
i=0; /* fill margin space with blanks */
|
|
while (i<viLeftMarginLen)
|
|
{
|
|
*pchDest++ = ' ';
|
|
i++;
|
|
}
|
|
|
|
while (fSameLine)
|
|
{
|
|
c=*pchSrc;
|
|
#ifdef DBCS
|
|
if( IsDBCSLeadByte(*pchSrc) )
|
|
*pchDest++ = *pchSrc++;
|
|
*pchDest++ = *pchSrc++;
|
|
#else
|
|
*pchDest++ = *pchSrc++;
|
|
#endif
|
|
|
|
if (c=='\r')
|
|
{
|
|
/* Eat multiple CRs if present. */
|
|
while (*pchSrc == '\r')
|
|
pchSrc++;
|
|
|
|
/* Eat line feeds following carriage return if
|
|
there are any.
|
|
*/
|
|
while (*pchSrc == '\n')
|
|
*pchSrc++;
|
|
/* Terminate the line. */
|
|
*(pchDest - 1) = 0;
|
|
|
|
fSameLine = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (c=='\0')
|
|
{
|
|
/* Backup to point to the 0 so the outer
|
|
loop terminates. */
|
|
pchSrc--;
|
|
fSameLine = FALSE;
|
|
}
|
|
}
|
|
|
|
i++;
|
|
if ( i >= (hChars - viRightMarginLen))
|
|
fSameLine = FALSE;
|
|
}
|
|
|
|
while (*(vpchPrintBuf+viLeftMarginLen)==' ')
|
|
vpchPrintBuf++;
|
|
|
|
if (!PrintLine ())
|
|
goto error0;
|
|
|
|
vpchPrintBuf=rgchPrintBuf;
|
|
}
|
|
}
|
|
DrUnlock (idr);
|
|
return TRUE;
|
|
error0:
|
|
DrUnlock(idr);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** PrintHeading - print the heading if it hasn't been printed yet. */
|
|
|
|
BOOL APIENTRY PrintHeading (DT dt)
|
|
{
|
|
D3 d3;
|
|
if (vclnDate == 0)
|
|
{
|
|
/* The heading has not yet been printed - print it out
|
|
now since we now know that the date is not empty.
|
|
*/
|
|
|
|
/* First put out the lines between dates if this date is
|
|
not being printed at the top of a page.
|
|
*/
|
|
vclnPrinted++;
|
|
/* Convert the date into an ASCII string. */
|
|
GetD3FromDt (dt, &d3);
|
|
|
|
FillBuf (vpchPrintBuf, viLeftMarginLen, ' '); /* pad margin space
|
|
with blanks */
|
|
GetDateDisp (&d3, vpchPrintBuf + viLeftMarginLen);
|
|
|
|
if (!PrintLine ())
|
|
return FALSE;
|
|
if (!PrintBlankLn (CLNAFTERHEADING))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** PrintBlankLn */
|
|
|
|
INT APIENTRY PrintBlankLn (INT cln)
|
|
{
|
|
*vpchPrintBuf = '\0';
|
|
while (cln--)
|
|
if (!PrintLine ())
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** PrintLine */
|
|
|
|
BOOL APIENTRY PrintLine ()
|
|
{
|
|
if (vfPrint)
|
|
{
|
|
/* print footer if bottom of page is reached */
|
|
|
|
if (vclnPrinted >= vclnPage)
|
|
{
|
|
PrintHeaderFooter(FALSE);
|
|
if (!NewPage ())
|
|
return FALSE;
|
|
viCurrentPage++;
|
|
PrintHeaderFooter(TRUE); /* print header */
|
|
vclnPrinted = vlnTop;
|
|
}
|
|
|
|
TextOut (vhDCPrint, 0, vclnPrinted * vcyPrintLineToLine,
|
|
vpchPrintBuf, lstrlen (vpchPrintBuf));
|
|
vclnPrinted++;
|
|
}
|
|
|
|
vclnDate++;
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* BOOL PASCAL PrintHeaderFooter ( hdr )
|
|
*
|
|
* function : generates and formats the header/footer strings and copies
|
|
* them to the print buffer
|
|
*
|
|
* params : IN hdr : boolean indicating if header(TRUE) or footer(FALSE)
|
|
* is to be generated
|
|
*
|
|
* called by: Print(), PrintLine()
|
|
*
|
|
* returns : none
|
|
*
|
|
***************************************************************************/
|
|
|
|
BOOL APIENTRY PrintHeaderFooter(BOOL bHeader)
|
|
{
|
|
CHAR buf[80];
|
|
int len;
|
|
|
|
/* 1-bHeader gives 0 for header, 1 for footer. */
|
|
lstrcpy(buf, chPageText[1-bHeader]);
|
|
|
|
szHeadFoot=GlobalLock(hHeadFoot);
|
|
len=TranslateString(buf);
|
|
|
|
if (*szHeadFoot)
|
|
{
|
|
if (bHeader)
|
|
TextOut(vhDCPrint, dxLeft, dyHeadFoot - CharPrintHeight, szHeadFoot, len);
|
|
else
|
|
TextOut(vhDCPrint, dxLeft, yPrintRes-CharPrintHeight-dyHeadFoot, szHeadFoot, len);
|
|
}
|
|
GlobalUnlock(hHeadFoot);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* int TranslateString(char *src)
|
|
*
|
|
* purpose:
|
|
* translate a header/footer strings
|
|
*
|
|
* supports the following:
|
|
*
|
|
* && insert a & char
|
|
* &f current file name or (untitiled)
|
|
* &d date in Day Month Year
|
|
* &t time
|
|
* &p page number
|
|
* &p+num set first page number to num
|
|
*
|
|
* params:
|
|
* IN/OUT src this is the string to translate, gets filled with
|
|
* translate string. limited by len chars
|
|
* IN len # chars src pts to
|
|
*
|
|
* used by:
|
|
* Header Footer stuff
|
|
*
|
|
* uses:
|
|
* lots of c lib stuff
|
|
*
|
|
* restrictions:
|
|
* this function uses the following global data
|
|
*
|
|
* iPageNum
|
|
* text from main window caption
|
|
*
|
|
***************************************************************************/
|
|
|
|
int TranslateString(CHAR *src)
|
|
{
|
|
CHAR letters[15];
|
|
CHAR chBuff[3][80], buf[80];
|
|
CHAR *ptr, *dst=buf, *save_src=src;
|
|
INT page;
|
|
int nAlign=1, foo, nx,
|
|
nIndex[3];
|
|
struct tm *newtime;
|
|
time_t long_time;
|
|
|
|
nIndex[0]=0;
|
|
nIndex[1]=0;
|
|
nIndex[2]=0;
|
|
|
|
/* Get the time we need in case we use &t. */
|
|
time(&long_time);
|
|
newtime=localtime(&long_time);
|
|
|
|
LoadString(vhInstance, IDS_LETTERS, letters, 15);
|
|
|
|
while (*src) /* look at all of source */
|
|
{
|
|
while (*src && *src != '&')
|
|
{
|
|
chBuff[nAlign][nIndex[nAlign]]=*src++;
|
|
nIndex[nAlign] += 1;
|
|
}
|
|
|
|
if (*src == '&') /* is it the escape char? */
|
|
{
|
|
src++;
|
|
|
|
if (*src == letters[0] || *src == letters[1])
|
|
{ /* &f file name (no path) */
|
|
|
|
/* a bit of sleez... get the caption from
|
|
* the main window. search for the '-' and
|
|
* look two chars beyond, there is the
|
|
* file name or (untitiled) (cute hu?)
|
|
*/
|
|
|
|
GetWindowText(vhwnd0, buf, 80);
|
|
ptr=strchr(buf, '-') + 2;
|
|
|
|
/* Copy to the currently aligned string. */
|
|
lstrcpy((chBuff[nAlign]+nIndex[nAlign]), ptr);
|
|
|
|
/* Update insertion position. */
|
|
nIndex[nAlign] += lstrlen(ptr);
|
|
}
|
|
else
|
|
|
|
if (*src == letters[2] || *src == letters[3])
|
|
{ /* &P or &P+num page */
|
|
src++;
|
|
page = 0;
|
|
if (*src == '+') /* &p+num case */
|
|
{
|
|
src++;
|
|
while (isdigit(*src))
|
|
{
|
|
/* Convert to int on-the-fly*/
|
|
page = (10*page)+(UCHAR)(*src)-48;
|
|
src++;
|
|
}
|
|
|
|
}
|
|
|
|
_itoa(viCurrentPage+page, buf, 10);
|
|
lstrcpy((chBuff[nAlign]+nIndex[nAlign]), buf);
|
|
nIndex[nAlign] += lstrlen(buf);
|
|
src--;
|
|
}
|
|
else
|
|
|
|
if (*src == letters[4] || *src == letters[5])
|
|
{ /* &t time */
|
|
|
|
ptr = asctime(newtime);
|
|
|
|
/* extract time */
|
|
strncpy(chBuff[nAlign]+nIndex[nAlign], ptr+11, 8);
|
|
nIndex[nAlign] += 8;
|
|
|
|
}
|
|
else
|
|
|
|
if (*src == letters[6] || *src == letters[7])
|
|
{ /* &d date */
|
|
|
|
ptr = asctime(newtime);
|
|
|
|
/* extract day month day */
|
|
strncpy(chBuff[nAlign]+nIndex[nAlign], ptr, 11);
|
|
nIndex[nAlign] += 11;
|
|
|
|
/* extract year */
|
|
strncpy(chBuff[nAlign]+nIndex[nAlign], ptr+20, 4);
|
|
nIndex[nAlign] += 4;
|
|
|
|
}
|
|
else
|
|
|
|
if (*src == '&')
|
|
{ /* quote a single & */
|
|
|
|
chBuff[nAlign][nIndex[nAlign]]='&';
|
|
nIndex[nAlign] += 1;
|
|
|
|
}
|
|
else
|
|
|
|
/* Set the alignment for whichever has last occured. */
|
|
|
|
if (*src == letters[8] || *src == letters[9])
|
|
/* &c center */
|
|
|
|
nAlign=1;
|
|
|
|
else
|
|
|
|
if (*src == letters[10] || *src == letters[11])
|
|
/* &r right */
|
|
nAlign=2;
|
|
|
|
else
|
|
|
|
if (*src == letters[12] || *src == letters[13])
|
|
/* &d date */
|
|
nAlign=0;
|
|
|
|
|
|
src++;
|
|
|
|
|
|
}
|
|
}
|
|
/* Make sure all strings are null-terminated. */
|
|
for (nAlign=0; nAlign<3; nAlign++)
|
|
chBuff[nAlign][nIndex[nAlign]]=0;
|
|
|
|
/* Initialize Header/Footer string */
|
|
for (nx=0; nx<xCharPage; nx++)
|
|
*(szHeadFoot+nx)=32;
|
|
|
|
/* Copy Left aligned text. */
|
|
for (nx=0; nx < nIndex[0]; nx++)
|
|
*(szHeadFoot+nx)=chBuff[0][nx];
|
|
|
|
/* Calculate where the centered text should go. */
|
|
foo=(xCharPage-nIndex[1])/2;
|
|
for (nx=0; nx<nIndex[1]; nx++)
|
|
*(szHeadFoot+foo+nx)=(CHAR)chBuff[1][nx];
|
|
|
|
/* Calculate where the right aligned text should go. */
|
|
foo=xCharPage-nIndex[2];
|
|
for (nx=0; nx<nIndex[2]; nx++)
|
|
*(szHeadFoot+foo+nx)=(CHAR)chBuff[2][nx];
|
|
|
|
|
|
return lstrlen(szHeadFoot);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** NewPage */
|
|
BOOL APIENTRY NewPage ()
|
|
|
|
{
|
|
INT iErr;
|
|
|
|
if ((iErr = Escape(vhDCPrint, NEWFRAME, 0, NULL, 0)) < 0) {
|
|
EndPrint();
|
|
CalPrintAlert(iErr);
|
|
return FALSE;
|
|
}
|
|
vclnPrinted = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**** BeginPrint - code taken from Cardfile - inprint.c - modified
|
|
as necessary. Returns 0 if successful, SP_errorcode (which is
|
|
< 0 for error.
|
|
*/
|
|
|
|
INT APIENTRY BeginPrint ()
|
|
|
|
{
|
|
|
|
CHAR rgchWindowText [CCHSZWINDOWTEXTMAX];
|
|
INT iErr;
|
|
|
|
/* If can't create print DC, return FALSE to indicate can't print. */
|
|
if (!(vhDCPrint = GetPrinterDC()))
|
|
return (SP_ERROR);
|
|
|
|
/* Show the hour glass cursor. */
|
|
HourGlassOn ();
|
|
|
|
vfAbortPrint = FALSE;
|
|
SetAbortProc(vhDCPrint, FnProcAbortPrint);
|
|
GetWindowText (vhwnd0, rgchWindowText, CCHSZWINDOWTEXTMAX);
|
|
|
|
/* Gotta disable the window before doing the start doc so that the user
|
|
* can't quickly do multiple prints.
|
|
*/
|
|
EnableWindow (vhwnd0, FALSE);
|
|
|
|
if ((iErr = Escape(vhDCPrint, STARTDOC, lstrlen((LPSTR)rgchWindowText),
|
|
(LPSTR)rgchWindowText, (LPSTR)0)) < 0) {
|
|
EnableWindow (vhwnd0, TRUE);
|
|
DeleteDC(vhDCPrint);
|
|
HourGlassOff();
|
|
return iErr;
|
|
}
|
|
|
|
vhwndAbortPrint = CreateDialog(vhInstance, MAKEINTRESOURCE(IDD_ABORTPRINT),
|
|
vhwnd0,FnDlgAbortPrint);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
/**** EndPrint - code taken from Cardfile - inprint.c - modified
|
|
as necessary.
|
|
*/
|
|
|
|
VOID APIENTRY EndPrint ()
|
|
|
|
{
|
|
|
|
|
|
if (!vfAbortPrint)
|
|
Escape(vhDCPrint, ENDDOC, 0, (LPSTR)0, (LPSTR)0);
|
|
|
|
/* The previous Escape could have changed the value of vfAbortPrint;
|
|
* So, this has to be tested again;
|
|
* Fix for Bug #6029 --SANKAR-- 11-9-89
|
|
*/
|
|
if(!vfAbortPrint)
|
|
DestroyAbortWnd();
|
|
|
|
DeleteDC (vhDCPrint);
|
|
|
|
/* The waiting is over. */
|
|
HourGlassOff ();
|
|
|
|
}
|
|
|
|
|
|
/**** FnProcAbortPrint - code taken from Cardfile - inprint.c - modified
|
|
as necessary.
|
|
*/
|
|
|
|
INT APIENTRY FnProcAbortPrint (
|
|
HDC hDC,
|
|
INT iReserved)
|
|
{
|
|
|
|
MSG msg;
|
|
|
|
while (!vfAbortPrint && PeekMessage(&msg, NULL, 0L, 0L, TRUE))
|
|
if (vhwndAbortPrint == NULL || !IsDialogMessage(vhwndAbortPrint, &msg))
|
|
{
|
|
TranslateMessage (&msg);
|
|
DispatchMessage (&msg);
|
|
}
|
|
return (!vfAbortPrint);
|
|
|
|
}
|
|
|
|
|
|
/**** FnDlgAbortPrint - code taken from Cardfile - inprint.c - modified
|
|
as necessary.
|
|
*/
|
|
|
|
INT_PTR CALLBACK FnDlgAbortPrint (
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
|
|
static HMENU hMenuSys;
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_COMMAND:
|
|
vfAbortPrint = TRUE;
|
|
DestroyAbortWnd();
|
|
vhwndAbortPrint = NULL;
|
|
return (TRUE);
|
|
|
|
case WM_INITDIALOG:
|
|
hMenuSys = GetSystemMenu (hwnd, FALSE);
|
|
SetDlgItemText (hwnd, IDCN_PATH,
|
|
(LPSTR)PFileInPath (vszFileSpec));
|
|
CalSetFocus (hwnd);
|
|
return (TRUE);
|
|
|
|
case WM_INITMENU:
|
|
EnableMenuItem (hMenuSys, SC_CLOSE, MF_GRAYED);
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
/**** Enable Tiled window, THEN destroy dialog window. */
|
|
VOID DestroyAbortWnd()
|
|
{
|
|
EnableWindow (vhwnd0, TRUE);
|
|
DestroyWindow (vhwndAbortPrint);
|
|
vhwndAbortPrint = NULL;
|
|
}
|
|
|
|
/**** Post printing error message box */
|
|
VOID APIENTRY CalPrintAlert(INT iErr)
|
|
{
|
|
INT iszErr;
|
|
|
|
/* Map error code to string index */
|
|
if (iErr == SP_OUTOFDISK)
|
|
iszErr = IDS_NEDSTP;
|
|
else if (iErr == SP_OUTOFMEMORY)
|
|
iszErr = IDS_NEMTP;
|
|
else iszErr = IDS_CANNOTPRINT;
|
|
|
|
AlertBox (vrgsz[iszErr], PFileInPath (vszFileSpec),
|
|
MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
|
|
|
|
|
|
/* ** GetPrinterDc()
|
|
Get Dc for current device on current output port according to
|
|
info in win.ini.
|
|
returns
|
|
DC if success
|
|
NULL if failure
|
|
|
|
*/
|
|
HDC APIENTRY GetPrinterDC()
|
|
{
|
|
extern BOOL bPrinterSetupDone;
|
|
LPDEVMODE lpDevMode;
|
|
LPDEVNAMES lpDevNames;
|
|
|
|
if(!bPrinterSetupDone){ /* Retrieve default printer if none selected. */
|
|
vPD.Flags = PD_RETURNDEFAULT|PD_PRINTSETUP;
|
|
vPD.hDevNames = NULL;
|
|
vPD.hDevMode = NULL;
|
|
PrintDlg(&vPD);
|
|
}
|
|
|
|
if(!vPD.hDevNames)
|
|
return NULL;
|
|
|
|
lpDevNames = (LPDEVNAMES)GlobalLock(vPD.hDevNames);
|
|
|
|
if(vPD.hDevMode)
|
|
lpDevMode = (LPDEVMODE)GlobalLock(vPD.hDevMode);
|
|
else
|
|
lpDevMode = NULL;
|
|
|
|
|
|
/* For pre 3.0 Drivers,hDevMode will be null from Commdlg so lpDevMode
|
|
* will be NULL after GlobalLock()
|
|
*/
|
|
|
|
vPD.hDC = CreateDC((LPSTR)lpDevNames+lpDevNames->wDriverOffset,
|
|
(LPSTR)lpDevNames+lpDevNames->wDeviceOffset,
|
|
(LPSTR)lpDevNames+lpDevNames->wOutputOffset,
|
|
lpDevMode);
|
|
|
|
GlobalUnlock(vPD.hDevNames);
|
|
|
|
if (vPD.hDevMode)
|
|
GlobalUnlock(vPD.hDevMode);
|
|
|
|
return vPD.hDC;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** IsDefaultPrinterStillValid()
|
|
** The user might setup the app for a particular printer and a port
|
|
** using the Printer Setup available in the application command menu;
|
|
** But later, he might go to control panel and delete that printer
|
|
** driver altogether or he might connect it to a different port;
|
|
** So, the application must process the WININICHANGE message and at that
|
|
** time, it must check whether the printer setup is still valid or not!
|
|
** This function is used to make that check;
|
|
** The input parameter is a string containing the printer name, driver,
|
|
** port selected by the printer setup; This function checks if the printer
|
|
** name is still present under the [Devices] section of Win.INI and if so,
|
|
** it will check if the port selected is listed among the ports to which
|
|
** this printer is connected; If not, it will automatically select the
|
|
** first port listed as the default port and modify the input "lpszPrinter"
|
|
** string accordingly;
|
|
** If the default printer is not listed in WIN.INI at all, then this
|
|
** function return FALSE;
|
|
** Fix for Bug #5607 -- SANKAR -- 10-30-89
|
|
**
|
|
****************************************************************************/
|
|
BOOL FAR APIENTRY IsDefaultPrinterStillValid(LPSTR lpszPrinter)
|
|
{
|
|
CHAR PrinterBuff[128];
|
|
CHAR DeviceBuff[128];
|
|
LPSTR lpPort; /* Default port name */
|
|
LPSTR lpFirstPort = NULL;
|
|
LPSTR lpch;
|
|
LPSTR lpListedPorts;
|
|
|
|
/* lpszPrinter contains "PrinterName,DriverName,Port" */
|
|
lstrcpy(PrinterBuff, lpszPrinter); /* Make a local copy of the default printer name */
|
|
|
|
/* Search for the end of printer name */
|
|
for(lpch = PrinterBuff; (*lpch)&&(*lpch != ','); lpch = AnsiNext(lpch))
|
|
;
|
|
if(*lpch)
|
|
*lpch++ = '\0';
|
|
/* Skip the Driver name; We do not need it! */
|
|
while(*lpch && *lpch <= ' ') /* Skip the blanks preceeding the driver name */
|
|
lpch = AnsiNext(lpch);
|
|
while(*lpch && *lpch != ',' && *lpch > ' ') /* Search for ',' following driver name */
|
|
lpch = AnsiNext(lpch);
|
|
while (*lpch && (*lpch <= ' ' || *lpch == ',')) /* Search for begining of port name */
|
|
lpch = AnsiNext(lpch);
|
|
lpPort = lpch; /* Default port name */
|
|
|
|
/* Search for the printer name among the [devices] section */
|
|
if (!GetProfileString("devices", PrinterBuff, "", DeviceBuff, 128))
|
|
return(FALSE); /* Default printer no longer exists */
|
|
|
|
lpch = DeviceBuff;
|
|
|
|
/* Skip the Driver filename */
|
|
while(*lpch && *lpch != ',')
|
|
lpch = AnsiNext(lpch);
|
|
|
|
while(*lpch)
|
|
{
|
|
/* Skip all blanks */
|
|
while(*lpch && (*lpch <= ' ' || *lpch == ','))
|
|
lpch = AnsiNext(lpch);
|
|
lpListedPorts = lpch;
|
|
if(!lpFirstPort)
|
|
lpFirstPort = lpch; /* Save the first port in the list */
|
|
/* Search for the end of the Port name */
|
|
while(*lpch && *lpch != ',' && *lpch > ' ')
|
|
lpch = AnsiNext(lpch);
|
|
if(*lpch)
|
|
*lpch++ = '\0';
|
|
|
|
/* Check if the port names are the same */
|
|
if(lstrcmp(lpPort, lpListedPorts) == 0)
|
|
return(TRUE); /* Default port exists among the listed ones */
|
|
}
|
|
|
|
/* The default port does not exist among the listed ports; So, change
|
|
* the default port to the first port in the list;
|
|
*/
|
|
if(*lpFirstPort)
|
|
{
|
|
/* Search for the end of the printer name */
|
|
for (lpch = szPrinter; (*lpch)&&(*lpch != ','); lpch = AnsiNext(lpch))
|
|
;
|
|
/* Skip the driver file name */
|
|
if(*lpch)
|
|
lpch++;
|
|
while(*lpch && *lpch != ',')
|
|
lpch = AnsiNext(lpch);
|
|
if(*lpch)
|
|
lpch++;
|
|
lstrcpy(lpch, lpFirstPort);
|
|
return(TRUE);
|
|
}
|
|
return(FALSE);
|
|
}
|