home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 9 / 09.iso / l / l040 / 13.ddi / RTLOWL.ZIP / OPRINTER.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-10-28  |  43.0 KB  |  1,502 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-,R-}
  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.     drUseSelection := False;
  697.     drStart := 1;
  698.     if Pages = 0 then drStop := MaxInt
  699.     else drStop := Pages;
  700.     drCopies := 1;
  701.     drCollate := True;
  702.   end;
  703.  
  704.   if UsePageRangeDlg then
  705.   begin
  706.     if Application^.ExecDialog(InitPrintDialog(ParentWin, PrnDC, Pages,
  707.         Printout^.GetSelection(SelStart, SelStop), PageRange)) <> id_OK then
  708.     begin
  709.       DeleteDC(PrnDC);
  710.       Exit;
  711.     end;
  712.   end;
  713.  
  714.   if PageRange.drCollate then
  715.     Copies := PageRange.drCopies
  716.   else
  717.   begin
  718.     Flags := PageRange.drCopies;
  719.     Escape(PrnDC, SetCopyCount, SizeOf(Flags), @Flags, nil);
  720.     Copies := 1;
  721.   end;
  722.  
  723.   with PageRange do
  724.     if drUseSelection then
  725.     begin
  726.       drStart := SelStart;
  727.       drStop := SelStop;
  728.     end;
  729.  
  730.   Dlg := Application^.MakeWindow(InitAbortDialog(ParentWin,
  731.     PrintOut^.Title));
  732.  
  733.   if Dlg = nil then
  734.   begin
  735.     DeleteDC(PrnDC);
  736.     Exit;
  737.   end;
  738.  
  739.   RestoreCursor;
  740.  
  741.   EnableWindow(ParentWin^.HWindow, False);
  742.  
  743.   AbortProcInst := MakeProcInstance(@AbortProc, hInstance);
  744.   Escape(PrnDC, SetAbortProc, 0, PChar(AbortProcInst), nil);
  745.  
  746.   { Only band if the user requests banding and the printer
  747.     supports banding }
  748.   Banding := PrintOut^.Banding and
  749.     (GetDeviceCaps(PrnDC, RasterCaps) or rc_Banding <> 0);
  750.  
  751.   if not Banding then
  752.   begin
  753.     { Set the banding rectangle to full page }
  754.     LongInt((@BandRect.left)^) := 0;
  755.     TPoint(Pointer(@BandRect.right)^) := PageSize;
  756.   end
  757.   else
  758.   begin
  759.     { Only use BandInfo if supported (note: using Flags as a temporary) }
  760.     Flags := BandInfo;
  761.     UseBandInfo :=
  762.       Escape(PrnDC, QueryEscSupport, SizeOf(Flags), @Flags, nil) <> 0;
  763.   end;
  764.  
  765.   Printout^.BeginPrinting;
  766.  
  767.   repeat
  768.     Flags := pf_Both;
  769.     if Banding then Flags := pf_Banding;
  770.     if PageRange.drUseSelection then
  771.       Flags := Flags or pf_Selection;
  772.     Error := Escape(PrnDC, StartDoc, StrLen(PrintOut^.Title),
  773.       PrintOut^.Title, nil);
  774.     if Error > 0 then
  775.     begin
  776.       Printout^.BeginDocument(PageRange.drStart, PageRange.drStop,
  777.         Flags);
  778.       PageNumber := PageRange.drStart;
  779.       repeat
  780.         if Banding then
  781.         begin
  782.           FirstBand := True;
  783.           Error := Escape(PrnDC, NextBand, 0, nil, @BandRect);
  784.         end;
  785.         repeat
  786.           { Call the abort proc between bands or pages }
  787.           TAbortProc(AbortProcInst)(PrnDC, 0);
  788.  
  789.           if Banding then
  790.           begin
  791.             CalcBandingFlags;
  792.             if (PrintOut^.ForceAllBands) and
  793.                (Flags and pf_Both = pf_Text) then
  794.               SetPixel(PrnDC, 0, 0, 0);
  795.           end;
  796.  
  797.           if Error > 0 then
  798.           begin
  799.             PrintOut^.PrintPage(PageNumber, BandRect, Flags);
  800.             if Banding then
  801.               Error := Escape(PrnDC, NextBand, 0, nil, @BandRect);
  802.           end;
  803.         until (Error <= 0) or not Banding or IsRectEmpty(BandRect);
  804.  
  805.         { NewFrame should only be called if not banding }
  806.         if (Error > 0) and not Banding then
  807.           Error := Escape(PrnDC, NewFrame, 0, nil, nil);
  808.  
  809.         Inc(PageNumber);
  810.       until (Error <= 0) or not PrintOut^.HasNextPage(PageNumber) or
  811.         (PageNumber > PageRange.drStop);
  812.  
  813.       Printout^.EndDocument;
  814.  
  815.       { Tell GDI the document is finished }
  816.       if Error > 0 then
  817.         if Banding and UserAbort then
  818.           Escape(PrnDC, AbortDoc, 0, nil, nil)
  819.         else
  820.           Escape(PrnDC, EndDoc, 0, nil, nil);
  821.     end;
  822.     Dec(Copies);
  823.   until (Copies = 0) or UserAbort;
  824.  
  825.   Printout^.EndPrinting;
  826.  
  827.   { Reset copies }
  828.   if not PageRange.drCollate then
  829.   begin
  830.     Flags := 1;
  831.     Escape(PrnDC, SetCopyCount, SizeOf(Flags), @Flags, nil);
  832.   end;
  833.  
  834.   { Free allocated resources }
  835.   FreeProcInstance(AbortProcInst);
  836.   EnableWindow(ParentWin^.HWindow, True);
  837.   Dispose(Dlg, Done);
  838.   DeleteDC(PrnDC);
  839.  
  840.   if Error and sp_NotReported <> 0 then
  841.     ReportError(PrintOut);
  842.  
  843.   Print := (Error > 0) and not UserAbort;
  844.  
  845.   UserAbort := False;
  846. end;
  847.  
  848. function TPrinter.InitAbortDialog(Parent: PWindowsObject;
  849.   Title: PChar): PDialog;
  850. var
  851.   Dlg: PDialog;
  852.   Template: PChar;
  853. begin
  854.   if BWCCClassNames then Template := 'AbortDialogB'
  855.   else Template := 'AbortDialog';
  856.   InitAbortDialog := New(PPrinterAbortDlg, Init(Parent, Template, Title,
  857.     Device, Port));
  858. end;
  859.  
  860. function TPrinter.InitPrintDialog(Parent: PWindowsObject; PrnDC: HDC;
  861.   Pages: Integer; SelAllowed: Boolean; var Data: TPrintDialogRec): PDialog;
  862. var
  863.   Template: PChar;
  864. begin
  865.   if BWCCClassNames then Template := 'PrintDialogB'
  866.   else Template := 'PrintDialog';
  867.   InitPrintDialog := New(PPrintDialog, Init(Parent, Template, PrnDC, Pages,
  868.     @Self, SelAllowed, Data));
  869. end;
  870.  
  871. function TPrinter.InitSetupDialog(Parent: PWindowsObject): PDialog;
  872. var
  873.   Template: PChar;
  874. begin
  875.   if BWCCClassNames then Template := 'PrinterSetupB'
  876.   else Template := 'PrinterSetup';
  877.   InitSetupDialog := New(PPrinterSetupDlg, Init(Parent, Template,
  878.     @Self));
  879. end;
  880.  
  881. procedure TPrinter.Setup(Parent: PWindowsObject);
  882. begin
  883.   if Status = ps_Ok then
  884.     Application^.ExecDialog(InitSetupDialog(Parent));
  885. end;
  886.  
  887. procedure TPrinter.ReportError(PrintOut: PPrintOut);
  888. var
  889.   ErrorMsg: array[0..80] of Char;
  890.   ErrorCaption: array[0..80] of Char;
  891.   ErrorTemplate: array[0..40] of Char;
  892.   ErrorStr: array[0..40] of Char;
  893.   ErrorId: Word;
  894.   Msg, Title: PChar;
  895. begin
  896.   case Error of
  897.     sp_AppAbort:    ErrorId := sr_PrnCancel;
  898.     sp_Error:       ErrorId := sr_GenError;
  899.     sp_OutOfDisk:   ErrorId := sr_OutOfDisk;
  900.     sp_OutOfMemory: ErrorId := sr_OutOfMemory;
  901.     sp_UserAbort:   ErrorId := sr_PrnMgrAbort;
  902.   else
  903.     Exit;
  904.   end;
  905.  
  906.   LoadString(hInstance, sr_ErrorTemplate, ErrorTemplate,
  907.     SizeOf(ErrorTemplate));
  908.   LoadString(hInstance, ErrorId, ErrorStr, SizeOf(ErrorStr));
  909.   Title := PrintOut^.Title;
  910.   Msg := ErrorStr;
  911.   WVSPrintF(ErrorMsg, ErrorTemplate, Title);
  912.   LoadString(hInstance, sr_ErrorCaption, ErrorCaption,
  913.     SizeOf(ErrorCaption));
  914.   MessageBox(0, ErrorMsg, ErrorCaption, mb_Ok or mb_IconStop);
  915. end;
  916.  
  917. { TPrinterSetupDlg ------------------------------------------------- }
  918.  
  919. { TPrinterSetupDlg assumes the template passed has a ComboBox with
  920.   the control ID of 100 and a "Setup" button with id 101 }
  921.  
  922. const
  923.   pdStrWidth = 80;
  924.  
  925. type
  926.   PTransferRec = ^TTransferRec;
  927.   TTransferRec = record
  928.     Strings: PCollection;
  929.     Selected: array[0..0] of Char;
  930.   end;
  931.  
  932.   PDeviceRec = ^TDeviceRec;
  933.   TDeviceRec = record
  934.     Driver, Device, Port: PChar;
  935.   end;
  936.  
  937.   PDeviceCollection = ^TDeviceCollection;
  938.   TDeviceCollection = object(TCollection)
  939.     procedure FreeItem(P: Pointer); virtual;
  940.   end;
  941.  
  942. procedure TDeviceCollection.FreeItem(P: Pointer);
  943. begin
  944.   with PDeviceRec(P)^ do
  945.   begin
  946.     StrDispose(Driver);
  947.     StrDispose(Device);
  948.     StrDispose(Port);
  949.   end;
  950.   Dispose(PDeviceRec(P));
  951. end;
  952.  
  953. constructor TPrinterSetupDlg.Init(AParent: PWindowsObject;
  954.   TemplateName: PChar; APrinter: PPrinter);
  955. var
  956.   tmp: PComboBox;
  957.   Devices,                                  { List of devices from the
  958.                                               WIN.INI }
  959.   Device: PChar;                            { Current device }
  960.   DevicesSize: Integer;                     { Amount of bytes allocated
  961.                                               to store 'devices' }
  962.   Driver,                                   { Name of the driver for the
  963.                                               device }
  964.   Port: PChar;                              { Name of the port for the
  965.                                               device }
  966.   DriverLine: array[0..pdStrWidth] of Char; { Device line from WIN.INI }
  967.   LineCur: PChar;                           { FetchStr pointer into
  968.                                               DriverLine }
  969.   DriverStr: array[0..pdStrWidth] of Char;  { Text being built for display }
  970.   StrCur: PChar;                            { Temp pointer used for copying
  971.                                               Port into the line }
  972.   StrCurSize: Integer;                      { Room left in DriverStr to
  973.                                               copy Port }
  974.   DevRec: PDeviceRec;                       { Record pointer built to
  975.                                               store in DeviceCollection }
  976. begin
  977.   TDialog.Init(AParent, TemplateName);
  978.   tmp := New(PComboBox, InitResource(@Self, id_Combo, pdStrWidth));
  979.   GetMem(TransferBuffer, SizeOf(PCollection) + pdStrWidth);
  980.   PTransferRec(TransferBuffer)^.Strings := New(PStrCollection,
  981.     Init(5, 5));
  982.   Printer := APrinter;
  983.   DeviceCollection := New(PDeviceCollection, Init(5, 5));
  984.  
  985.   if MaxAvail div 2 > 4096 then DevicesSize := 4096
  986.   else DevicesSize := MaxAvail div 2;
  987.   GetMem(Devices, DevicesSize);
  988.  
  989.   { Save initial values of printer for Cancel }
  990.   OldDevice := StrNew(Printer^.Device);
  991.   OldDriver := StrNew(Printer^.Driver);
  992.   OldPort := StrNew(Printer^.Port);
  993.  
  994.   with PTransferRec(TransferBuffer)^ do
  995.   begin
  996.     { Get a list of devices from WIN.INI.  Stored in the form of
  997.       <device 1>#0<device 2>#0...<driver n>#0#0
  998.     }
  999.     GetProfileString('devices', nil, '', Devices, DevicesSize);
  1000.  
  1001.     Device := Devices;
  1002.     while Device^ <> #0 do
  1003.     begin
  1004.       GetProfileString('devices', Device, '', DriverLine,
  1005.         SizeOf(DriverLine) - 1);
  1006.  
  1007.       FormDriverStr(DriverStr, SizeOf(DriverStr) - 1,Device, '');
  1008.  
  1009.       { Get driver portion of DeviceLine }
  1010.       LineCur := DriverLine;
  1011.       Driver := FetchStr(LineCur);
  1012.  
  1013.       { Copy the port information from the line }
  1014.       (*   This code is complicated because the device line is of
  1015.           the form:
  1016.            <device name> = <driver name> , <port> { , <port> }
  1017.           where port (in {}) can be repeated. *)
  1018.  
  1019.       StrCur := @DriverStr[StrLen(DriverStr)];
  1020.       StrCurSize := SizeOf(DriverStr) - StrLen(DriverStr) - 1;
  1021.       Port := FetchStr(LineCur);
  1022.       while Port^ <> #0 do
  1023.       begin
  1024.         StrLCopy(StrCur, Port, StrCurSize);
  1025.         Strings^.Insert(StrNew(DriverStr));
  1026.         New(DevRec);
  1027.         DevRec^.Device := StrNew(Device);
  1028.         DevRec^.Driver := StrNew(Driver);
  1029.         DevRec^.Port := StrNew(Port);
  1030.         DeviceCollection^.AtInsert(Strings^.IndexOf(@DriverStr), DevRec);
  1031.         Port := FetchStr(LineCur);
  1032.       end;
  1033.       Inc(Device, StrLen(Device) + 1);
  1034.     end;
  1035.     FreeMem(Devices, DevicesSize);
  1036.  
  1037.     { Set the current selection to Printer's current device }
  1038.     FormDriverStr(Selected, pdStrWidth, Printer^.Device, Printer^.Port);
  1039.   end;
  1040. end;
  1041.  
  1042. destructor TPrinterSetupDlg.Done;
  1043. begin
  1044.   StrDispose(OldDevice);
  1045.   StrDispose(OldDriver);
  1046.   StrDispose(OldPort);
  1047.   Dispose(DeviceCollection, Done);
  1048.   Dispose(PTransferRec(TransferBuffer)^.Strings, Done);
  1049.   FreeMem(TransferBuffer, SizeOf(PCollection) + pdStrWidth);
  1050.   TDialog.Done;
  1051. end;
  1052.  
  1053. procedure TPrinterSetupDlg.TransferData(TransferFlag: Word);
  1054. var
  1055.   DevRec: PDeviceRec;
  1056. begin
  1057.   TDialog.TransferData(TransferFlag);
  1058.   if TransferFlag = tf_GetData then
  1059.     with PTransferRec(TransferBuffer)^ do
  1060.       { Use the current selection to set Printer }
  1061.       with PDeviceRec(DeviceCollection^.At(Strings^.IndexOf(@Selected)))^ do
  1062.         { Set the printer to the new device }
  1063.         Printer^.SetDevice(Device, Driver, Port);
  1064. end;
  1065.  
  1066. procedure TPrinterSetupDlg.IDSetup(var Msg: TMessage);
  1067. begin
  1068.   TransferData(tf_GetData);
  1069.   Printer^.Configure(@Self);
  1070. end;
  1071.  
  1072. procedure TPrinterSetupDlg.Cancel(var Msg: TMessage);
  1073. begin
  1074.   TDialog.Cancel(Msg);
  1075.   { Restore old settings, just in case the user pressed the Setup button }
  1076.   if OldDriver = nil then Printer^.ClearDevice
  1077.   else Printer^.SetDevice(OldDevice, OldDriver, OldPort);
  1078. end;
  1079.  
  1080. { TNumeric }
  1081.  
  1082. type
  1083.   PNumeric = ^TNumeric;
  1084.   TNumeric = object(TEdit)
  1085.     Min, Max: LongInt;
  1086.     constructor Init(AParent: PWindowsObject; AnId, X, Y, W, H: Integer;
  1087.       AMin, AMax: Integer; Digits: Integer);
  1088.     constructor InitResource(AParent: PWindowsObject; Id: Integer;
  1089.       AMin, AMax: Integer; Digits: Integer);
  1090.     function CanClose: Boolean; virtual;
  1091.     function GetValue(var Value: Integer): Boolean;
  1092.     procedure SetRange(AMin, AMax: Integer);
  1093.     procedure SetValue(Value: Integer);
  1094.     procedure WMChar(var Msg: TMessage);
  1095.       virtual wm_First + wm_Char;
  1096.   end;
  1097.  
  1098. constructor TNumeric.Init(AParent: PWindowsObject; AnId, X, Y, W,
  1099.   H: Integer; AMin, AMax: Integer; Digits: Integer);
  1100. begin
  1101.   TEdit.Init(AParent, AnId, '', X, Y, W, H, Digits + 1, False);
  1102.   Min := AMin;
  1103.   Max := AMax;
  1104. end;
  1105.  
  1106. constructor TNumeric.InitResource(AParent: PWindowsObject; Id: Integer;
  1107.   AMin, AMax: Integer; Digits: Integer);
  1108. begin
  1109.   TEdit.InitResource(AParent, Id, Digits + 1);
  1110.   Min := AMin;
  1111.   Max := AMax;
  1112. end;
  1113.  
  1114. function TNumeric.CanClose: Boolean;
  1115. var
  1116.   Value: Integer;
  1117.   Valid: Boolean;
  1118.   Text: array[0..255] of Char;
  1119.   P: array[0..1] of LongInt;
  1120. begin
  1121.   Valid := not IsWindowEnabled(HWindow) or
  1122.     (GetValue(Value) and (Value >= Min) and (Value <= Max));
  1123.   if not Valid then
  1124.   begin
  1125.     P[0] := Min;
  1126.     P[1] := Max;
  1127.     WVSPrintF(Text, 'Value not within range (%ld-%ld).', P);
  1128.     MessageBox(HWindow, Text, 'Invalid Range', mb_IconStop or mb_Ok);
  1129.     SetSelection(0, MaxInt);
  1130.     SetFocus(HWindow);
  1131.   end;
  1132.   CanClose := Valid;
  1133. end;
  1134.  
  1135. function TNumeric.GetValue(var Value: Integer): Boolean;
  1136. var
  1137.   Text: array[0..255] of Char;
  1138.   Code: Integer;
  1139. begin
  1140.   GetText(Text, SizeOf(Text));
  1141.   Val(Text, Value, Code);
  1142.   GetValue := Code = 0;
  1143. end;
  1144.  
  1145. procedure TNumeric.SetRange(AMin, AMax: Integer);
  1146. begin
  1147.   Min := AMin;
  1148.   Max := AMax;
  1149. end;
  1150.  
  1151. procedure TNumeric.SetValue(Value: Integer);
  1152. var
  1153.   Text: array[0..20] of Char;
  1154. begin
  1155.   Str(Value, Text);
  1156.   SetText(Text);
  1157. end;
  1158.  
  1159. procedure TNumeric.WMChar(var Msg: TMessage);
  1160. begin
  1161.   if not (Char(Msg.wParamLo) in ['A'..'Z','a'..'z',',','.','<','>',
  1162.     '/','?','~','`','!','@','#','$','%','^','&','*','(',')','_','=',
  1163.     '{','}','[',']','|','\',';',':','"']) then
  1164.     DefWndProc(Msg)
  1165.   else MessageBeep(0);
  1166. end;
  1167.  
  1168. { TSelRadio }
  1169.  
  1170. type
  1171.   PSelRadio = ^TSelRadio;
  1172.   TSelRadio = object(TRadioButton)
  1173.     Enbl: Boolean;
  1174.     Controls: PCollection;
  1175.     constructor InitResource(AParent: PWindowsObject; ResourceID: Word;
  1176.       AEnbl: Boolean; AControls: PCollection);
  1177.     procedure BNClicked(var Msg: TMessage);
  1178.       virtual nf_First + bn_Clicked;
  1179.   end;
  1180.  
  1181. constructor TSelRadio.InitResource(AParent: PWindowsObject;
  1182.   ResourceID: Word; AEnbl: Boolean; AControls: PCollection);
  1183. begin
  1184.   TRadioButton.InitResource(AParent, ResourceId);
  1185.   Enbl := AEnbl;
  1186.   Controls := AControls;
  1187. end;
  1188.  
  1189. { Assumes the Controls collection contains PWindowsObjects }
  1190.  
  1191. procedure TSelRadio.BNClicked(var Msg: TMessage);
  1192.  
  1193.   procedure DoEnableDisable(P: PWindowsObject); far;
  1194.   begin
  1195.     if Enbl then P^.Enable else P^.Disable;
  1196.   end;
  1197.  
  1198. begin
  1199.   TRadioButton.BNClicked(Msg);
  1200.   Controls^.ForEach(@DoEnableDisable);
  1201.   if Enbl then PWindowsObject(Controls^.At(0))^.Focus;
  1202. end;
  1203.  
  1204. { TPrintDialog }
  1205.  
  1206. constructor TPrintDialog.Init(AParent: PWindowsObject; Template: PChar;
  1207.   APrnDC: HDC; APages: Integer; APrinter: PPrinter; ASelAllowed: Boolean;
  1208.   var Data: TPrintDialogRec);
  1209. var
  1210.   P: PWindowsObject;
  1211.  
  1212.   function QLog10(X: Integer): Integer;
  1213.   var
  1214.     I, L: Integer;
  1215.   begin
  1216.     I := 1;
  1217.     L := 0;
  1218.     if X >= 10000 then QLog10 := 5
  1219.     else
  1220.     begin
  1221.       repeat
  1222.         I := I * 10;
  1223.         Inc(L);
  1224.       until I > X;
  1225.       QLog10 := L;
  1226.     end;
  1227.   end;
  1228.  
  1229. begin
  1230.   TDialog.Init(AParent, Template);
  1231.   Printer := APrinter;
  1232.   PData := @Data;
  1233.   PrnDC := APrnDC;
  1234.   Pages := APages;
  1235.   SelAllowed := ASelAllowed;
  1236.  
  1237.   PrinterName := New(PStatic, InitResource(@Self, id_PrinterName, 0));
  1238.   Controls := New(PCollection, Init(4, 4));
  1239.   if Pages <> 0 then
  1240.   begin
  1241.     FromPage := New(PNumeric, InitResource(@Self, id_From, 1, Pages,
  1242.       QLog10(Pages)));
  1243.     ToPage := New(PNumeric, InitResource(@Self, id_To, 1, Pages,
  1244.       QLog10(Pages)));
  1245.   end
  1246.   else
  1247.   begin
  1248.     FromPage := New(PNumeric, InitResource(@Self, id_From, 1, 32767, 0));
  1249.     ToPage := New(PNumeric, InitResource(@Self, id_To, 1, 32767, 0));
  1250.   end;
  1251.   Controls^.Insert(FromPage);
  1252.   Controls^.Insert(ToPage);
  1253.   Controls^.Insert(New(PStatic, InitResource(@Self, id_FromText, 0)));
  1254.   Controls^.Insert(New(PStatic, InitResource(@Self, id_ToText, 0)));
  1255.   AllBtn := New(PSelRadio, InitResource(@Self, id_All, False, Controls));
  1256.   SelectBtn := New(PSelRadio, InitResource(@Self, id_Selection, False,
  1257.     Controls));
  1258.   PageBtn := New(PSelRadio, InitResource(@Self, id_Pages, True, Controls));
  1259.   Copies := New(PNumeric, InitResource(@Self, id_Copies, 1, 999, 3));
  1260.   Collate := New(PCheckBox, InitResource(@Self, id_Collate));
  1261. end;
  1262.  
  1263. procedure TPrintDialog.SetupWindow;
  1264. var
  1265.   NameText: array[0..80] of Char;
  1266. begin
  1267.   TDialog.SetupWindow;
  1268.   with Printer^ do
  1269.     FormDriverStr(NameText, SizeOf(NameText), Device, Port);
  1270.   PrinterName^.SetText(NameText);
  1271. end;
  1272.  
  1273. procedure TPrintDialog.TransferData(Direction: Word);
  1274. var
  1275.   Esc: Integer;
  1276.   Val: LongInt;
  1277.   Msg: TMessage;
  1278. begin
  1279.   case Direction of
  1280.     tf_SetData:
  1281.       with PData^ do
  1282.       begin
  1283.         Collate^.SetCheck(Word(drCollate));
  1284.         Esc := SetCopyCount;
  1285.         if Escape(PrnDC, QueryEscSupport, SizeOf(Esc), @Esc, @Esc) = 0 then
  1286.           Collate^.Disable;
  1287.         PNumeric(Copies)^.SetValue(drCopies);
  1288.         AllBtn^.SetCheck(bf_Checked);
  1289.         AllBtn^.BNClicked(Msg);
  1290.         if not SelAllowed then SelectBtn^.Disable;
  1291.         if Pages = 1 then
  1292.           PageBtn^.Disable
  1293.         else
  1294.         begin
  1295.           if Pages <> 0 then
  1296.           begin
  1297.             PNumeric(FromPage)^.SetValue(drStart);
  1298.             PNumeric(ToPage)^.SetValue(drStop);
  1299.           end;
  1300.         end;
  1301.       end;
  1302.     tf_GetData:
  1303.       with PData^ do
  1304.       begin
  1305.         drCollate := Boolean(Collate^.GetCheck);
  1306.         PNumeric(Copies)^.GetValue(drCopies);
  1307.         if SelectBtn^.GetCheck = bf_Checked then
  1308.           drUseSelection := True
  1309.         else
  1310.         begin
  1311.           drUseSelection := False;
  1312.  
  1313.           if PageBtn^.GetCheck = bf_Checked then
  1314.           begin
  1315.             PNumeric(FromPage)^.GetValue(drStart);
  1316.             PNumeric(ToPage)^.GetValue(drStop);
  1317.           end;
  1318.         end;
  1319.       end;
  1320.   end;
  1321. end;
  1322.  
  1323. procedure TPrintDialog.IDSetup(var Msg: TMessage);
  1324. begin
  1325.   Printer^.Configure(@Self);
  1326. end;
  1327.  
  1328. { TEditPrintout }
  1329.  
  1330. { This object will print-out the contents of a TEdit control }
  1331.  
  1332. constructor TEditPrintout.Init(AEditor: PEdit; ATitle: PChar);
  1333. begin
  1334.   TPrintout.Init(ATitle);
  1335.   Editor := AEditor;
  1336.  
  1337.   { The following are calculated by SetPrintParams which is called
  1338.     before any other methods are called. }
  1339.   LinesPerPage := 0;
  1340.   NumLines := 0;
  1341.   LineHeight := 0;
  1342.   StartPos := 0;
  1343.   StopPos := 0;
  1344.   StartLine := 0;
  1345.   StopLine := 0;
  1346. end;
  1347.  
  1348. procedure TEditPrintout.BeginDocument(StartPage, EndPage: Integer;
  1349.   Flags: Word);
  1350. begin
  1351.   if Flags and pf_Selection = 0 then
  1352.   begin
  1353.     { not using the selection, print everything }
  1354.     StartLine := 0;
  1355.     StopLine := NumLines - 1;
  1356.     StartPos := 0;
  1357.     StopPos := 32767;
  1358.   end; { else leave values set by GetSelection }
  1359. end;
  1360.  
  1361. function TEditPrintout.GetSelection(var Start, Stop: Integer): Boolean;
  1362. begin
  1363.   Editor^.GetSelection(StartPos, StopPos);
  1364.   if StartPos = StopPos then GetSelection := False
  1365.   else
  1366.   begin
  1367.     with Editor^ do
  1368.     begin
  1369.       StartLine := GetLineFromPos(StartPos);
  1370.       StopLine := GetLineFromPos(StopPos);
  1371.       Start := 1;
  1372.       Stop := (StopLine - StartLine) div LinesPerPage + 1;
  1373.     end;
  1374.     GetSelection := True;
  1375.   end;
  1376. end;
  1377.  
  1378. function TEditPrintout.GetDialogInfo(var Pages: Integer): Boolean;
  1379. begin
  1380.   Pages := NumLines div LinesPerPage + 1;
  1381.   GetDialogInfo := True;
  1382. end;
  1383.  
  1384. procedure TEditPrintout.PrintPage(Page: Word; var Rect: TRect; Flags: Word);
  1385. var
  1386.   LineBuffer: array[0..255] of Char;
  1387.   I: Integer;
  1388.   FirstLine: Integer;
  1389.   CurLine: Integer;
  1390.   Len: Integer;
  1391.   XOff: Integer;
  1392. begin
  1393.   FirstLine := StartLine + (Page - 1) * LinesPerPage;
  1394.   for I := 0 to LinesPerPage - 1 do
  1395.   begin
  1396.     CurLine := I + FirstLine;
  1397.     if CurLine > StopLine then Exit;
  1398.  
  1399.     XOff := 0;
  1400.  
  1401.     with Editor^ do
  1402.     begin
  1403.       GetLine(LineBuffer, SizeOf(LineBuffer), CurLine);
  1404.  
  1405.       { Order of these next lines is important since the beginning
  1406.         and ending selections can be on the same line.  We don't want
  1407.         to move the text before we have cut off the end of the text. }
  1408.       if (CurLine = StopLine) and (StopPos < StrLen(LineBuffer)) then
  1409.         LineBuffer[StopPos - GetLineIndex(CurLine)] := #0;
  1410.       if CurLine = StartLine then
  1411.       begin
  1412.         Len := StartPos - GetLineIndex(CurLine);
  1413.         XOff := GetTextExtent(DC, LineBuffer, Len);
  1414.         StrCopy(LineBuffer, @LineBuffer[Len]);
  1415.       end;
  1416.     end;
  1417.     TextOut(DC, XOff, I * LineHeight, LineBuffer, StrLen(LineBuffer));
  1418.   end;
  1419. end;
  1420.  
  1421. function TEditPrintout.HasNextPage(Page: Word): Boolean;
  1422. begin
  1423.   { Always a next page.  Will never be asked for a page beyond what
  1424.     is calculated by Paginate }
  1425.   HasNextPage := True;
  1426. end;
  1427.  
  1428. procedure TEditPrintout.SetPrintParams(ADC: HDC; ASize: TPoint);
  1429. var
  1430.   TextMetrics: TTextMetric;
  1431. begin
  1432.   TPrintout.SetPrintParams(ADC, ASize);
  1433.   NumLines := Editor^.GetNumLines;
  1434.   GetTextMetrics(DC, TextMetrics);
  1435.   with TextMetrics do
  1436.     LineHeight := tmHeight + tmExternalLeading;
  1437.   LinesPerPage := Size.Y div LineHeight;
  1438. end;
  1439.  
  1440. { TWindowPrintout }
  1441.  
  1442. constructor TWindowPrintout.Init(ATitle: PChar; AWindow: PWindow);
  1443. begin
  1444.   TPrintOut.Init(ATitle);
  1445.   Window := AWindow;
  1446.   Scale := True;
  1447. end;
  1448.  
  1449. procedure TWindowPrintout.PrintPage(Page: Word; var Rect: TRect;
  1450.   Flags: Word);
  1451. var
  1452.   PS: TPaintStruct;
  1453.   PrevMode: Integer;
  1454.   WindowSize: TRect;
  1455.   OldVExt, OldWExt: LongInt;
  1456. begin
  1457.  
  1458.   { Fake up a TPaintStruct to give the window banding information }
  1459.   with PS do
  1460.   begin
  1461.     rcPaint := Rect;
  1462.     fErase := False;
  1463.     fRestore := False;
  1464.   end;
  1465.  
  1466.   { Conditionally scale the DC to the window so the printout will
  1467.     resemble the window }
  1468.   if Scale then
  1469.   begin
  1470.     PrevMode := SetMapMode(DC, mm_Isotropic);
  1471.     GetClientRect(Window^.HWindow, WindowSize);
  1472.     OldVExt := SetViewportExt(DC, Size.X, Size.Y);
  1473.     with WindowSize do
  1474.     begin
  1475.       OldWExt := SetWindowExt(DC, right - left, bottom - top);
  1476.       IntersectClipRect(DC, left, top, right, bottom);
  1477.     end;
  1478.     DPtoLP(DC, PS.rcPaint, 2);
  1479.   end;
  1480.  
  1481.   { Call the window to paint itself }
  1482.   Window^.Paint(DC, PS);
  1483.  
  1484.   { Remove changes made to the DC }
  1485.   if Scale then
  1486.   begin
  1487.     SetWindowExt(DC, TPoint(OldWExt).X, TPoint(OldWExt).Y);
  1488.     SetViewportExt(DC, TPoint(OldVExt).X, TPoint(OldVExt).Y);
  1489.     SetMapMode(DC, PrevMode);
  1490.   end;
  1491. end;
  1492.  
  1493. { Do not bring up the print dialog since only one page is to be printed }
  1494.  
  1495. function TWindowPrintout.GetDialogInfo(var Pages: Integer): Boolean;
  1496. begin
  1497.   Pages := 0;
  1498.   GetDialogInfo := False;
  1499. end;
  1500.  
  1501. end.
  1502.