/*--------------------------------------------------------------------- PRINTCAL.C -- OS/2 Presentation Manager program to print a calendar (c) 1990, Ziff Communications Co. PC Magazine * Charles Petzold, 10/89 ---------------------------------------------------------------------*/ #define INCL_WIN #define INCL_GPI #define INCL_DEV #define INCL_DOSPROCESS #include #include #include #include #include #include "printcal.h" #define LCID_CALFONT 1L #define STACKSIZE (4096 * sizeof (int)) #define WM_USER_PRINT_OK (WM_USER + 0) #define WM_USER_PRINT_ERROR (WM_USER + 1) typedef struct { SHORT sYear, sMonthBeg, sMonthEnd ; } CALPARAMS ; typedef struct { int aiThreadStack[STACKSIZE / sizeof (int)] ; CALPARAMS cp ; HWND hwndNotify ; } THREADPARAMS ; MPARAM EXPENTRY ClientWndProc (HWND, USHORT, MPARAM, MPARAM) ; MPARAM EXPENTRY AboutDlgProc (HWND, USHORT, MPARAM, MPARAM) ; MPARAM EXPENTRY PrintDlgProc (HWND, USHORT, MPARAM, MPARAM) ; VOID DisplayPage (HPS, SIZEL *, SHORT, SHORT) ; VOID Message (HWND, SHORT, CHAR *) ; VOID FAR PrintThread (THREADPARAMS *) ; HDC OpenDefaultPrinterDC (HAB) ; // in PRINTDC.C HAB hab ; USHORT usActiveThreads = 0 ; int main (void) { static CHAR szClientClass[] = "PrintCal" ; static ULONG flFrameFlags = FCF_TITLEBAR | FCF_SYSMENU | FCF_SIZEBORDER | FCF_MINMAX | FCF_MENU | FCF_SHELLPOSITION | FCF_TASKLIST ; HMQ hmq ; HWND hwndFrame, hwndClient ; QMSG qmsg ; hab = WinInitialize (0) ; hmq = WinCreateMsgQueue (hab, 0) ; WinRegisterClass (hab, szClientClass, ClientWndProc, CS_SIZEREDRAW, 0) ; hwndFrame = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE, &flFrameFlags, szClientClass, NULL, 0L, NULL, ID_RESOURCE, &hwndClient) ; while (TRUE) { while (WinGetMsg (hab, &qmsg, NULL, 0, 0)) WinDispatchMsg (hab, &qmsg) ; if (usActiveThreads == 0) break ; Message (hwndClient, MB_ICONEXCLAMATION, "Printing thread still active.\n" "Program cannot be closed now.") ; WinCancelShutdown (hmq, FALSE) ; } WinDestroyWindow (hwndFrame) ; WinDestroyMsgQueue (hmq) ; WinTerminate (hab) ; return 0 ; } MPARAM EXPENTRY ClientWndProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2) { static CALPARAMS cp ; static HPS hps ; static SIZEL sizlClient ; DATETIME dt ; HDC hdc ; SHORT sResult ; SIZEL sizlPage ; THREADPARAMS *ptp ; switch (msg) { case WM_CREATE: hdc = WinOpenWindowDC (hwnd) ; sizlPage.cx = 0 ; sizlPage.cy = 0 ; hps = GpiCreatePS (hab, hdc, &sizlPage, PU_ARBITRARY | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC) ; DosGetDateTime (&dt) ; cp.sYear = dt.year ; cp.sMonthBeg = dt.month - 1 ; cp.sMonthEnd = dt.month - 1 ; return 0 ; case WM_SIZE: sizlClient.cx = SHORT1FROMMP (mp2) ; sizlClient.cy = SHORT2FROMMP (mp2) ; GpiConvert (hps, CVTC_DEVICE, CVTC_PAGE, 1L, (PPOINTL) &sizlClient) ; return 0 ; case WM_PAINT: WinBeginPaint (hwnd, hps, NULL) ; GpiErase (hps) ; DisplayPage (hps, &sizlClient, cp.sYear, cp.sMonthBeg) ; WinEndPaint (hps) ; return 0 ; case WM_COMMAND: switch (COMMANDMSG(&msg)->cmd) { case IDM_ABOUT: WinDlgBox (HWND_DESKTOP, hwnd, AboutDlgProc, NULL, IDD_ABOUT, NULL) ; return 0 ; case IDM_PRINT: sResult = WinDlgBox (HWND_DESKTOP, hwnd, PrintDlgProc, NULL, IDD_PRINT, &cp) ; if (sResult == DID_CANCEL) return 0 ; WinInvalidateRect (hwnd, NULL, FALSE) ; if (sResult == IDD_PREVIEW) return 0 ; if ((ptp = malloc (sizeof (THREADPARAMS))) == NULL) { Message (hwnd, MB_ICONEXCLAMATION, "Cannot allocate memory for print thread!") ; return 0 ; } ptp->cp = cp ; ptp->hwndNotify = hwnd ; if (_beginthread (PrintThread, ptp->aiThreadStack, STACKSIZE, ptp) == -1) { free (ptp) ; Message (hwnd, MB_ICONEXCLAMATION, "Cannot create print thread!") ; return 0 ; } usActiveThreads++ ; Message (hwnd, MB_ICONASTERISK, "Print job successfully started.") ; return 0 ; } break ; case WM_USER_PRINT_OK: ptp = PVOIDFROMMP (mp1) ; free (ptp) ; usActiveThreads-- ; Message (hwnd, MB_ICONASTERISK, "Print job sent to spooler.") ; return 0 ; case WM_USER_PRINT_ERROR: ptp = PVOIDFROMMP (mp1) ; free (ptp) ; usActiveThreads-- ; Message (hwnd, MB_ICONEXCLAMATION, "Error encountered during printing.") ; return 0 ; case WM_DESTROY: GpiDestroyPS (hps) ; return 0 ; } return WinDefWindowProc (hwnd, msg, mp1, mp2) ; } MRESULT EXPENTRY AboutDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2) { switch (msg) { case WM_COMMAND: switch (COMMANDMSG(&msg)->cmd) { case DID_OK: case DID_CANCEL: WinDismissDlg (hwnd, TRUE) ; return 0 ; } break ; } return WinDefDlgProc (hwnd, msg, mp1, mp2) ; } MRESULT EXPENTRY PrintDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2) { static CALPARAMS cpLocal, *pcpCurrent ; switch (msg) { case WM_INITDLG: pcpCurrent = PVOIDFROMMP (mp2) ; cpLocal = *pcpCurrent ; WinSendDlgItemMsg (hwnd, IDD_MONTHBEG + cpLocal.sMonthBeg, BM_SETCHECK, MPFROM2SHORT (TRUE, 0), NULL) ; WinSendDlgItemMsg (hwnd, IDD_MONTHEND + cpLocal.sMonthEnd, BM_SETCHECK, MPFROM2SHORT (TRUE, 0), NULL) ; WinSendDlgItemMsg (hwnd, IDD_YEAR, EM_SETTEXTLIMIT, MPFROM2SHORT (4, 0), NULL) ; WinSetDlgItemShort (hwnd, IDD_YEAR, cpLocal.sYear, FALSE) ; return 0 ; case WM_CONTROL: if (SHORT1FROMMP (mp1) >= IDD_MONTHBEG && SHORT1FROMMP (mp1) < IDD_MONTHBEG + 12) cpLocal.sMonthBeg = SHORT1FROMMP (mp1) - IDD_MONTHBEG ; else if (SHORT1FROMMP (mp1) >= IDD_MONTHEND && SHORT1FROMMP (mp1) < IDD_MONTHEND + 12) cpLocal.sMonthEnd = SHORT1FROMMP (mp1) - IDD_MONTHEND ; return 0 ; case WM_COMMAND: switch (COMMANDMSG(&msg)->cmd) { case DID_OK: case IDD_PREVIEW: WinQueryDlgItemShort (hwnd, IDD_YEAR, &cpLocal.sYear, FALSE) ; if (cpLocal.sYear < 1900 || cpLocal.sYear > 2099) { Message (hwnd, MB_ICONEXCLAMATION, "Year must be between 1900 and 2099!") ; WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, IDD_YEAR)) ; return 0 ; } if (cpLocal.sMonthBeg > cpLocal.sMonthEnd) { Message (hwnd, MB_ICONEXCLAMATION, "Begin month cannot be later " "than end month!") ; WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, IDD_MONTHBEG + cpLocal.sMonthBeg)) ; return 0 ; } *pcpCurrent = cpLocal ; // fall through case DID_CANCEL: WinDismissDlg (hwnd, COMMANDMSG(&msg)->cmd) ; return 0 ; } break ; } return WinDefDlgProc (hwnd, msg, mp1, mp2) ; } VOID DisplayPage (HPS hps, SIZEL *psizlPage, SHORT sYear, SHORT sMonth) { static CHAR *apszMonths[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" } ; static SHORT asMonthLen[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } ; static SHORT asMonthStart[] = { 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 } ; CHAR szBuffer[16] ; BOOL fLeap ; FATTRS fat ; LONG lLength ; POINTL ptl, aptlTextBox[TXTBOX_COUNT] ; SHORT sDayStart, sDay, sExtraDay ; SIZEF sizfx ; SIZEL sizlCell ; GpiSavePS (hps) ; // Determine size of day cell sizlCell.cx = (psizlPage->cx - 1) / 7 ; sizlCell.cy = (psizlPage->cy - 1) / 7 ; // Create the vector font and use it in the PS fat.usRecordLength = sizeof fat ; fat.fsSelection = 0 ; fat.lMatch = 0 ; fat.idRegistry = 0 ; fat.usCodePage = GpiQueryCp (hps) ; fat.lMaxBaselineExt = 0 ; fat.lAveCharWidth = 0 ; fat.fsType = 0 ; fat.fsFontUse = FATTR_FONTUSE_OUTLINE | FATTR_FONTUSE_TRANSFORMABLE ; strcpy (fat.szFacename, "Helv") ; GpiCreateLogFont (hps, NULL, LCID_CALFONT, &fat) ; GpiSetCharSet (hps, LCID_CALFONT) ; // Scale the font for the month and year name lLength = sprintf (szBuffer, " %s %d ", apszMonths[sMonth], sYear) ; GpiQueryTextBox (hps, lLength, szBuffer, TXTBOX_COUNT, aptlTextBox) ; GpiQueryCharBox (hps, &sizfx) ; sizfx.cx = sizlCell.cx * sizfx.cx / aptlTextBox[TXTBOX_CONCAT].x * 7 ; sizfx.cy = sizlCell.cy * sizfx.cy / (aptlTextBox[TXTBOX_TOPLEFT].y - aptlTextBox[TXTBOX_BOTTOMLEFT].y) ; sizfx.cx = sizfx.cy = min (sizfx.cx, sizfx.cy) ; GpiSetCharBox (hps, &sizfx) ; GpiQueryTextBox (hps, lLength, szBuffer, TXTBOX_COUNT, aptlTextBox) ; // Display month and year at top of page ptl.x = (psizlPage->cx - aptlTextBox[TXTBOX_CONCAT].x) / 2 ; ptl.y = 6 * sizlCell.cy - aptlTextBox[TXTBOX_BOTTOMLEFT].y ; GpiCharStringAt (hps, &ptl, lLength, szBuffer) ; // Set font size for day numbers sizfx.cx = sizfx.cy = MAKEFIXED (min (sizlCell.cx, sizlCell.cy) / 4, 0) ; GpiSetCharBox (hps, &sizfx) ; // Calculate some variables for showing days in month fLeap = (sYear % 4 == 0) && ((sYear % 100 != 0) || (sYear % 400 == 0)) ; sExtraDay = fLeap && sMonth == 1 ? 1 : 0 ; sDayStart = 1 + sYear - 1900 + (sYear - 1901) / 4 ; sDayStart += asMonthStart[sMonth] + (fLeap && sMonth > 1 ? 1 : 0) ; sDayStart %= 7 ; // Loop through days for (sDay = 0 ; sDay < asMonthLen[sMonth] + sExtraDay ; sDay++) { ptl.x = (sDayStart + sDay) % 7 * sizlCell.cx ; ptl.y = (5 - (sDayStart + sDay) / 7) * sizlCell.cy ; GpiMove (hps, &ptl) ; ptl.x += sizlCell.cx ; ptl.y += sizlCell.cy ; GpiBox (hps, DRO_OUTLINE, &ptl, 0L, 0L) ; lLength = sprintf (szBuffer, " %d", sDay + 1) ; GpiQueryTextBox (hps, lLength, szBuffer, TXTBOX_COUNT, aptlTextBox) ; GpiQueryCurrentPosition (hps, &ptl) ; ptl.y += sizlCell.cy - aptlTextBox[TXTBOX_TOPLEFT].y ; GpiCharStringAt (hps, &ptl, lLength, szBuffer) ; } // Clean up GpiSetCharSet (hps, LCID_DEFAULT) ; GpiDeleteSetId (hps, LCID_CALFONT) ; GpiRestorePS (hps, -1L) ; } VOID Message (HWND hwnd, SHORT sIcon, CHAR *pszMessage) { WinMessageBox (HWND_DESKTOP, hwnd, pszMessage, "PrintCal", 0, sIcon | MB_OK | MB_MOVEABLE) ; } VOID FAR PrintThread (THREADPARAMS *ptp) { HAB hab ; HDC hdcPrinter ; HPS hpsPrinter ; SHORT msgReturn ; SHORT sMonth ; SIZEL sizlPage ; hab = WinInitialize (0) ; if ((hdcPrinter = OpenDefaultPrinterDC (hab)) != DEV_ERROR) { // Create the presentation space for the printer sizlPage.cx = 0 ; sizlPage.cy = 0 ; hpsPrinter = GpiCreatePS (hab, hdcPrinter, &sizlPage, PU_ARBITRARY | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC) ; GpiQueryPS (hpsPrinter, &sizlPage) ; // Start the document if (DevEscape (hdcPrinter, DEVESC_STARTDOC, 8L, "Calendar", NULL, NULL) != DEVESC_ERROR) { // Loop through months for (sMonth = ptp->cp.sMonthBeg ; sMonth <= ptp->cp.sMonthEnd ; sMonth++) { DisplayPage (hpsPrinter, &sizlPage, ptp->cp.sYear, sMonth); DevEscape (hdcPrinter, DEVESC_NEWFRAME, 0L, NULL, NULL, NULL) ; } // End the document DevEscape (hdcPrinter, DEVESC_ENDDOC, 0L, NULL, NULL, NULL) ; msgReturn = WM_USER_PRINT_OK ; } else msgReturn = WM_USER_PRINT_ERROR ; // Clean up GpiDestroyPS (hpsPrinter) ; DevCloseDC (hdcPrinter) ; } else msgReturn = WM_USER_PRINT_ERROR ; // Post message to client window and end thread DosEnterCritSec () ; WinPostMsg (ptp->hwndNotify, msgReturn, MPFROMP (ptp), NULL) ; WinTerminate (hab) ; _endthread () ; }