home *** CD-ROM | disk | FTP | other *** search
/ C Programming Starter Kit 2.0 / SamsPublishing-CProgrammingStarterKit-v2.0-Win31.iso / bc45 / owlsrc.pak / PRINTER.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-24  |  17.4 KB  |  675 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // (C) Copyright 1992, 1994 by Borland International, All Rights Reserved
  4. //
  5. //----------------------------------------------------------------------------
  6. #pragma hdrignore SECTION
  7. #include <owl/owlpch.h>
  8. #include <owl/applicat.h>
  9. #include <owl/appdict.h>
  10. #include <owl/window.h>
  11. #include <owl/framewin.h>
  12. #include <owl/dc.h>
  13. #include <owl/static.h>
  14. #include <owl/printer.h>
  15.  
  16. #if !defined(SECTION) || SECTION == 1
  17.  
  18. //
  19. // Macro used to set and clear a bunch of flags at once
  20. //
  21. #define SETCLEAR(flag, set, clear)((flag) = ((flag)&~(clear))|(set))
  22.  
  23. //#define MANUAL_ABORT_CALL   // define to manually call abort proc
  24.  
  25.  
  26. //----------------------------------------------------------------------------
  27.  
  28. // TFormattedStatic - Static control that uses its resource title as a printf
  29. // format string to format one or two text strings provided in the constructor
  30. //
  31. class TFormattedStatic: public TStatic {
  32.   public:
  33.     TFormattedStatic(TWindow* parent, int resId, const char far* text,
  34.                      const char far* text2=0);
  35.    ~TFormattedStatic();
  36.   
  37.   protected:
  38.     void SetupWindow();
  39.  
  40.   private:
  41.     char far*  Text;
  42.     char far*  Text2;
  43. };
  44.  
  45. TFormattedStatic::TFormattedStatic(TWindow* parent, int resId,
  46.                                    const char far* text, const char far* text2)
  47. :
  48.   TStatic(parent, resId, 0)
  49. {
  50.   Text = strnewdup(text);
  51.   Text2 = text2 ? strnewdup(text2) : 0;
  52. }
  53.  
  54. TFormattedStatic::~TFormattedStatic()
  55. {
  56.   delete Text;
  57.   delete Text2;
  58. }
  59.  
  60. void
  61. TFormattedStatic::SetupWindow()
  62. {
  63.   TStatic::SetupWindow();
  64.  
  65.   // Use the Title retrieved from the resource as a printf template for
  66.   // the one or two text strings, then set the text directly to the control to
  67.   // preserve the Title
  68.   //
  69.   char buff[80];
  70.   wsprintf(buff, Title, Text, Text2);  // second text string is optional
  71.   SetText(buff);
  72. }
  73.  
  74. // TNumericStatic - Static control that uses its resource title as a printf
  75. // format string to format one or two text strings provided in the constructor
  76. //
  77. class TNumericStatic: public TStatic {
  78.   public:
  79.     TNumericStatic(TWindow* parent, int resId, int number);
  80.  
  81.   protected:
  82.     LRESULT EvSetNumber(WPARAM wParam, LPARAM lParam);
  83.  
  84.   private:
  85.     int Number;
  86.  
  87.   DECLARE_RESPONSE_TABLE(TNumericStatic);
  88. };
  89. #define WM_SETNUMBER    WM_USER+100
  90.  
  91. DEFINE_RESPONSE_TABLE1(TNumericStatic, TStatic)
  92.   EV_MESSAGE(WM_SETNUMBER, EvSetNumber),
  93. END_RESPONSE_TABLE;
  94.  
  95. TNumericStatic::TNumericStatic(TWindow* parent, int resId, int number)
  96. :
  97.   TStatic(parent, resId, 0),
  98.   Number(number)
  99. {
  100. }
  101.  
  102. //
  103. // Handle our user defined message to set the number displayed in the %d part
  104. // of the Title format string. If the number is <= 0, then hide this window
  105. //
  106. LRESULT
  107. TNumericStatic::EvSetNumber(WPARAM wParam, LPARAM)
  108. {
  109.   Number = wParam;
  110.   if (Number > 0) {
  111.     char buff[80];
  112.     wsprintf(buff, Title, Number);
  113.     SetText(buff);
  114.   }
  115.   HWND hWndDefault = Parent->GetDlgItem(-1);
  116.   if (Number > 0) {
  117.     SetWindowPos(HWND_TOP, 0, 0, 0, 0,
  118.                  SWP_NOSIZE|SWP_NOMOVE|SWP_SHOWWINDOW);
  119.     if (hWndDefault)
  120.       ::ShowWindow(hWndDefault, SW_HIDE);
  121.     UpdateWindow();
  122.   }
  123.   return 0;
  124. }
  125.  
  126.  
  127. //----------------------------------------------------------------------------
  128.  
  129. DEFINE_RESPONSE_TABLE1(TPrinterAbortDlg, TDialog)
  130.   EV_COMMAND(IDCANCEL, CmCancel),
  131. END_RESPONSE_TABLE;
  132.  
  133.  
  134. TPrinterAbortDlg::TPrinterAbortDlg(TWindow*        parent,
  135.                                    TResId          resId,
  136.                                    const char far* title,
  137.                                    const char far* device,
  138.                                    const char far* port,
  139.                                    HDC             prnDC)
  140. :
  141.   TDialog(parent, resId),
  142.   TWindow(parent, 0),
  143.   PrnDC(prnDC)
  144. {
  145.   new TNumericStatic(this, ID_PAGE, 0);
  146.   new TFormattedStatic(this, ID_TITLE, title);
  147.   new TFormattedStatic(this, ID_DEVICE, device, port);
  148. }
  149.  
  150. void
  151. TPrinterAbortDlg::SetupWindow()
  152. {
  153.   // ignore missing controls, resource may be different
  154.   //
  155.   TRY {
  156.     TDialog::SetupWindow();
  157.   }
  158.   CATCH( (...) {
  159.   } )
  160.   EnableMenuItem(GetSystemMenu(false), SC_CLOSE, MF_GRAYED);
  161. }
  162.  
  163. //
  164. // Handle the print-cancel button by setting the user print abort flag in the
  165. // application.
  166. //
  167. void
  168. TPrinterAbortDlg::CmCancel()
  169. {
  170.   TPrinter::SetUserAbort(PrnDC);
  171.   HWND hBtn = GetDlgItem(IDCANCEL);
  172.   if (hBtn)
  173.     ::EnableWindow(hBtn, false);
  174. }
  175.  
  176.  
  177. //----------------------------------------------------------------------------
  178. //
  179. //  TPrinter is an encapsulation around the Windows printer device interface.
  180. //
  181. //  Examples:
  182. //   Creating a default device printing object:
  183. //
  184. //     DefaultPrinter = new TPrinter();
  185. //
  186. //   Creating a device for a specific printer:
  187. //
  188. //     PostScriptPrinter = new TPrinter();
  189. //     PostScriptPrinter->SetDevice("PostScript Printer", "PSCRIPT", "LPT2:");
  190. //
  191. //   Allowing the user to setup the printer:
  192. //
  193. //     DefaultPrinter->Setup(MyWindow);
  194. //
  195.  
  196. HDC TPrinter::UserAbortDC = 0;    // Set by printing dialog if user cancels
  197.  
  198. //
  199. // Initialize the TPrinter object assigned to the default printer
  200. //
  201. TPrinter::TPrinter(TApplication* app)
  202. {
  203.   Data = new TPrintDialog::TData;
  204.   Error = 0;
  205.   Application = app ? app : ::GetApplicationObject();
  206.   CHECK(Application);
  207.   GetDefaultPrinter();
  208. }
  209.  
  210. //
  211. // Deallocate allocated resources
  212. //
  213. TPrinter::~TPrinter()
  214. {
  215.   delete Data;
  216. }
  217.  
  218. //
  219. // Clears the association of this printer object with the current device
  220. //
  221. void TPrinter::ClearDevice()
  222. {
  223.   Data->ClearDevMode();
  224.   Data->ClearDevNames();
  225. }
  226.  
  227. //
  228. // Updata Data with info on the user's default printer.
  229. //
  230. void
  231. TPrinter::GetDefaultPrinter()
  232. {
  233.   //
  234.   // Have a printDialog go get the default printer information for us
  235.   // into DevMode and DevNames. We never actually Execute() the dialog.
  236.   //
  237.   SETCLEAR(Data->Flags, 0, PD_RETURNDC);
  238.   TPrintDialog printDialog(Application->GetMainWindow(), *Data);
  239.   printDialog.GetDefaultPrinter();
  240. }
  241.  
  242. //
  243. // Associate this printer object with actual printer info: driver name, device
  244. // name and output port name.
  245. //
  246. void
  247. TPrinter::SetPrinter(const char* driver, const char* device, const char* output)
  248. {
  249.   Data->SetDevNames(driver, device, output);
  250. }
  251.  
  252. //
  253. // Abort procedure used during printing, called by windows. Returns true to
  254. // continue the print job, false to cancel.
  255. //
  256. int CALLBACK __export
  257. TPrinterAbortProc(HDC hDC, int code)
  258. {
  259.   GetApplicationObject()->PumpWaitingMessages();
  260.  
  261.   // UserAbortDC will have been set by the AbortDialog
  262.   //
  263.   if (TPrinter::GetUserAbort() == hDC || TPrinter::GetUserAbort() == HDC(-1)) {
  264.     TPrinter::SetUserAbort(0);
  265.     return false;
  266.   }
  267.   return code == 0 || code == SP_OUTOFDISK;
  268. }
  269.  
  270. //
  271. // Virtual called from within Print() to construct and execute a print dialog
  272. // before actual printing occurs. Return true to continue printing, false to
  273. // cancel
  274. //
  275. bool
  276. TPrinter::ExecPrintDialog(TWindow* parent)
  277. {
  278.   return TPrintDialog(parent, *Data).Execute() == IDOK;
  279. }
  280.  
  281. //
  282. // Virtual called from withing Print() just before the main printing loop to
  283. // construct and create the printing status, or abort window. This window
  284. // should use the control IDs specified in printer.rh
  285. //
  286. TWindow*
  287. TPrinter::CreateAbortWindow(TWindow* parent, TPrintout& printout)
  288. {
  289.   TDC* dc = printout.GetPrintDC();
  290.   TWindow* win = new TPrinterAbortDlg(parent, IDD_ABORTDIALOG,
  291.                                       printout.GetTitle(),
  292.                                       Data->GetDeviceName(),
  293.                                       Data->GetOutputName(),
  294.                                       dc ? HDC(*dc) : HDC(-1));
  295.   win->Create();
  296.   return win;
  297. }
  298.  
  299. //
  300. // Helper function for Print when it is banding used to calculate banding flags
  301. // and rectangle.
  302. //
  303. void
  304. TPrinter::CalcBandingFlags(TPrintDC& prnDC)
  305. {
  306.   //
  307.   // Calculate text versus graphics banding
  308.   //
  309.   if (UseBandInfo) {
  310.     TBandInfo bandInfo;
  311.     unsigned  flags = 0;
  312.  
  313.     prnDC.BandInfo(bandInfo);
  314.     if (bandInfo.HasGraphics)
  315.       flags = pfGraphics;
  316.     if (bandInfo.HasText)
  317.       flags |= pfGraphics;
  318.     Flags = (Flags & ~pfBoth) | flags;
  319.   }
  320.   else {
  321.     //
  322.     // If a driver does not support BANDINFO the Microsoft recommended way
  323.     // of determining text only bands is if the first band is the full page,
  324.     // all others are graphics only.  Otherwise it handles both.
  325.     //
  326.     if (FirstBand && !BandRect.left && !BandRect.top &&
  327.         BandRect.right == PageSize.cx && BandRect.bottom == PageSize.cy)
  328.       Flags = pfText;
  329.     else
  330.       if ((Flags & pfBoth) == pfGraphics)
  331.         // All other bands are graphics only
  332.         Flags = (Flags & ~pfBoth) | pfGraphics;
  333.       else
  334.         Flags = Flags | pfBoth;
  335.   }
  336.   FirstBand = false;
  337. }
  338.  
  339. //
  340. // Print the given printout to the printer, prompting the user with a
  341. // commdlg print dialog if indicated.
  342. //
  343. bool
  344. TPrinter::Print(TWindow* parent, TPrintout& printout, bool prompt)
  345. {
  346.   PRECONDITION(parent);
  347.  
  348.   // Start from scratch
  349.   //
  350.   Error = 1;           // Positive 'Error' means print job is OK
  351.   TPrintDC*   prnDC;
  352.   HCURSOR hOrigCursor = ::SetCursor(::LoadCursor(0, IDC_WAIT));
  353.  
  354.   //
  355.   // Get page range & selection range (if any) from document
  356.   //
  357.   int selFromPage;
  358.   int selToPage;
  359.   printout.GetDialogInfo(Data->MinPage, Data->MaxPage, selFromPage, selToPage);
  360.   if (selFromPage)
  361.     Data->Flags &= ~PD_NOSELECTION;
  362.   else
  363.     Data->Flags |= PD_NOSELECTION;
  364.   if (Data->MinPage) {
  365.     Data->Flags &= ~PD_NOPAGENUMS;
  366.     if (Data->FromPage < Data->MinPage)
  367.       Data->FromPage = Data->MinPage;
  368.     else if (Data->FromPage > Data->MaxPage)
  369.       Data->FromPage = Data->MaxPage;
  370.     if (Data->ToPage < Data->MinPage)
  371.       Data->ToPage = Data->MinPage;
  372.     else if (Data->ToPage > Data->MaxPage)
  373.       Data->ToPage = Data->MaxPage;
  374.   }
  375.   else
  376.     Data->Flags |= PD_NOPAGENUMS;
  377.  
  378.   //
  379.   // Create & execute a TPrintDialog (or derived) and have it return a usable
  380.   // DC if prompt is enabled. If the dialog fails because the default printer
  381.   // changed, clear our device information & try again.
  382.   //
  383.   if (prompt) {
  384.     SETCLEAR(Data->Flags, PD_RETURNDC, PD_RETURNDEFAULT|PD_PRINTSETUP);
  385.     bool ok = ExecPrintDialog(parent);
  386.     if (!ok && Data->Error == PDERR_DEFAULTDIFFERENT) {
  387.       ClearDevice();
  388.       ok = ExecPrintDialog(parent);
  389.     }
  390.     if (!ok)
  391.       return false;
  392.  
  393.     prnDC = Data->TransferDC();   // We now own the DC, let prnDC manage it
  394.   }
  395.   // Construct the DC directly if prompting was not enabled.
  396.   //
  397.   else {
  398.     prnDC = new TPrintDC(Data->GetDriverName(), Data->GetDeviceName(),
  399.                          Data->GetOutputName(), Data->GetDevMode());
  400.   }
  401.   if (!prnDC)
  402.     THROW( TXPrinter() );
  403.  
  404.   //
  405.   // Get the page size
  406.   //
  407.   PageSize.cx = prnDC->GetDeviceCaps(HORZRES);
  408.   PageSize.cy = prnDC->GetDeviceCaps(VERTRES);
  409.   printout.SetPrintParams(prnDC, PageSize);
  410.  
  411.   //
  412.   // Create modeless abort dialog using strings
  413.   //
  414.   TWindow* abortWin;
  415.   TRY {
  416.     abortWin = CreateAbortWindow(parent, printout);
  417.   }
  418.   CATCH( (...) {
  419.     delete prnDC;
  420.     RETHROW;
  421.   })
  422.  
  423.   ::SetCursor(hOrigCursor);
  424.   parent->EnableWindow(false);
  425.  
  426.   prnDC->SetAbortProc(TPrinterAbortProc);
  427.  
  428.   //
  429.   // Only band if the user requests banding and the printer
  430.   // supports banding
  431.   //
  432.   bool banding = printout.WantBanding() &&
  433.                 (prnDC->GetDeviceCaps(RASTERCAPS) & RC_BANDING);
  434.   if (banding)
  435.     //
  436.     // Only use BANDINFO if supported.
  437.     //
  438.     UseBandInfo = ToBool(prnDC->QueryEscSupport(BANDINFO));
  439.  
  440.   else
  441.     //
  442.     // Set the banding rectangle to full page
  443.     //
  444.     BandRect.Set(0, 0, PageSize.cx, PageSize.cy);
  445.  
  446.   //
  447.   // If more than one (uncollated) copy was requested, see if printer can
  448.   // handle it for performance and user convenience.
  449.   //
  450.   int copiesPerPass = 1;
  451.   if (!(Data->Flags & PD_COLLATE))
  452.     prnDC->SetCopyCount(Data->Copies, copiesPerPass);
  453.  
  454.   //
  455.   // Figure out which page range to use: Selection, Dialog's from/to,
  456.   // whole doc range or all possible pages.
  457.   //
  458.   int fromPage;
  459.   int toPage;
  460.   if (prompt && (Data->Flags & PD_SELECTION) || selFromPage) {
  461.     fromPage = selFromPage;
  462.     toPage = selToPage;
  463.   }
  464.   else if (prompt && (Data->Flags & PD_PAGENUMS)) {
  465.     fromPage = Data->FromPage;
  466.     toPage = Data->ToPage;
  467.   }
  468.   else if (Data->MinPage) {
  469.     fromPage = Data->MinPage;
  470.     toPage = Data->MaxPage;
  471.   }
  472.   else {
  473.     fromPage = 1;
  474.     toPage = INT_MAX;
  475.   }
  476.  
  477.   //
  478.   // Copies loop, one pass per block of document copies.
  479.   //
  480.   printout.BeginPrinting();
  481.   for (int copies = Data->Copies;
  482.        copies > 0 && Error > 0;
  483.        copies -= copiesPerPass) {
  484.  
  485.     //
  486.     // On last multi-copy pass, may need to adjust copy count
  487.     //
  488.     if (copiesPerPass > 1 && copies < copiesPerPass)
  489.       prnDC->SetCopyCount(copies, copiesPerPass);
  490.  
  491.     //
  492.     // Whole document loop, one pass per page
  493.     //
  494.     Flags = pfBoth;
  495.     Error = prnDC->StartDoc(printout.GetTitle(), 0);  // get PD_PRINTTOFILE ?
  496.     printout.BeginDocument(fromPage, toPage, Flags);
  497.  
  498.     for (int pageNum = fromPage;
  499.          Error > 0 && pageNum <= toPage && printout.HasPage(pageNum);
  500.          pageNum++) {
  501.  
  502.       abortWin->SendDlgItemMessage(ID_PAGE, WM_SETNUMBER, pageNum);
  503.  
  504.       //
  505.       // Begin the page by getting the first band or calling StartPage()
  506.       //
  507.       if (banding) {
  508.         FirstBand = true;
  509.         Error = prnDC->NextBand(BandRect);
  510.       }
  511.       else
  512.         Error = prnDC->StartPage();
  513.  
  514.       //
  515.       // Whole page loop, one pass per band (once when not banding)
  516.       //
  517.       while (Error > 0 && !BandRect.IsEmpty()) {
  518.         //
  519.         // [Manually call the abort proc between bands or pages]
  520.         //
  521. #if defined(MANUAL_ABORT_CALL)
  522.         prnDC->QueryAbort();
  523. #endif
  524.  
  525.         if (banding) {
  526.           CalcBandingFlags(*prnDC);
  527.           if (printout.WantForceAllBands() && (Flags & pfBoth) == pfGraphics)
  528.             prnDC->SetPixel(TPoint(0, 0), 0);  // Some old drivers need this 
  529.           prnDC->DPtoLP(BandRect, 2);
  530.         }
  531.  
  532.         printout.PrintPage(pageNum, BandRect, Flags);
  533.         if (banding)
  534.           Error = prnDC->NextBand(BandRect);
  535.         else
  536.           break;
  537.       }
  538.  
  539.       //
  540.       // EndPage (NEWFRAME) need only called if not banding
  541.       //
  542.       if (Error > 0 && !banding) {
  543.         Error = prnDC->EndPage();
  544.         if (Error == 0)    // a zero return here is OK for this call
  545.           Error = 1;
  546.       }
  547.  
  548.     }  // End of Whole Document-loop
  549.  
  550.     // Tell GDI the document is finished
  551.     //
  552.     if (Error > 0)
  553. #if defined(MANUAL_ABORT_CALL)
  554.       if (banding && (UserAbortDC == *prnDC || UserAbortDC == HDC(-1))
  555.         prnDC->AbortDoc();
  556.       else
  557.         prnDC->EndDoc();
  558. #else
  559.       prnDC->EndDoc();
  560. #endif
  561.  
  562.     printout.EndDocument();
  563.  
  564.   } // End of Copy-loop
  565.   printout.EndPrinting();
  566.  
  567.   // Set the printer back to 1 copy
  568.   //
  569.   if (copiesPerPass > 1)
  570.     prnDC->SetCopyCount(1, copiesPerPass);
  571.  
  572.   //
  573.   // Re-enable parent and free allocated resources
  574.   //
  575.   parent->EnableWindow(true);
  576.   abortWin->Destroy();
  577.   delete abortWin;
  578.   if (UserAbortDC == *prnDC)  // User tried to abort, but it was too late
  579.     UserAbortDC = 0;
  580.   delete prnDC;
  581.  
  582.   //
  583.   // Report error if not already done so by printmgr
  584.   //
  585.   if (Error & SP_NOTREPORTED)
  586.     ReportError(parent, printout);
  587.  
  588.   return Error > 0;
  589. }
  590.  
  591. //
  592. // Setup opens a dialog as a child of the given window to setup the
  593. // printer using the commdlg printer setup. The options button
  594. // allows the user acces to the specific driver's options.
  595. //
  596. void TPrinter::Setup(TWindow* parent)
  597. {
  598.   SETCLEAR(Data->Flags, PD_PRINTSETUP, PD_RETURNDEFAULT|PD_RETURNDC);
  599.   ExecPrintDialog(parent);
  600. }
  601.  
  602. //
  603. // Report the printer error in a message box. First translate the SP_ codes
  604. // into ObjectWindows string resource IDs, and then load the strings. Don't
  605. // display anything if the string is not found, or the code is unknown.
  606. //
  607. void
  608. TPrinter::ReportError(TWindow* parent, TPrintout& printout)
  609. {
  610.   uint16  errorId;
  611.  
  612.   switch (Error) {
  613.     case SP_ERROR:
  614.       errorId = IDS_PRNGENERROR;
  615.       break;
  616.     case SP_APPABORT:
  617.       errorId = IDS_PRNCANCEL;
  618.       break;
  619.     case SP_USERABORT:
  620.       errorId = IDS_PRNMGRABORT;
  621.       break;
  622.     case SP_OUTOFDISK:
  623.       errorId = IDS_PRNOUTOFDISK;
  624.       break;
  625.     case SP_OUTOFMEMORY:
  626.       errorId = IDS_PRNOUTOFMEMORY;
  627.       break;
  628.     default:
  629.       return;
  630.   }
  631.  
  632.   TModule& module = *parent->GetModule();
  633.  
  634.   char  errorCaption[80];
  635.   if (!module.LoadString(IDS_PRNERRORCAPTION, errorCaption, sizeof(errorCaption)))
  636.     return;
  637.  
  638.   char  errorTemplate[40];
  639.   char  errorStr[40];
  640.   if (!module.LoadString(IDS_PRNERRORTEMPLATE, errorTemplate, sizeof(errorTemplate)))
  641.     return;
  642.   if (!module.LoadString(errorId, errorStr, sizeof(errorStr)))
  643.     return;
  644.  
  645.   char  errorMsg[80];
  646.   wsprintf(errorMsg, errorTemplate, printout.GetTitle(), (char far*)errorStr);
  647.  
  648.   parent->MessageBox(errorMsg, errorCaption, MB_OK | MB_ICONSTOP);
  649. }
  650.  
  651. TPrinter::TXPrinter::TXPrinter(uint resId) : TXOwl(resId)
  652. {
  653. }
  654.  
  655. #endif
  656. #if !defined(SECTION) || SECTION == 2
  657.  
  658. IMPLEMENT_STREAMABLE(TPrinter);
  659.  
  660. void*
  661. TPrinter::Streamer::Read(ipstream& is, uint32 version) const
  662. {
  663.   GetObject()->Data->Read(is, version);
  664.   return GetObject();
  665. }
  666.  
  667. void
  668. TPrinter::Streamer::Write(opstream& os) const
  669. {
  670.   GetObject()->Data->Write(os);
  671. }
  672.  
  673. #endif
  674.  
  675.