home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / Pascal / BPASCAL.700 / D11 / OWLSRC.ZIP / OPRINTER.PAS next >
Encoding:
Pascal/Delphi Source File  |  1992-10-01  |  43.0 KB  |  1,501 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Turbo Pascal for Windows Run-time Library       }
  5. {       ObjectWindows Unit                              }
  6. {                                                       }
  7. {       Copyright (c) 1991 Borland International        }
  8. {                                                       }
  9. {*******************************************************}
  10.  
  11. unit OPrinter;
  12.  
  13. {$R OPRINTER.RES}
  14.  
  15. {$S-}
  16.  
  17. interface
  18.  
  19. uses WinTypes, WinProcs, Objects, OWindows, ODialogs;
  20.  
  21. { TPrinter states }
  22. const
  23.   ps_Ok = 0;
  24.   ps_InvalidDevice = -1;     { Device parameters (to set device)
  25.                                invalid }
  26.   ps_Unassociated = -2;      { Object not associated with a printer }
  27.  
  28. { TPrintOut flags }
  29. const
  30.   pf_Graphics  = $01;        { Current band only accepts text }
  31.   pf_Text      = $02;        { Current band only accepts graphics }
  32.   pf_Both      = $03;        { Current band accepts both text and
  33.                                graphics }
  34.   pf_Banding   = $04;        { Set the printout is being banded }
  35.   pf_Selection = $08;        { Printing the selection }
  36.  
  37. type
  38.   PPrintDialogRec = ^TPrintDialogRec;
  39.   TPrintDialogRec = record
  40.     drStart: Integer;             { Starting page }
  41.     drStop: Integer;              { Ending page }
  42.     drCopies: Integer;            { Number of copies to print }
  43.     drCollate: Boolean;           { Tell the printer to collate copies }
  44.     drUseSelection: Boolean;      { Use seletion instead of Start, Stop }
  45.   end;
  46.  
  47. { TPrintOut represents the physical printed document which is to
  48.   sent to a printer to be printed. TPrintOut does the rendering of
  49.   the document onto the printer.  For every document, or document
  50.   type, a cooresponding TPrintOut class should be created. }
  51.  
  52. type
  53.   PPrintOut = ^TPrintOut;
  54.   TPrintOut = object(TObject)
  55.     Title: PChar;
  56.     Banding: Boolean;
  57.     ForceAllBands: Boolean;
  58.     DC: HDC;
  59.     Size: TPoint;
  60.     constructor Init(ATitle: PChar);
  61.     destructor Done; virtual;
  62.     procedure BeginDocument(StartPage, EndPage: Integer;
  63.       Flag: Word); virtual;
  64.     procedure BeginPrinting; virtual;
  65.     procedure EndDocument; virtual;
  66.     procedure EndPrinting; virtual;
  67.     function GetDialogInfo(var Pages: Integer): Boolean; virtual;
  68.     function GetSelection(var Start, Stop: Integer): Boolean; virtual;
  69.     function HasNextPage(Page: Word): Boolean; virtual;
  70.     procedure PrintPage(Page: Word; var Rect: TRect; Flags: Word); virtual;
  71.     procedure SetPrintParams(ADC: HDC; ASize: TPoint); virtual;
  72.    end;
  73.  
  74. { TPrinter represent the physical printer device.  To print a
  75.   TPrintOut, send the TPrintOut to the TPrinter's Print method. }
  76.  
  77.   PPrinter = ^TPrinter;
  78.   TPrinter = object(TObject)
  79.     Device, Driver, Port: PChar;        { Printer device description }
  80.     Status: Integer;                    { Device status, error is <> ps_Ok }
  81.     Error: Integer;                     { < 0 if error occured during print }
  82.     DeviceModule: THandle;              { Handle to printer driver module }
  83.     DeviceMode: TDeviceMode;            { Function pointer to DevMode }
  84.     ExtDeviceMode: TExtDeviceMode;      { Function pointer to ExtDevMode }
  85.     DevSettings: PDevMode;              { Local copy of printer settings }
  86.     DevSettingSize: Integer;            { Size of the printer settings }
  87.  
  88.     constructor Init;
  89.     destructor Done; virtual;
  90.     procedure ClearDevice;
  91.     procedure Configure(Window: PWindowsObject);
  92.     function GetDC: HDC; virtual;
  93.     function InitAbortDialog(Parent: PWindowsObject;
  94.       Title: PChar): PDialog; virtual;
  95.     function InitPrintDialog(Parent: PWindowsObject; PrnDC: HDC;
  96.       Pages: Integer; SelAllowed: Boolean;
  97.       var Data: TPrintDialogRec): PDialog; virtual;
  98.     function InitSetupDialog(Parent: PWindowsObject): PDialog; virtual;
  99.     procedure ReportError(PrintOut: PPrintOut); virtual;
  100.     procedure SetDevice(ADevice, ADriver, APort: PChar);
  101.     procedure Setup(Parent: PWindowsObject);
  102.     function Print(ParentWin: PWindowsObject; PrintOut: PPrintOut): Boolean;
  103.   end;
  104.  
  105. { TPrinterSetupDlg is a dialog to modify which printer a TPrinter
  106.   object is attached to.  It displays the all the active printers
  107.   in the system allowing the user to select the desired printer.
  108.   The dialog also allow the user to call up the printer's
  109.   "setup" dialog for further configuration of the printer. }
  110.  
  111. const
  112.   id_Combo = 100;
  113.   id_Setup = 101;
  114.  
  115. type
  116.   PPrinterSetupDlg = ^TPrinterSetupDlg;
  117.   TPrinterSetupDlg = object(TDialog)
  118.     Printer: PPrinter;
  119.     constructor Init(AParent: PWindowsObject; TemplateName: PChar;
  120.       APrinter: PPrinter);
  121.     destructor Done; virtual;
  122.     procedure TransferData(TransferFlag: Word); virtual;
  123.     procedure IDSetup(var Msg: TMessage);
  124.       virtual id_First + id_Setup;
  125.     procedure Cancel(var Msg: TMessage);
  126.       virtual id_First + id_Cancel;
  127.   private
  128.     OldDevice, OldDriver, OldPort: PChar;
  129.     DeviceCollection: PCollection;
  130.   end;
  131.  
  132. const
  133.   id_Title  = 101;
  134.   id_Device = 102;
  135.   id_Port   = 103;
  136.  
  137. type
  138.   PPrinterAbortDlg = ^TPrinterAbortDlg;
  139.   TPrinterAbortDlg = object(TDialog)
  140.     constructor Init(AParent: PWindowsObject; Template, Title,
  141.       Device, Port: PChar);
  142.     procedure SetupWindow; virtual;
  143.     procedure WMCommand(var Msg: TMessage);
  144.       virtual wm_First + wm_Command;
  145.   end;
  146.  
  147. const
  148.   id_PrinterName  = 102;
  149.   id_All          = 103;
  150.   id_Selection    = 104;
  151.   id_Pages        = 105;
  152.   id_FromText     = 106;
  153.   id_From         = 107;
  154.   id_ToText       = 108;
  155.   id_To           = 109;
  156.   id_PrintQuality = 110;
  157.   id_Copies       = 111;
  158.   id_Collate      = 112;
  159.  
  160. type
  161.   PPrintDialog = ^TPrintDialog;
  162.   TPrintDialog = object(TDialog)
  163.     Printer: PPrinter;
  164.     PData: PPrintDialogRec;
  165.     PrinterName: PStatic;
  166.     Pages: Integer;
  167.     Controls: PCollection;
  168.     AllBtn, SelectBtn, PageBtn: PRadioButton;
  169.     FromPage, ToPage: PEdit;
  170.     Copies: PEdit;
  171.     Collate: PCheckBox;
  172.     PrnDC: HDC;
  173.     SelAllowed: Boolean;
  174.     constructor Init(AParent: PWindowsObject; Template: PChar; APrnDC: HDC;
  175.       APages: Integer; APrinter: PPrinter; ASelAllowed: Boolean;
  176.       var Data: TPrintDialogRec);
  177.     procedure SetupWindow; virtual;
  178.     procedure TransferData(Direction: Word); virtual;
  179.     procedure IDSetup(var Msg: TMessage);
  180.       virtual id_First + id_Setup;
  181.   end;
  182.  
  183. type
  184.   PEditPrintout = ^TEditPrintout;
  185.   TEditPrintout = object(TPrintout)
  186.     Editor: PEdit;
  187.     NumLines: Integer;
  188.     LinesPerPage: Integer;
  189.     LineHeight: Integer;
  190.     StartPos: Integer;
  191.     StopPos: Integer;
  192.     StartLine: Integer;
  193.     StopLine: Integer;
  194.     constructor Init(AEditor: PEdit; ATitle: PChar);
  195.     procedure BeginDocument(StartPage, EndPage: Integer;
  196.       Flags: Word); virtual;
  197.     function GetDialogInfo(var Pages: Integer): Boolean; virtual;
  198.     function GetSelection(var Start, Stop: Integer): Boolean; virtual;
  199.     function HasNextPage(Page: Word): Boolean; virtual;
  200.     procedure PrintPage(Page: Word; var Rect: TRect; Flags: Word); virtual;
  201.     procedure SetPrintParams(ADC: HDC; ASize: TPoint); virtual;
  202.   end;
  203.  
  204. type
  205.   PWindowPrintout = ^TWindowPrintout;
  206.   TWindowPrintout = object(TPrintOut)
  207.     Window: PWindow;
  208.     Scale: Boolean;
  209.     constructor Init(ATitle: PChar; AWindow: PWindow);
  210.     function GetDialogInfo(var Pages: Integer): Boolean; virtual;
  211.     procedure PrintPage(Page: Word; var Rect: TRect; Flags: Word); virtual;
  212.   end;
  213.  
  214. implementation
  215.  
  216. uses Strings;
  217.  
  218. const
  219.   sr_On             = 32512;
  220.   sr_ErrorTemplate  = 32513;
  221.   sr_OutOfMemory    = 32514;
  222.   sr_OutOfDisk      = 32515;
  223.   sr_PrnCancel      = 32516;
  224.   sr_PrnMgrAbort    = 32517;
  225.   sr_GenError       = 32518;
  226.   sr_ErrorCaption   = 32519;
  227.  
  228. const
  229.   UserAbort: Boolean = False;
  230.  
  231. { FormDriverStr ---------------------------------------------------- }
  232.  
  233. procedure FormDriverStr(DriverStr: PChar; MaxLen: Integer;
  234.   Device, Port: PChar);
  235. begin
  236.   StrLCopy(DriverStr, Device, MaxLen);
  237.   LoadString(hInstance, sr_On, @DriverStr[StrLen(DriverStr)],
  238.     MaxLen - StrLen(DriverStr) - 1);
  239.   StrLCat(DriverStr, Port, MaxLen);
  240. end;
  241.  
  242. { TPrintOut -------------------------------------------------------- }
  243.  
  244. constructor TPrintOut.Init(ATitle: PChar);
  245. const
  246.   Blank: array[0..0] of Char = '';
  247. var
  248.   S: array[0..31] of Char;
  249. begin
  250.   TObject.Init;
  251.   if (ATitle = nil) or (ATitle^ = #0) then
  252.     Title := @Blank
  253.   else
  254.   begin
  255.     { Force the length to be 31 chars or less }
  256.     StrLCopy(S, ATitle, SizeOf(S));
  257.     Title := StrNew(S);
  258.   end;
  259.   Banding := False;
  260.   ForceAllBands := True;
  261. end;
  262.  
  263. destructor TPrintOut.Done;
  264. begin
  265.   StrDispose(Title);
  266.   TObject.Done;
  267. end;
  268.  
  269. { This method is called before a document begins printing.  It is
  270.   called once for every copy of the document that is printed.  The
  271.   Flags parameter contains whether the selection is being printed
  272.   and whether the document is going to be banded. }
  273.  
  274. procedure TPrintOut.BeginDocument(StartPage, EndPage: Integer; Flag: Word);
  275. begin
  276. end;
  277.  
  278. { Called at the beginning of printing.  It is called once, regardless
  279.   of how many copies of the document are being printed. }
  280.  
  281. procedure TPrintOut.BeginPrinting;
  282. begin
  283. end;
  284.  
  285. { Called after each copy of the document is printed. }
  286.  
  287. procedure TPrintOut.EndDocument;
  288. begin
  289. end;
  290.  
  291. { Called after all the copies of the documents are printed. }
  292.  
  293. procedure TPrintOut.EndPrinting;
  294. begin
  295. end;
  296.  
  297. { Get the information necessary to bring up the page range selection
  298.   dialog.  If this function returns true, the dialog will brought up.
  299.   The pages value is optional,  if the page count is easily caluclated
  300.   return the number of pages in the doucment; otherwise, return 0 and
  301.   no limit will be applied to the dialog.  The document will stop
  302.   printing when HasNextPage returns false. }
  303.  
  304. function TPrintOut.GetDialogInfo(var Pages: Integer): Boolean;
  305. begin
  306.   Pages := 0;
  307.   GetDialogInfo := True;
  308. end;
  309.  
  310. { Called to determine, first, if the document being printed has a
  311.   selection and then what is it.  If there is not a selection the
  312.   selection radio button is disabled on the default print dialog. }
  313.  
  314. function TPrintOut.GetSelection(var Start, Stop: Integer): Boolean;
  315. begin
  316.   GetSelection := False;
  317. end;
  318.  
  319. { Called after every page to determine if another page is ready to
  320.   print. }
  321.  
  322. function TPrintOut.HasNextPage(Page: Word): Boolean;
  323. begin
  324.   HasNextPage := False;
  325. end;
  326.  
  327. { Called to render the given page of the printout.  The pages
  328.   will come in order in the range passed to BeginDocument.  The
  329.   page might be called multiple time if banding is enabled. }
  330.  
  331. procedure TPrintOut.PrintPage(Page: Word; var Rect: TRect; Flags: Word);
  332. begin
  333.   Abstract;
  334. end;
  335.  
  336. { Called to register the DC and page size with the object.  This
  337.   is the first method called after the object is passed to
  338.   the Print method of a Printer object.  If this method is
  339.   overriden, the inherited function must be called. }
  340.  
  341. procedure TPrintOut.SetPrintParams(ADC: HDC; ASize: TPoint);
  342. begin
  343.   DC := ADC;
  344.   Size := ASize;
  345. end;
  346.  
  347. { FetchStr --------------------------------------------------------- }
  348. {   Returns a pointer to the first comma delimited field pointed to  }
  349. {   by Str. It replaces the comma with a #0 and moves the Str to the }
  350. {   beginning of the next string (skipping white space).  Str will   }
  351. {   will point to a #0 character if no more strings are left.  This  }
  352. {   routine is used to fetch strings out of text retrieved from      }
  353. {   WIN.INI.                                                         }
  354.  
  355. function FetchStr(var Str: PChar): PChar;
  356. begin
  357.   FetchStr := Str;
  358.   if Str = nil then Exit;
  359.   while (Str^ <> #0) and (Str^ <> ',') do
  360.     Str := AnsiNext(Str);
  361.   if Str^ = #0 then Exit;
  362.   Str^ := #0;
  363.   Inc(Str);
  364.   while Str^ = ' ' do
  365.     Str := AnsiNext(Str);
  366. end;
  367.  
  368. { TReplaceStatic --------------------------------------------------- }
  369.  
  370. type
  371.   PReplaceStatic = ^TReplaceStatic;
  372.   TReplaceStatic = object(TStatic)
  373.     Text: PChar;
  374.     constructor InitResource(AParent: PWindowsObject; ResourceID: Word;
  375.       AText: PChar);
  376.     destructor Done; virtual;
  377.     procedure SetupWindow; virtual;
  378.   end;
  379.  
  380. constructor TReplaceStatic.InitResource(AParent: PWindowsObject; ResourceID: Word;
  381.   AText: PChar);
  382. begin
  383.   TStatic.InitResource(AParent, ResourceID, 0);
  384.   Text := StrNew(AText);
  385. end;
  386.  
  387. destructor TReplaceStatic.Done;
  388. begin
  389.   StrDispose(Text);
  390.   TStatic.Done;
  391. end;
  392.  
  393. procedure TReplaceStatic.SetupWindow;
  394. var
  395.   A: array[0..80] of Char;
  396.   B: array[0..80] of Char;
  397. begin
  398.   TStatic.SetupWindow;
  399.   GetText(A, SizeOf(A) - 1);
  400.   WVSPrintF(B, A, Text);
  401.   SetText(B);
  402. end;
  403.  
  404. { TPrinterAbortDlg ----------------------------------------------------- }
  405.  
  406. constructor TPrinterAbortDlg.Init(AParent: PWindowsObject; Template,
  407.   Title, Device, Port: PChar);
  408. var
  409.   Tmp: PWindowsObject;
  410. begin
  411.   TDialog.Init(AParent, Template);
  412.   Tmp := New(PReplaceStatic, InitResource(@Self, id_Title, Title));
  413.   Tmp := New(PReplaceStatic, InitResource(@Self, id_Device, Device));
  414.   Tmp := New(PReplaceStatic, InitResource(@Self, id_Port, Port));
  415. end;
  416.  
  417. procedure TPrinterAbortDlg.SetupWindow;
  418. begin
  419.   TDialog.SetupWindow;
  420.   EnableMenuItem(GetSystemMenu(HWindow, False), sc_Close, mf_Grayed);
  421. end;
  422.  
  423. procedure TPrinterAbortDlg.WMCommand(var Msg: TMessage);
  424. begin
  425.   UserAbort := True;
  426. end;
  427.  
  428. { TPrinter --------------------------------------------------------- }
  429.  
  430. { This object type is an ecapsulation around the Windows printer
  431.   device interface.  After the object is initialized the Status
  432.   field must be check to see of the object was created correctly.
  433.   Examples:
  434.     Creating a default device printing object:
  435.  
  436.       DefaultPrinter := New(PPrinter, Init);
  437.  
  438.     Creating a device for a specific printer:
  439.  
  440.       PostScriptPrinter := New(PPrinter, Init);
  441.       PostScriptPrinter^.SetDevice('PostScript Printer',
  442.         'PSCRIPT','LPT2:');
  443.  
  444.     Allowing the user to configure the printer:
  445.  
  446.       DefaultPrinter^.Configure(MyWindow);
  447. }
  448.  
  449. { Initialize the TPrinter object assigned to the default printer }
  450.  
  451. constructor TPrinter.Init;
  452. begin
  453.   TObject.Init;
  454.   Device := nil;
  455.   Driver := nil;
  456.   Port := nil;
  457.   DeviceModule := 0;
  458.   DevSettings := nil;
  459.   Error := 0;
  460.   SetDevice(nil, nil, nil);  { Associate with default printer }
  461. end;
  462.  
  463. { Deallocate allocated resources }
  464.  
  465. destructor TPrinter.Done;
  466. begin
  467.   ClearDevice;
  468.   TObject.Done;
  469. end;
  470.  
  471. { Clears the association of this object with the current device }
  472.  
  473. procedure TPrinter.ClearDevice;
  474. begin
  475.   StrDispose(Device); Device := nil;
  476.   StrDispose(Driver); Driver := nil;
  477.   StrDispose(Port); Port := nil;
  478.   if DeviceModule >= 32 then
  479.   begin
  480.     FreeLibrary(DeviceModule);
  481.     DeviceModule := 0;
  482.   end;
  483.   if DevSettings <> nil then
  484.     FreeMem(DevSettings, DevSettingSize);
  485.   Status := ps_Unassociated;
  486. end;
  487.  
  488. { Associates the printer object with a new device. If the ADevice
  489.   parameter is nil the Windows default printer is used, otherwise,
  490.   the parameters must be ones contained in the [devices] section
  491.   of the WIN.INI file. }
  492.  
  493. procedure TPrinter.SetDevice(ADevice, ADriver, APort: PChar);
  494. var
  495.   DriverName: array[0..80] of Char;
  496.   DevModeSize: Integer;
  497.   StubDevMode: TDevMode;
  498.  
  499.   procedure GetDefaultPrinter;
  500.   var
  501.     Printer: array[0..80] of Char;
  502.     Cur: PChar;
  503.  
  504.   begin
  505.     GetProfileString('windows', 'device', '', Printer,
  506.       SizeOf(Printer) - 1);
  507.     Cur := Printer;
  508.     Device := StrNew(FetchStr(Cur));
  509.     Driver := StrNew(FetchStr(Cur));
  510.     Port := StrNew(FetchStr(Cur));
  511.   end;
  512.  
  513.   function Equal(S1, S2: PChar): Boolean;
  514.   begin
  515.     Equal := (S1 <> nil) and (S2 <> nil) and
  516.       (StrComp(S1, S2) = 0);
  517.   end;
  518.  
  519. begin
  520.   if Equal(Device, ADevice) and Equal(Driver, ADriver) and
  521.     Equal(Port, APort) then Exit;
  522.   ClearDevice;
  523.   if ADevice = nil then
  524.     GetDefaultPrinter
  525.   else
  526.   begin
  527.     Device := StrNew(ADevice);
  528.     Driver := StrNew(ADriver);
  529.     Port := StrNew(APort);
  530.   end;
  531.   if (Device = nil) or (Driver = nil) or (Port = nil) then
  532.   begin
  533.     Status := ps_Unassociated;
  534.     Exit;
  535.   end;
  536.   Status := ps_Ok;
  537.   StrLCopy(DriverName, Driver, SizeOf(DriverName) - 1);
  538.   StrLCat(DriverName, '.DRV', SizeOf(DriverName) - 1);
  539.   DeviceModule := LoadLibrary(DriverName);
  540.   if DeviceModule < 32 then Status := ps_InvalidDevice
  541.   else
  542.   begin
  543.     { Grab the DevMode procedures }
  544.     @ExtDeviceMode := GetProcAddress(DeviceModule, 'ExtDeviceMode');
  545.     @DeviceMode := GetProcAddress(DeviceModule, 'DeviceMode');
  546.     if (@DeviceMode = nil) and (@ExtDeviceMode = nil) then
  547.       Status := ps_InvalidDevice;
  548.     if @ExtDeviceMode <> nil then
  549.     begin
  550.       { Get default printer settings }
  551.       DevSettingSize := ExtDeviceMode(0, DeviceModule, StubDevMode,
  552.         Device, Port, StubDevMode, nil, 0);
  553.       GetMem(DevSettings, DevSettingSize);
  554.       ExtDeviceMode(0, DeviceModule, DevSettings^, Device, Port,
  555.         DevSettings^, nil, dm_Out_Buffer);
  556.     end
  557.     else
  558.       DevSettings := nil; { Cannot use local settings }
  559.   end;
  560. end;
  561.  
  562. { Configure brings up a dialog as a child of the given window
  563.   to configure the associated printer driver. }
  564.  
  565. procedure TPrinter.Configure(Window: PWindowsObject);
  566. begin
  567.   if Status = ps_Ok then
  568.     if @ExtDeviceMode = nil then { driver is only supports DevMode }
  569.       { If DeviceMode = nil, Status will <> ps_Ok }
  570.       DeviceMode(Window^.HWindow, DeviceModule, Device, Port)
  571.     else
  572.       { Request driver to modify local copy of printer settings }
  573.       ExtDeviceMode(Window^.HWindow, DeviceModule, DevSettings^, Device,
  574.         Port, DevSettings^, nil, dm_In_Buffer or dm_Prompt or
  575.           dm_Out_Buffer);
  576. end;
  577.  
  578. { Returns a device context for the associated printer, 0 if an
  579.   error occurs or Status is <> ps_Ok }
  580.  
  581. function TPrinter.GetDC: HDC;
  582. begin
  583.   if Status = ps_Ok then
  584.     GetDC := CreateDC(Driver, Device, Port, DevSettings)
  585.   else GetDC := 0;
  586. end;
  587.  
  588. { Abort procedure used for printing }
  589. function AbortProc(Prn: HDC; Code: Integer): Boolean; export;
  590. var
  591.   Msg: TMsg;
  592. begin
  593.   while not UserAbort and PeekMessage(Msg, 0, 0, 0, pm_Remove) do
  594.     if not Application^.ProcessAppMsg(Msg) then
  595.     begin
  596.       TranslateMessage(Msg);
  597.       DispatchMessage(Msg);
  598.     end;
  599.   AbortProc := not UserAbort;
  600. end;
  601.  
  602. function TPrinter.Print(ParentWin: PWindowsObject;
  603.   PrintOut: PPrintOut): Boolean;
  604. type
  605.   TAbortProc = function (Prn: HDC; Code: Integer): Boolean;
  606. var
  607.   PageSize: TPoint;
  608.   PrnDC: HDC;
  609.   Pages: Integer;
  610.   BandRect: TRect;
  611.   Banding: Boolean;
  612.   FirstBand: Boolean;
  613.   Flags: Word;
  614.   AbortProcInst: TFarProc;
  615.   Dlg: PWindowsObject;
  616.   UseBandInfo: Boolean;
  617.   PageNumber: Word;
  618.   PageRange: TPrintDialogRec;
  619.   OldCursor: HCursor;
  620.   Copies: Integer;
  621.   SelStart, SelStop: Integer;
  622.   UsePageRangeDlg: Boolean;
  623.  
  624. procedure CalcBandingFlags;
  625. type
  626.   TBandInfoStruct = record
  627.     fGraphicsFlag: Bool;
  628.     fTextFlag: Bool;
  629.     GraphcisRect: TRect;
  630.   end;
  631. var
  632.   BandInfoRec: TBandInfoStruct;
  633.   pFlags: Word;
  634. begin
  635.   { Calculate text verses graphics banding }
  636.   if UseBandInfo then
  637.   begin
  638.     Escape(PrnDC, BandInfo, SizeOf(TBandInfoStruct), nil, @BandInfoRec);
  639.     if BandInfoRec.fGraphicsFlag then pFlags := pf_Graphics;
  640.     if BandInfoRec.fTextFlag then pFlags := pf_Text;
  641.     Flags := (Flags and not pf_Both) or pFlags;
  642.   end
  643.   else
  644.   begin
  645.     { If a driver does not support BandInfo the Microsoft
  646.       Recommended way of determining text only bands is if
  647.       the first band is the full page, all others are
  648.       graphcis only.  Otherwise it handles both. }
  649.     if FirstBand and (LongInt((@BandRect.left)^) = 0)
  650.        and (BandRect.right = PageSize.X) and
  651.        (BandRect.bottom = PageSize.Y) then
  652.       Flags := (Flags and not pf_Both) or pf_Text
  653.     else
  654.       if Flags and pf_Both = pf_Text then
  655.         { All other bands are graphics only }
  656.         Flags := (Flags and not pf_Both) or pf_Graphics
  657.       else
  658.         Flags := Flags or pf_Both;
  659.   end;
  660.  
  661.   FirstBand := False;
  662. end;
  663.  
  664. procedure WaitCursor;
  665. begin
  666.   OldCursor := SetCursor(LoadCursor(0, idc_Wait));
  667. end;
  668.  
  669. procedure RestoreCursor;
  670. begin
  671.   SetCursor(OldCursor);
  672. end;
  673.  
  674. begin
  675.   Print := False; { Assume error occured }
  676.  
  677.   Error := 0;
  678.  
  679.   if PrintOut = nil then Exit;
  680.   if ParentWin = nil then Exit;
  681.  
  682.   WaitCursor;
  683.  
  684.   PrnDC := GetDC;
  685.   if PrnDC = 0 then Exit;
  686.  
  687.   { Get the page size }
  688.   PageSize.X := GetDeviceCaps(PrnDC, HorzRes);
  689.   PageSize.Y := GetDeviceCaps(PrnDC, VertRes);
  690.  
  691.   Printout^.SetPrintParams(PrnDC, PageSize);
  692.   UsePageRangeDlg := Printout^.GetDialogInfo(Pages);
  693.  
  694.   with PageRange do
  695.   begin
  696.     drStart := 1;
  697.     if Pages = 0 then drStop := MaxInt
  698.     else drStop := Pages;
  699.     drCopies := 1;
  700.     drCollate := True;
  701.   end;
  702.  
  703.   if UsePageRangeDlg then
  704.   begin
  705.     if Application^.ExecDialog(InitPrintDialog(ParentWin, PrnDC, Pages,
  706.         Printout^.GetSelection(SelStart, SelStop), PageRange)) <> id_OK then
  707.     begin
  708.       DeleteDC(PrnDC);
  709.       Exit;
  710.     end;
  711.   end;
  712.  
  713.   if PageRange.drCollate then
  714.     Copies := PageRange.drCopies
  715.   else
  716.   begin
  717.     Flags := PageRange.drCopies;
  718.     Escape(PrnDC, SetCopyCount, SizeOf(Flags), @Flags, nil);
  719.     Copies := 1;
  720.   end;
  721.  
  722.   with PageRange do
  723.     if drUseSelection then
  724.     begin
  725.       drStart := SelStart;
  726.       drStop := SelStop;
  727.     end;
  728.  
  729.   Dlg := Application^.MakeWindow(InitAbortDialog(ParentWin,
  730.     PrintOut^.Title));
  731.  
  732.   if Dlg = nil then
  733.   begin
  734.     DeleteDC(PrnDC);
  735.     Exit;
  736.   end;
  737.  
  738.   RestoreCursor;
  739.  
  740.   EnableWindow(ParentWin^.HWindow, False);
  741.  
  742.   AbortProcInst := MakeProcInstance(@AbortProc, hInstance);
  743.   Escape(PrnDC, SetAbortProc, 0, PChar(AbortProcInst), nil);
  744.  
  745.   { Only band if the user requests banding and the printer
  746.     supports banding }
  747.   Banding := PrintOut^.Banding and
  748.     (GetDeviceCaps(PrnDC, RasterCaps) or rc_Banding <> 0);
  749.  
  750.   if not Banding then
  751.   begin
  752.     { Set the banding rectangle to full page }
  753.     LongInt((@BandRect.left)^) := 0;
  754.     TPoint(Pointer(@BandRect.right)^) := PageSize;
  755.   end
  756.   else
  757.   begin
  758.     { Only use BandInfo if supported (note: using Flags as a temporary) }
  759.     Flags := BandInfo;
  760.     UseBandInfo :=
  761.       Escape(PrnDC, QueryEscSupport, SizeOf(Flags), @Flags, nil) <> 0;
  762.   end;
  763.  
  764.   Printout^.BeginPrinting;
  765.  
  766.   repeat
  767.     Flags := pf_Both;
  768.     if Banding then Flags := pf_Banding;
  769.     if PageRange.drUseSelection then
  770.       Flags := Flags or pf_Selection;
  771.     Error := Escape(PrnDC, StartDoc, StrLen(PrintOut^.Title),
  772.       PrintOut^.Title, nil);
  773.     if Error > 0 then
  774.     begin
  775.       Printout^.BeginDocument(PageRange.drStart, PageRange.drStop,
  776.         Flags);
  777.       PageNumber := PageRange.drStart;
  778.       repeat
  779.         if Banding then
  780.         begin
  781.           FirstBand := True;
  782.           Error := Escape(PrnDC, NextBand, 0, nil, @BandRect);
  783.         end;
  784.         repeat
  785.           { Call the abort proc between bands or pages }
  786.           TAbortProc(AbortProcInst)(PrnDC, 0);
  787.  
  788.           if Banding then
  789.           begin
  790.             CalcBandingFlags;
  791.             if (PrintOut^.ForceAllBands) and
  792.                (Flags and pf_Both = pf_Text) then
  793.               SetPixel(PrnDC, 0, 0, 0);
  794.           end;
  795.  
  796.           if Error > 0 then
  797.           begin
  798.             PrintOut^.PrintPage(PageNumber, BandRect, Flags);
  799.             if Banding then
  800.               Error := Escape(PrnDC, NextBand, 0, nil, @BandRect);
  801.           end;
  802.         until (Error <= 0) or not Banding or IsRectEmpty(BandRect);
  803.  
  804.         { NewFrame should only be called if not banding }
  805.         if (Error > 0) and not Banding then
  806.           Error := Escape(PrnDC, NewFrame, 0, nil, nil);
  807.  
  808.         Inc(PageNumber);
  809.       until (Error <= 0) or not PrintOut^.HasNextPage(PageNumber) or
  810.         (PageNumber > PageRange.drStop);
  811.  
  812.       Printout^.EndDocument;
  813.  
  814.       { Tell GDI the document is finished }
  815.       if Error > 0 then
  816.         if Banding and UserAbort then
  817.           Escape(PrnDC, AbortDoc, 0, nil, nil)
  818.         else
  819.           Escape(PrnDC, EndDoc, 0, nil, nil);
  820.     end;
  821.     Dec(Copies);
  822.   until (Copies = 0) or UserAbort;
  823.  
  824.   Printout^.EndPrinting;
  825.  
  826.   { Reset copies }
  827.   if not PageRange.drCollate then
  828.   begin
  829.     Flags := 1;
  830.     Escape(PrnDC, SetCopyCount, SizeOf(Flags), @Flags, nil);
  831.   end;
  832.  
  833.   { Free allocated resources }
  834.   FreeProcInstance(AbortProcInst);
  835.   EnableWindow(ParentWin^.HWindow, True);
  836.   Dispose(Dlg, Done);
  837.   DeleteDC(PrnDC);
  838.  
  839.   if Error and sp_NotReported <> 0 then
  840.     ReportError(PrintOut);
  841.  
  842.   Print := (Error > 0) and not UserAbort;
  843.  
  844.   UserAbort := False;
  845. end;
  846.  
  847. function TPrinter.InitAbortDialog(Parent: PWindowsObject;
  848.   Title: PChar): PDialog;
  849. var
  850.   Dlg: PDialog;
  851.   Template: PChar;
  852. begin
  853.   if BWCCClassNames then Template := 'AbortDialogB'
  854.   else Template := 'AbortDialog';
  855.   InitAbortDialog := New(PPrinterAbortDlg, Init(Parent, Template, Title,
  856.     Device, Port));
  857. end;
  858.  
  859. function TPrinter.InitPrintDialog(Parent: PWindowsObject; PrnDC: HDC;
  860.   Pages: Integer; SelAllowed: Boolean; var Data: TPrintDialogRec): PDialog;
  861. var
  862.   Template: PChar;
  863. begin
  864.   if BWCCClassNames then Template := 'PrintDialogB'
  865.   else Template := 'PrintDialog';
  866.   InitPrintDialog := New(PPrintDialog, Init(Parent, Template, PrnDC, Pages,
  867.     @Self, SelAllowed, Data));
  868. end;
  869.  
  870. function TPrinter.InitSetupDialog(Parent: PWindowsObject): PDialog;
  871. var
  872.   Template: PChar;
  873. begin
  874.   if BWCCClassNames then Template := 'PrinterSetupB'
  875.   else Template := 'PrinterSetup';
  876.   InitSetupDialog := New(PPrinterSetupDlg, Init(Parent, Template,
  877.     @Self));
  878. end;
  879.  
  880. procedure TPrinter.Setup(Parent: PWindowsObject);
  881. begin
  882.   if Status = ps_Ok then
  883.     Application^.ExecDialog(InitSetupDialog(Parent));
  884. end;
  885.  
  886. procedure TPrinter.ReportError(PrintOut: PPrintOut);
  887. var
  888.   ErrorMsg: array[0..80] of Char;
  889.   ErrorCaption: array[0..80] of Char;
  890.   ErrorTemplate: array[0..40] of Char;
  891.   ErrorStr: array[0..40] of Char;
  892.   ErrorId: Word;
  893.   Msg, Title: PChar;
  894. begin
  895.   case Error of
  896.     sp_AppAbort:    ErrorId := sr_PrnCancel;
  897.     sp_Error:       ErrorId := sr_GenError;
  898.     sp_OutOfDisk:   ErrorId := sr_OutOfDisk;
  899.     sp_OutOfMemory: ErrorId := sr_OutOfMemory;
  900.     sp_UserAbort:   ErrorId := sr_PrnMgrAbort;
  901.   else
  902.     Exit;
  903.   end;
  904.  
  905.   LoadString(hInstance, sr_ErrorTemplate, ErrorTemplate,
  906.     SizeOf(ErrorTemplate));
  907.   LoadString(hInstance, ErrorId, ErrorStr, SizeOf(ErrorStr));
  908.   Title := PrintOut^.Title;
  909.   Msg := ErrorStr;
  910.   WVSPrintF(ErrorMsg, ErrorTemplate, Title);
  911.   LoadString(hInstance, sr_ErrorCaption, ErrorCaption,
  912.     SizeOf(ErrorCaption));
  913.   MessageBox(0, ErrorMsg, ErrorCaption, mb_Ok or mb_IconStop);
  914. end;
  915.  
  916. { TPrinterSetupDlg ------------------------------------------------- }
  917.  
  918. { TPrinterSetupDlg assumes the template passed has a ComboBox with
  919.   the control ID of 100 and a "Setup" button with id 101 }
  920.  
  921. const
  922.   pdStrWidth = 80;
  923.  
  924. type
  925.   PTransferRec = ^TTransferRec;
  926.   TTransferRec = record
  927.     Strings: PCollection;
  928.     Selected: array[0..0] of Char;
  929.   end;
  930.  
  931.   PDeviceRec = ^TDeviceRec;
  932.   TDeviceRec = record
  933.     Driver, Device, Port: PChar;
  934.   end;
  935.  
  936.   PDeviceCollection = ^TDeviceCollection;
  937.   TDeviceCollection = object(TCollection)
  938.     procedure FreeItem(P: Pointer); virtual;
  939.   end;
  940.  
  941. procedure TDeviceCollection.FreeItem(P: Pointer);
  942. begin
  943.   with PDeviceRec(P)^ do
  944.   begin
  945.     StrDispose(Driver);
  946.     StrDispose(Device);
  947.     StrDispose(Port);
  948.   end;
  949.   Dispose(PDeviceRec(P));
  950. end;
  951.  
  952. constructor TPrinterSetupDlg.Init(AParent: PWindowsObject;
  953.   TemplateName: PChar; APrinter: PPrinter);
  954. var
  955.   tmp: PComboBox;
  956.   Devices,                                  { List of devices from the
  957.                                               WIN.INI }
  958.   Device: PChar;                            { Current device }
  959.   DevicesSize: Integer;                     { Amount of bytes allocated
  960.                                               to store 'devices' }
  961.   Driver,                                   { Name of the driver for the
  962.                                               device }
  963.   Port: PChar;                              { Name of the port for the
  964.                                               device }
  965.   DriverLine: array[0..pdStrWidth] of Char; { Device line from WIN.INI }
  966.   LineCur: PChar;                           { FetchStr pointer into
  967.                                               DriverLine }
  968.   DriverStr: array[0..pdStrWidth] of Char;  { Text being built for display }
  969.   StrCur: PChar;                            { Temp pointer used for copying
  970.                                               Port into the line }
  971.   StrCurSize: Integer;                      { Room left in DriverStr to
  972.                                               copy Port }
  973.   DevRec: PDeviceRec;                       { Record pointer built to
  974.                                               store in DeviceCollection }
  975. begin
  976.   TDialog.Init(AParent, TemplateName);
  977.   tmp := New(PComboBox, InitResource(@Self, id_Combo, pdStrWidth));
  978.   GetMem(TransferBuffer, SizeOf(PCollection) + pdStrWidth);
  979.   PTransferRec(TransferBuffer)^.Strings := New(PStrCollection,
  980.     Init(5, 5));
  981.   Printer := APrinter;
  982.   DeviceCollection := New(PDeviceCollection, Init(5, 5));
  983.  
  984.   if MaxAvail div 2 > 4096 then DevicesSize := 4096
  985.   else DevicesSize := MaxAvail div 2;
  986.   GetMem(Devices, DevicesSize);
  987.  
  988.   { Save initial values of printer for Cancel }
  989.   OldDevice := StrNew(Printer^.Device);
  990.   OldDriver := StrNew(Printer^.Driver);
  991.   OldPort := StrNew(Printer^.Port);
  992.  
  993.   with PTransferRec(TransferBuffer)^ do
  994.   begin
  995.     { Get a list of devices from WIN.INI.  Stored in the form of
  996.       <device 1>#0<device 2>#0...<driver n>#0#0
  997.     }
  998.     GetProfileString('devices', nil, '', Devices, DevicesSize);
  999.  
  1000.     Device := Devices;
  1001.     while Device^ <> #0 do
  1002.     begin
  1003.       GetProfileString('devices', Device, '', DriverLine,
  1004.         SizeOf(DriverLine) - 1);
  1005.  
  1006.       FormDriverStr(DriverStr, SizeOf(DriverStr) - 1,Device, '');
  1007.  
  1008.       { Get driver portion of DeviceLine }
  1009.       LineCur := DriverLine;
  1010.       Driver := FetchStr(LineCur);
  1011.  
  1012.       { Copy the port information from the line }
  1013.       (*   This code is complicated because the device line is of
  1014.           the form:
  1015.            <device name> = <driver name> , <port> { , <port> }
  1016.           where port (in {}) can be repeated. *)
  1017.  
  1018.       StrCur := @DriverStr[StrLen(DriverStr)];
  1019.       StrCurSize := SizeOf(DriverStr) - StrLen(DriverStr) - 1;
  1020.       Port := FetchStr(LineCur);
  1021.       while Port^ <> #0 do
  1022.       begin
  1023.         StrLCopy(StrCur, Port, StrCurSize);
  1024.         Strings^.Insert(StrNew(DriverStr));
  1025.         New(DevRec);
  1026.         DevRec^.Device := StrNew(Device);
  1027.         DevRec^.Driver := StrNew(Driver);
  1028.         DevRec^.Port := StrNew(Port);
  1029.         DeviceCollection^.AtInsert(Strings^.IndexOf(@DriverStr), DevRec);
  1030.         Port := FetchStr(LineCur);
  1031.       end;
  1032.       Inc(Device, StrLen(Device) + 1);
  1033.     end;
  1034.     FreeMem(Devices, DevicesSize);
  1035.  
  1036.     { Set the current selection to Printer's current device }
  1037.     FormDriverStr(Selected, pdStrWidth, Printer^.Device, Printer^.Port);
  1038.   end;
  1039. end;
  1040.  
  1041. destructor TPrinterSetupDlg.Done;
  1042. begin
  1043.   StrDispose(OldDevice);
  1044.   StrDispose(OldDriver);
  1045.   StrDispose(OldPort);
  1046.   Dispose(DeviceCollection, Done);
  1047.   Dispose(PTransferRec(TransferBuffer)^.Strings, Done);
  1048.   FreeMem(TransferBuffer, SizeOf(PCollection) + pdStrWidth);
  1049.   TDialog.Done;
  1050. end;
  1051.  
  1052. procedure TPrinterSetupDlg.TransferData(TransferFlag: Word);
  1053. var
  1054.   DevRec: PDeviceRec;
  1055. begin
  1056.   TDialog.TransferData(TransferFlag);
  1057.   if TransferFlag = tf_GetData then
  1058.     with PTransferRec(TransferBuffer)^ do
  1059.       { Use the current selection to set Printer }
  1060.       with PDeviceRec(DeviceCollection^.At(Strings^.IndexOf(@Selected)))^ do
  1061.         { Set the printer to the new device }
  1062.         Printer^.SetDevice(Device, Driver, Port);
  1063. end;
  1064.  
  1065. procedure TPrinterSetupDlg.IDSetup(var Msg: TMessage);
  1066. begin
  1067.   TransferData(tf_GetData);
  1068.   Printer^.Configure(@Self);
  1069. end;
  1070.  
  1071. procedure TPrinterSetupDlg.Cancel(var Msg: TMessage);
  1072. begin
  1073.   TDialog.Cancel(Msg);
  1074.   { Restore old settings, just in case the user pressed the Setup button }
  1075.   if OldDriver = nil then Printer^.ClearDevice
  1076.   else Printer^.SetDevice(OldDevice, OldDriver, OldPort);
  1077. end;
  1078.  
  1079. { TNumeric }
  1080.  
  1081. type
  1082.   PNumeric = ^TNumeric;
  1083.   TNumeric = object(TEdit)
  1084.     Min, Max: LongInt;
  1085.     constructor Init(AParent: PWindowsObject; AnId, X, Y, W, H: Integer;
  1086.       AMin, AMax: Integer; Digits: Integer);
  1087.     constructor InitResource(AParent: PWindowsObject; Id: Integer;
  1088.       AMin, AMax: Integer; Digits: Integer);
  1089.     function CanClose: Boolean; virtual;
  1090.     function GetValue(var Value: Integer): Boolean;
  1091.     procedure SetRange(AMin, AMax: Integer);
  1092.     procedure SetValue(Value: Integer);
  1093.     procedure WMChar(var Msg: TMessage);
  1094.       virtual wm_First + wm_Char;
  1095.   end;
  1096.  
  1097. constructor TNumeric.Init(AParent: PWindowsObject; AnId, X, Y, W,
  1098.   H: Integer; AMin, AMax: Integer; Digits: Integer);
  1099. begin
  1100.   TEdit.Init(AParent, AnId, '', X, Y, W, H, Digits + 1, False);
  1101.   Min := AMin;
  1102.   Max := AMax;
  1103. end;
  1104.  
  1105. constructor TNumeric.InitResource(AParent: PWindowsObject; Id: Integer;
  1106.   AMin, AMax: Integer; Digits: Integer);
  1107. begin
  1108.   TEdit.InitResource(AParent, Id, Digits + 1);
  1109.   Min := AMin;
  1110.   Max := AMax;
  1111. end;
  1112.  
  1113. function TNumeric.CanClose: Boolean;
  1114. var
  1115.   Value: Integer;
  1116.   Valid: Boolean;
  1117.   Text: array[0..255] of Char;
  1118.   P: array[0..1] of LongInt;
  1119. begin
  1120.   Valid := not IsWindowEnabled(HWindow) or
  1121.     (GetValue(Value) and (Value >= Min) and (Value <= Max));
  1122.   if not Valid then
  1123.   begin
  1124.     P[0] := Min;
  1125.     P[1] := Max;
  1126.     WVSPrintF(Text, 'Value not within range (%ld-%ld).', P);
  1127.     MessageBox(HWindow, Text, 'Invalid Range', mb_IconStop or mb_Ok);
  1128.     SetSelection(0, MaxInt);
  1129.     SetFocus(HWindow);
  1130.   end;
  1131.   CanClose := Valid;
  1132. end;
  1133.  
  1134. function TNumeric.GetValue(var Value: Integer): Boolean;
  1135. var
  1136.   Text: array[0..255] of Char;
  1137.   Code: Integer;
  1138. begin
  1139.   GetText(Text, SizeOf(Text));
  1140.   Val(Text, Value, Code);
  1141.   GetValue := Code = 0;
  1142. end;
  1143.  
  1144. procedure TNumeric.SetRange(AMin, AMax: Integer);
  1145. begin
  1146.   Min := AMin;
  1147.   Max := AMax;
  1148. end;
  1149.  
  1150. procedure TNumeric.SetValue(Value: Integer);
  1151. var
  1152.   Text: array[0..20] of Char;
  1153. begin
  1154.   Str(Value, Text);
  1155.   SetText(Text);
  1156. end;
  1157.  
  1158. procedure TNumeric.WMChar(var Msg: TMessage);
  1159. begin
  1160.   if not (Char(Msg.wParamLo) in ['A'..'Z','a'..'z',',','.','<','>',
  1161.     '/','?','~','`','!','@','#','$','%','^','&','*','(',')','_','=',
  1162.     '{','}','[',']','|','\',';',':','"']) then
  1163.     DefWndProc(Msg)
  1164.   else MessageBeep(0);
  1165. end;
  1166.  
  1167. { TSelRadio }
  1168.  
  1169. type
  1170.   PSelRadio = ^TSelRadio;
  1171.   TSelRadio = object(TRadioButton)
  1172.     Enbl: Boolean;
  1173.     Controls: PCollection;
  1174.     constructor InitResource(AParent: PWindowsObject; ResourceID: Word;
  1175.       AEnbl: Boolean; AControls: PCollection);
  1176.     procedure BNClicked(var Msg: TMessage);
  1177.       virtual nf_First + bn_Clicked;
  1178.   end;
  1179.  
  1180. constructor TSelRadio.InitResource(AParent: PWindowsObject;
  1181.   ResourceID: Word; AEnbl: Boolean; AControls: PCollection);
  1182. begin
  1183.   TRadioButton.InitResource(AParent, ResourceId);
  1184.   Enbl := AEnbl;
  1185.   Controls := AControls;
  1186. end;
  1187.  
  1188. { Assumes the Controls collection contains PWindowsObjects }
  1189.  
  1190. procedure TSelRadio.BNClicked(var Msg: TMessage);
  1191.  
  1192.   procedure DoEnableDisable(P: PWindowsObject); far;
  1193.   begin
  1194.     if Enbl then P^.Enable else P^.Disable;
  1195.   end;
  1196.  
  1197. begin
  1198.   TRadioButton.BNClicked(Msg);
  1199.   Controls^.ForEach(@DoEnableDisable);
  1200.   if Enbl then PWindowsObject(Controls^.At(0))^.Focus;
  1201. end;
  1202.  
  1203. { TPrintDialog }
  1204.  
  1205. constructor TPrintDialog.Init(AParent: PWindowsObject; Template: PChar;
  1206.   APrnDC: HDC; APages: Integer; APrinter: PPrinter; ASelAllowed: Boolean;
  1207.   var Data: TPrintDialogRec);
  1208. var
  1209.   P: PWindowsObject;
  1210.  
  1211.   function QLog10(X: Integer): Integer;
  1212.   var
  1213.     I, L: Integer;
  1214.   begin
  1215.     I := 1;
  1216.     L := 0;
  1217.     if X >= 10000 then QLog10 := 5
  1218.     else
  1219.     begin
  1220.       repeat
  1221.         I := I * 10;
  1222.         Inc(L);
  1223.       until I > X;
  1224.       QLog10 := L;
  1225.     end;
  1226.   end;
  1227.  
  1228. begin
  1229.   TDialog.Init(AParent, Template);
  1230.   Printer := APrinter;
  1231.   PData := @Data;
  1232.   PrnDC := APrnDC;
  1233.   Pages := APages;
  1234.   SelAllowed := ASelAllowed;
  1235.  
  1236.   PrinterName := New(PStatic, InitResource(@Self, id_PrinterName, 0));
  1237.   Controls := New(PCollection, Init(4, 4));
  1238.   if Pages <> 0 then
  1239.   begin
  1240.     FromPage := New(PNumeric, InitResource(@Self, id_From, 1, Pages,
  1241.       QLog10(Pages)));
  1242.     ToPage := New(PNumeric, InitResource(@Self, id_To, 1, Pages,
  1243.       QLog10(Pages)));
  1244.   end
  1245.   else
  1246.   begin
  1247.     FromPage := New(PNumeric, InitResource(@Self, id_From, 1, 32767, 0));
  1248.     ToPage := New(PNumeric, InitResource(@Self, id_To, 1, 32767, 0));
  1249.   end;
  1250.   Controls^.Insert(FromPage);
  1251.   Controls^.Insert(ToPage);
  1252.   Controls^.Insert(New(PStatic, InitResource(@Self, id_FromText, 0)));
  1253.   Controls^.Insert(New(PStatic, InitResource(@Self, id_ToText, 0)));
  1254.   AllBtn := New(PSelRadio, InitResource(@Self, id_All, False, Controls));
  1255.   SelectBtn := New(PSelRadio, InitResource(@Self, id_Selection, False,
  1256.     Controls));
  1257.   PageBtn := New(PSelRadio, InitResource(@Self, id_Pages, True, Controls));
  1258.   Copies := New(PNumeric, InitResource(@Self, id_Copies, 1, 999, 3));
  1259.   Collate := New(PCheckBox, InitResource(@Self, id_Collate));
  1260. end;
  1261.  
  1262. procedure TPrintDialog.SetupWindow;
  1263. var
  1264.   NameText: array[0..80] of Char;
  1265. begin
  1266.   TDialog.SetupWindow;
  1267.   with Printer^ do
  1268.     FormDriverStr(NameText, SizeOf(NameText), Device, Port);
  1269.   PrinterName^.SetText(NameText);
  1270. end;
  1271.  
  1272. procedure TPrintDialog.TransferData(Direction: Word);
  1273. var
  1274.   Esc: Integer;
  1275.   Val: LongInt;
  1276.   Msg: TMessage;
  1277. begin
  1278.   case Direction of
  1279.     tf_SetData:
  1280.       with PData^ do
  1281.       begin
  1282.         Collate^.SetCheck(Word(drCollate));
  1283.         Esc := SetCopyCount;
  1284.         if Escape(PrnDC, QueryEscSupport, SizeOf(Esc), @Esc, @Esc) = 0 then
  1285.           Collate^.Disable;
  1286.         PNumeric(Copies)^.SetValue(drCopies);
  1287.         AllBtn^.SetCheck(bf_Checked);
  1288.         AllBtn^.BNClicked(Msg);
  1289.         if not SelAllowed then SelectBtn^.Disable;
  1290.         if Pages = 1 then
  1291.           PageBtn^.Disable
  1292.         else
  1293.         begin
  1294.           if Pages <> 0 then
  1295.           begin
  1296.             PNumeric(FromPage)^.SetValue(drStart);
  1297.             PNumeric(ToPage)^.SetValue(drStop);
  1298.           end;
  1299.         end;
  1300.       end;
  1301.     tf_GetData:
  1302.       with PData^ do
  1303.       begin
  1304.         drCollate := Boolean(Collate^.GetCheck);
  1305.         PNumeric(Copies)^.GetValue(drCopies);
  1306.         if SelectBtn^.GetCheck = bf_Checked then
  1307.           drUseSelection := True
  1308.         else
  1309.         begin
  1310.           drUseSelection := False;
  1311.  
  1312.           if PageBtn^.GetCheck = bf_Checked then
  1313.           begin
  1314.             PNumeric(FromPage)^.GetValue(drStart);
  1315.             PNumeric(ToPage)^.GetValue(drStop);
  1316.           end;
  1317.         end;
  1318.       end;
  1319.   end;
  1320. end;
  1321.  
  1322. procedure TPrintDialog.IDSetup(var Msg: TMessage);
  1323. begin
  1324.   Printer^.Configure(@Self);
  1325. end;
  1326.  
  1327. { TEditPrintout }
  1328.  
  1329. { This object will print-out the contents of a TEdit control }
  1330.  
  1331. constructor TEditPrintout.Init(AEditor: PEdit; ATitle: PChar);
  1332. begin
  1333.   TPrintout.Init(ATitle);
  1334.   Editor := AEditor;
  1335.  
  1336.   { The following are calculated by SetPrintParams which is called
  1337.     before any other methods are called. }
  1338.   LinesPerPage := 0;
  1339.   NumLines := 0;
  1340.   LineHeight := 0;
  1341.   StartPos := 0;
  1342.   StopPos := 0;
  1343.   StartLine := 0;
  1344.   StopLine := 0;
  1345. end;
  1346.  
  1347. procedure TEditPrintout.BeginDocument(StartPage, EndPage: Integer;
  1348.   Flags: Word);
  1349. begin
  1350.   if Flags and pf_Selection = 0 then
  1351.   begin
  1352.     { not using the selection, print everything }
  1353.     StartLine := 0;
  1354.     StopLine := NumLines - 1;
  1355.     StartPos := 0;
  1356.     StopPos := 32767;
  1357.   end; { else leave values set by GetSelection }
  1358. end;
  1359.  
  1360. function TEditPrintout.GetSelection(var Start, Stop: Integer): Boolean;
  1361. begin
  1362.   Editor^.GetSelection(StartPos, StopPos);
  1363.   if StartPos = StopPos then GetSelection := False
  1364.   else
  1365.   begin
  1366.     with Editor^ do
  1367.     begin
  1368.       StartLine := GetLineFromPos(StartPos);
  1369.       StopLine := GetLineFromPos(StopPos);
  1370.       Start := 1;
  1371.       Stop := (StopLine - StartLine) div LinesPerPage + 1;
  1372.     end;
  1373.     GetSelection := True;
  1374.   end;
  1375. end;
  1376.  
  1377. function TEditPrintout.GetDialogInfo(var Pages: Integer): Boolean;
  1378. begin
  1379.   Pages := NumLines div LinesPerPage + 1;
  1380.   GetDialogInfo := True;
  1381. end;
  1382.  
  1383. procedure TEditPrintout.PrintPage(Page: Word; var Rect: TRect; Flags: Word);
  1384. var
  1385.   LineBuffer: array[0..255] of Char;
  1386.   I: Integer;
  1387.   FirstLine: Integer;
  1388.   CurLine: Integer;
  1389.   Len: Integer;
  1390.   XOff: Integer;
  1391. begin
  1392.   FirstLine := StartLine + (Page - 1) * LinesPerPage;
  1393.   for I := 0 to LinesPerPage - 1 do
  1394.   begin
  1395.     CurLine := I + FirstLine;
  1396.     if CurLine > StopLine then Exit;
  1397.  
  1398.     XOff := 0;
  1399.  
  1400.     with Editor^ do
  1401.     begin
  1402.       GetLine(LineBuffer, SizeOf(LineBuffer), CurLine);
  1403.  
  1404.       { Order of these next lines is important since the beginning
  1405.         and ending selections can be on the same line.  We don't want
  1406.         to move the text before we have cut off the end of the text. }
  1407.       if (CurLine = StopLine) and (StopPos < StrLen(LineBuffer)) then
  1408.         LineBuffer[StopPos - GetLineIndex(CurLine)] := #0;
  1409.       if CurLine = StartLine then
  1410.       begin
  1411.         Len := StartPos - GetLineIndex(CurLine);
  1412.         XOff := GetTextExtent(DC, LineBuffer, Len);
  1413.         StrCopy(LineBuffer, @LineBuffer[Len]);
  1414.       end;
  1415.     end;
  1416.     TextOut(DC, XOff, I * LineHeight, LineBuffer, StrLen(LineBuffer));
  1417.   end;
  1418. end;
  1419.  
  1420. function TEditPrintout.HasNextPage(Page: Word): Boolean;
  1421. begin
  1422.   { Always a next page.  Will never be asked for a page beyond what
  1423.     is calculated by Paginate }
  1424.   HasNextPage := True;
  1425. end;
  1426.  
  1427. procedure TEditPrintout.SetPrintParams(ADC: HDC; ASize: TPoint);
  1428. var
  1429.   TextMetrics: TTextMetric;
  1430. begin
  1431.   TPrintout.SetPrintParams(ADC, ASize);
  1432.   NumLines := Editor^.GetNumLines;
  1433.   GetTextMetrics(DC, TextMetrics);
  1434.   with TextMetrics do
  1435.     LineHeight := tmHeight + tmExternalLeading;
  1436.   LinesPerPage := Size.Y div LineHeight;
  1437. end;
  1438.  
  1439. { TWindowPrintout }
  1440.  
  1441. constructor TWindowPrintout.Init(ATitle: PChar; AWindow: PWindow);
  1442. begin
  1443.   TPrintOut.Init(ATitle);
  1444.   Window := AWindow;
  1445.   Scale := True;
  1446. end;
  1447.  
  1448. procedure TWindowPrintout.PrintPage(Page: Word; var Rect: TRect;
  1449.   Flags: Word);
  1450. var
  1451.   PS: TPaintStruct;
  1452.   PrevMode: Integer;
  1453.   WindowSize: TRect;
  1454.   OldVExt, OldWExt: LongInt;
  1455. begin
  1456.  
  1457.   { Fake up a TPaintStruct to give the window banding information }
  1458.   with PS do
  1459.   begin
  1460.     rcPaint := Rect;
  1461.     fErase := False;
  1462.     fRestore := False;
  1463.   end;
  1464.  
  1465.   { Conditionally scale the DC to the window so the printout will
  1466.     resemble the window }
  1467.   if Scale then
  1468.   begin
  1469.     PrevMode := SetMapMode(DC, mm_Isotropic);
  1470.     GetClientRect(Window^.HWindow, WindowSize);
  1471.     OldVExt := SetViewportExt(DC, Size.X, Size.Y);
  1472.     with WindowSize do
  1473.     begin
  1474.       OldWExt := SetWindowExt(DC, right - left, bottom - top);
  1475.       IntersectClipRect(DC, left, top, right, bottom);
  1476.     end;
  1477.     DPtoLP(DC, PS.rcPaint, 2);
  1478.   end;
  1479.  
  1480.   { Call the window to paint itself }
  1481.   Window^.Paint(DC, PS);
  1482.  
  1483.   { Remove changes made to the DC }
  1484.   if Scale then
  1485.   begin
  1486.     SetWindowExt(DC, TPoint(OldWExt).X, TPoint(OldWExt).Y);
  1487.     SetViewportExt(DC, TPoint(OldVExt).X, TPoint(OldVExt).Y);
  1488.     SetMapMode(DC, PrevMode);
  1489.   end;
  1490. end;
  1491.  
  1492. { Do not bring up the print dialog since only one page is to be printed }
  1493.  
  1494. function TWindowPrintout.GetDialogInfo(var Pages: Integer): Boolean;
  1495. begin
  1496.   Pages := 0;
  1497.   GetDialogInfo := False;
  1498. end;
  1499.  
  1500. end.
  1501.