home *** CD-ROM | disk | FTP | other *** search
-
- {*******************************************************}
- { }
- { Turbo Pascal for Windows Run-time Library }
- { ObjectWindows Unit }
- { }
- { Copyright (c) 1991 Borland International }
- { }
- {*******************************************************}
-
- unit OPrinter;
-
- {$R OPRINTER.RES}
-
- {$S-,R-}
-
- interface
-
- uses WinTypes, WinProcs, Objects, OWindows, ODialogs;
-
- { TPrinter states }
- const
- ps_Ok = 0;
- ps_InvalidDevice = -1; { Device parameters (to set device)
- invalid }
- ps_Unassociated = -2; { Object not associated with a printer }
-
- { TPrintOut flags }
- const
- pf_Graphics = $01; { Current band only accepts text }
- pf_Text = $02; { Current band only accepts graphics }
- pf_Both = $03; { Current band accepts both text and
- graphics }
- pf_Banding = $04; { Set the printout is being banded }
- pf_Selection = $08; { Printing the selection }
-
- type
- PPrintDialogRec = ^TPrintDialogRec;
- TPrintDialogRec = record
- drStart: Integer; { Starting page }
- drStop: Integer; { Ending page }
- drCopies: Integer; { Number of copies to print }
- drCollate: Boolean; { Tell the printer to collate copies }
- drUseSelection: Boolean; { Use seletion instead of Start, Stop }
- end;
-
- { TPrintOut represents the physical printed document which is to
- sent to a printer to be printed. TPrintOut does the rendering of
- the document onto the printer. For every document, or document
- type, a cooresponding TPrintOut class should be created. }
-
- type
- PPrintOut = ^TPrintOut;
- TPrintOut = object(TObject)
- Title: PChar;
- Banding: Boolean;
- ForceAllBands: Boolean;
- DC: HDC;
- Size: TPoint;
- constructor Init(ATitle: PChar);
- destructor Done; virtual;
- procedure BeginDocument(StartPage, EndPage: Integer;
- Flag: Word); virtual;
- procedure BeginPrinting; virtual;
- procedure EndDocument; virtual;
- procedure EndPrinting; virtual;
- function GetDialogInfo(var Pages: Integer): Boolean; virtual;
- function GetSelection(var Start, Stop: Integer): Boolean; virtual;
- function HasNextPage(Page: Word): Boolean; virtual;
- procedure PrintPage(Page: Word; var Rect: TRect; Flags: Word); virtual;
- procedure SetPrintParams(ADC: HDC; ASize: TPoint); virtual;
- end;
-
- { TPrinter represent the physical printer device. To print a
- TPrintOut, send the TPrintOut to the TPrinter's Print method. }
-
- PPrinter = ^TPrinter;
- TPrinter = object(TObject)
- Device, Driver, Port: PChar; { Printer device description }
- Status: Integer; { Device status, error is <> ps_Ok }
- Error: Integer; { < 0 if error occured during print }
- DeviceModule: THandle; { Handle to printer driver module }
- DeviceMode: TDeviceMode; { Function pointer to DevMode }
- ExtDeviceMode: TExtDeviceMode; { Function pointer to ExtDevMode }
- DevSettings: PDevMode; { Local copy of printer settings }
- DevSettingSize: Integer; { Size of the printer settings }
-
- constructor Init;
- destructor Done; virtual;
- procedure ClearDevice;
- procedure Configure(Window: PWindowsObject);
- function GetDC: HDC; virtual;
- function InitAbortDialog(Parent: PWindowsObject;
- Title: PChar): PDialog; virtual;
- function InitPrintDialog(Parent: PWindowsObject; PrnDC: HDC;
- Pages: Integer; SelAllowed: Boolean;
- var Data: TPrintDialogRec): PDialog; virtual;
- function InitSetupDialog(Parent: PWindowsObject): PDialog; virtual;
- procedure ReportError(PrintOut: PPrintOut); virtual;
- procedure SetDevice(ADevice, ADriver, APort: PChar);
- procedure Setup(Parent: PWindowsObject);
- function Print(ParentWin: PWindowsObject; PrintOut: PPrintOut): Boolean;
- end;
-
- { TPrinterSetupDlg is a dialog to modify which printer a TPrinter
- object is attached to. It displays the all the active printers
- in the system allowing the user to select the desired printer.
- The dialog also allow the user to call up the printer's
- "setup" dialog for further configuration of the printer. }
-
- const
- id_Combo = 100;
- id_Setup = 101;
-
- type
- PPrinterSetupDlg = ^TPrinterSetupDlg;
- TPrinterSetupDlg = object(TDialog)
- Printer: PPrinter;
- constructor Init(AParent: PWindowsObject; TemplateName: PChar;
- APrinter: PPrinter);
- destructor Done; virtual;
- procedure TransferData(TransferFlag: Word); virtual;
- procedure IDSetup(var Msg: TMessage);
- virtual id_First + id_Setup;
- procedure Cancel(var Msg: TMessage);
- virtual id_First + id_Cancel;
- private
- OldDevice, OldDriver, OldPort: PChar;
- DeviceCollection: PCollection;
- end;
-
- const
- id_Title = 101;
- id_Device = 102;
- id_Port = 103;
-
- type
- PPrinterAbortDlg = ^TPrinterAbortDlg;
- TPrinterAbortDlg = object(TDialog)
- constructor Init(AParent: PWindowsObject; Template, Title,
- Device, Port: PChar);
- procedure SetupWindow; virtual;
- procedure WMCommand(var Msg: TMessage);
- virtual wm_First + wm_Command;
- end;
-
- const
- id_PrinterName = 102;
- id_All = 103;
- id_Selection = 104;
- id_Pages = 105;
- id_FromText = 106;
- id_From = 107;
- id_ToText = 108;
- id_To = 109;
- id_PrintQuality = 110;
- id_Copies = 111;
- id_Collate = 112;
-
- type
- PPrintDialog = ^TPrintDialog;
- TPrintDialog = object(TDialog)
- Printer: PPrinter;
- PData: PPrintDialogRec;
- PrinterName: PStatic;
- Pages: Integer;
- Controls: PCollection;
- AllBtn, SelectBtn, PageBtn: PRadioButton;
- FromPage, ToPage: PEdit;
- Copies: PEdit;
- Collate: PCheckBox;
- PrnDC: HDC;
- SelAllowed: Boolean;
- constructor Init(AParent: PWindowsObject; Template: PChar; APrnDC: HDC;
- APages: Integer; APrinter: PPrinter; ASelAllowed: Boolean;
- var Data: TPrintDialogRec);
- procedure SetupWindow; virtual;
- procedure TransferData(Direction: Word); virtual;
- procedure IDSetup(var Msg: TMessage);
- virtual id_First + id_Setup;
- end;
-
- type
- PEditPrintout = ^TEditPrintout;
- TEditPrintout = object(TPrintout)
- Editor: PEdit;
- NumLines: Integer;
- LinesPerPage: Integer;
- LineHeight: Integer;
- StartPos: Integer;
- StopPos: Integer;
- StartLine: Integer;
- StopLine: Integer;
- constructor Init(AEditor: PEdit; ATitle: PChar);
- procedure BeginDocument(StartPage, EndPage: Integer;
- Flags: Word); virtual;
- function GetDialogInfo(var Pages: Integer): Boolean; virtual;
- function GetSelection(var Start, Stop: Integer): Boolean; virtual;
- function HasNextPage(Page: Word): Boolean; virtual;
- procedure PrintPage(Page: Word; var Rect: TRect; Flags: Word); virtual;
- procedure SetPrintParams(ADC: HDC; ASize: TPoint); virtual;
- end;
-
- type
- PWindowPrintout = ^TWindowPrintout;
- TWindowPrintout = object(TPrintOut)
- Window: PWindow;
- Scale: Boolean;
- constructor Init(ATitle: PChar; AWindow: PWindow);
- function GetDialogInfo(var Pages: Integer): Boolean; virtual;
- procedure PrintPage(Page: Word; var Rect: TRect; Flags: Word); virtual;
- end;
-
- implementation
-
- uses Strings;
-
- const
- sr_On = 32512;
- sr_ErrorTemplate = 32513;
- sr_OutOfMemory = 32514;
- sr_OutOfDisk = 32515;
- sr_PrnCancel = 32516;
- sr_PrnMgrAbort = 32517;
- sr_GenError = 32518;
- sr_ErrorCaption = 32519;
-
- const
- UserAbort: Boolean = False;
-
- { FormDriverStr ---------------------------------------------------- }
-
- procedure FormDriverStr(DriverStr: PChar; MaxLen: Integer;
- Device, Port: PChar);
- begin
- StrLCopy(DriverStr, Device, MaxLen);
- LoadString(hInstance, sr_On, @DriverStr[StrLen(DriverStr)],
- MaxLen - StrLen(DriverStr) - 1);
- StrLCat(DriverStr, Port, MaxLen);
- end;
-
- { TPrintOut -------------------------------------------------------- }
-
- constructor TPrintOut.Init(ATitle: PChar);
- const
- Blank: array[0..0] of Char = '';
- var
- S: array[0..31] of Char;
- begin
- TObject.Init;
- if (ATitle = nil) or (ATitle^ = #0) then
- Title := @Blank
- else
- begin
- { Force the length to be 31 chars or less }
- StrLCopy(S, ATitle, SizeOf(S));
- Title := StrNew(S);
- end;
- Banding := False;
- ForceAllBands := True;
- end;
-
- destructor TPrintOut.Done;
- begin
- StrDispose(Title);
- TObject.Done;
- end;
-
- { This method is called before a document begins printing. It is
- called once for every copy of the document that is printed. The
- Flags parameter contains whether the selection is being printed
- and whether the document is going to be banded. }
-
- procedure TPrintOut.BeginDocument(StartPage, EndPage: Integer; Flag: Word);
- begin
- end;
-
- { Called at the beginning of printing. It is called once, regardless
- of how many copies of the document are being printed. }
-
- procedure TPrintOut.BeginPrinting;
- begin
- end;
-
- { Called after each copy of the document is printed. }
-
- procedure TPrintOut.EndDocument;
- begin
- end;
-
- { Called after all the copies of the documents are printed. }
-
- procedure TPrintOut.EndPrinting;
- begin
- end;
-
- { Get the information necessary to bring up the page range selection
- dialog. If this function returns true, the dialog will brought up.
- The pages value is optional, if the page count is easily caluclated
- return the number of pages in the doucment; otherwise, return 0 and
- no limit will be applied to the dialog. The document will stop
- printing when HasNextPage returns false. }
-
- function TPrintOut.GetDialogInfo(var Pages: Integer): Boolean;
- begin
- Pages := 0;
- GetDialogInfo := True;
- end;
-
- { Called to determine, first, if the document being printed has a
- selection and then what is it. If there is not a selection the
- selection radio button is disabled on the default print dialog. }
-
- function TPrintOut.GetSelection(var Start, Stop: Integer): Boolean;
- begin
- GetSelection := False;
- end;
-
- { Called after every page to determine if another page is ready to
- print. }
-
- function TPrintOut.HasNextPage(Page: Word): Boolean;
- begin
- HasNextPage := False;
- end;
-
- { Called to render the given page of the printout. The pages
- will come in order in the range passed to BeginDocument. The
- page might be called multiple time if banding is enabled. }
-
- procedure TPrintOut.PrintPage(Page: Word; var Rect: TRect; Flags: Word);
- begin
- Abstract;
- end;
-
- { Called to register the DC and page size with the object. This
- is the first method called after the object is passed to
- the Print method of a Printer object. If this method is
- overriden, the inherited function must be called. }
-
- procedure TPrintOut.SetPrintParams(ADC: HDC; ASize: TPoint);
- begin
- DC := ADC;
- Size := ASize;
- end;
-
- { FetchStr --------------------------------------------------------- }
- { Returns a pointer to the first comma delimited field pointed to }
- { by Str. It replaces the comma with a #0 and moves the Str to the }
- { beginning of the next string (skipping white space). Str will }
- { will point to a #0 character if no more strings are left. This }
- { routine is used to fetch strings out of text retrieved from }
- { WIN.INI. }
-
- function FetchStr(var Str: PChar): PChar;
- begin
- FetchStr := Str;
- if Str = nil then Exit;
- while (Str^ <> #0) and (Str^ <> ',') do
- Str := AnsiNext(Str);
- if Str^ = #0 then Exit;
- Str^ := #0;
- Inc(Str);
- while Str^ = ' ' do
- Str := AnsiNext(Str);
- end;
-
- { TReplaceStatic --------------------------------------------------- }
-
- type
- PReplaceStatic = ^TReplaceStatic;
- TReplaceStatic = object(TStatic)
- Text: PChar;
- constructor InitResource(AParent: PWindowsObject; ResourceID: Word;
- AText: PChar);
- destructor Done; virtual;
- procedure SetupWindow; virtual;
- end;
-
- constructor TReplaceStatic.InitResource(AParent: PWindowsObject; ResourceID: Word;
- AText: PChar);
- begin
- TStatic.InitResource(AParent, ResourceID, 0);
- Text := StrNew(AText);
- end;
-
- destructor TReplaceStatic.Done;
- begin
- StrDispose(Text);
- TStatic.Done;
- end;
-
- procedure TReplaceStatic.SetupWindow;
- var
- A: array[0..80] of Char;
- B: array[0..80] of Char;
- begin
- TStatic.SetupWindow;
- GetText(A, SizeOf(A) - 1);
- WVSPrintF(B, A, Text);
- SetText(B);
- end;
-
- { TPrinterAbortDlg ----------------------------------------------------- }
-
- constructor TPrinterAbortDlg.Init(AParent: PWindowsObject; Template,
- Title, Device, Port: PChar);
- var
- Tmp: PWindowsObject;
- begin
- TDialog.Init(AParent, Template);
- Tmp := New(PReplaceStatic, InitResource(@Self, id_Title, Title));
- Tmp := New(PReplaceStatic, InitResource(@Self, id_Device, Device));
- Tmp := New(PReplaceStatic, InitResource(@Self, id_Port, Port));
- end;
-
- procedure TPrinterAbortDlg.SetupWindow;
- begin
- TDialog.SetupWindow;
- EnableMenuItem(GetSystemMenu(HWindow, False), sc_Close, mf_Grayed);
- end;
-
- procedure TPrinterAbortDlg.WMCommand(var Msg: TMessage);
- begin
- UserAbort := True;
- end;
-
- { TPrinter --------------------------------------------------------- }
-
- { This object type is an ecapsulation around the Windows printer
- device interface. After the object is initialized the Status
- field must be check to see of the object was created correctly.
- Examples:
- Creating a default device printing object:
-
- DefaultPrinter := New(PPrinter, Init);
-
- Creating a device for a specific printer:
-
- PostScriptPrinter := New(PPrinter, Init);
- PostScriptPrinter^.SetDevice('PostScript Printer',
- 'PSCRIPT','LPT2:');
-
- Allowing the user to configure the printer:
-
- DefaultPrinter^.Configure(MyWindow);
- }
-
- { Initialize the TPrinter object assigned to the default printer }
-
- constructor TPrinter.Init;
- begin
- TObject.Init;
- Device := nil;
- Driver := nil;
- Port := nil;
- DeviceModule := 0;
- DevSettings := nil;
- Error := 0;
- SetDevice(nil, nil, nil); { Associate with default printer }
- end;
-
- { Deallocate allocated resources }
-
- destructor TPrinter.Done;
- begin
- ClearDevice;
- TObject.Done;
- end;
-
- { Clears the association of this object with the current device }
-
- procedure TPrinter.ClearDevice;
- begin
- StrDispose(Device); Device := nil;
- StrDispose(Driver); Driver := nil;
- StrDispose(Port); Port := nil;
- if DeviceModule >= 32 then
- begin
- FreeLibrary(DeviceModule);
- DeviceModule := 0;
- end;
- if DevSettings <> nil then
- FreeMem(DevSettings, DevSettingSize);
- Status := ps_Unassociated;
- end;
-
- { Associates the printer object with a new device. If the ADevice
- parameter is nil the Windows default printer is used, otherwise,
- the parameters must be ones contained in the [devices] section
- of the WIN.INI file. }
-
- procedure TPrinter.SetDevice(ADevice, ADriver, APort: PChar);
- var
- DriverName: array[0..80] of Char;
- DevModeSize: Integer;
- StubDevMode: TDevMode;
-
- procedure GetDefaultPrinter;
- var
- Printer: array[0..80] of Char;
- Cur: PChar;
-
- begin
- GetProfileString('windows', 'device', '', Printer,
- SizeOf(Printer) - 1);
- Cur := Printer;
- Device := StrNew(FetchStr(Cur));
- Driver := StrNew(FetchStr(Cur));
- Port := StrNew(FetchStr(Cur));
- end;
-
- function Equal(S1, S2: PChar): Boolean;
- begin
- Equal := (S1 <> nil) and (S2 <> nil) and
- (StrComp(S1, S2) = 0);
- end;
-
- begin
- if Equal(Device, ADevice) and Equal(Driver, ADriver) and
- Equal(Port, APort) then Exit;
- ClearDevice;
- if ADevice = nil then
- GetDefaultPrinter
- else
- begin
- Device := StrNew(ADevice);
- Driver := StrNew(ADriver);
- Port := StrNew(APort);
- end;
- if (Device = nil) or (Driver = nil) or (Port = nil) then
- begin
- Status := ps_Unassociated;
- Exit;
- end;
- Status := ps_Ok;
- StrLCopy(DriverName, Driver, SizeOf(DriverName) - 1);
- StrLCat(DriverName, '.DRV', SizeOf(DriverName) - 1);
- DeviceModule := LoadLibrary(DriverName);
- if DeviceModule < 32 then Status := ps_InvalidDevice
- else
- begin
- { Grab the DevMode procedures }
- @ExtDeviceMode := GetProcAddress(DeviceModule, 'ExtDeviceMode');
- @DeviceMode := GetProcAddress(DeviceModule, 'DeviceMode');
- if (@DeviceMode = nil) and (@ExtDeviceMode = nil) then
- Status := ps_InvalidDevice;
- if @ExtDeviceMode <> nil then
- begin
- { Get default printer settings }
- DevSettingSize := ExtDeviceMode(0, DeviceModule, StubDevMode,
- Device, Port, StubDevMode, nil, 0);
- GetMem(DevSettings, DevSettingSize);
- ExtDeviceMode(0, DeviceModule, DevSettings^, Device, Port,
- DevSettings^, nil, dm_Out_Buffer);
- end
- else
- DevSettings := nil; { Cannot use local settings }
- end;
- end;
-
- { Configure brings up a dialog as a child of the given window
- to configure the associated printer driver. }
-
- procedure TPrinter.Configure(Window: PWindowsObject);
- begin
- if Status = ps_Ok then
- if @ExtDeviceMode = nil then { driver is only supports DevMode }
- { If DeviceMode = nil, Status will <> ps_Ok }
- DeviceMode(Window^.HWindow, DeviceModule, Device, Port)
- else
- { Request driver to modify local copy of printer settings }
- ExtDeviceMode(Window^.HWindow, DeviceModule, DevSettings^, Device,
- Port, DevSettings^, nil, dm_In_Buffer or dm_Prompt or
- dm_Out_Buffer);
- end;
-
- { Returns a device context for the associated printer, 0 if an
- error occurs or Status is <> ps_Ok }
-
- function TPrinter.GetDC: HDC;
- begin
- if Status = ps_Ok then
- GetDC := CreateDC(Driver, Device, Port, DevSettings)
- else GetDC := 0;
- end;
-
- { Abort procedure used for printing }
- function AbortProc(Prn: HDC; Code: Integer): Boolean; export;
- var
- Msg: TMsg;
- begin
- while not UserAbort and PeekMessage(Msg, 0, 0, 0, pm_Remove) do
- if not Application^.ProcessAppMsg(Msg) then
- begin
- TranslateMessage(Msg);
- DispatchMessage(Msg);
- end;
- AbortProc := not UserAbort;
- end;
-
- function TPrinter.Print(ParentWin: PWindowsObject;
- PrintOut: PPrintOut): Boolean;
- type
- TAbortProc = function (Prn: HDC; Code: Integer): Boolean;
- var
- PageSize: TPoint;
- PrnDC: HDC;
- Pages: Integer;
- BandRect: TRect;
- Banding: Boolean;
- FirstBand: Boolean;
- Flags: Word;
- AbortProcInst: TFarProc;
- Dlg: PWindowsObject;
- UseBandInfo: Boolean;
- PageNumber: Word;
- PageRange: TPrintDialogRec;
- OldCursor: HCursor;
- Copies: Integer;
- SelStart, SelStop: Integer;
- UsePageRangeDlg: Boolean;
-
- procedure CalcBandingFlags;
- type
- TBandInfoStruct = record
- fGraphicsFlag: Bool;
- fTextFlag: Bool;
- GraphcisRect: TRect;
- end;
- var
- BandInfoRec: TBandInfoStruct;
- pFlags: Word;
- begin
- { Calculate text verses graphics banding }
- if UseBandInfo then
- begin
- Escape(PrnDC, BandInfo, SizeOf(TBandInfoStruct), nil, @BandInfoRec);
- if BandInfoRec.fGraphicsFlag then pFlags := pf_Graphics;
- if BandInfoRec.fTextFlag then pFlags := pf_Text;
- Flags := (Flags and not pf_Both) or pFlags;
- end
- else
- begin
- { If a driver does not support BandInfo the Microsoft
- Recommended way of determining text only bands is if
- the first band is the full page, all others are
- graphcis only. Otherwise it handles both. }
- if FirstBand and (LongInt((@BandRect.left)^) = 0)
- and (BandRect.right = PageSize.X) and
- (BandRect.bottom = PageSize.Y) then
- Flags := (Flags and not pf_Both) or pf_Text
- else
- if Flags and pf_Both = pf_Text then
- { All other bands are graphics only }
- Flags := (Flags and not pf_Both) or pf_Graphics
- else
- Flags := Flags or pf_Both;
- end;
-
- FirstBand := False;
- end;
-
- procedure WaitCursor;
- begin
- OldCursor := SetCursor(LoadCursor(0, idc_Wait));
- end;
-
- procedure RestoreCursor;
- begin
- SetCursor(OldCursor);
- end;
-
- begin
- Print := False; { Assume error occured }
-
- Error := 0;
-
- if PrintOut = nil then Exit;
- if ParentWin = nil then Exit;
-
- WaitCursor;
-
- PrnDC := GetDC;
- if PrnDC = 0 then Exit;
-
- { Get the page size }
- PageSize.X := GetDeviceCaps(PrnDC, HorzRes);
- PageSize.Y := GetDeviceCaps(PrnDC, VertRes);
-
- Printout^.SetPrintParams(PrnDC, PageSize);
- UsePageRangeDlg := Printout^.GetDialogInfo(Pages);
-
- with PageRange do
- begin
- drUseSelection := False;
- drStart := 1;
- if Pages = 0 then drStop := MaxInt
- else drStop := Pages;
- drCopies := 1;
- drCollate := True;
- end;
-
- if UsePageRangeDlg then
- begin
- if Application^.ExecDialog(InitPrintDialog(ParentWin, PrnDC, Pages,
- Printout^.GetSelection(SelStart, SelStop), PageRange)) <> id_OK then
- begin
- DeleteDC(PrnDC);
- Exit;
- end;
- end;
-
- if PageRange.drCollate then
- Copies := PageRange.drCopies
- else
- begin
- Flags := PageRange.drCopies;
- Escape(PrnDC, SetCopyCount, SizeOf(Flags), @Flags, nil);
- Copies := 1;
- end;
-
- with PageRange do
- if drUseSelection then
- begin
- drStart := SelStart;
- drStop := SelStop;
- end;
-
- Dlg := Application^.MakeWindow(InitAbortDialog(ParentWin,
- PrintOut^.Title));
-
- if Dlg = nil then
- begin
- DeleteDC(PrnDC);
- Exit;
- end;
-
- RestoreCursor;
-
- EnableWindow(ParentWin^.HWindow, False);
-
- AbortProcInst := MakeProcInstance(@AbortProc, hInstance);
- Escape(PrnDC, SetAbortProc, 0, PChar(AbortProcInst), nil);
-
- { Only band if the user requests banding and the printer
- supports banding }
- Banding := PrintOut^.Banding and
- (GetDeviceCaps(PrnDC, RasterCaps) or rc_Banding <> 0);
-
- if not Banding then
- begin
- { Set the banding rectangle to full page }
- LongInt((@BandRect.left)^) := 0;
- TPoint(Pointer(@BandRect.right)^) := PageSize;
- end
- else
- begin
- { Only use BandInfo if supported (note: using Flags as a temporary) }
- Flags := BandInfo;
- UseBandInfo :=
- Escape(PrnDC, QueryEscSupport, SizeOf(Flags), @Flags, nil) <> 0;
- end;
-
- Printout^.BeginPrinting;
-
- repeat
- Flags := pf_Both;
- if Banding then Flags := pf_Banding;
- if PageRange.drUseSelection then
- Flags := Flags or pf_Selection;
- Error := Escape(PrnDC, StartDoc, StrLen(PrintOut^.Title),
- PrintOut^.Title, nil);
- if Error > 0 then
- begin
- Printout^.BeginDocument(PageRange.drStart, PageRange.drStop,
- Flags);
- PageNumber := PageRange.drStart;
- repeat
- if Banding then
- begin
- FirstBand := True;
- Error := Escape(PrnDC, NextBand, 0, nil, @BandRect);
- end;
- repeat
- { Call the abort proc between bands or pages }
- TAbortProc(AbortProcInst)(PrnDC, 0);
-
- if Banding then
- begin
- CalcBandingFlags;
- if (PrintOut^.ForceAllBands) and
- (Flags and pf_Both = pf_Text) then
- SetPixel(PrnDC, 0, 0, 0);
- end;
-
- if Error > 0 then
- begin
- PrintOut^.PrintPage(PageNumber, BandRect, Flags);
- if Banding then
- Error := Escape(PrnDC, NextBand, 0, nil, @BandRect);
- end;
- until (Error <= 0) or not Banding or IsRectEmpty(BandRect);
-
- { NewFrame should only be called if not banding }
- if (Error > 0) and not Banding then
- Error := Escape(PrnDC, NewFrame, 0, nil, nil);
-
- Inc(PageNumber);
- until (Error <= 0) or not PrintOut^.HasNextPage(PageNumber) or
- (PageNumber > PageRange.drStop);
-
- Printout^.EndDocument;
-
- { Tell GDI the document is finished }
- if Error > 0 then
- if Banding and UserAbort then
- Escape(PrnDC, AbortDoc, 0, nil, nil)
- else
- Escape(PrnDC, EndDoc, 0, nil, nil);
- end;
- Dec(Copies);
- until (Copies = 0) or UserAbort;
-
- Printout^.EndPrinting;
-
- { Reset copies }
- if not PageRange.drCollate then
- begin
- Flags := 1;
- Escape(PrnDC, SetCopyCount, SizeOf(Flags), @Flags, nil);
- end;
-
- { Free allocated resources }
- FreeProcInstance(AbortProcInst);
- EnableWindow(ParentWin^.HWindow, True);
- Dispose(Dlg, Done);
- DeleteDC(PrnDC);
-
- if Error and sp_NotReported <> 0 then
- ReportError(PrintOut);
-
- Print := (Error > 0) and not UserAbort;
-
- UserAbort := False;
- end;
-
- function TPrinter.InitAbortDialog(Parent: PWindowsObject;
- Title: PChar): PDialog;
- var
- Dlg: PDialog;
- Template: PChar;
- begin
- if BWCCClassNames then Template := 'AbortDialogB'
- else Template := 'AbortDialog';
- InitAbortDialog := New(PPrinterAbortDlg, Init(Parent, Template, Title,
- Device, Port));
- end;
-
- function TPrinter.InitPrintDialog(Parent: PWindowsObject; PrnDC: HDC;
- Pages: Integer; SelAllowed: Boolean; var Data: TPrintDialogRec): PDialog;
- var
- Template: PChar;
- begin
- if BWCCClassNames then Template := 'PrintDialogB'
- else Template := 'PrintDialog';
- InitPrintDialog := New(PPrintDialog, Init(Parent, Template, PrnDC, Pages,
- @Self, SelAllowed, Data));
- end;
-
- function TPrinter.InitSetupDialog(Parent: PWindowsObject): PDialog;
- var
- Template: PChar;
- begin
- if BWCCClassNames then Template := 'PrinterSetupB'
- else Template := 'PrinterSetup';
- InitSetupDialog := New(PPrinterSetupDlg, Init(Parent, Template,
- @Self));
- end;
-
- procedure TPrinter.Setup(Parent: PWindowsObject);
- begin
- if Status = ps_Ok then
- Application^.ExecDialog(InitSetupDialog(Parent));
- end;
-
- procedure TPrinter.ReportError(PrintOut: PPrintOut);
- var
- ErrorMsg: array[0..80] of Char;
- ErrorCaption: array[0..80] of Char;
- ErrorTemplate: array[0..40] of Char;
- ErrorStr: array[0..40] of Char;
- ErrorId: Word;
- Msg, Title: PChar;
- begin
- case Error of
- sp_AppAbort: ErrorId := sr_PrnCancel;
- sp_Error: ErrorId := sr_GenError;
- sp_OutOfDisk: ErrorId := sr_OutOfDisk;
- sp_OutOfMemory: ErrorId := sr_OutOfMemory;
- sp_UserAbort: ErrorId := sr_PrnMgrAbort;
- else
- Exit;
- end;
-
- LoadString(hInstance, sr_ErrorTemplate, ErrorTemplate,
- SizeOf(ErrorTemplate));
- LoadString(hInstance, ErrorId, ErrorStr, SizeOf(ErrorStr));
- Title := PrintOut^.Title;
- Msg := ErrorStr;
- WVSPrintF(ErrorMsg, ErrorTemplate, Title);
- LoadString(hInstance, sr_ErrorCaption, ErrorCaption,
- SizeOf(ErrorCaption));
- MessageBox(0, ErrorMsg, ErrorCaption, mb_Ok or mb_IconStop);
- end;
-
- { TPrinterSetupDlg ------------------------------------------------- }
-
- { TPrinterSetupDlg assumes the template passed has a ComboBox with
- the control ID of 100 and a "Setup" button with id 101 }
-
- const
- pdStrWidth = 80;
-
- type
- PTransferRec = ^TTransferRec;
- TTransferRec = record
- Strings: PCollection;
- Selected: array[0..0] of Char;
- end;
-
- PDeviceRec = ^TDeviceRec;
- TDeviceRec = record
- Driver, Device, Port: PChar;
- end;
-
- PDeviceCollection = ^TDeviceCollection;
- TDeviceCollection = object(TCollection)
- procedure FreeItem(P: Pointer); virtual;
- end;
-
- procedure TDeviceCollection.FreeItem(P: Pointer);
- begin
- with PDeviceRec(P)^ do
- begin
- StrDispose(Driver);
- StrDispose(Device);
- StrDispose(Port);
- end;
- Dispose(PDeviceRec(P));
- end;
-
- constructor TPrinterSetupDlg.Init(AParent: PWindowsObject;
- TemplateName: PChar; APrinter: PPrinter);
- var
- tmp: PComboBox;
- Devices, { List of devices from the
- WIN.INI }
- Device: PChar; { Current device }
- DevicesSize: Integer; { Amount of bytes allocated
- to store 'devices' }
- Driver, { Name of the driver for the
- device }
- Port: PChar; { Name of the port for the
- device }
- DriverLine: array[0..pdStrWidth] of Char; { Device line from WIN.INI }
- LineCur: PChar; { FetchStr pointer into
- DriverLine }
- DriverStr: array[0..pdStrWidth] of Char; { Text being built for display }
- StrCur: PChar; { Temp pointer used for copying
- Port into the line }
- StrCurSize: Integer; { Room left in DriverStr to
- copy Port }
- DevRec: PDeviceRec; { Record pointer built to
- store in DeviceCollection }
- begin
- TDialog.Init(AParent, TemplateName);
- tmp := New(PComboBox, InitResource(@Self, id_Combo, pdStrWidth));
- GetMem(TransferBuffer, SizeOf(PCollection) + pdStrWidth);
- PTransferRec(TransferBuffer)^.Strings := New(PStrCollection,
- Init(5, 5));
- Printer := APrinter;
- DeviceCollection := New(PDeviceCollection, Init(5, 5));
-
- if MaxAvail div 2 > 4096 then DevicesSize := 4096
- else DevicesSize := MaxAvail div 2;
- GetMem(Devices, DevicesSize);
-
- { Save initial values of printer for Cancel }
- OldDevice := StrNew(Printer^.Device);
- OldDriver := StrNew(Printer^.Driver);
- OldPort := StrNew(Printer^.Port);
-
- with PTransferRec(TransferBuffer)^ do
- begin
- { Get a list of devices from WIN.INI. Stored in the form of
- <device 1>#0<device 2>#0...<driver n>#0#0
- }
- GetProfileString('devices', nil, '', Devices, DevicesSize);
-
- Device := Devices;
- while Device^ <> #0 do
- begin
- GetProfileString('devices', Device, '', DriverLine,
- SizeOf(DriverLine) - 1);
-
- FormDriverStr(DriverStr, SizeOf(DriverStr) - 1,Device, '');
-
- { Get driver portion of DeviceLine }
- LineCur := DriverLine;
- Driver := FetchStr(LineCur);
-
- { Copy the port information from the line }
- (* This code is complicated because the device line is of
- the form:
- <device name> = <driver name> , <port> { , <port> }
- where port (in {}) can be repeated. *)
-
- StrCur := @DriverStr[StrLen(DriverStr)];
- StrCurSize := SizeOf(DriverStr) - StrLen(DriverStr) - 1;
- Port := FetchStr(LineCur);
- while Port^ <> #0 do
- begin
- StrLCopy(StrCur, Port, StrCurSize);
- Strings^.Insert(StrNew(DriverStr));
- New(DevRec);
- DevRec^.Device := StrNew(Device);
- DevRec^.Driver := StrNew(Driver);
- DevRec^.Port := StrNew(Port);
- DeviceCollection^.AtInsert(Strings^.IndexOf(@DriverStr), DevRec);
- Port := FetchStr(LineCur);
- end;
- Inc(Device, StrLen(Device) + 1);
- end;
- FreeMem(Devices, DevicesSize);
-
- { Set the current selection to Printer's current device }
- FormDriverStr(Selected, pdStrWidth, Printer^.Device, Printer^.Port);
- end;
- end;
-
- destructor TPrinterSetupDlg.Done;
- begin
- StrDispose(OldDevice);
- StrDispose(OldDriver);
- StrDispose(OldPort);
- Dispose(DeviceCollection, Done);
- Dispose(PTransferRec(TransferBuffer)^.Strings, Done);
- FreeMem(TransferBuffer, SizeOf(PCollection) + pdStrWidth);
- TDialog.Done;
- end;
-
- procedure TPrinterSetupDlg.TransferData(TransferFlag: Word);
- var
- DevRec: PDeviceRec;
- begin
- TDialog.TransferData(TransferFlag);
- if TransferFlag = tf_GetData then
- with PTransferRec(TransferBuffer)^ do
- { Use the current selection to set Printer }
- with PDeviceRec(DeviceCollection^.At(Strings^.IndexOf(@Selected)))^ do
- { Set the printer to the new device }
- Printer^.SetDevice(Device, Driver, Port);
- end;
-
- procedure TPrinterSetupDlg.IDSetup(var Msg: TMessage);
- begin
- TransferData(tf_GetData);
- Printer^.Configure(@Self);
- end;
-
- procedure TPrinterSetupDlg.Cancel(var Msg: TMessage);
- begin
- TDialog.Cancel(Msg);
- { Restore old settings, just in case the user pressed the Setup button }
- if OldDriver = nil then Printer^.ClearDevice
- else Printer^.SetDevice(OldDevice, OldDriver, OldPort);
- end;
-
- { TNumeric }
-
- type
- PNumeric = ^TNumeric;
- TNumeric = object(TEdit)
- Min, Max: LongInt;
- constructor Init(AParent: PWindowsObject; AnId, X, Y, W, H: Integer;
- AMin, AMax: Integer; Digits: Integer);
- constructor InitResource(AParent: PWindowsObject; Id: Integer;
- AMin, AMax: Integer; Digits: Integer);
- function CanClose: Boolean; virtual;
- function GetValue(var Value: Integer): Boolean;
- procedure SetRange(AMin, AMax: Integer);
- procedure SetValue(Value: Integer);
- procedure WMChar(var Msg: TMessage);
- virtual wm_First + wm_Char;
- end;
-
- constructor TNumeric.Init(AParent: PWindowsObject; AnId, X, Y, W,
- H: Integer; AMin, AMax: Integer; Digits: Integer);
- begin
- TEdit.Init(AParent, AnId, '', X, Y, W, H, Digits + 1, False);
- Min := AMin;
- Max := AMax;
- end;
-
- constructor TNumeric.InitResource(AParent: PWindowsObject; Id: Integer;
- AMin, AMax: Integer; Digits: Integer);
- begin
- TEdit.InitResource(AParent, Id, Digits + 1);
- Min := AMin;
- Max := AMax;
- end;
-
- function TNumeric.CanClose: Boolean;
- var
- Value: Integer;
- Valid: Boolean;
- Text: array[0..255] of Char;
- P: array[0..1] of LongInt;
- begin
- Valid := not IsWindowEnabled(HWindow) or
- (GetValue(Value) and (Value >= Min) and (Value <= Max));
- if not Valid then
- begin
- P[0] := Min;
- P[1] := Max;
- WVSPrintF(Text, 'Value not within range (%ld-%ld).', P);
- MessageBox(HWindow, Text, 'Invalid Range', mb_IconStop or mb_Ok);
- SetSelection(0, MaxInt);
- SetFocus(HWindow);
- end;
- CanClose := Valid;
- end;
-
- function TNumeric.GetValue(var Value: Integer): Boolean;
- var
- Text: array[0..255] of Char;
- Code: Integer;
- begin
- GetText(Text, SizeOf(Text));
- Val(Text, Value, Code);
- GetValue := Code = 0;
- end;
-
- procedure TNumeric.SetRange(AMin, AMax: Integer);
- begin
- Min := AMin;
- Max := AMax;
- end;
-
- procedure TNumeric.SetValue(Value: Integer);
- var
- Text: array[0..20] of Char;
- begin
- Str(Value, Text);
- SetText(Text);
- end;
-
- procedure TNumeric.WMChar(var Msg: TMessage);
- begin
- if not (Char(Msg.wParamLo) in ['A'..'Z','a'..'z',',','.','<','>',
- '/','?','~','`','!','@','#','$','%','^','&','*','(',')','_','=',
- '{','}','[',']','|','\',';',':','"']) then
- DefWndProc(Msg)
- else MessageBeep(0);
- end;
-
- { TSelRadio }
-
- type
- PSelRadio = ^TSelRadio;
- TSelRadio = object(TRadioButton)
- Enbl: Boolean;
- Controls: PCollection;
- constructor InitResource(AParent: PWindowsObject; ResourceID: Word;
- AEnbl: Boolean; AControls: PCollection);
- procedure BNClicked(var Msg: TMessage);
- virtual nf_First + bn_Clicked;
- end;
-
- constructor TSelRadio.InitResource(AParent: PWindowsObject;
- ResourceID: Word; AEnbl: Boolean; AControls: PCollection);
- begin
- TRadioButton.InitResource(AParent, ResourceId);
- Enbl := AEnbl;
- Controls := AControls;
- end;
-
- { Assumes the Controls collection contains PWindowsObjects }
-
- procedure TSelRadio.BNClicked(var Msg: TMessage);
-
- procedure DoEnableDisable(P: PWindowsObject); far;
- begin
- if Enbl then P^.Enable else P^.Disable;
- end;
-
- begin
- TRadioButton.BNClicked(Msg);
- Controls^.ForEach(@DoEnableDisable);
- if Enbl then PWindowsObject(Controls^.At(0))^.Focus;
- end;
-
- { TPrintDialog }
-
- constructor TPrintDialog.Init(AParent: PWindowsObject; Template: PChar;
- APrnDC: HDC; APages: Integer; APrinter: PPrinter; ASelAllowed: Boolean;
- var Data: TPrintDialogRec);
- var
- P: PWindowsObject;
-
- function QLog10(X: Integer): Integer;
- var
- I, L: Integer;
- begin
- I := 1;
- L := 0;
- if X >= 10000 then QLog10 := 5
- else
- begin
- repeat
- I := I * 10;
- Inc(L);
- until I > X;
- QLog10 := L;
- end;
- end;
-
- begin
- TDialog.Init(AParent, Template);
- Printer := APrinter;
- PData := @Data;
- PrnDC := APrnDC;
- Pages := APages;
- SelAllowed := ASelAllowed;
-
- PrinterName := New(PStatic, InitResource(@Self, id_PrinterName, 0));
- Controls := New(PCollection, Init(4, 4));
- if Pages <> 0 then
- begin
- FromPage := New(PNumeric, InitResource(@Self, id_From, 1, Pages,
- QLog10(Pages)));
- ToPage := New(PNumeric, InitResource(@Self, id_To, 1, Pages,
- QLog10(Pages)));
- end
- else
- begin
- FromPage := New(PNumeric, InitResource(@Self, id_From, 1, 32767, 0));
- ToPage := New(PNumeric, InitResource(@Self, id_To, 1, 32767, 0));
- end;
- Controls^.Insert(FromPage);
- Controls^.Insert(ToPage);
- Controls^.Insert(New(PStatic, InitResource(@Self, id_FromText, 0)));
- Controls^.Insert(New(PStatic, InitResource(@Self, id_ToText, 0)));
- AllBtn := New(PSelRadio, InitResource(@Self, id_All, False, Controls));
- SelectBtn := New(PSelRadio, InitResource(@Self, id_Selection, False,
- Controls));
- PageBtn := New(PSelRadio, InitResource(@Self, id_Pages, True, Controls));
- Copies := New(PNumeric, InitResource(@Self, id_Copies, 1, 999, 3));
- Collate := New(PCheckBox, InitResource(@Self, id_Collate));
- end;
-
- procedure TPrintDialog.SetupWindow;
- var
- NameText: array[0..80] of Char;
- begin
- TDialog.SetupWindow;
- with Printer^ do
- FormDriverStr(NameText, SizeOf(NameText), Device, Port);
- PrinterName^.SetText(NameText);
- end;
-
- procedure TPrintDialog.TransferData(Direction: Word);
- var
- Esc: Integer;
- Val: LongInt;
- Msg: TMessage;
- begin
- case Direction of
- tf_SetData:
- with PData^ do
- begin
- Collate^.SetCheck(Word(drCollate));
- Esc := SetCopyCount;
- if Escape(PrnDC, QueryEscSupport, SizeOf(Esc), @Esc, @Esc) = 0 then
- Collate^.Disable;
- PNumeric(Copies)^.SetValue(drCopies);
- AllBtn^.SetCheck(bf_Checked);
- AllBtn^.BNClicked(Msg);
- if not SelAllowed then SelectBtn^.Disable;
- if Pages = 1 then
- PageBtn^.Disable
- else
- begin
- if Pages <> 0 then
- begin
- PNumeric(FromPage)^.SetValue(drStart);
- PNumeric(ToPage)^.SetValue(drStop);
- end;
- end;
- end;
- tf_GetData:
- with PData^ do
- begin
- drCollate := Boolean(Collate^.GetCheck);
- PNumeric(Copies)^.GetValue(drCopies);
- if SelectBtn^.GetCheck = bf_Checked then
- drUseSelection := True
- else
- begin
- drUseSelection := False;
-
- if PageBtn^.GetCheck = bf_Checked then
- begin
- PNumeric(FromPage)^.GetValue(drStart);
- PNumeric(ToPage)^.GetValue(drStop);
- end;
- end;
- end;
- end;
- end;
-
- procedure TPrintDialog.IDSetup(var Msg: TMessage);
- begin
- Printer^.Configure(@Self);
- end;
-
- { TEditPrintout }
-
- { This object will print-out the contents of a TEdit control }
-
- constructor TEditPrintout.Init(AEditor: PEdit; ATitle: PChar);
- begin
- TPrintout.Init(ATitle);
- Editor := AEditor;
-
- { The following are calculated by SetPrintParams which is called
- before any other methods are called. }
- LinesPerPage := 0;
- NumLines := 0;
- LineHeight := 0;
- StartPos := 0;
- StopPos := 0;
- StartLine := 0;
- StopLine := 0;
- end;
-
- procedure TEditPrintout.BeginDocument(StartPage, EndPage: Integer;
- Flags: Word);
- begin
- if Flags and pf_Selection = 0 then
- begin
- { not using the selection, print everything }
- StartLine := 0;
- StopLine := NumLines - 1;
- StartPos := 0;
- StopPos := 32767;
- end; { else leave values set by GetSelection }
- end;
-
- function TEditPrintout.GetSelection(var Start, Stop: Integer): Boolean;
- begin
- Editor^.GetSelection(StartPos, StopPos);
- if StartPos = StopPos then GetSelection := False
- else
- begin
- with Editor^ do
- begin
- StartLine := GetLineFromPos(StartPos);
- StopLine := GetLineFromPos(StopPos);
- Start := 1;
- Stop := (StopLine - StartLine) div LinesPerPage + 1;
- end;
- GetSelection := True;
- end;
- end;
-
- function TEditPrintout.GetDialogInfo(var Pages: Integer): Boolean;
- begin
- Pages := NumLines div LinesPerPage + 1;
- GetDialogInfo := True;
- end;
-
- procedure TEditPrintout.PrintPage(Page: Word; var Rect: TRect; Flags: Word);
- var
- LineBuffer: array[0..255] of Char;
- I: Integer;
- FirstLine: Integer;
- CurLine: Integer;
- Len: Integer;
- XOff: Integer;
- begin
- FirstLine := StartLine + (Page - 1) * LinesPerPage;
- for I := 0 to LinesPerPage - 1 do
- begin
- CurLine := I + FirstLine;
- if CurLine > StopLine then Exit;
-
- XOff := 0;
-
- with Editor^ do
- begin
- GetLine(LineBuffer, SizeOf(LineBuffer), CurLine);
-
- { Order of these next lines is important since the beginning
- and ending selections can be on the same line. We don't want
- to move the text before we have cut off the end of the text. }
- if (CurLine = StopLine) and (StopPos < StrLen(LineBuffer)) then
- LineBuffer[StopPos - GetLineIndex(CurLine)] := #0;
- if CurLine = StartLine then
- begin
- Len := StartPos - GetLineIndex(CurLine);
- XOff := GetTextExtent(DC, LineBuffer, Len);
- StrCopy(LineBuffer, @LineBuffer[Len]);
- end;
- end;
- TextOut(DC, XOff, I * LineHeight, LineBuffer, StrLen(LineBuffer));
- end;
- end;
-
- function TEditPrintout.HasNextPage(Page: Word): Boolean;
- begin
- { Always a next page. Will never be asked for a page beyond what
- is calculated by Paginate }
- HasNextPage := True;
- end;
-
- procedure TEditPrintout.SetPrintParams(ADC: HDC; ASize: TPoint);
- var
- TextMetrics: TTextMetric;
- begin
- TPrintout.SetPrintParams(ADC, ASize);
- NumLines := Editor^.GetNumLines;
- GetTextMetrics(DC, TextMetrics);
- with TextMetrics do
- LineHeight := tmHeight + tmExternalLeading;
- LinesPerPage := Size.Y div LineHeight;
- end;
-
- { TWindowPrintout }
-
- constructor TWindowPrintout.Init(ATitle: PChar; AWindow: PWindow);
- begin
- TPrintOut.Init(ATitle);
- Window := AWindow;
- Scale := True;
- end;
-
- procedure TWindowPrintout.PrintPage(Page: Word; var Rect: TRect;
- Flags: Word);
- var
- PS: TPaintStruct;
- PrevMode: Integer;
- WindowSize: TRect;
- OldVExt, OldWExt: LongInt;
- begin
-
- { Fake up a TPaintStruct to give the window banding information }
- with PS do
- begin
- rcPaint := Rect;
- fErase := False;
- fRestore := False;
- end;
-
- { Conditionally scale the DC to the window so the printout will
- resemble the window }
- if Scale then
- begin
- PrevMode := SetMapMode(DC, mm_Isotropic);
- GetClientRect(Window^.HWindow, WindowSize);
- OldVExt := SetViewportExt(DC, Size.X, Size.Y);
- with WindowSize do
- begin
- OldWExt := SetWindowExt(DC, right - left, bottom - top);
- IntersectClipRect(DC, left, top, right, bottom);
- end;
- DPtoLP(DC, PS.rcPaint, 2);
- end;
-
- { Call the window to paint itself }
- Window^.Paint(DC, PS);
-
- { Remove changes made to the DC }
- if Scale then
- begin
- SetWindowExt(DC, TPoint(OldWExt).X, TPoint(OldWExt).Y);
- SetViewportExt(DC, TPoint(OldVExt).X, TPoint(OldVExt).Y);
- SetMapMode(DC, PrevMode);
- end;
- end;
-
- { Do not bring up the print dialog since only one page is to be printed }
-
- function TWindowPrintout.GetDialogInfo(var Pages: Integer): Boolean;
- begin
- Pages := 0;
- GetDialogInfo := False;
- end;
-
- end.
-